好的我知道之前已经提到了这个问题,我只提出了一个有限的例子,用于缩放[-1, 1]
间隔[a, b]
Different intervals for Gauss-Legendre quadrature in numpy但是没有人发布过如何对[-a, Infinity]
进行概括(如在下面完成,但不是(还)快。这也显示了如何使用多个实现调用复杂函数(无论如何在定量选项定价中)。有基准quad
代码,后跟leggauss
,其中包含指向如何实现自适应算法的代码示例的链接。我已经解决了大多数链接的adaptive algorithm
难题 - 它目前打印分割积分的总和以显示它正常工作。在这里,您可以找到将范围从[-1, 1]
转换为[0, 1]
到[a, Infinity]
的功能(感谢@AlexisClarembeau)。要使用自适应算法,我必须创建另一个函数,以便从[-1, 1]
转换为[a, b]
,并将其反馈到[a, Infinity]
函数。
import numpy as np
from scipy.stats import norm, lognorm
from scipy.integrate import quad
a = 0
degrees = 50
flag=-1.0000
F = 1.2075
K = 0.1251
vol = 0.43
T2 = 0.0411
T1 = 0.0047
def integrand(x, flag, F, K, vol, T2, T1):
d1 = (np.log(x / (x+K)) + 0.5 * (vol**2) * (T2-T1)) / (vol * np.sqrt(T2 - T1))
d2 = d1 - vol*np.sqrt(T2 - T1)
mu = np.log(F) - 0.5 *vol **2 * T1
sigma = vol * np.sqrt(T1)
return lognorm.pdf(x, mu, sigma) * (flag * x*norm.cdf(flag * d1) - flag * (x+K)*norm.cdf(flag * d2))
def transform_integral_0_1_to_Infinity(x, a):
return integrand(a+(x/(1-x)), flag, F, K, vol, T2, T1) *(1/(1-x)**2);
def transform_integral_negative1_1_to_0_1(x, a):
return 0.5 * transform_integral_0_1_to_Infinity((x+1)/2, a)
def transform_integral_negative1_1_to_a_b(x, w, a, b):
return np.sum(w*(0.5 * transform_integral_0_1_to_Infinity(((x+1)/2*(b-a)+a), a)))
def adaptive_integration(x, w, a=-1, b=1, lastsplit=False, precision=1e-10):
#split the integral in half assuming [-1, 1] range
midpoint = (a+b)/2
interval1 = transform_integral_negative1_1_to_a_b(x, w, a, midpoint)
interval2 = transform_integral_negative1_1_to_a_b(x, w, midpoint, b)
return interval1+interval2 #just shows this is correct for splitting the interval
def integrate(x, w, a):
return np.sum(w*transform_integral_negative1_1_to_0_1(x, a))
x, w = np.polynomial.legendre.leggauss(degrees)
quadresult = quad(integrand, a, np.Inf, args=(flag, F, K, vol, T2, T1), epsabs=1e-1000)[0]
GL = integrate(x, w, a)
print("Adaptive Sum Result:")
print(adaptive_integration(x, w))
print("GL result");
print(GL)
print("QUAD result")
print(quadresult)
仍然需要以更小的尺寸来提高速度和准确度,因为我无法手动调整degrees
的{{1}}范围以获得收敛。为了说明这是一个问题,请改为使用这些值:-a
,a=-20
,然后运行。你可以增加F=50
,看看如果没有智能地应用这个Gauss-Legendre算法没有任何好处。我对速度的要求是每循环达到0.0004s,而最后一次算法我Cython化大约需要0.75s,这就是我尝试使用Gauss-Legendre的低度,高精度算法的原因。使用Cython和多线程,来自完全优化的Python实现的这个要求每个循环大约为0.007s(非向量化,循环,低效的例程,每个循环可以是0.1s,degrees=1000
,即{{1} }。
我已经半实现的一个可能的解决方案是{5}页degrees=20
,%timeit adaptive_integration(x,w)
,而间隔adaptive integration
(在这种情况下,我写了a-b
函数)其中区间被划分为2(@ transform_integral_negative1_1_to_a_b
),然后在这1/2间隔上评估函数,并比较两个0.5
+ 0->0.5
的总和到整个范围0.5->1
的函数结果。如果精度不在容差范围内,则范围进一步细分为0->1
和0.25
,再次针对每个子区间评估函数,并将其与先前的1/2区间和@ 0.75
进行比较。如果1侧在公差范围内(例如0.5
),但另一侧不在公差范围内,则分裂在公差范围内停止,但在另一侧继续,直到达到abs(0->0.5 - (0->0.25 + 0.25->0.5)) < precision
。此时,将每个区间切片的结果相加,得到具有更高精度的完整积分。
可能有更快更好的方法来解决这个问题。只要它快速准确,我就不在乎。以下是我参考的综合例程的最佳描述http://online.sfsu.edu/meredith/Numerical_Analysis/improper_integrals奖励是100分赏金+ 15分答案接受。感谢您协助使此代码快速准确!
编辑:
以下是我对precision
代码的更改 - 如果有人可以快速完成这项工作,我可以接受答案并奖励赏金。第7页的这个Mathematica代码http://orion.math.iastate.edu/keinert/computation_notes/chapter5.pdf执行我尝试的例程。它有一个不能很好地收敛的例程,请参阅下面的变量。现在我的代码错误输出:adaptive_integration
在某些输入上,或者如果RecursionError: maximum recursion depth exceeded in comparison
设置得太高,或者在它工作时没有接近degrees
结果,那么这里显然是错的。
quad
带有def adaptive_integration(x, w, a, b, integralA2B, remainingIterations, firstIteration, precision=1e-9):
#split the integral in half assuming [-1, 1] range
if remainingIterations == 0:
print('Adaptive integration failed on the interval',a,'->',b)
if np.isnan(integralA2B): return np.nan
midpoint = (a+b)/2
interval1 = transform_integral_negative1_1_to_a_b(x, w, a, midpoint)
interval2 = transform_integral_negative1_1_to_a_b(x, w, midpoint, b)
if np.abs(integralA2B - (interval1 + interval2)) < precision :
return(interval1 + interval2)
else:
return adaptive_integration(x, w, a, midpoint, interval1, (remainingIterations-1), False) + adaptive_integration(x, w, midpoint, b, interval2, (remainingIterations-1), False)
#This example doesn't converge to Quad
# non-converging interval inputs
a = 0 # AND a = -250
degrees = 10
flag= 1
F = 50
K = 0.1251
vol = 0.43
T2 = 0.0411
T1 = 0.0047
print(adaptive_integration(x, w, -1, 1, GL, 500, False))
的输出(在用degrees=100
计算GL
以获得更好的初始估算之后,否则,算法总是显然与其自身的准确性一致,并且不会调用自适应路径每次都失败了):
degrees=10000
答案 0 :(得分:6)
我认为代码可以胜任:
import numpy as np import math deg = 10 x, w = np.polynomial.legendre.leggauss(deg) def function(x): # the function to integrate return math.exp(-x) def function2(x, a): return function(a+x/(1-x))/((1-x)**2); def anotherOne(x, a): return 0.5 * function2(x/2 + 1/2, a) def integrate(deg, a): sum = 0 x, w = np.polynomial.legendre.leggauss(deg) for i in range(deg): print("sum({}) += {} * {} (eval in {})".format(sum, w[i], anotherOne(x[i], a), x[i])) sum += w[i]*anotherOne(x[i], a) return sum; print("result"); print(integrate(10, 1))
它结合了你的方程,从a到inf和方程的积分来改变积分的界限。
我希望它能解决你的问题(它至少适用于exp(-x)):)
这是以下各项的组合:
并且:
并且:
答案 1 :(得分:2)
In&#34;数值规划:科学家和工程师使用Python和C / C ++的实用指南&#34;通过Titus A. Beu,您可以在此处找到代码示例integral.py
和specfunc.py
中的方法:http://phys.ubbcluj.ro/~tbeu/INP/libraries.html您可以调用函数xGaussLag(a, deg)
来调用Laguerre
另一个.py文件,并在(x,w)
和a
之间返回经过调整的infinity
。以下是如何进行设置的注意事项(请注意deg=80
以上它非常慢,我只是通过修改上面的行来向您展示如何应用它):
x, w = np.array(xGaussLag(a,deg))
gauss = sum(w * integrand(x, flag, F, K, vol, T2, T1))
在deg=80
上得到非常接近的收敛(更快),但我只是将eps=1e-13
放在xGaussLag
中,然后用这些结果推送deg=150
,但速度比{{1}快33%:
QUADPACK解决方案:0.149221620346,错误:1.49870924498e-12 Gauss-Legendre解决方案:0.149238273747 QUADPACK和Gauss-Legendre之间的区别:1.66534003601e-05
在Cython中,这比直接的Python BTW快6倍仍然太慢,所以我将尝试使用&#34; FastGL&#34;现在打包@Alexis的回答,只是发布,因为我认为这将对其他SO用户有用。
答案 2 :(得分:0)
延伸到无限域的积分总会引起您的怀疑。毕竟,大多数“简单”功能甚至都不能在那里集成!这就是为什么通常在其中使用诸如exp(-x)
或exp(-x**2)
这样的阻尼项的原因。确实,对于这两种情况,您有特定的集成规则:
exp(-x)
和exp(-x**2)
。因此,与其尝试将积分转换为有限域,不如将其转换为以下任意一个
通常可以手动完成。