Morten Hjorth-Jensen, Department of Physics, University of Oslo and Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University
Date: Aug 23, 2017
Copyright 1999-2017, Morten Hjorth-Jensen. Released under CC Attribution-NonCommercial 4.0 license
Here we will discuss some of the classical methods for integrating a function. The methods we discuss are
Equal step methods like the trapezoidal, rectangular and Simpson's rule, parts of what are called Newton-Cotes quadrature methods.
Integration approaches based on Gaussian quadrature.
The latter are more suitable for the case where the abscissas are not equally spaced. We emphasize methods for evaluating few-dimensional (typically up to four dimensions) integrals. Multi-dimensional integrals will be discussed in connection with Monte Carlo methods.
The integral
has a very simple meaning. The integral is the area enscribed by the function $f(x)$ starting from $x=a$ to $x=b$. It is subdivided in several smaller areas whose evaluation is to be approximated by different techniques. The areas under the curve can for example be approximated by rectangular boxes or trapezoids.
In considering equal step methods, our basic approach is that of approximating a function $f(x)$ with a polynomial of at most degree $N-1$, given $N$ integration points. If our polynomial is of degree $1$, the function will be approximated with $f(x)\approx a_0+a_1x$.
The algorithm for these integration methods is rather simple, and the number of approximations perhaps unlimited!
Choose a step size $h=(b-a)/N$ where $N$ is the number of steps and $a$ and $b$ the lower and upper limits of integration.
With a given step length we rewrite the integral as
The strategy then is to find a reliable polynomial approximation for $f(x)$ in the various intervals. Choosing a given approximation for $f(x)$, we obtain a specific approximation to the integral.
With this approximation to $f(x)$ we perform the integration by computing the integrals over all subintervals.
One possible strategy then is to find a reliable polynomial expansion for $f(x)$ in the smaller subintervals. Consider for example evaluating
which we rewrite as
we could attempt to approximate the function $f(x)$ with a first-order polynomial in $x$ in the two sub-intervals $x\in[x_0-h,x_0]$ and $x\in[x_0,x_0+h]$. A first order polynomial means simply that we have for say the interval $x\in[x_0,x_0+h]$
and for the interval $x\in[x_0-h,x_0]$
we can easily calculate for example the second integral as
resulting in
and adding up we obtain
which is the well-known trapezoidal rule. Concerning the error in the approximation made, $O(h^3)=O((b-a)^3/N^3)$, you should note that this is the local error. Since we are splitting the integral from $a$ to $b$ in $N$ pieces, we will have to perform approximately $N$ such operations.
This means that the global error goes like $\approx O(h^2)$. The trapezoidal reads then
and the global error reads
where $T_h$ is the trapezoidal result and $\xi \in [a,b]$.
The trapezoidal rule is easy to implement numerically through the following simple algorithm
Choose the number of mesh points and fix the step length.
calculate $f(a)$ and $f(b)$ and multiply with $h/2$.
Perform a loop over $n=1$ to $n-1$ ($f(a)$ and $f(b)$ are known) and sum up the terms $f(a+h) +f(a+2h)+f(a+3h)+\dots +f(b-h)$. Each step in the loop corresponds to a given value $a+nh$.
Multiply the final result by $h$ and add $hf(a)/2$ and $hf(b)/2$.
A simple function which implements this algorithm is as follows
double TrapezoidalRule(double a, double b, int n, double (*func)(double))
{
double TrapezSum;
double fa, fb, x, step;
int j;
step=(b-a)/((double) n);
fa=(*func)(a)/2. ;
fb=(*func)(b)/2. ;
TrapezSum=0.;
for (j=1; j <= n-1; j++){
x=j*step+a;
TrapezSum+=(*func)(x);
}
TrapezSum=(TrapezSum+fb+fa)*step;
return TrapezSum;
} // end TrapezoidalRule
void TrapezoidalRule(double a, double b, int n, double *TrapezSum, double (*func)(double) )
What happens here is that we are transferring a pointer to the name of a user defined function, which has as input a double precision variable and returns a double precision number. The function TrapezoidalRule is called as
TrapezoidalRule(a, b, n, &MyFunction )
in the calling function. We note that a, b and n are called by value, while TrapezSum and the user defined function MyFunction are called by reference.
Symbolic calculations and numerical calculations in one code!
Python offers an extremely versatile programming environment, allowing for the inclusion of analytical studies in a numerical program. Here we show an example code with the trapezoidal rule using SymPy to evaluate an integral and compute the absolute error with respect to the numerically evaluated one of the integral $4\int_0^1 dx/(1+x^2) = \pi$:
In [2]:
from math import *
from sympy import *
def Trapez(a,b,f,n):
h = (b-a)/float(n)
s = 0
x = a
for i in range(1,n,1):
x = x+h
s = s+ f(x)
s = 0.5*(f(a)+f(b)) +s
return h*s
# function to compute pi
def function(x):
return 4.0/(1+x*x)
a = 0.0; b = 1.0; n = 100
result = Trapez(a,b,function,n)
print("Trapezoidal rule=", result)
# define x as a symbol to be used by sympy
x = Symbol('x')
exact = integrate(function(x), (x, 0.0, 1.0))
print("Sympy integration=", exact)
# Find relative error
print("Relative error", abs((exact-result)/exact))
In [2]:
%matplotlib inline
from math import log10
import numpy as np
from sympy import Symbol, integrate
import matplotlib.pyplot as plt
# function for the trapezoidal rule
def Trapez(a,b,f,n):
h = (b-a)/float(n)
s = 0
x = a
for i in range(1,n,1):
x = x+h
s = s+ f(x)
s = 0.5*(f(a)+f(b)) +s
return h*s
# function to compute pi
def function(x):
return 4.0/(1+x*x)
# define integration limits
a = 0.0; b = 1.0;
# find result from sympy
# define x as a symbol to be used by sympy
x = Symbol('x')
exact = integrate(function(x), (x, a, b))
# set up the arrays for plotting the relative error
n = np.zeros(9); y = np.zeros(9);
# find the relative error as function of integration points
for i in range(1, 8, 1):
npts = 10**i
result = Trapez(a,b,function,npts)
RelativeError = abs((exact-result)/exact)
n[i] = log10(npts); y[i] = log10(RelativeError);
plt.plot(n,y, 'ro')
plt.xlabel('n')
plt.ylabel('Relative error')
plt.show()
The last example shows the potential of combining numerical algorithms with symbolic calculations, allowing us thereby to
Validate and verify our algorithms.
Including concepts like unit testing, one has the possibility to test and validate several or all parts of the code.
Validation and verification are then included naturally.
The above example allows you to test the mathematical error of the algorithm for the trapezoidal rule by changing the number of integration points. You get trained from day one to think error analysis.
Another very simple approach is the so-called midpoint or rectangle method. In this case the integration area is split in a given number of rectangles with length $h$ and height given by the mid-point value of the function. This gives the following simple rule for approximating an integral
where $f(x_{i-1/2})$ is the midpoint value of $f$ for a given rectangle. We will discuss its truncation error below. It is easy to implement this algorithm, as shown here
double RectangleRule(double a, double b, int n, double (*func)(double))
{
double RectangleSum;
double fa, fb, x, step;
int j;
step=(b-a)/((double) n);
RectangleSum=0.;
for (j = 0; j <= n; j++){
x = (j+0.5)*step+; // midpoint of a given rectangle
RectangleSum+=(*func)(x); // add value of function.
}
RectangleSum *= step; // multiply with step length.
return RectangleSum;
} // end RectangleRule
and the global error reads
where $R_h$ is the result obtained with rectangular rule and $\xi \in [a,b]$.
Instead of using the above first-order polynomials approximations for $f$, we attempt at using a second-order polynomials. In this case we need three points in order to define a second-order polynomial approximation
Using again Lagrange's interpolation formula we have
Inserting this formula in the integral of Eq. (eq:hhint) we obtain
which is Simpson's rule.
Note that the improved accuracy in the evaluation of the derivatives gives a better error approximation, $O(h^5)$ vs.\ $O(h^3)$ . But this is again the local error approximation. Using Simpson's rule we can easily compute the integral of Eq. (eq:integraldef) to be
and for the global error
with $\xi\in[a,b]$ and $S_h$ the results obtained with Simpson's method.
The method can easily be implemented numerically through the following simple algorithm
Choose the number of mesh points and fix the step.
calculate $f(a)$ and $f(b)$
Perform a loop over $n=1$ to $n-1$ ($f(a)$ and $f(b)$ are known) and sum up the terms $4f(a+h) +2f(a+2h)+4f(a+3h)+\dots +4f(b-h)$. Each step in the loop corresponds to a given value $a+nh$. Odd values of $n$ give $4$ as factor while even values yield $2$ as factor.
Multiply the final result by $\frac{h}{3}$.
In more general terms, what we have done here is to approximate a given function $f(x)$ with a polynomial of a certain degree. One can show that given $n+1$ distinct points $x_0,\dots, x_n\in[a,b]$ and $n+1$ values $y_0,\dots,y_n$ there exists a unique polynomial $P_n(x)$ with the property
with the Lagrange factors
which we recognize as the equation for a straight line.
The polynomial interpolatory quadrature of order $n$ with equidistant quadrature points $x_k=a+kh$ and step $h=(b-a)/n$ is called the Newton-Cotes quadrature formula of order $n$.
The methods we have presented hitherto are tailored to problems where the mesh points $x_i$ are equidistantly spaced, $x_i$ differing from $x_{i+1}$ by the step $h$.
The basic idea behind all integration methods is to approximate the integral
where $\omega$ and $x$ are the weights and the chosen mesh points, respectively. In our previous discussion, these mesh points were fixed at the beginning, by choosing a given number of points $N$. The weigths $\omega$ resulted then from the integration method we applied. Simpson's rule, see Eq. (eq:simpson) would give
for the weights, while the trapezoidal rule resulted in
In general, an integration formula which is based on a Taylor series using $N$ points,
will integrate exactly a polynomial $P$ of degree $N-1$. That is, the $N$ weights
$\omega_n$ can be chosen to satisfy $N$ linear equations, see chapter 3 of Ref.\ [3].
A greater precision for a given amount of numerical work can be achieved
if we are willing to give up the requirement of equally spaced integration points.
In Gaussian quadrature (hereafter GQ), both the mesh points and the weights are to
be determined. The points will not be equally spaced.
The theory behind GQ is to obtain an arbitrary weight $\omega$ through the use of so-called orthogonal polynomials. These polynomials are orthogonal in some interval say e.g., [-1,1]. Our points $x_i$ are chosen in some optimal sense subject only to the constraint that they should lie in this interval. Together with the weights we have then $2N$ ($N$ the number of points) parameters at our disposal.
Even though the integrand is not smooth, we could render it smooth by extracting from it the weight function of an orthogonal polynomial, i.e., we are rewriting
where $g$ is smooth and $W$ is the weight function, which is to be associated with a given orthogonal polynomial. Note that with a given weight function we end up evaluating the integrand for the function $g(x_i)$.
The weight function $W$ is non-negative in the integration interval $x\in [a,b]$ such that for any $n \ge 0$, the integral $\int_a^b |x|^n W(x) dx$ is integrable. The naming weight function arises from the fact that it may be used to give more emphasis to one part of the interval than another. A quadrature formula
with $N$ distinct quadrature points (mesh points) is a called a Gaussian quadrature formula if it integrates all polynomials $p\in P_{2N-1}$ exactly, that is
It is assumed that $W(x)$ is continuous and positive and that the integral
exists. Note that the replacement of $f\rightarrow Wg$ is normally a better approximation due to the fact that we may isolate possible singularities of $W$ and its derivatives at the endpoints of the interval.
The quadrature weights or just weights (not to be confused with the weight function) are positive and the sequence of Gaussian quadrature formulae is convergent if the sequence $Q_N$ of quadrature formulae
is convergent for all polynomials $p$, that is
if there exits a constant $C$ such that
where $q_{N}$ is the chosen orthogonal polynomial and $\xi$ is a number in the interval $[a,b]$. We have assumed that $f\in C^{2N}[a,b]$, viz. the space of all real or complex $2N$ times continuously differentiable functions.
In science there are several important orthogonal polynomials which arise
from the solution of differential equations. Well-known examples are the
Legendre, Hermite, Laguerre and Chebyshev polynomials. They have the following weight functions
Weight function | Interval | Polynomial |
---|---|---|
$W(x)=1$ | $x\in [-1,1]$ | Legendre |
$W(x)=e^{-x^2}$ | $-\infty \le x \le \infty$ | Hermite |
$W(x)=x^{\alpha}e^{-x}$ | $0 \le x \le \infty$ | Laguerre |
$W(x)=1/(\sqrt{1-x^2})$ | $-1 \le x \le 1$ | Chebyshev |
The importance of the use of orthogonal polynomials in the evaluation of integrals can be summarized as follows.
Methods based on Taylor series using $N$ points will integrate exactly a polynomial $P$ of degree $N-1$. If a function $f(x)$ can be approximated with a polynomial of degree $N-1$
with $N$ mesh points we should be able to integrate exactly the polynomial $P_{N-1}$.
Gaussian quadrature methods promise more than this. We can get a better polynomial approximation with order greater than $N$ to $f(x)$ and still get away with only $N$ mesh points. More precisely, we approximate
and with only $N$ mesh points these methods promise that
The reason why we can represent a function $f(x)$ with a polynomial of degree $2N-1$ is due to the fact that we have $2N$ equations, $N$ for the mesh points and $N$ for the weights.
The mesh points are the zeros of the chosen orthogonal polynomial of order $N$, and the weights are determined from the inverse of a matrix. An orthogonal polynomials of degree $N$ defined in an interval $[a,b]$ has precisely $N$ distinct zeros on the open interval $(a,b)$.
Before we detail how to obtain mesh points and weights with orthogonal polynomials, let us revisit some features of orthogonal polynomials by specializing to Legendre polynomials. In the text below, we reserve hereafter the labelling $L_N$ for a Legendre polynomial of order $N$, while $P_N$ is an arbitrary polynomial of order $N$. These polynomials form then the basis for the Gauss-Legendre method.
The Legendre polynomials are the solutions of an important differential equation in Science, namely
Here $C$ is a constant. For $m_l=0$ we obtain the Legendre polynomials as solutions, whereas $m_l \ne 0$ yields the so-called associated Legendre polynomials. This differential equation arises in for example the solution of the angular dependence of Schroedinger's equation with spherically symmetric potentials such as the Coulomb potential.
The corresponding polynomials $P$ are
which, up to a factor, are the Legendre polynomials $L_k$. The latter fulfil the orthogonality relation
and the recursion relation
With these equations we can determine a Legendre polynomial of arbitrary order with input polynomials of order $N-1$ and $N-2$.
As an example, consider the determination of $L_0$, $L_1$ and $L_2$. We have that
with $c$ a constant. Using the normalization equation $L_0(1)=1$ we get that
and using the orthogonality relation
we obtain $a=0$ and with the condition $L_1(1)=1$, we obtain $b=1$, yielding
using the orthogonality relations
and
and the condition $L_2(1)=1$ we would get
We note that we have three equations to determine the three coefficients $a$, $b$ and $c$.
Alternatively, we could have employed the recursion relation of Eq. (eq:legrecur), resulting in
which leads to Eq. (eq:l2).
The orthogonality relation above is important in our discussion on how to obtain the weights and mesh points. Suppose we have an arbitrary polynomial $Q_{N-1}$ of order $N-1$ and a Legendre polynomial $L_N(x)$ of order $N$. We could represent $Q_{N-1}$ by the Legendre polynomials through
where $\alpha_k$'s are constants.
Using the orthogonality relation of Eq. (eq:ortholeg) we see that
6 1
< < < ! ! M A T H _ B L O C K
6 2
< < < ! ! M A T H _ B L O C K
6 3
< < < ! ! M A T H _ B L O C K
and
The following simple function implements the above recursion relation of Eq. (eq:legrecur). for computing Legendre polynomials of order $N$.
// This function computes the Legendre polynomial of degree N
double Legendre( int n, double x)
{
double r, s, t;
int m;
r = 0; s = 1.;
// Use recursion relation to generate p1 and p2
for (m=0; m < n; m++ )
{
t = r; r = s;
s = (2*m+1)*x*r - m*t;
s /= (m+1);
} // end of do loop
return s;
} // end of function Legendre
The variable $s$ represents $L_{j+1}(x)$, while $r$ holds $L_j(x)$ and $t$ the value $L_{j-1}(x)$.
To understand how the weights and the mesh points are generated, we define first a polynomial of degree $2N-1$ (since we have $2N$ variables at hand, the mesh points and weights for $N$ points). This polynomial can be represented through polynomial division by
where $P_{N-1}(x)$ and $Q_{N-1}(x)$ are some polynomials of degree $N-1$ or less. The function $L_N(x)$ is a Legendre polynomial of order $N$.
Recall that we wanted to approximate an arbitrary function $f(x)$ with a polynomial $P_{2N-1}$ in order to evaluate
We can use Eq. (eq:ortholeg2) to rewrite the above integral as
due to the orthogonality properties of the Legendre polynomials. We see that it suffices to evaluate the integral over $\int_{-1}^1Q_{N-1}(x)dx$ in order to evaluate $\int_{-1}^1P_{2N-1}(x)dx$. In addition, at the points $x_k$ where $L_N$ is zero, we have
and we see that through these $N$ points we can fully define $Q_{N-1}(x)$ and thereby the integral. Note that we have chosen to let the numbering of the points run from $0$ to $N-1$. The reason for this choice is that we wish to have the same numbering as the order of a polynomial of degree $N-1$. This numbering will be useful below when we introduce the matrix elements which define the integration weights $w_i$.
We develope then $Q_{N-1}(x)$ in terms of Legendre polynomials, as done in Eq. (eq:legexpansion),
Using the orthogonality property of the Legendre polynomials we have
where we have just inserted $L_0(x)=1$!
Instead of an integration problem we need now to define the coefficient $\alpha_0$.
Since we know the values of $Q_{N-1}$ at the zeros of $L_N$, we may rewrite
Eq. (eq:lsum1) as
Multiplying both sides of Eq. (eq:lsum2) with $\sum_{j=0}^{N-1}L_{ji}^{-1}$ results in
and the matrix
yielding (if $\hat{L}$ has an inverse)
which is Eq. (eq:lsum3).
Using the above results and the fact that
we get
and if our function $f(x)$ can be approximated by a polynomial $P$ of degree $2N-1$, we have finally that
In summary, the mesh points $x_i$ are defined by the zeros of an orthogonal polynomial of degree $N$, that is $L_N$, while the weights are given by $2(L^{-1})_{0i}$.
Let us apply the above formal results to the case $N=2$. This means that we can approximate a function $f(x)$ with a polynomial $P_3(x)$ of order $2N-1=3$.
The mesh points are the zeros of $L_2(x)=1/2(3x^2-1)$. These points are $x_0=-1/\sqrt{3}$ and $x_1=1/\sqrt{3}$.
Specializing Eq. (eq:lsum2)
to $N=2$ yields
and
since $L_0(x=\pm 1/\sqrt{3})=1$ and $L_1(x=\pm 1/\sqrt{3})=\pm 1/\sqrt{3}$.
The matrix $L_{ik}$ defined in Eq. (eq:lsum2) is then
with an inverse given by
The weights are given by the matrix elements $2(L_{0k})^{-1}$. We have thence $\omega_0=1$ and $\omega_1=1$.
Obviously, there is no problem in changing the numbering of the matrix elements $i,k=0,1,2,\dots,N-1$ to $i,k=1,2,\dots,N$. We have chosen to start from zero, since we deal with polynomials of degree $N-1$.
Summarizing, for Legendre polynomials with $N=2$ we have weights
and mesh points
with $f(x)=x^2$, we approximate
the exact answer!
If we were to emply the trapezoidal rule we would get
With just two points we can calculate exactly the integral for a second-order polynomial since our methods approximates the exact function with higher order polynomial. How many points do you need with the trapezoidal rule in order to achieve a similar accuracy?
Note that the Gauss-Legendre method is not limited to an interval [-1,1], since we can always through a change of variable
rewrite the integral for an interval [a,b]
we can choose new mesh points and weights by using the mapping
and
where $x_i$ and $\omega_i$ are the original mesh points and weights in the interval $[-1,1]$, while $\tilde{x}_i$ and $\tilde{\omega}_i$ are the new mesh points and weights for the interval $[0,\infty)$.
To see that this is correct by inserting the the value of $x_i=-1$ (the lower end of the interval $[-1,1]$) into the expression for $\tilde{x}_i$. That gives $\tilde{x}_i=0$, the lower end of the interval $[0,\infty)$. For $x_i=1$, we obtain $\tilde{x}_i=\infty$. To check that the new weights are correct, recall that the weights should correspond to the derivative of the mesh points. Try to convince yourself that the above expression fulfills this condition.
If we are able to rewrite our integral of Eq. (eq:generalint) with a weight function $W(x)=x^{\alpha}e^{-x}$ with integration limits $[0,\infty)$, we could then use the Laguerre polynomials. The polynomials form then the basis for the Gauss-Laguerre method which can be applied to integrals of the form
1 0 1
< < < ! ! M A T H _ B L O C K
1 0 2
< < < ! ! M A T H _ B L O C K
1 0 3
< < < ! ! M A T H _ B L O C K
and
and the recursion relation
we could use the Hermite polynomials in order to extract weights and mesh points. The Hermite polynomials are the solutions of the following differential equation
1 1 0
< < < ! ! M A T H _ B L O C K
1 1 1
< < < ! ! M A T H _ B L O C K
1 1 2
< < < ! ! M A T H _ B L O C K
and
They fulfil the orthogonality relation
and the recursion relation
and
#include <iostream>
#include "lib.h"
using namespace std;
// Here we define various functions called by the main program
// this function defines the function to integrate
double int_function(double x);
// Main function begins here
int main()
{
int n;
double a, b;
cout << "Read in the number of integration points" << endl;
cin >> n;
cout << "Read in integration limits" << endl;
cin >> a >> b;
// reserve space in memory for vectors containing the mesh points
// weights and function values for the use of the gauss-legendre
// method
double *x = new double [n];
double *w = new double [n];
// set up the mesh points and weights
gauss_legendre(a, b,x,w, n);
// evaluate the integral with the Gauss-Legendre method
// Note that we initialize the sum
double int_gauss = 0.;
for ( int i = 0; i < n; i++){
int_gauss+=w[i]*int_function(x[i]);
}
// final output
cout << "Trapez-rule = " << trapezoidal_rule(a, b,n, int_function)
<< endl;
cout << "Simpson's rule = " << simpson(a, b,n, int_function)
<< endl;
cout << "Gaussian quad = " << int_gauss << endl;
delete [] x;
delete [] w;
return 0;
} // end of main program
// this function defines the function to integrate
double int_function(double x)
{
double value = 4./(1.+x*x);
return value;
} // end of function to evaluate
To be noted in this program is that we can transfer the name of a given function to integrate. In the table here we show the results for the first integral using various mesh points,.
$N$ | Trapez | Simpson | Gauss-Legendre |
---|---|---|---|
10 | 1.821020 | 1.214025 | 0.1460448 |
20 | 0.912678 | 0.609897 | 0.2178091 |
40 | 0.478456 | 0.333714 | 0.2193834 |
100 | 0.273724 | 0.231290 | 0.2193839 |
1000 | 0.219984 | 0.219387 | 0.2193839 |
$N$ | Trapez | Simpson | Gauss-Legendre |
---|---|---|---|
10 | 0.798861 | 0.799231 | 0.799233 |
20 | 0.799140 | 0.799233 | 0.799233 |
40 | 0.799209 | 0.799233 | 0.799233 |
100 | 0.799229 | 0.799233 | 0.799233 |
1000 | 0.799233 | 0.799233 | 0.799233 |
The following python code allows you to run interactively either in a browser or using ipython notebook. It compares the trapezoidal rule and Gaussian quadrature with the exact result from symbolic python SYMPY up to 1000 integration points for the integral
For the trapezoidal rule the results will vary strongly depending on how the infinity limit is approximated. Try to run the code below for different finite approximations to $\infty$.
In [3]:
from math import exp
import numpy as np
from sympy import Symbol, integrate, exp, oo
# function for the trapezoidal rule
def TrapezoidalRule(a,b,f,n):
h = (b-a)/float(n)
s = 0
x = a
for i in range(1,n,1):
x = x+h
s = s+ f(x)
s = 0.5*(f(a)+f(b)) +s
return h*s
# function for the Gaussian quadrature with Laguerre polynomials
def GaussLaguerreRule(n):
s = 0
xgauleg, wgauleg = np.polynomial.laguerre.laggauss(n)
for i in range(1,n,1):
s = s+ xgauleg[i]*xgauleg[i]*wgauleg[i]
return s
# function to compute
def function(x):
return x*x*exp(-x)
# Integration limits for the Trapezoidal rule
a = 0.0; b = 10000.0
# define x as a symbol to be used by sympy
x = Symbol('x')
# find result from sympy
exact = integrate(function(x), (x, a, oo))
# set up the arrays for plotting the relative error
n = np.zeros(40); Trapez = np.zeros(4); LagGauss = np.zeros(4);
# find the relative error as function of integration points
for i in range(1, 3, 1):
npts = 10**i
n[i] = npts
Trapez[i] = abs((TrapezoidalRule(a,b,function,npts)-exact)/exact)
LagGauss[i] = abs((GaussLaguerreRule(npts)-exact)/exact)
print "Integration points=", n[1], n[2]
print "Trapezoidal relative error=", Trapez[1], Trapez[2]
print "LagGuass relative error=", LagGauss[1], LagGauss[2]
So-called principal value (PV) integrals are often employed in physics, from Green's functions for scattering to dispersion relations. Dispersion relations are often related to measurable quantities and provide important consistency checks in atomic, nuclear and particle physics. A PV integral is defined as
and arises in applications of Cauchy's residue theorem when the pole $x$ lies on the real axis within the interval of integration $[a,b]$. Here ${\cal P}$ stands for the principal value. An important assumption is that the function $f(t)$ is continuous on the interval of integration.
In case $f(t)$ is a closed form expression or it has an analytic continuation in the complex plane, it may be possible to obtain an expression on closed form for the above integral.
However, the situation which we are often confronted with is that $f(t)$ is only known at some points $t_i$ with corresponding values $f(t_i)$. In order to obtain $I(x)$ we need to resort to a numerical evaluation.
To evaluate such an integral, let us first rewrite it as
One possibility is to Taylor expand $f(u+x)$ around $u=0$, and compute derivatives to a certain order as we did for the Trapezoidal rule or Simpson's rule. Since all terms with even powers of $u$ in the Taylor expansion dissapear, we have that
To evaluate higher-order derivatives may be both time consuming and delicate from a numerical point of view, since there is always the risk of loosing precision when calculating derivatives numerically. Unless we have an analytic expression for $f(u+x)$ and can evaluate the derivatives in a closed form, the above approach is not the preferred one.
Rather, we show here how to use the Gauss-Legendre method to compute Eq. (eq:deltaint). Let us first introduce a new variable $s=u/\Delta$ and rewrite Eq. (eq:deltaint) as
The integration limits are now from $-1$ to $1$, as for the Legendre polynomials. The principal value in Eq. (eq:deltaint2) is however rather tricky to evaluate numerically, mainly since computers have limited precision. We will here use a subtraction trick often used when dealing with singular integrals in numerical calculations. We introduce first the calculus relation
Subtracting this equation from Eq. (eq:deltaint2) yields
and the integrand is no longer singular since we have that $\lim_{s \rightarrow 0} (f(s+x) -f(x))=0$ and for the particular case $s=0$ the integrand is now finite.
Eq. (eq:deltaint3) is now rewritten using the Gauss-Legendre method resulting in
where $s_i$ are the mesh points ($N$ in total) and $\omega_i$ are the weights.
In the selection of mesh points for a PV integral, it is important to use an even number of points, since an odd number of mesh points always picks $s_i=0$ as one of the mesh points. The sum in Eq. (eq:deltaint4) will then diverge.
Let us apply this method to the integral
The integrand diverges at $x=t=0$. We rewrite it using Eq. (eq:deltaint3) as
since $e^x=e^0=1$. With Eq. (eq:deltaint4) we have then
The exact results is $2.11450175075....$. With just two mesh points we recall from the previous subsection that $\omega_1=\omega_2=1$ and that the mesh points are the zeros of $L_2(x)$, namely $x_1=-1/\sqrt{3}$ and $x_2=1/\sqrt{3}$. Setting $N=2$ and inserting these values in the last equation gives
With six mesh points we get even the exact result to the tenth digit
A change of variable $u=-k$ in the integral with limits from $-\infty$ to $0$ gives
where the right-hand side is no longer singular at $k=k_0$, it is proportional to the derivative $df/dk$, and can be evaluated numerically as any other integral.
Such a trick is often used when evaluating integral equations.
Here we show an example of a multidimensional integral which appears in quantum mechanical calculations.
The ansatz for the wave function for two electrons is given by the product of two $1s$ wave functions as
The integral we need to solve is the quantum mechanical expectation value of the correlation energy between two electrons, namely
double *x = new double [N];
double *w = new double [N];
// set up the mesh points and weights
GaussLegendrePoints(a,b,x,w, N);
// evaluate the integral with the Gauss-Legendre method
// Note that we initialize the sum
double int_gauss = 0.;
// six-double loops
for (int i=0;i<N;i++){
for (int j = 0;j<N;j++){
for (int k = 0;k<N;k++){
for (int l = 0;l<N;l++){
for (int m = 0;m<N;m++){
for (int n = 0;n<N;n++){
int_gauss+=w[i]*w[j]*w[k]*w[l]*w[m]*w[n]
*int_function(x[i],x[j],x[k],x[l],x[m],x[n]);
}}}}}
}
// this function defines the function to integrate
double int_function(double x1, double y1, double z1, double x2, double y2, double z2)
{
double alpha = 2.;
// evaluate the different terms of the exponential
double exp1=-2*alpha*sqrt(x1*x1+y1*y1+z1*z1);
double exp2=-2*alpha*sqrt(x2*x2+y2*y2+z2*z2);
double deno=sqrt(pow((x1-x2),2)+pow((y1-y2),2)+pow((z1-z2),2));
return exp(exp1+exp2)/deno;
} // end of function to evaluate
Using Legendre polynomials for the Gaussian quadrature is not very efficient. There are several reasons for this:
You can easily end up in situations where the integrand diverges
The limits $\pm \infty$ have to be approximated with a finite number
It is very useful here to change to spherical coordinates
and
with
where we have defined
with
For the angles we need to perform the integrations over $\theta_i\in [0,\pi]$ and $\phi_i \in [0,2\pi]$. However, for the radial part we can now either use
Gauss-Legendre wth an appropriate mapping or
Gauss-Laguerre taking properly care of the integrands involving the $r_i^2 \exp{-(2\alpha r_i)}$ terms.
$r_{\mathrm{max}}$ | Integral | Error |
---|---|---|
1.00 | 0.161419805 | 0.0313459063 |
1.50 | 0.180468967 | 0.012296744 |
2.00 | 0.177065182 | 0.0157005292 |
2.50 | 0.167970694 | 0.0247950165 |
3.00 | 0.156139391 | 0.0366263199 |