Tanh-sinh正交数值积分方法收敛于错误值

时间:2014-07-27 23:14:16

标签: python python-2.7 numerical-integration

我正在尝试编写一个Python程序来使用Tanh-sinh求积来计算值:

an integral

但是虽然程序收敛到一个合理的值,并且在每种情况下都没有错误,但它没有收敛到正确的值(这是特定积分的pi),我找不到问题。

该程序不是要求达到所需的准确度,而是要求所需的功能评估数量,以便更容易地将收敛与更简单的集成方法进行比较。评估的数量需要是奇数,因为使用的近似值是

enter image description here

任何人都可以提出我可能做错的事吗?

import math

def func(x):
    # Function to be integrated, with singular points set = 0
    if x == 1 or x == -1 :
        return 0
    else:
        return 1 / math.sqrt(1 - x ** 2)

# Input number of evaluations
N = input("Please enter number of evaluations \n")
if N % 2 == 0:
    print "The number of evaluations must be odd"
else:
    print "N =", N  

# Set step size
h = 2.0 / (N - 1)
print "h =", h

# k ranges from -(N-1)/2 to +(N-1)/2
k = -1 * ((N - 1) / 2.0)
k_max  = ((N - 1) / 2.0)
sum = 0

# Loop across integration interval
while k < k_max + 1:

    # Compute abscissa
    x_k = math.tanh(math.pi * 0.5 * math.sinh(k * h))

    # Compute weight
    numerator = 0.5 * h * math.pi * math.cosh(k * h)
    denominator = math.pow(math.cosh(0.5 * math.pi * math.sinh(k * h)),2)
    w_k =  numerator / denominator

    sum += w_k * func(x_k)

    k += 1

print "Integral =", sum

5 个答案:

答案 0 :(得分:2)

对于它的价值,Scipy具有数值积分功能

例如,

from scipy import integrate
check = integrate.quad(lambda x: 1 / math.sqrt(1 - x ** 2), -1, 1)
print 'Scipy quad integral = ', check

给出结果

  

Scipy quad integral =(3.141592653589591,6.200897573194197e-10)

其中元组中的第二个数字是绝对错误。

那就是说,我能够让你的程序进行一些调整(尽管这只是一次初步尝试):

1)按照this paper

的建议,将步长h设置为0.0002(大约1/2 ^ 12)

但请注意 - 本文实际上建议迭代地改变步长 - 使用固定的步长,您将达到sinh或cosh对于足够大的 kh 值而言变得太大的点。根据该论文的方法尝试实施可能会更好。

但坚持手头的问题,

2)确保为集成设置足够的迭代以真正收敛,即math.fabs(w_k * func(x_k))&lt;的足够迭代次数。 1.0E-9

通过这些调整,我能够使用&gt;将积分收敛到正确的pi值到4个有效数字。 30000次迭代。

例如,对于31111次迭代,pi计算的值为3.14159256208

带有修改的示例代码(注意我用sum替换了sum,sum是Python内置函数的名称):

import math

def func(x):
    # Function to be integrated, with singular points set = 0
    if x == 1 or x == -1 :
        return 0
    else:
        return 1 / math.sqrt(1 - x ** 2)

# Input number of evaluations
N = input("Please enter number of evaluations \n")
if N % 2 == 0:
    print "The number of evaluations must be odd"
else:
    print "N =", N  

# Set step size
#h = 2.0 / (N - 1)
h=0.0002 #(1/2^12)
print "h =", h

# k ranges from -(N-1)/2 to +(N-1)/2
k = -1 * ((N - 1) / 2.0)
k_max  = ((N - 1) / 2.0)
thesum = 0

# Loop across integration interval
actual_iter =0
while k < k_max + 1:

    # Compute abscissa
    x_k = math.tanh(math.pi * 0.5 * math.sinh(k * h))

    # Compute weight
    numerator = 0.5 * h * math.pi * math.cosh(k * h)
    dcosh  = math.cosh(0.5 * math.pi * math.sinh(k * h))
    denominator = dcosh*dcosh
    #denominator = math.pow(math.cosh(0.5 * math.pi * math.sinh(k * h)),2)
    w_k =  numerator / denominator

    thesum += w_k * func(x_k)
    myepsilon = math.fabs(w_k * func(x_k))
    if actual_iter%2000 ==0 and actual_iter > k_max/2:
        print "Iteration = %d , myepsilon = %g"%(actual_iter,myepsilon)


    k += 1
    actual_iter += 1

print 'Actual iterations = ',actual_iter
print "Integral =", thesum

答案 1 :(得分:2)

使用multiprecision库mpmath

from mpmath import *

mp.dps = 100

h = mpf(2**-12);

def weights(k):
    num = mpf(0.5)*h*pi*cosh(k*h)
    den = cosh(mpf(0.5)*pi*sinh(k*h))**2
    return (num/den)

