在 C 中使用函数时将参数传递给 scipy.LowLevelCallable
Passing arguments to scipy.LowLevelCallable while using functions in C
我正在尝试使用 C 定义的函数在 SciPy 中进行数值积分。给出的示例案例
here (SciPy documentation) 工作正常。
在我的例子中,testlib.c
文件是
/* testlib.c */
#include <math.h>
#define PI 3.14159265358979323846
double factor(double phi, double r) {
double val = (2*PI)/(pow(r, 2) + 3*cos(phi));
return val;
}
//------------------------------------
double f2(int n, double *x, void *user_data) {
double c = *(double *)user_data;
double v1 = factor(c, 0.25); // value of phi defined inline but it is an argument
return v1 + x[0] - x[1] ; /* corresponds to v1 + x - y */
}
并且,调用编译后得到的testlib.so
文件的test.py函数如下,
import os, ctypes
from scipy import integrate, LowLevelCallable
lib = ctypes.CDLL(os.path.abspath('testlib.so'))
# define return type in .restype
lib.f2.restype = ctypes.c_double
# define argument type in .argtypes
lib.f2.argtypes = (ctypes.c_int, ctypes.POINTER(ctypes.c_double), ctypes.c_void_p)
# additional argument, here a constant, casting needed
c = ctypes.c_double(1.0)
user_data = ctypes.cast(ctypes.pointer(c), ctypes.c_void_p)
# pass extra argument
func = LowLevelCallable(lib.f2, user_data)
# quadrature in 3-dimensions
out=integrate.nquad(func, [[0, 10], [-10, 0]])
print(out)
# -----------------------------------------------
phi = 0.25 # How to pass these to the C function
esv = 1.25
cv1 = 0.03
cv2 = -0.15
cv3 = 3.0
我的问题:如何将额外的参数如c
传递给函数f2
。在我的例子中,我有 5 个这样的参数,在调用 py 文件中作为 np.float64
可用。
我想知道我是否可以将参数作为数组作为 user_data
传递给函数 f2
。
从documentation for nquad
,发现arguments要以数组的形式传递而C函数中的int n
是传递的arguments个数
此外,我愿意尝试其他选项,例如 cython、pyCapsule,但没有这方面的经验。使用 numba 和 jit 发现了非常相似的问题,其中没有传递额外的参数。
用于编译 testlib.c
:$ gcc -shared -fPIC -o testlib.so testlib.c
给猫剥皮的方法不止一种,但如果您使用的是 ctypes
,则可以坚持使用 ctypes
,例如:
您可以创建一个数组并用值对其进行初始化,例如:
ptr_to_buffer=(ctypes.c_double*5)(phi,esv,cv1,cv2,cv3)
user_data = ctypes.cast(ptr_to_buffer, ctypes.c_void_p)
或者如果数据已经在一个 numpy 数组中(正如我最初理解你的问题):
import numpy as np
a=np.array([1.0, 2.0, 3.0, 4.0, 5.0], np.float64)
import ctypes
ptr_to_buffer=(ctypes.c_double*5).from_buffer(a)
user_data = ctypes.cast(ptr_to_buffer, ctypes.c_void_p)
在这种情况下 user_data
是 a 的副本并且不共享内存,这有时是件好事,有时则不是。
对于更大的数组,也可以让 user_data
也与 numpy 数组共享内存:
user_data = ctypes.c_void_p(a.__array_interface__['data'][0])
可以通过以下方式验证:
ctypes.cast(user_data, ctypes.POINTER(ctypes.c_double))[0] = 42.0
print(a[0])
# 42.0 and not 1.0
对于这个变体,你实际上必须检查,numpy 数组的内存是连续的,例如如何获得这个信息可以在 numpy.ctypeslib.as_ctypes
.[=21 中查找=]
也许获取指针的低级方法是
user_data = ctypes.cast(np.ctypeslib.as_ctypes(a), ctypes.c_void_p)
但仍然需要检查 shape/strides。
我正在尝试使用 C 定义的函数在 SciPy 中进行数值积分。给出的示例案例 here (SciPy documentation) 工作正常。
在我的例子中,testlib.c
文件是
/* testlib.c */
#include <math.h>
#define PI 3.14159265358979323846
double factor(double phi, double r) {
double val = (2*PI)/(pow(r, 2) + 3*cos(phi));
return val;
}
//------------------------------------
double f2(int n, double *x, void *user_data) {
double c = *(double *)user_data;
double v1 = factor(c, 0.25); // value of phi defined inline but it is an argument
return v1 + x[0] - x[1] ; /* corresponds to v1 + x - y */
}
并且,调用编译后得到的testlib.so
文件的test.py函数如下,
import os, ctypes
from scipy import integrate, LowLevelCallable
lib = ctypes.CDLL(os.path.abspath('testlib.so'))
# define return type in .restype
lib.f2.restype = ctypes.c_double
# define argument type in .argtypes
lib.f2.argtypes = (ctypes.c_int, ctypes.POINTER(ctypes.c_double), ctypes.c_void_p)
# additional argument, here a constant, casting needed
c = ctypes.c_double(1.0)
user_data = ctypes.cast(ctypes.pointer(c), ctypes.c_void_p)
# pass extra argument
func = LowLevelCallable(lib.f2, user_data)
# quadrature in 3-dimensions
out=integrate.nquad(func, [[0, 10], [-10, 0]])
print(out)
# -----------------------------------------------
phi = 0.25 # How to pass these to the C function
esv = 1.25
cv1 = 0.03
cv2 = -0.15
cv3 = 3.0
我的问题:如何将额外的参数如c
传递给函数f2
。在我的例子中,我有 5 个这样的参数,在调用 py 文件中作为 np.float64
可用。
我想知道我是否可以将参数作为数组作为 user_data
传递给函数 f2
。
从documentation for
nquad
,发现arguments要以数组的形式传递而C函数中的int n
是传递的arguments个数此外,我愿意尝试其他选项,例如 cython、pyCapsule,但没有这方面的经验。使用 numba 和 jit 发现了非常相似的问题,其中没有传递额外的参数。
用于编译 testlib.c
:$ gcc -shared -fPIC -o testlib.so testlib.c
给猫剥皮的方法不止一种,但如果您使用的是 ctypes
,则可以坚持使用 ctypes
,例如:
您可以创建一个数组并用值对其进行初始化,例如:
ptr_to_buffer=(ctypes.c_double*5)(phi,esv,cv1,cv2,cv3)
user_data = ctypes.cast(ptr_to_buffer, ctypes.c_void_p)
或者如果数据已经在一个 numpy 数组中(正如我最初理解你的问题):
import numpy as np
a=np.array([1.0, 2.0, 3.0, 4.0, 5.0], np.float64)
import ctypes
ptr_to_buffer=(ctypes.c_double*5).from_buffer(a)
user_data = ctypes.cast(ptr_to_buffer, ctypes.c_void_p)
在这种情况下 user_data
是 a 的副本并且不共享内存,这有时是件好事,有时则不是。
对于更大的数组,也可以让 user_data
也与 numpy 数组共享内存:
user_data = ctypes.c_void_p(a.__array_interface__['data'][0])
可以通过以下方式验证:
ctypes.cast(user_data, ctypes.POINTER(ctypes.c_double))[0] = 42.0
print(a[0])
# 42.0 and not 1.0
对于这个变体,你实际上必须检查,numpy 数组的内存是连续的,例如如何获得这个信息可以在 numpy.ctypeslib.as_ctypes
.[=21 中查找=]
也许获取指针的低级方法是
user_data = ctypes.cast(np.ctypeslib.as_ctypes(a), ctypes.c_void_p)
但仍然需要检查 shape/strides。