我可以通过包装或cys重新声明gsl_function来为gsl_function结构分配一个cython类方法吗?

时间:2015-10-02 21:00:34

标签: python oop wrapper cython gsl

这个问题建立在here

之上

我有以下cython代码导入文件(cGslInteg.pxd),其中包含gsl/gsl_integration.hgsl/gsl_math.h头文件的相关部分的cython声明,然后定义要计算的类积分:

from libc.math cimport log
from libc.math cimport sqrt
from cGslInteg cimport *

ctypedef double * double_ptr

cdef double integrand(double x, void * params):
    """integrand implemented outside class """

    cdef double alpha = (<double_ptr> params)[0]
    cdef double f = log(alpha*x) / sqrt(x)
    return f


cdef class CalcSomething(object):

    def integrate(self, double a, double b, double alpha):
        """This does the integral"""

        cdef double result, error

        cdef gsl_integration_workspace * W
        W = gsl_integration_workspace_alloc(1000)

        cdef gsl_function F

        F.function = &integrand          # works 
        # F.function = &self._integrand  # doesn't work

        cdef double params[1]
        params[0] = alpha
        F.params = &params

        cdef double epsabs = 0.
        cdef double epsrel = 1e-7
        gsl_integration_qags(&F, a, b, epsabs, epsrel, 1000, W, &result, &error)
        gsl_integration_workspace_free(W)

        return result


    cdef double _integrand(self, double x, void * params):
        """integrand implemented inside class """

        cdef double alpha = (<double_ptr> params)[0]
        cdef double f = log(alpha*x) / sqrt(x)
        return f

上面的代码编译并正确运行,因为使用了类之外的集成函数F.function = &integrand)。但是,为了在类中使用integrand函数而将此行更改为在(F.function = &self._integrand)下面注释掉的行,会带来以下编译错误:

cython_class_gsl.pyx:31:21: Cannot assign type 'double (*)(CalcSomething, double, void *)' to 'double (*)(double, void *)'

这是有道理的,因为我在gsl_function中对cGslInteg.pxd的声明是:

cdef extern from "gsl/gsl_math.h":

    # Definition of an arbitrary function with parameters
    ctypedef struct gsl_function:
        double (* function) (double x, void * params)
        void * params

我的问题是:我可以重新声明gsl_function所以它需要类型的内容,例如double (*)(PythonObject, double, void *)或者我可以将self._integrand打包以使其显示为double (*)( double, void *)类型吗?

编辑: 一些额外的注释,使其更接近现实生活中的问题/问题:

  1. self._integrand的任何包装器要在CalcSomething类中定义。
  2. 集成参数alpha不能是类变量(即必须在params参数内传递给被积函数)

1 个答案:

答案 0 :(得分:1)

这是params的用途。你做了这样的事情(大大削减了例子......)

from libc.math cimport log,sqrt

cdef class CalcSomething:   
    cdef double integrand(self, double x, double alpha):
        return log(alpha*x)/sqrt(x)

cdef double wrapped_integrand_func(double x, void* params):
    cdef object params_as_object = <object>params
    cdef CalcSomething instance = params_as_object[0] # we need to know the type to use cdef functions
    alpha = params_as_object[1]
    return instance.integrand(x,alpha)

def test(alpha):
    cdef object o = (CalcSomething(),alpha) # store Calc something and alpha as a tuple
    cdef void* o_ptr = <void*>o # be careful with reference counts here - o_ptr doesn't keep o alive!
    print(wrapped_integrand_func(4.3,o_ptr))

这显然不是问题的一般解决方案是包装绑定方法,但在这种情况下,它正是gsl允许您传递void*的原因。

唯一需要注意的是,只要您想使用o,就必须确保o_ptr保持活着。

更新(以匹配略有编辑的问题):

  

积分参数alpha不能是类变量(即必须在params参数内传递给被积函数)

这很容易完成。在我的原始代码中,我使用了一个类变量,但我现在将它们作为一个元组单独传递。