Python 如何加速scipy.integrate.quad?

Python 如何加速scipy.integrate.quad?,python,performance,scipy,numerical-integration,Python,Performance,Scipy,Numerical Integration,考虑以下代码: def disFunc(x,n): y = np.power(x, n-1)*np.exp(-x) return y def disFunc2(t,n,beta): y = integrate.quad(disFunc, a = 0, b = beta*t, args = (n)) return y[0] def disFunc3(func): y = np.vectorize(func) ret

考虑以下代码:

def disFunc(x,n):
        y = np.power(x, n-1)*np.exp(-x)
        return y
    

def disFunc2(t,n,beta):
    y = integrate.quad(disFunc, a = 0, b = beta*t, args = (n))
    return y[0]   

def disFunc3(func):
    y = np.vectorize(func)
    return(y)

maxCounter = 6000
stepSize = .001
n = 5
beta = 25
t = np.cumsum(np.ones(maxCounter)*stepSize)-stepSize
x = disFunc3(disFunc2)
start_time = time.time()
y = x(t,n,beta)
time_taken = (time.time() - start_time)
print (time_taken)

工作起来像个符咒,但速度太慢了(1.85秒)。如何加快速度?

首先,矢量化通常非常慢。也许您可以尝试更改代码,使其不必依赖于此

否则,您可以尝试
Numba
/
Numba-Scipy
的jit(即时编译)。这大大加快了使用NumPy和Scipy的代码的速度:

然后可以导入jit装饰器,并在函数上使用它:

from numba import jit

@jit  # or jit(nopython=False) in other scenarios
def disFunc(x,n):
        y = np.power(x, n-1)*np.exp(-x)
        return y
    
> ...
确保同时安装Numba和Numba Scipy

如果您只想集成此处提供的功能: 请注意,您希望集成的函数实际上相当于
scipy
包括用于近似较低不完整伽马函数的函数,该函数由(完整)伽马函数正则化(即:其输出除以伽马(n))。因此,我们可以使用这些特殊函数更有效地近似积分:

from numba import jit

@jit  # or jit(nopython=False) in other scenarios
def disFunc(x,n):
        y = np.power(x, n-1)*np.exp(-x)
        return y
    
> ...
来自scipy导入专用
导入时间
# ...
开始时间=time.time()
y=特殊的gammainc(n,β*t)
y*=特殊伽马(n)#反转正则化
所用时间=(time.time()-开始时间)
打印(所用时间)
打印(y)
对于不同的n值,所需的时间会有所不同,但对于n=5.5,在我的机器上运行的时间约为0.0005秒,而下面的方法为0.3秒

如果要集成不存在闭合形式的任意函数: 这里有一个想法,它只是改进了您在数学上使用的方法,而不是使用JIT编译。这在我的机器上运行速度快了约10倍(您的代码为我运行大约需要2.2秒,这需要约0.3秒):

将numpy导入为np
从scipy导入集成
导入时间
def disFunc(x,n):
y=np.幂(x,n-1)*np.exp(-x)
返回y
def disFunc2(t_prev,t_cur,n,beta):
y=integrate.quad(disFunc,a=beta*t\u prev,b=beta*t\u cur,args=(n))
返回y[0]
def disFunc3(func):
y=np.矢量化(func)
返回y
最大计数器=6000
步长=0.001
n=5
β=25
t=np.cumsum(np.one(最大计数器)*步长)-步长
x=disFunc3(disFunc2)
开始时间=time.time()
y=x(t[:-1],t[1:],n,β)
所用时间=time.time()-开始时间
打印(所用时间)
打印(np.cumsum(y))
这个想法利用了积分的性质:对于a和b之间的一些c,范围[a,b]上的积分等于[a,c]上的积分加上[c,b]上的积分。因此,我们不是每次计算(0,b*t)之间的积分,而是计算(b*prev\t,b*t)之间的积分(其中prev\t是我们使用的最后一个t),然后运行一个累积和。这只会使它更快,因为在较小范围内执行积分所需的近似迭代次数要少得多


需要注意的是,这会跳过第一个积分(从0到beta*t[0]),因此您可能需要单独计算并将其添加到数组的前面。

不要使用
np.vectorize
。它不比列表理解快多少。至于
quad
本身,除了确保
disFunc
的速度很快之外,您不能做很多事情。我认为
quad
没有旋转参数,但我可能错了。你的递归闭合形式很棒,但我应该提到n可以取小数。是否有可能修改“按零件积分”函数以处理n可以有小数的情况(例如,5.5)?@user1363251 Ah,不幸的是,对于自然数以外的n值,初等函数中不存在闭合形式的积分,因此不可能修改函数以适应此情况,数值积分是必要的。我的建议是使用来执行近似,而不是使用积分模块,因为它可能会更好地专门针对该积分进行优化。甚至更好的是:应该正是您所寻找的,您只需要将它乘以完整的伽马函数。@user1363251我已经更新了我的答案,以说明如何使用较低的不完整伽马函数计算积分。