我需要在低至-150
的范围内计算以下函数的积分:
import numpy as np
from scipy.special import ndtr
def my_func(x):
return np.exp(x ** 2) * 2 * ndtr(x * np.sqrt(2))
问题在于这部分功能
np.exp(x ** 2)
倾向于无限 - 我inf
的值x
小于约-26
。{/ p>
这部分功能
2 * ndtr(x * np.sqrt(2))
相当于
from scipy.special import erf
1 + erf(x)
倾向于0。
所以,一个非常非常小的数字应该给我一个合理大小的数字 - 但是,python
正在给我nan
。
我可以做些什么来规避这个问题?
答案 0 :(得分:5)
我认为@ askewchan的解决方案和scipy.special.log_ndtr
的组合可以解决问题:
from scipy.special import log_ndtr
_log2 = np.log(2)
_sqrt2 = np.sqrt(2)
def my_func(x):
return np.exp(x ** 2) * 2 * ndtr(x * np.sqrt(2))
def my_func2(x):
return np.exp(x * x + _log2 + log_ndtr(x * _sqrt2))
print(my_func(-150))
# nan
print(my_func2(-150)
# 0.0037611803122451198
对于x <= -20
,log_ndtr(x)
uses a Taylor series expansion of the error function to iteratively compute the log CDF directly,这比仅仅log(ndtr(x))
更加数字稳定。
正如您在评论中提到的,如果exp
足够大,x
也会溢出。虽然您可以使用mpmath.exp
解决此问题,但更简单,更快捷的方法是投射到np.longdouble
,在我的机器上,它可以表示最高为1.189731495357231765e + 4932的值:
import mpmath
def my_func3(x):
return mpmath.exp(x * x + _log2 + log_ndtr(x * _sqrt2))
def my_func4(x):
return np.exp(np.float128(x * x + _log2 + log_ndtr(x * _sqrt2)))
print(my_func2(50))
# inf
print(my_func3(50))
# mpf('1.0895188633566085e+1086')
print(my_func4(50))
# 1.0895188633566084842e+1086
%timeit my_func3(50)
# The slowest run took 8.01 times longer than the fastest. This could mean that
# an intermediate result is being cached 100000 loops, best of 3: 15.5 µs per
# loop
%timeit my_func4(50)
# The slowest run took 11.11 times longer than the fastest. This could mean
# that an intermediate result is being cached 100000 loops, best of 3: 2.9 µs
# per loop
答案 1 :(得分:4)
已有这样的功能:erfcx
。我认为erfcx(-x)
应该为您提供所需的积分(请注意1+erf(x)=erfc(-x)
)。
答案 2 :(得分:2)
不确定这会有多大帮助,但这里有一些想法太长而无法发表评论。
您需要计算的积分,correctly identified为。打开括号,您可以整合总和的两个部分。
Scipy有imaginary error function implemented
第二部分更难:
这是generalized hypergeometric function。可悲的是,它看起来像scipy does not have an implementation of it,但this package声称它确实如此。
这里我使用了不带常数的不定积分,知道from
to
值很清楚如何使用明确的值。