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

时间:2018-04-06 00:52:49

标签: python scipy numba

文档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中做同样的事情?

2 个答案:

答案 0 :(得分:6)

<强> 1。通过scipy.integrate.quad

传递额外的参数

quad docs说:

  

如果用户希望提高集成性能,那么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_datascipy.LowLevelCallable中包含的数据。在使用xx的致电表单中,nxx数组的长度,其中包含xx[0] == x其余项目包含args中的数字1}} quad 的参数。

因此,要将额外参数传递给integrandquad,您最好使用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,))