5.5. Gaussian quadrature#
Gaussian quadrature selects the points in an optimal manner (instead of an equally-spaced way). Consequently, the nodes \(x_j,~1\leq j \leq n\) in the interval [a, b] and coefficients \(w_j,~1\leq j \leq n\), are determined to minimize the anticipated error conveyed in the approximation
Whenever \hlg{\(f(x)\) is a polynomial of degree (up to) \(2n-1\), this approximation is exact [Burden and Faires, 2005]. Moreover, usually, these approximations are done using \([-1,~1]\) interval.
For example, for \(f(x) = a_{0} + a_{1} x + a_{2}x^{2} + a_{3}x^{3}\) with \(n=2\), we have,
On the other hand, since
and
we have,
Therefore, we need to identify \(w_{1}\), \(w_{2}\), \(x_{1}\), and \(x_{2}\) from the following system of equations,
Solving this system of equations leads to \(w_{1} = w_{2} = 1\) and \(x_{1} = -\dfrac{\sqrt{3}}{3} = -x_{2}\). Hence,
This formula (5.64) has a precision of 3. This it delivers the exact solution for any polynomial of degree 3 or less.
import numpy as np
def TwoPointGaussian(f):
'''
Parameters
----------
f : function
DESCRIPTION. A function. Here we use lambda functions
Returns
-------
S : float
DESCRIPTION. Numerical Integration of f(x) on [a,b]
through TwoPointGaussian
'''
return f(-np.sqrt(3)/3) + f(np.sqrt(3)/3)
function [Int] = TwoPointGaussian(f)
%{
Parameters
----------
f : function
DESCRIPTION. A function. Here we use lambda functions
Returns
-------
S : float
DESCRIPTION. Numerical Integration of f(x) on [a,b]
through TwoPointGaussian
%}
Int = f(-sqrt(3)/3) + f(sqrt(3)/3);
First, we have
from IPython.display import display, Latex
import sys
sys.path.insert(0,'..')
from hd_Numerical_Integration_Algorithms import TwoPointGaussian
Int = TwoPointGaussian(f = lambda x: x**2)
display(Latex('''\\left(-\\frac{\\sqrt{3}}{3}\\right) + f\\left(\\frac{\\sqrt{3}}{3}\\right) = %.4f''' % Int))
Moreover, $\(\int _{1}^{-1}x^{3} - x^{2} + 1\,dx =\frac{1}{4} x^{4}\Big|_{-1}^{1} - \frac{1}{3} x^{3}\Big|_{-1}^{1} + x\Big|_{-1}^{1}= \frac{4}{3}\)$.
Trying this numerically,
Int = TwoPointGaussian(f = lambda x: x**3-x**2+1)
display(Latex('''\\left(-\\frac{\\sqrt{3}}{3}\\right) + f\\left(\\frac{\\sqrt{3}}{3}\\right) = %.4f''' % Int))
5.5.1. Legendre polynomials#
The Legendre polynomials is a collection \(\{P_0(x), P_1(x), \ldots , P_n(x), \ldots \}\) with properties [Burden and Faires, 2005]:
\(P_n(x),~n\in \mathbb{N}\) is a monic (univariate) polynomial of degree \(n\).
\({\displaystyle \int_{-1}^{1} P(x)P_{n}(x)\,dx = 0}\) whenever \(P(x)\) is a polynomial of degree less than \(n\).
~\ \noindent Rodrigues’ formula provides a simple formulation for the Legendre polynomials \cite{hom2006, aratyn2006short}:
This equation allows for the derivation of a large number of \(P_{n}\) polynomials. The above also can be expressed as follows \cite{aratyn2006short},
The first few Legendre polynomials:
\(P_{0}(x) = 1\),
\(P_{1}(x) = x\),
\(P_{2}(x) = \dfrac{1}{2} (3x^2 - 1)\),
\(P_{3}(x) = \dfrac{1}{2} (5x^3 - 3x)\),
\(P_{4}(x) = \dfrac{1}{8} (35x^4 - 30x^2 + 3)\),
\(P_{5}(x) = \dfrac{1}{16} (63x^5 - 70x^3 + 15x)\),
\(P_{6}(x) = \dfrac{1}{16} (231x^6 - 315x^4 + 105x^2 - 5)\),
In MATLAB, \href{https://www.mathworks.com/help/symbolic/sym.legendrep.html}{legendreP(n,x)} returns the \(n\)th degree Legendre polynomial at \(x\). For example,
legendreP(3, x)
ans = (5x^3)/2 - (3x)/2
In Python, \href{https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.legendre.html}{scipy} includes legendre polynomials. For example, to generate the 3rd-order Legendre polynomial:
from scipy.special import legendre
from numpy import poly1d
print(legendre(3))
Output: 3 2.5 x - 1.5 x
The following proposition is Theorem 4.7 from [Burden and Faires, 2005].
Theorem
Let \(x_{1}\), \(x_{2}\), \ldots , \(x_{n}\) be the roots of the \(n\)th Legendre polynomial \(P_n(x)\) and that for each \(i = 1,~2,~\ldots,~n\), the numbers \(w_{i}\) are defined as follows,
Furthermore, if \(P(x)\) is any polynomial of degree less than \(2n\), then, the coefficient \(w_i\) can be calculated using
Proof: First, let’s assume that the degree of \(P(x)\) is less than \(n\). Then, we can express \(P(x)\) in terms of \((n - 1)\)st Lagrange coefficient polynomials with nodes at the roots of the \(n\)th Legendre polynomial \(P_{n}(x)\). Note that, for this representation, the error term would include the \(n\)th derivative of \(P(x)\). Since we assumed that \(P(x)\) is of degree less than \(n\), the \(n\)th derivative of \(P(x)\) is 0, and this representation would be exact (why!?). Therefore,
and
Therefore, the result is true for polynomials of degree less than \(n\).
Next, let’s assume a polynomial \(P(x)\) of degree betweeb \(n\) and \(2n\). Also, divide \(P(x)\) by the \(n\)th Legendre polynomial \(P_n(x)\). This assumption can lead to two polynomials with less than degree \(n\), \(Q(x)\) and \(R(x)\).
Observe that \(x_{i}\) is a root of \(P_n(x)\) for each \(i = 1, 2, \ldots , n\). Therefore,
Now, since the degree of the polynomial \(Q(x)\) is less than \(n\), so (by Legendre property (2)),
Furthermore, since \(R(x)\) is a polynomial of degree less than \(n\), we have,
Therefore
These coefficients can be calculated using numpy leggauss function. For example, for polynomials of degree \(n\) with \(n=1,2\ldots,8\), we have
import pandas as pd
pd.options.mode.chained_assignment = None
Table = pd.DataFrame(columns = ['n', 'xi', 'wi'])
N = 9
Table['n'] = np.arange(1, N)
for n in range(Table.shape[0]):
Table['xi'][n], Table['wi'][n] = np.round(np.polynomial.legendre.leggauss(Table.loc[n, 'n']), 6)
display(Table.style.set_properties(subset=['n'],
**{'background-color': 'Honeydew', 'color': 'Black','border-color': 'DarkGreen'}).hide(axis='index'))
5.5.2. Change of interval#
To approximate the integral of a function over an interval \([a,~b]\), this interval should be transformed into an integral over \([-1,~1]\) before using the Gaussian quadrature rule. This can be done as follows [Bojanov and Petrov, 2001, Wendroff, 2014]:
Now, a \(n\)-point Gaussian quadrature over an interval \([a, b]\) can be expressed as follows:
def GaussQuadrature(f, a, b, n):
'''
Parameters
----------
f : function
DESCRIPTION. A function. Here we use lambda functions
a : float
DESCRIPTION. a is the left side of interval [a, b]
b : float
DESCRIPTION. b is the right side of interval [a, b]
N : int
DESCRIPTION. Number of xn points
Returns
-------
S : float
DESCRIPTION. Numerical Integration of f(x) on [a,b]
through GaussQuadrature rule
'''
x, w = np.polynomial.legendre.leggauss(n)
return 0.5*(b-a)*sum(w*f(0.5*(b-a)*x+0.5*(b+a)))
import sys
sys.path.insert(0,'..')
import hd_tools as hd
Example: Approximate each integration using 2-point and 3-point Gaussian quadrature rules and the change of interval formula
a. \task \({\displaystyle \int_{0}^{2}x^{2}\,dx}\),
b. \task \({\displaystyle \int_{-2}^{4}(x^{3} - x^{2} + 1)\,dx}\).
a. First, note that,
Observe that the degree \(x^{2}\) is less than \hl{\(2n\)} and we know that formula \eqref{gq_eq.04} would provide the exact value. Using the 2-point Gaussian quadrature rule, \hlg{\(n=2\)}, we have,
Now, using Table \ref{Table_Legendre_coefficients}
Using the 3-point Gaussian quadrature rule, \hlg{\(n=3\)}, similarly, the degree of the polynomial is 2 and it is less than \hl{\(2n\)}. Now, using Table \ref{Table_Legendre_coefficients}, we have,
b. Similarly,
Using the 2-point Gaussian quadrature rule, the degree of \(x^{3} - x^{2} + 1\), 3 is less than \(2n\), therefore, the approximation would be, in fact, exact. We have,
Using the 3-point Gaussian quadrature rule, \hlg{\(n=3\)}, likewise, the degree of \(x^{3} - x^{2} + 1\) is 3 and the approximation would be exact. The exact:
from hd_Numerical_Integration_Algorithms import GaussQuadrature
for n in range(2, 5):
print('Gaussian quadrature using the %i-point = %.8f' % (n, GaussQuadrature(f = lambda x : x**2, a = 0, b = 1, n = n)))