使用 python 函数作为参数调用 Cython(C) 函数
我想将本地定义的 python 函数与 gsl 库集成。 为此,我使用 Cython 实现了以下代码(以 Gauss-Legendre 求积为例):
pxd 文件:
cdef extern from "gsl/gsl_math.h":
ctypedef struct gsl_function:
double (* function) (double x, void * params) nogil
void * params
cdef extern from "gsl/gsl_integration.h":
gsl_integration_glfixed_table * gsl_integration_glfixed_table_alloc(size_t n) nogil
double gsl_integration_glfixed(gsl_function *f, double a, double b, gsl_integration_glfixed_table * t) nogil
void gsl_integration_glfixed_table_free(gsl_integration_glfixed_table *t) nogil
cdef double int_gsl_GaussLegendre(double func(double, void *) nogil, void * p, double xmin, double xmax) nogil
和 pyx 文件:
cdef size_t size_GL=1000
cdef double int_gsl_GaussLegendre(double func(double, void *) nogil, void * p, double xmin, double xmax) nogil:
cdef double result, error;
cdef gsl_integration_glfixed_table * W
cdef gsl_function F
W = gsl_integration_glfixed_table_alloc(size_GL)
F.function = func
F.params = p
result = gsl_integration_glfixed(&F, xmin, xmax, W)
gsl_integration_glfixed_table_free(W)
return result
此代码适用于我的 Cython 代码中声明的任何 C 函数。当然,当我传递一个 python 函数作为参数时,这会失败。 我在 python 中的脚本:
def gsl_integral(py_func, xmin, xmax, args=()):
cdef size_t sizep = <int>(args.size)
cdef double[:] params = np.empty(sizep, dtype=np.double)
for i in range(0,sizep):
params[i]=args[i]
cdef gsl_function F
F.function = py_func
F.params = params
返回:“无法将 Python 对象转换为'double (*)(double, void *) nogil'”
如果我改用:
def gsl_integral(py_func, xmin, xmax, args=()):
cdef size_t sizep = <int>(args.size)
cdef double[:] params = np.empty(sizep, dtype=np.double)
for i in range(0,sizep):
params[i]=args[i]
cdef gsl_function F
F.function = <double *>py_func
F.params = params
返回:“Python 对象不能转换为原始类型的指针”
我有看到我可以将我的 cython 函数包装到一个类中(将 c 函数作为参数传递给 cython 中的 python 函数 ),但我不太确定在这种情况下该怎么做(加上这个例子对我不起作用。)作为一种解决方法,我一直传递给 Cython 两个数组 x 和 f(x),后者估计为我的本地 python f,为了定义一个 gsl 样条线,我可以稍后集成,但这一点也不优雅。 还有其他办法吗? 我想在没有 GIL 的情况下使用 GSL 集成
非常感谢, 罗曼
I would like to integrate locally defined python functions with the gsl libraries.
To do that, i have implemented the following code with Cython (example with Gauss-Legendre quadrature) :
pxd file :
cdef extern from "gsl/gsl_math.h":
ctypedef struct gsl_function:
double (* function) (double x, void * params) nogil
void * params
cdef extern from "gsl/gsl_integration.h":
gsl_integration_glfixed_table * gsl_integration_glfixed_table_alloc(size_t n) nogil
double gsl_integration_glfixed(gsl_function *f, double a, double b, gsl_integration_glfixed_table * t) nogil
void gsl_integration_glfixed_table_free(gsl_integration_glfixed_table *t) nogil
cdef double int_gsl_GaussLegendre(double func(double, void *) nogil, void * p, double xmin, double xmax) nogil
and the pyx file :
cdef size_t size_GL=1000
cdef double int_gsl_GaussLegendre(double func(double, void *) nogil, void * p, double xmin, double xmax) nogil:
cdef double result, error;
cdef gsl_integration_glfixed_table * W
cdef gsl_function F
W = gsl_integration_glfixed_table_alloc(size_GL)
F.function = func
F.params = p
result = gsl_integration_glfixed(&F, xmin, xmax, W)
gsl_integration_glfixed_table_free(W)
return result
This code work for any C function declared within my Cython code. Of course, this will fail when i pass as argument a python function.
My script in python :
def gsl_integral(py_func, xmin, xmax, args=()):
cdef size_t sizep = <int>(args.size)
cdef double[:] params = np.empty(sizep, dtype=np.double)
for i in range(0,sizep):
params[i]=args[i]
cdef gsl_function F
F.function = py_func
F.params = params
which return : "Cannot convert Python object to 'double (*)(double, void *) nogil'"
If i use instead :
def gsl_integral(py_func, xmin, xmax, args=()):
cdef size_t sizep = <int>(args.size)
cdef double[:] params = np.empty(sizep, dtype=np.double)
for i in range(0,sizep):
params[i]=args[i]
cdef gsl_function F
F.function = <double *>py_func
F.params = params
which return: "Python objects cannot be cast to pointers of primitive types"
I have seen that i could wrap my cython function into a class (Pass c function as argument to python function in cython), but i'm not quite sure to understand how to do it in this situation (plus the example doesn't work me.) As a workaround, i have been passing to Cython two array, x and f(x), the latter estimated with my local python f, in order to define a gsl spline that i can latter integrate, but this not elegant at all.
Is there any other ways?
I would like to use GSL integration without the GIL
Many thanks,
Romain
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的基本前提存在一些基本问题:
此答案概述了您的选项(C++
std::function
选项此处不适用) 。本质上,您可以ctypes
从 Python 函数生成函数指针(它在运行时通过 hacky 代码生成来实现这一点 - 这在标准 C 中是不可能的),cdef
函数具有适当的签名作为函数指针传递,并将 Python 可调用函数作为void*
参数的一部分传递给该函数。我推荐后者,但这都不是一个很好的选择。
There's a few fundamental issues with your basic premise:
This answer outlines your options (the C++
std::function
option doesn't apply here). Essentially you can eitherctypes
to generate a function pointer from the Python function (it does this with hacky code-generation at runtime - it isn't possible in standard C),cdef
function with an appropriate signature to pass as the function pointer, and pass your Python callable to that as part of thevoid*
params.I recommend the latter, but neither is a great options.