如何将附加参数传递给作为LowLevelCallable传递给nipa cfunc的scipy.integrate.quad

Ily*_*rov 12 python scipy numba

该文档讨论了使用numba的cfuncs作为LowLevelCallable参数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)
Run Code Online (Sandbox Code Playgroud)

但是,它不起作用,因为params它们应该是voidptr/ void*并且它们无法转换为double.我有以下错误消息:

TypingError: Failed at nopython (nopython frontend)
Invalid usage of getitem with parameters (void*, int64)
 * parameterized
Run Code Online (Sandbox Code Playgroud)

我没有找到任何关于如何从void*Numba中提取值的信息.在C中,应该是这样的a = *((double*) params)- 在Numba中可以做同样的事情吗?

Jac*_*din 6

1.通过额外的论点 scipy.integrate.quad

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的论点quad.

因此传递到一个额外的参数integrand通过quad,您正在使用的更好的double func(int n, double *xx)签名.

你可以在你的integrand函数中编写一个装饰器来将它转换成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)
Run Code Online (Sandbox Code Playgroud)

或者,如果您不想要装饰器,请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)
Run Code Online (Sandbox Code Playgroud)

3.从voidptrpython类型转换

我认为这还不可能.从2016 年的讨论来看,似乎voidptr只是将上下文传递给C回调.

void*指针的情况适用于API,其中外部C代码并不是每次尝试取消引用指针,而只是将其传递回回调,作为回调在调用之间保持状态的方式.我认为此刻并不特别重要,但我想提出这个问题.

并尝试以下方法:

numba.types.RawPointer('p').can_convert_to(
    numba.typing.context.Context(), CPointer(numba.types.Any)))
>>>None
Run Code Online (Sandbox Code Playgroud)

似乎也不鼓励!