将 cython 函数与 cython 方法传递给 scipy.integrate

Posted

技术标签:

【中文标题】将 cython 函数与 cython 方法传递给 scipy.integrate【英文标题】:Passing a cython function vs a cython method to scipy.integrate 【发布时间】:2015-10-01 19:38:36 【问题描述】:

我正在尝试研究如何使用 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 的三种方式

    使用在类外部定义的 cython 被积函数:integrate_other(好的!) 使用在类中定义的 cython 被积函数:integrate_self(产生错误) 将类内定义的 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) v calcSomething.wrap_integrand(.5) 会发生什么? calcSomething._integrand(None, .5) 怎么样。我想知道是否尝试从类外部调用该函数是问题所在。 按照你的建议做: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' 尝试使用cpdef 获取要从 Python 调用的函数。 作为进一步的评论:我认为当您传递到期望 Python 函数的地方时,围绕 cdef 函数自动生成 wrap 函数是一个相当新的功能,但仍然存在一些问题。他们可能会欣赏错误报告。 @scipio 是的,Python 调用确实会产生速度成本,是的,纯 C/c++/Fortran 解决方案可能是最好的选择。 Scipy 有 started to develop a solution,但它看起来还没有被应用到任何地方。 【参考方案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

【讨论】:

以上是关于将 cython 函数与 cython 方法传递给 scipy.integrate的主要内容,如果未能解决你的问题,请参考以下文章

将带有字符串的结构化 numpy 数组传递给 cython 函数

如何使用 cython 将 python 类公开给 c++

如何将字符串从 Python3 传递给 cythonized C++ 函数

如何正确地将 numpy 数组传递给 Cython 函数?

使用 Cython 时如何将一个 C++ 类(引用)传递给另一个?

Cython 将扩展模块传递给 python 导致 to_py_call_code 错误