scipy.integrate.quad对大数字的精确度

时间:2016-09-15 16:00:25

标签: python numpy scipy calculus

我尝试通过scipy.integrate.quad()计算这样一个积分(实际上是pdf的指数分布及其pdf):

import numpy as np
from scipy.integrate import quad

def g(x):
    return .5 * np.exp(-.5 * x)

print quad(g, a=0., b=np.inf)
print quad(g, a=0., b=10**6)
print quad(g, a=0., b=10**5)
print quad(g, a=0., b=10**4)

结果如下:

(1.0, 3.5807346295637055e-11)
(0.0, 0.0)
(3.881683817604194e-22, 7.717972744764185e-22)
(1.0, 1.6059202674761255e-14)

尽管使用np.inf解决了问题,但所有尝试使用较大的上限积分限制都会产生错误的答案。

scipy issue #5428 at GitHub中讨论了类似的案例。

如何避免在集成其他密度函数时出现这样的错误?

2 个答案:

答案 0 :(得分:4)

我认为问题是np.exp(-x)随着x的增加而变得非常小,导致由于数值精度有限而评估为零。例如,即使x小到x=10**2*np.exp(-x)评估为3.72007597602e-44,而x10**3以上的值会导致0

我不知道quad的实现细节,但它可能会执行某种类型的函数抽样,以便在给定的积分范围内进行集成。对于较大的上积分限制,np.exp(-x)的大多数样本评估为零,因此低估了积分值。 (请注意,在这些情况下,quad提供的绝对误差与积分值的顺序相同,后者是不可靠的指标。)

避免这个问题的一种方法是将积分上限限制在一个值之上,在该值之上,数值函数变得非常小(因此,对积分值略微有贡献)。从您的代码snipet中,10**4的值似乎是一个不错的选择,但是,10**2的值也会导致对积分的准确评估。

避免数值精度问题的另一种方法是使用在任意精度算术中执行计算的模块,例如mpmath。例如,对于x=10**5mpmath按如下方式评估exp(-x)(使用本机mpmath指数函数)

import mpmath as mp
print(mp.exp(-10**5))
  

3.56294956530937e-43430

注意这个值有多小。使用标准硬件数值精度(由numpy使用),此值变为0

mpmath提供了一个积分函数(mp.quad),它可以准确估计上限积分的任意值的积分。

import mpmath as mp

print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, mp.inf]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**13]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**8]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**5]))
1.0
0.999999650469474
0.999999999996516
0.999999999999997

我们还可以通过将精度提高到50小数点(来自15这是标准精度)来获得更准确的估算值

mp.mp.dps = 50; 

print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, mp.inf]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**13]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**8]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**5]))
1.0
0.99999999999999999999999999999999999999999829880262
0.99999999999999999999999999999999999999999999997463
0.99999999999999999999999999999999999999999999999998

通常,获得此准确性的成本是增加的计算时间。

P.S。:不言而喻,如果您能够首先分析评估您的积分(例如,在Sympy的帮助下),您可以忘记以上所有内容。

答案 1 :(得分:2)

使用points参数告诉算法大致支持函数的位置:

import numpy as np
from scipy.integrate import quad

def g(x):
    return .5 * np.exp(-.5 * x)

print quad(g, a=0., b=10**3, points=[1, 100])
print quad(g, a=0., b=10**6, points=[1, 100])
print quad(g, a=0., b=10**9, points=[1, 100])
print quad(g, a=0., b=10**12, points=[1, 100])