The Lagrange interpolation polynomial of degree N N N is
p ( x ) = ∑ j = 0 N f j ℓ j ( x ) where ℓ j ( x ) = ∏ i = 0 , i ≠ j N ( x − x i ) ∏ i = 0 , i ≠ j N ( x j − x i ) p(x) = \sum_{j=0}^N f_j \ell_j(x) \qquad \textrm{where} \qquad
\ell_j(x) = \frac{\prod\limits_{i=0,i\ne j}^N (x-x_i)}{\prod\limits_{i=0,i \ne j}^N(x_j - x_i)} p ( x ) = j = 0 ∑ N f j ℓ j ( x ) where ℓ j ( x ) = i = 0 , i = j ∏ N ( x j − x i ) i = 0 , i = j ∏ N ( x − x i ) The denominators in ℓ j \ell_j ℓ j can be pre-computed and stored. Then for every x x x , the evaluation of p ( x ) p(x) p ( x ) requires O ( N 2 ) O(N^2) O ( N 2 ) flops. Ideally, we desire a method which takes O ( N ) O(N) O ( N ) flops. Also, if we add a new data pair ( x N + 1 , f N + 1 ) (x_{N+1},f_{N+1}) ( x N + 1 , f N + 1 ) , then this requires a new computation from scratch. Moreover, the numerical evaluation is also unstable to round-off errors (See page 51 in Powell, Approximation theory and methods, 1981.)
Define
ℓ ( x ) = ( x − x 0 ) ( x − x 1 ) … ( x − x N ) \ell(x) = (x-x_0)(x-x_1) \ldots (x-x_N) ℓ ( x ) = ( x − x 0 ) ( x − x 1 ) … ( x − x N ) w j = 1 ∏ k = 0 , k ≠ j N ( x j − x k ) = 1 ℓ ′ ( x j ) , j = 0 , 1 , … , N w_j = \frac{1}{\prod\limits_{k=0, k \ne j}^N (x_j - x_k)} = \frac{1}{\ell'(x_j)}, \qquad j=0,1,\ldots,N w j = k = 0 , k = j ∏ N ( x j − x k ) 1 = ℓ ′ ( x j ) 1 , j = 0 , 1 , … , N The Lagrange polynomials can be written as
ℓ j ( x ) = ℓ ( x ) w j x − x j \ell_j(x) = \ell(x) \frac{w_j}{x-x_j} ℓ j ( x ) = ℓ ( x ) x − x j w j and the interpolant is given by
p ( x ) = ℓ ( x ) ∑ j = 0 N w j x − x j f j p(x) = \ell(x) \sum_{j=0}^N \frac{w_j}{x-x_j} f_j p ( x ) = ℓ ( x ) j = 0 ∑ N x − x j w j f j This is called the first form of barycentric interpolation formula and is due to Rutishauser. For each x x x
calculation of w j w_j w j requires O ( N 2 ) O(N^2) O ( N 2 ) flops, and these are
independent of x x x
calculation of ℓ ( x ) \ell(x) ℓ ( x ) requires O ( N ) O(N) O ( N ) flops
calculation of the other term requires O ( N ) O(N) O ( N ) flops
so the complexity of this formula is O ( N ) O(N) O ( N ) . Once the weights w j w_j w j are known, this permits interpolation of as many functions as desired in O ( N ) O(N) O ( N ) operations. If a new data point ( x N + 1 , f N + 1 ) (x_{N+1},f_{N+1}) ( x N + 1 , f N + 1 ) is added
The total cost of updating the formula is O ( N ) O(N) O ( N ) flops.
Since ℓ j \ell_j ℓ j form a partition of unity (Assignment, See Atkinson, page 186, problem 2)
1 = ∑ j = 0 N ℓ j ( x ) = ℓ ( x ) ∑ j = 0 N w j x − x j 1 = \sum_{j=0}^N \ell_j(x) = \ell(x) \sum_{j=0}^N \frac{w_j}{x-x_j} 1 = j = 0 ∑ N ℓ j ( x ) = ℓ ( x ) j = 0 ∑ N x − x j w j the interpolant can be written as
p ( x ) = ∑ j = 0 N w j x − x j f j ∑ j = 0 N w j x − x j p(x) = \frac{ \sum_{j=0}^N \frac{w_j}{x-x_j} f_j }{ \sum_{j=0}^N \frac{w_j}{x-
x_j} } p ( x ) = ∑ j = 0 N x − x j w j ∑ j = 0 N x − x j w j f j which is called the second form of barycentric formula . This has same complexity as the previous form. Higham has shown that barycentric formula is numerically stable. This code is general since it takes any node sets and computes the weights. For special node distributions, we can simplify the weight calculation as in next two sections.
2.1 Equispaced points ¶ If we have equispaced data points in [ − 1 , + 1 ] [-1,+1] [ − 1 , + 1 ] , with spacing h = 2 N h = \frac{2}{N} h = N 2 , the weights are given by
w j = ( − 1 ) N − j ( N j ) 1 h n n ! w_j = (-1)^{N-j} \binom{N}{j} \frac{1}{h^n n!} w j = ( − 1 ) N − j ( j N ) h n n ! 1 Since the weights appear both in numerator and denominator, we can remove any factor independent of j j j and use
w j = ( − 1 ) j ( N j ) w_j = (-1)^{j} \binom{N}{j} w j = ( − 1 ) j ( j N ) If the interval is [ a , b ] [a,b] [ a , b ] , we have to multiply the above w j w_j w j by 2 N ( b − a ) − N 2^N (b-a)^{-N} 2 N ( b − a ) − N , but this factor can also be dropped since it is common to numerator and denominator. The weights w j w_j w j change by exponentially large factors, of order approximately 2 N 2^N 2 N . This leads to ill-conditioning and Runge phenomenon. This is not a consequence of the barycentric foruma, but is intrinsic to interpolation using equispaced data.
2.2 Chebyshev points of first kind ¶ The points and weights are given by
x j = cos ( 2 j + 1 2 N + 2 π ) , w j = ( − 1 ) j sin ( 2 j + 1 2 N + 2 π ) , j = 0 , 1 , … , N x_j = \cos\left( \frac{2j+1}{2N+2} \pi \right), \quad w_j = (-1)^j \sin\left( \frac{2j+1}
{2N+2} \pi \right), \quad j=0,1,\ldots,N x j = cos ( 2 N + 2 2 j + 1 π ) , w j = ( − 1 ) j sin ( 2 N + 2 2 j + 1 π ) , j = 0 , 1 , … , N These weights vary by factors of O ( N ) O(N) O ( N ) , since
max w j min w j ≈ sin ( 2 ( N / 2 ) + 1 2 N + 2 π ) sin ( 2 ( 0 ) + 1 2 N + 2 π ) ≈ sin ( 1 2 π ) π 2 N + 2 = O ( N ) for large N \frac{\max w_j}{\min w_j} \approx \frac{ \sin\left( \frac{2(N/2)+1}{2N+2} \pi \right) }
{ \sin\left( \frac{2(0)+1}{2N+2} \pi \right)} \approx \frac{\sin(\half\pi)}{\frac{\pi}
{2N+2}} = O(N) \quad \textrm{for large $N$} min w j max w j ≈ sin ( 2 N + 2 2 ( 0 ) + 1 π ) sin ( 2 N + 2 2 ( N /2 ) + 1 π ) ≈ 2 N + 2 π sin ( 2 1 π ) = O ( N ) for large N not exponentially as in case of uniformly spaced points. The weights can be computed in O ( N ) O(N) O ( N ) operations. Newton interpolation always requires O ( N 2 ) O(N^2) O ( N 2 ) operations for the divided differences.
2.3 Chebyshev points of second kind ¶ The points are given by
x i = cos ( N − i N π ) , i = 0 , 1 , 2 , … , N x_i = \cos\left( \frac{N-i}{N} \pi \right), \qquad i=0,1,2,\ldots,N x i = cos ( N N − i π ) , i = 0 , 1 , 2 , … , N and the weights are
w 0 = 1 2 , w i = ( − 1 ) i , i = 1 , 2 , … , N − 1 , w N = 1 2 ( − 1 ) N w_0 = \half, \qquad w_i = (-1)^i, \quad i=1,2,\ldots,N-1, \qquad w_N = \half (-1)^N w 0 = 2 1 , w i = ( − 1 ) i , i = 1 , 2 , … , N − 1 , w N = 2 1 ( − 1 ) N Let us interpolate the Runge function on [ − 1 , + 1 ] [-1,+1] [ − 1 , + 1 ]
f ( x ) = 1 1 + 16 x 2 f(x) = \frac{1}{1 + 16 x^2} f ( x ) = 1 + 16 x 2 1 using the Barycentric formula
p ( x ) = ∑ i = 0 N w i x − x i f i ∑ i = 0 N w i x − x i p(x) = \frac{ \sum_{i=0}^N \frac{w_i}{x - x_i} f_i }{ \sum_{i=0}^N \frac{w_i}{x - x_i} } p ( x ) = ∑ i = 0 N x − x i w i ∑ i = 0 N x − x i w i f i where the weight w i w_i w i is given by
w i = 1 ∏ j = 0 , j ≠ i N ( x i − x j ) w_i = \frac{1}{\prod_{j=0,j\ne i}^N (x_i - x_j)} w i = ∏ j = 0 , j = i N ( x i − x j ) 1 This is the function we want to interpolate.
def fun(x):
f = 1.0/(1.0+16.0*x**2)
return f
We next write a function that constructs and evaluates the Lagrange interpolation.
# X[nX], Y[nX] : Data points
# x[nx] : points where we want to evaluate
def LagrangeInterpolation(X,Y,x):
nx = size(x)
nX = size(X)
# compute the weights
w = ones(nX)
for i in range(nX):
for j in range(nX):
if i != j:
w[i] = w[i]/(X[i]-X[j])
# Evaluate the polynomial at x
num= zeros(nx)
den= zeros(nx)
eps=1.0e-14
for i in range(nX):
num = num + Y[i]*w[i]/((x-X[i])+eps)
den = den + w[i]/((x-X[i])+eps)
f = num/den
return f
xmin, xmax = -1.0, +1.0
N = 15 # Degree of polynomial
We first interpolate on uniformly spaced points.
X = linspace(xmin,xmax,N+1)
Y = fun(X)
x = linspace(xmin,xmax,100)
fi = LagrangeInterpolation(X,Y,x)
fe = fun(x)
plot(x,fe,'b--',x,fi,'r-',X,Y,'o')
title('Degree '+str(N)+' using uniform points')
legend(("True function","Interpolation","Data"),loc='lower center');
Next, we interpolate on Chebyshev points.
X = cos(linspace(0.0,pi,N+1))
Y = fun(X)
x = linspace(xmin,xmax,100)
fi = LagrangeInterpolation(X,Y,x)
fe = fun(x)
plot(x,fe,'b--',x,fi,'r-',X,Y,'o')
title('Degree '+str(N)+' using Chebyshev points')
legend(("True function","Interpolation","Data"),loc='upper right');
Example 2 (Barycentric Lagrange interpolation on Chebyshev points)
Let us interpolate the Runge function on [ − 1 , + 1 ] [-1,+1] [ − 1 , + 1 ]
f ( x ) = 1 1 + 16 x 2 f(x) = \frac{1}{1 + 16 x^2} f ( x ) = 1 + 16 x 2 1 using the Barycentric formula
p ( x ) = ∑ i = 0 N ′ ( − 1 ) i x − x i f i ∑ i = 0 N ′ ( − 1 ) i x − x i p(x) = \frac{ \sum\limits_{i=0}^N{}' \frac{(-1)^i}{x - x_i} f_i }{ \sum\limits_{i=0}^N{}' \frac{(-1)^i}{x - x_i} } p ( x ) = i = 0 ∑ N ′ x − x i ( − 1 ) i i = 0 ∑ N ′ x − x i ( − 1 ) i f i where the prime on the summation means that the first and last terms must be multiplied by a factor of half.
Define the function to be interpolated.
def fun(x):
f = 1.0/(1.0+16.0*x**2)
return f
The next function evaluates the Lagrange interpolation using Chebyshev points.
def BaryInterp(X,Y,x):
nx = size(x)
nX = size(X)
f = 0*x
# Compute weights
w = (-1.0)**arange(0,nX)
w[0] = 0.5*w[0]
w[nX-1] = 0.5*w[nX-1]
# Evaluate barycentric foruma at x values
for i in range(nx):
num, den = 0.0, 0.0
for j in range(nX):
if abs(x[i]-X[j]) < 1.0e-15:
num = Y[j]
den = 1.0
break
else:
num += Y[j]*w[j]/((x[i]-X[j]))
den += w[j]/(x[i]-X[j])
f[i] = num/den
return f
xmin, xmax = -1.0, +1.0
N = 19 # degree of polynomial
Let us interpolate on Chebyshev points.
X = cos(linspace(0.0,pi,N+1))
Y = fun(X)
x = linspace(xmin,xmax,100)
fi = BaryInterp(X,Y,x)
fe = fun(x)
figure(figsize=(8,5))
plot(x,fe,'b--',x,fi,'r-',X,Y,'o')
legend(("True function","Interpolation","Data"),loc='upper right')
axis([-1.0,+1.0,0.0,1.1]);
3 Using scipy.interpolate
¶ scipy.interpolate provides many methods for polynomial interpolation, including barycentric form.
fun = lambda x: 1.0/(1.0 + 16.0 * x**2)
N = 20
xi = cos(linspace(0.0,pi,N+1))
yi = fun(xi)
x = linspace(xmin,xmax,100)
from scipy.interpolate import barycentric_interpolate
y = barycentric_interpolate(xi, yi, x)
plot(x, fun(x), '--', label='True function')
plot(xi, yi, 'o', label='Data')
plot(x, y, '-', label='Interpolant')
legend(), xlabel('x'), ylabel('y')
from scipy.interpolate import BarycentricInterpolator
P = BarycentricInterpolator(xi, yi)
plot(x, fun(x), '--', label='True function')
plot(xi, yi, 'o', label='Data')
plot(x, P(x), '-', label='Interpolant')
legend(), xlabel('x'), ylabel('y')