将 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 函数
如何将字符串从 Python3 传递给 cythonized C++ 函数