将一个Cython函数和一个Cython方法传递给scipy.integrate的区别

3

我想找到如何使用cython来加速涉及集成的计算,这些计算是在我定义的类中进行的。我正在尝试更好地理解cython如何与用户自定义的Python类一起使用。我想更多地了解下面描述的错误发生的原因。

我在名为ex.pyx的文件中编写了以下cython代码。

from libc.math cimport log
from libc.math cimport sqrt
import scipy.integrate as integ


cdef double integrand_outside(double x):
    """Cython: integrand outside the class"""
    cdef double f = log(x) / sqrt(x)
    return f


cdef class CalcSomething(object):

    def integrate_other(self, double a, double b):
        """This does the integral with the integrand outside the class"""
        return integ.quad(integrand_outside, a, b)[0]

    def integrate_self(self, double a, double b):
        """This does the integral with the integrand inside the class"""
        return integ.quad(self._integrand, a, b)[0]

    def integrate_wrap_self(self, double a, double b):
        """This does the integral with the integrand inside the class"""
        return integ.quad(self.wrap_integrand, a, b)[0]

    def wrap_integrand(self, double x):
        """Python method that wraps _integrand"""
        return self._integrand(x)

    cdef double _integrand(self, double x):
        """Cython: integrand inside the class"""
        cdef double f = log(x) / sqrt(x)
        return f

这段文字展示了在类内部调用scipy.integrate.quad的三种方式:

  1. 使用在类外定义的cython积分函数:integrate_other(可行!)
  2. 使用在类内定义的cython积分函数:integrate_self(会产生错误)
  3. 将在类内定义的cython积分函数封装在一个在类内定义的python函数中:integrate_wrap_self(可行!)

上述的cython代码编译正常。现在我调用每个积分方法,例如:

import ex

calcSomething = ex.CalcSomething()

a = 0.001
b = 0.1

calcSomething.integrate_other(a,b)     # works
calcSomething.integrate_wrap_self(a,b) # works
calcSomething.integrate_self(a,b)      # doesn't work

以下是追溯信息:

Traceback (most recent call last):
File "../examples/example.py", line 10, in <module>
print "integrate self =", calcSomething.integrate_self(a,b)   # doesn't work
File "ex.pyx", line 17, in ex.CalcSomething.integrate_self (ex.c:989)
return integ.quad(self._integrand, a, b)[0]
File "/home/alex/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/scipy/integrate/quadpack.py", line 281, in quad
retval = _quad(func,a,b,args,full_output,epsabs,epsrel,limit,points)
File "/home/alex/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/scipy/integrate/quadpack.py", line 345, in _quad
return _quadpack._qagse(func,a,b,args,full_output,epsabs,epsrel,limit)
File "stringsource", line 30, in cfunc.to_py.__Pyx_CFunc_double____CalcSomething____double___to_py.wrap (ex.c:1560)
TypeError: wrap() takes exactly 2 positional arguments (1 given)

以下是我的问题:

  • 为什么可以将scipy.integrate作为cython函数或python方法传递(因此现在实例是第一个参数),但不能将其作为cython方法传递?错误:TypeError: wrap() takes exactly 2 positional arguments (1 given)表明问题出在传递给cython方法的实例参数上吗?

  • 这个错误是来自我对如何使用cython的误解,还是来自scipy的限制?

  • 如果我想通过cython加速,那么在类中计算积分(通过调用类内部的积分函数)是一个不好的解决方案吗?披露:真正的代码将调用GSL积分函数,而不是scipy


calcSomething._integrand(.5)calcSomething.wrap_integrand(.5)有什么区别?calcSomething._integrand(None, .5)呢?我在想,尝试从类外调用函数是否是问题所在。 - hpaulj
按照您的建议执行:calcSomething._wrap_integrand(0.5)是可以工作的,但是calcSomething._integrand(0.5)不行,会抛出AttributeError: 'ex.CalcSomething' object has no attribute '_integrand'的错误。然而,调用ex.integrand_outside(double x)会产生AttributeError: 'module' object has no attribute 'integrand_outside'的错误。 - alexabate
尝试使用cpdef来定义那些你想从Python中调用的函数。 - hpaulj
1
作为进一步的评论:我认为当您将cdef函数传递给期望Python函数的位置时,自动生成一个wrap函数是一个相对较新的功能,但仍存在一些问题。他们可能会欣赏一个错误报告。 - DavidW
1
@scipio 是的,Python调用确实会带来速度成本,而且纯C / c ++ / Fortran解决方案可能是最佳选择。Scipy已经开始开发解决方案,但似乎还没有应用到所有地方。 - DavidW
显示剩余2条评论
1个回答

1
从hpaulj的上面的帮助中可以得出答案:答案是需要将_integrand方法声明为cpdef而不是cdef
cpdef double _integrand(self, double x):
    """Cython: integrand inside the class"""
    cdef double f = log(x) / sqrt(x)
    return f

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接