间隔高斯 - 勒让德 - x - >无穷大:有效转换权重和节点的自适应算法

时间:2016-05-21 20:31:45

标签: python numpy scipy wolfram-mathematica numerical-integration

好的我知道之前已经提到了这个问题,我只提出了一个有限的例子,用于缩放[-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}}范围以获得收敛。为了说明这是一个问题,请改为使用这些值:-aa=-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->10.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

3 个答案:

答案 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)):)

如果您想要内联计算,程序会执行以下操作: enter image description here

这是以下各项的组合:

enter image description here

并且:

enter image description here

并且:

enter image description here

答案 1 :(得分:2)

In&#34;数值规划:科学家和工程师使用Python和C / C ++的实用指南&#34;通过Titus A. Beu,您可以在此处找到代码示例integral.pyspecfunc.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)这样的阻尼项的原因。确实,对于这两种情况,您有特定的集成规则:

因此,与其尝试将积分转换为有限域,不如将其转换为以下任意一个

通常可以手动完成。