Tanh-sinh quadrature numerical integration method converging to wrong value

折月煮酒 提交于 2019-12-24 03:23:15

问题


I'm trying to write a Python program to use Tanh-sinh quadrature to compute the value of:

but although the program converges to a sensible value with no errors in every case, it's not converging to the correct value (which is pi for this particular integral) and I can't find the problem.

Instead of asking for a desired level of accuracy, the program asks for the number of function evaluations wanted, to make comparisons of convergence with simpler integration methods easier. The number of evaluations needs to be an odd number as the approximation used is

Can anyone suggest what I might have done wrong?

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

回答1:


For what it's worth, Scipy has numerical integration functions

For instance,

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

gives the result

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

where the second number in the tuple is the absolute error.

That said, I was able to get your program to work with some tuning (although this is just an initial attempt):

1) Set the step size h to 0.0002 (roughly 1/2^12) as suggested by this paper

But note - the paper actually suggests altering the step size iteratively - with a fixed step size you will reach a point where the sinh or cosh grow too large for large enough values of kh. It would probably be better to attempt an implementation based on that paper's approach.

But sticking to the question at hand,

2) Make sure you set enough iterations for the integration to really converge , i.e. enough iterations that math.fabs(w_k * func(x_k)) < 1.0e-9

With these tunings, I was able to get the integration to converge to the correct value of pi to 4 significant figures using > 30000 iterations.

For instance with 31111 iterations, the value of pi computed was 3.14159256208

Example code with modifications (note I replaced sum with thesum, sum is the name of a Python built-in function):

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



回答2:


Using the multiprecision library 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

gives for the error

-3.751800610920472803216259350430460844457732874052618682441090144344372471319795201134275503228835472e-45



回答3:


I think part of the problem might be due to range and step size. I've modified the code so you can put in the range and step size separately and rewritten some of the maths. It seems to give the right answers. Try for example 5 and 0.1 as inputs.

A particular problem is calculating math.cosh(0.5 * math.pi * math.sinh(k * h)) as k * h get large math.sinh(k * h) grows exponentially and calculating math.cosh of that can be hard. import math

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



回答4:


You have to realise that +1 and -1 are singular points of your integrand, f(x)-->+infinity as x-->+1,-1. As such, you can use your favourite quadrature formula away from the boundary points, but you have to work out a special quadrature based on a local expansion of f(x) in a neighbourhood of them.

Sketch of the approach:

  1. Pick some epsilon<<1.

  2. Decompose the integral I into smooth and singular parts:

    • I_smooth is the integral inside [-1+epsilon, 1-epsilon]
    • I_singular are the integrals from [-1, -1+epsilon] and [1-epsilon, 1].
  3. Apply your standard quadrature rule inside the interval [-1+epsilon, 1-epsilon] to get I_smooth.

  4. Perform a local expansion around the singular points (e.g. 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) + ..
    

    which is just a Taylor expansion about x=1 of f(x)*sqrt(1-x) premultiplied by 1/sqrt(1-x). (Unfortunately you have to do some math and work out the Taylor expansion unless you have Mathematica or you can find it tabulated somewhere.)

  5. Each single term fn(x-1) = an*(1-x)^n/sqrt(1-x) can be integrated exactly (it's just a power function). Let Fn be the integral of fn from 1-epsilon to 1. Approximate I_singular = F0 + F1 + F2 + ... up to the order you want.

  6. Finally:

    I = I_smooth + I_singular  
    

Note: to push accuracy you should not make epsilon too small because the blow-up of the integral makes the problem numerically ill-conditioned, but rather increase the order of the Taylor expansion.




回答5:


There are a bunch of pitfalls when it comes to tanh-sinh quadrature, one being that the integrand needs to be evaluated very closely to the interval boundaries, at distances less than machine precision, e.g., 1.0 - 1.0e-20 in the original example. When this point is evaluated, it rounds to 1.0 at which f has a singularity, and anything can happen. That's why you have you'll first have to transform the function such that the singularities sit at 0.

In the case of 1 / sqrt(1 - x**2), this is 1 / numpy.sqrt(-x**2 + 2*x) for both the left and the right singularity. With quadpy (a project of mine), one then gets

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


来源:https://stackoverflow.com/questions/24986588/tanh-sinh-quadrature-numerical-integration-method-converging-to-wrong-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!