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

问题描述 投票:0回答:2

文档讨论使用numba的

cfunc
作为
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)

但是,它不起作用,因为

params
应该是
voidptr
/
void*
并且它们不能转换为
double
。我收到以下错误消息:

TypingError: Failed at nopython (nopython frontend)
Invalid usage of getitem with parameters (void*, int64)
 * parameterized

我没有找到任何有关如何从 Numba 中的

void*
中提取值的信息。在 C 中,它应该类似于
a = *((double*) params)
— 在 Numba 中可以做同样的事情吗?

python scipy numba
2个回答
8
投票

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)
签名。

您可以为被积函数编写一个装饰器,将其转换为

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讨论来看,

voidptr
似乎只是在这里将上下文传递给C回调。

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

并尝试以下操作:

numba.types.RawPointer('p').can_convert_to(
    numba.typing.context.Context(), CPointer(numba.types.Any)))
>>>None

似乎也不令人鼓舞!


1
投票

这里与雅克·高丹建议的第一点相同的技术,但有几个参数。

import numpy as np
import scipy.integrate as si
import numba
from numba import cfunc, carray
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,))
© www.soinside.com 2019 - 2024. All rights reserved.