文档discusses使用numba的cfunc
作为LowLevelCallable
的{{1}}参数。我需要附加参数的相同内容。
我基本上试图做这样的事情:
scipy.integrate.quad
但是,它不起作用,因为import numpy as np
from numba import cfunc
import numba.types
voidp = numba.types.voidptr
def integrand(t, params):
a = params[0] # this is additional parameter
return np.exp(-t/a) / t**2
nb_integrand = cfunc(numba.float32(numba.float32, voidp))(integrand)
应该是params
/ voidptr
,并且它们无法转换为void*
。我有以下错误消息:
double
我没有找到任何关于如何从Numba中TypingError: Failed at nopython (nopython frontend)
Invalid usage of getitem with parameters (void*, int64)
* parameterized
中提取值的信息。在C中,它应该类似于void*
- 是否可以在Numba中做同样的事情?
答案 0 :(得分:6)
<强> 1。通过scipy.integrate.quad
如果用户希望提高集成性能,那么
f
可能是scipy.LowLevelCallable
,其中包含一个签名:
double func(double x)
double func(double x, void *user_data)
double func(int n, double *xx)
double func(int n, double *xx, void *user_data)
user_data
是scipy.LowLevelCallable
中包含的数据。在使用xx
的致电表单中,n
是xx
数组的长度,其中包含xx[0] == x
和其余项目包含args
中的数字1}}quad
的参数。
因此,要将额外参数传递给integrand
到quad
,您最好使用double func(int n, double *xx)
签名。
您可以为您的被积函数编写一个装饰器,将其转换为LowLevelCallable
,如下所示:
import numpy as np
import scipy.integrate as si
import numba
from numba import cfunc
from numba.types import intc, CPointer, float64
from scipy import LowLevelCallable
def jit_integrand_function(integrand_function):
jitted_function = numba.jit(integrand_function, nopython=True)
@cfunc(float64(intc, CPointer(float64)))
def wrapped(n, xx):
return jitted_function(xx[0], xx[1])
return LowLevelCallable(wrapped.ctypes)
@jit_integrand_function
def integrand(t, *args):
a = args[0]
return np.exp(-t/a) / t**2
def do_integrate(func, a):
"""
Integrate the given function from 1.0 to +inf with additional argument a.
"""
return si.quad(func, 1, np.inf, args=(a,))
print(do_integrate(integrand, 2.))
>>>(0.326643862324553, 1.936891932288535e-10)
或者,如果您不想要装饰器,请手动创建LowLevelCallable
并将其传递给quad
。
<强> 2。包装被积函数
我不确定以下内容是否符合您的要求,但您也可以包装integrand
函数以获得相同的结果:
import numpy as np
from numba import cfunc
import numba.types
def get_integrand(*args):
a = args[0]
def integrand(t):
return np.exp(-t/a) / t**2
return integrand
nb_integrand = cfunc(numba.float64(numba.float64))(get_integrand(2.))
import scipy.integrate as si
def do_integrate(func):
"""
Integrate the given function from 1.0 to +inf.
"""
return si.quad(func, 1, np.inf)
print(do_integrate(get_integrand(2)))
>>>(0.326643862324553, 1.936891932288535e-10)
print(do_integrate(nb_integrand.ctypes))
>>>(0.326643862324553, 1.936891932288535e-10)
第3。从voidptr
转换为python类型
我认为这还不可能。从2016年的this discussion开始,voidptr
似乎只是将上下文传递给C回调。
void *指针的情况适用于API,其中外部C代码并不是每次尝试取消引用指针,而只是将其传递回回调,作为回调在调用之间保持状态的方式。我认为此刻并不特别重要,但我想提出这个问题。
尝试以下方法:
numba.types.RawPointer('p').can_convert_to(
numba.typing.context.Context(), CPointer(numba.types.Any)))
>>>None
似乎也不鼓励!
答案 1 :(得分:1)
此处采用与 Jacques Gaudin 建议的第一点相同的技术,但有几个参数。
import numpy as np
import scipy.integrate as si
import numba
from numba import cfunc
from numba.types import intc, CPointer, float64
from scipy import LowLevelCallable
def jit_integrand_function(integrand_function):
jitted_function = numba.jit(integrand_function, nopython=True)
@cfunc(float64(intc, CPointer(float64)))
def wrapped(n, xx):
values = carray(xx, n)
return jitted_function(values)
return LowLevelCallable(wrapped.ctypes)
@jit_integrand_function
def integrand(args):
t = args[0]
a = args[1]
b = args[2]
return b * np.exp(-t/a) / t**2
def do_integrate(func, a):
"""
Integrate the given function from 1.0 to +inf with additional argument a.
"""
return si.quad(func, 1, np.inf, args=(a, b,))