def abscissas(k):
    return tanh(mpf(0.5)*pi*sinh(k*h))

def f(x):
    return 1/sqrt(1 - mpf(x)**2)

N = 20000

result = 0
for k in range(-N, N+1):
    result = result + weights(k)*f(abscissas(k))

print result - pi

给出错误

-3.751800610920472803216259350430460844457732874052618682441090144344372471319795201134275503228835472e-45

答案 2 :(得分:1)

我认为部分问题可能是由于范围和步长造成的。我修改了代码 所以你可以分别放入范围和步长,并重写一些数学。它似乎给出了正确的答案。尝试使用示例5和0.1作为输入。

一个特别的问题是计算math.cosh(0.5 * math.pi * math.sinh(k * h))k * h变大math.sinh(k * h)呈指数增长而计算math.cosh可能很难。     导入数学

def func(x):
#    return 1   # very simple test function
    # Function to be integrated, with singular points set = 0
    if x == 1 or x == -1 :
        return 0
    else:
        return 1 / math.sqrt(1 - x ** 2)

# Input number of evaluations
N = input("Please enter max value for range \n")
    print "N =", N
h = input("Please the step size \n")
print "h =", h

k = -N
k_max = N
sum = 0
count = 0
print "k ", k , " " , k_max

# Loop across integration interval
while k < k_max + 1:

    # Compute abscissa
    v = k
    u = math.pi * 0.5 * math.sinh(v)
    x_k = math.tanh(u)
    #print u
    # Compute weight 
    numerator = 0.5 * math.pi * math.cosh(v)
    csh = math.cosh(u)
    denominator = csh*csh
    w_k =  numerator / denominator
    print k, v, x_k , w_k
    sum += w_k * func(x_k)
    count += 1
    k += h      # note changed
res = sum * h
print "Integral =", sum * h

答案 3 :(得分:1)

你必须意识到+1和-1是你的被积函数的奇点,f(x)-->+infinityx-->+1,-1。因此,您可以从边界点使用您最喜欢的正交公式,但您必须根据f(x)局部扩展计算出特殊的正交公式在他们附近。

方法草图:

  1. 选择一些epsilon<<1

  2. 将积分I分解为平滑和奇异的部分:

    • I_smooth[-1+epsilon, 1-epsilon]
    • 中的积分
    • I_singular是来自[-1, -1+epsilon][1-epsilon, 1]的积分。
  3. [-1+epsilon, 1-epsilon]区间内应用标准正交规则 得到I_smooth

  4. 围绕奇点进行局部展开(例如 x = 1 ):

    f(x) = 1/sqrt(1-x) * (a0 + a1*(1-x) + a2*(1-x)^2 + ...)
    
         = f0(x-1) + f1(x-1) + f2(x-1) + ..
    

    这只是关于x=1 f(x)*sqrt(1-x) 1/sqrt(1-x)预乘fn(x-1) = an*(1-x)^n/sqrt(1-x的泰勒扩张。 (不幸的是,你必须做一些数学计算并解决泰勒扩展问题 除非您有Mathematica或者您可以在某处找到它。)

  5. 每个单词Fn)都可以完全集成(它只是一个幂函数)。让fn成为1-epsilon1I_singular = F0 + F1 + F2 + ...的积分。 大致I = I_smooth + I_singular 达到您想要的顺序。

  6. 最后:

    epsilon
  7. 注意:为了提高准确度,你不应该使{{1}}太小,因为积分的爆破会使问题在数值上受到限制,而是增加泰勒展开的顺序。

答案 4 :(得分:0)

tanh-sinh求积有很多陷阱,其中一个就是需要非常接近间隔边界,在小于机器精度的距离(例如,在原始示例中为1.0 - 1.0e-20。当评估这一点时,它会四舍五入到1.0,其中f具有奇异性,并且可能发生任何事情。这就是为什么要先转换函数,以使奇点为0的原因。

1 / sqrt(1 - x**2)的情况下,左奇点和右奇点均为1 / numpy.sqrt(-x**2 + 2*x)。有了quadpy(属于我的一个项目),一个人就会得到

import numpy
import quadpy

# def f(x):
#    return 1 / numpy.sqrt(1 - x ** 2)

val, error_estimate = quadpy.line_segment.tanh_sinh_lr(
      [lambda x: 1 / numpy.sqrt(-x**2 + 2*x)],  # = 1 / sqrt(1 - (x-1)**2)
      [lambda x: 1 / numpy.sqrt(-x**2 + 2*x)],  # = 1 / sqrt(1 - (-(x-1))**2)
      2,  # length of the interval
      1.0e-10
      )
print(val, val - numpy.pi)
3.1415926533203944 -2.693987255497632e-10