Skip to article frontmatterSkip to article content

6.9Piecewise polynomial interpolation

#%config InlineBackend.figure_format = 'svg'
from pylab import *
from matplotlib import animation
from IPython.display import HTML

Consider a partition of [a,b][a,b] into a grid

a=x0<x1<<xN=ba = x_0 < x_1 < \ldots < x_N = b

The points xjx_j are called knots, breakpoints or nodes. Let p(x)p(x) be a polynomial on each of the subintervals/elements/cells

[x0,x1],[x1,x2],,[xN1,xN][x_0,x_1], \quad [x_1,x_2], \quad \ldots, [x_{N-1},x_N]

i.e.,

p(x)Pr,x[xi,xi+1]p(x) \in \poly_r, \qquad x \in [x_i,x_{i+1}]

Note that p(x)p(x) need not be a polynomial in [a,b][a, b].

In general, no restrictions on the continuity of p(x)p(x) or its derivatives at the end points of the sub-intervals might exist. Putting additional continuity constraints leads to different types of approximations. In this chapter, we are interested in globally continuous approximations.

6.9.1Piecewise linear interpolation

Let us demand the we want a continuous approximation. We are given the function values yi=f(xi)y_i = f(x_i) at all the nodes of our grid. We can construct the polynomial in each sub-interval

p(x)=xxi+1xixi+1yi+xxixi+1xiyi+1,xixxi+1p(x) = \frac{x - x_{i+1}}{x_i - x_{i+1}} y_i + \frac{x - x_i}{x_{i+1} - x_i} y_{i+1}, \qquad x_i \le x \le x_{i+1}

Clearly, such a function is piecewise linear and continuous in the whole domain. Here is the approximation with N=10N=10 intervals of f(x)=sin(4πx)f(x) = \sin(4 \pi x) for x[0,1]x \in [0,1].

fun = lambda x: sin(4*pi*x)
xmin, xmax = 0.0, 1.0
N = 10
x = linspace(xmin,xmax,N+1)
f = fun(x)

xe = linspace(xmin,xmax,100) # for plotting only
fe = fun(xe)

fig, ax = subplots()
line1, = ax.plot(xe,fe,'-',linewidth=2,label='Function')
line2, = ax.plot(x,f,'or--',linewidth=2,label='Interpolant')
ax.set_xticks(x), ax.grid()
ax.set_xlabel('x'), ax.legend()
ax.set_title('Approximation with N = ' + str(N));
<Figure size 640x480 with 1 Axes>

The vertical grid lines show the boundaries of the sub-intervals.

From the error estimate of polynomial interpolation,

f(x)p(x)=12!(xxi)(xxi+1)f(ξ),ξ[xi,xi+1]f(x) - p(x) = \frac{1}{2!} (x-x_i)(x - x_{i+1}) f''(\xi), \qquad \xi \in [x_i, x_{i+1}]

we get the local interpolation error estimate

maxx[xi,xi+1]f(x)p(x)hi+128maxt[xi,xi+1]f(t),hi+1=xi+1xi\max_{x \in [x_i,x_{i+1}]} |f(x) - p(x)| \le \frac{h_{i+1}^2}{8} \max_{t \in [x_i,x_{i+1}]} |f''(t)|, \qquad h_{i+1} = x_{i+1} - x_i

from which we get the global error

maxx[a,b]f(x)p(x)h28maxt[a,b]f(t),h=maxihi\max_{x \in [a,b]} |f(x) - p(x)| \le \frac{h^2}{8} \max_{t \in [a,b]} |f''(t)|, \qquad h = \max_i h_i

Clearly, we have convergence as NN \to \infty, h0h \to 0. The convergence is quadratic; if hh becomes h/2h/2, the error reduces by a factor of 1/41/4.

6.9.1.1Finite element form

We have written down the polynomial in a piecewise manner but we can also write a global view of the polynomial by defining some basis functions. Since

x[xi,xi+1]:p(x)=xxi+1xixi+1yi+xxixi+1xiyi+1x \in [x_i, x_{i+1}]: \qquad p(x) = \clr{red}{\frac{x - x_{i+1}}{x_i - x_{i+1}}} y_i + \frac{x - x_i}{x_{i+1} - x_i} y_{i+1}

and similarly

x[xi1,xi]:p(x)=xxixi1xiyi1+xxi1xixi1yix \in [x_{i-1}, x_{i}]: \qquad p(x) = \frac{x - x_{i}}{x_{i-1} - x_{i}} y_{i-1} + \clr{red}{\frac{x - x_{i-1}}{x_{i} - x_{i-1}}} y_{i}

Let us define

ϕi(x)={xxi1xixi1,xi1xxixi+1xxi+1xi,xixxi+10,otherwise\phi_i(x) = \begin{cases} \frac{x - x_{i-1}}{x_i - x_{i-1}}, & x_{i-1} \le x \le x_i \\ \frac{x_{i+1} - x}{x_{i+1} - x_i}, & x_i \le x \le x_{i+1} \\ 0, & \textrm{otherwise} \end{cases}

These are triangular hat functions with compact support. Then the piecewise linear approximation is given by

p(x)=i=0Nyiϕi(x),x[a,b]p(x) = \sum_{i=0}^N y_i \phi_i(x), \qquad x \in [a,b]

For N=6N=6, here are the basis functions.

Source
def basis1(x,i,h):
    xi = i*h; xl,xr = xi-h,xi+h
    y = empty_like(x)
    for j in range(len(x)):
        if x[j]>xr or x[j] < xl:
            y[j] = 0
        elif x[j]>xi:
            y[j] = (xr - x[j])/h
        else:
            y[j] = (x[j] - xl)/h
    return y

N = 6 # Number of elements
xg = linspace(0.0, 1.0, N+1)
h = 1.0/N

# Finer grid for plotting purpose
x = linspace(0.0,1.0,1000)

fig = figure()
ax = fig.add_subplot(111)
ax.set_xlabel('$x$'); ax.set_ylabel('$\\phi$')
line1, = ax.plot(xg,0*xg,'o-')
line2, = ax.plot(x,0*x,'r-',linewidth=2)
for i in range(N+1):
    node = '$x_'+str(i)+'$'
    ax.text(xg[i],-0.02,node,ha='center',va='top')
ax.axis([-0.1,1.1,-0.1,1.1])
ax.set_xticks(xg)
grid(True), close()

def fplot(i):
    y = basis1(x,i,h)
    line2.set_ydata(y)
    t = '$\\phi_'+str(i)+'$'
    ax.set_title(t)
    return line2,

anim = animation.FuncAnimation(fig, fplot, frames=N+1, repeat=False)
HTML(anim.to_jshtml())
Loading...

Note each ϕi(x)\phi_i(x) takes value 1 at one of the nodes, and is zero at all other nodes, i.e.,

ϕi(xj)={1,i=j0,ij\phi_i(x_j) = \begin{cases} 1, & i = j \\ 0, & i \ne j \end{cases}

similar to the Lagrange polynomials.

6.9.1.2An adaptive algorithm

How do we choose the number of elements NN ? Having chosen some reasonable value of NN, we can use the local error estimate (6) to improve the approximation which tells us that the error is large in intervals where f|f''| is large. The error can be reduced by decreasing the length of the interval.

We have to first estimate the error in an interval Ii=[xi1,xi]I_i = [x_{i-1},x_i]

hi22Hi,Himaxt[xi1,xi]f(t)\frac{h_i^2}{2} H_i, \qquad H_i \approx \max_{t \in [x_{i-1},x_i]} |f''(t)|

by some finite difference formula HiH_i. If we want the error to be ϵ\le \epsilon, then we must choose hih_i so that

hi28Hiϵ\frac{h_i^2}{8} H_i \le \epsilon

If in some interval, it happens that

hi28Hi>ϵ\frac{h_i^2}{8} H_i > \epsilon

divide IiI_i into two intervals

[xi1,12(xi1+xi)][12(xi1+xi),xi]\left[ x_{i-1}, \half(x_{i-1}+x_i) \right] \qquad \left[ \half(x_{i-1}+x_i), x_i \right]

The derivative in the interval [xi1,xi][x_{i-1},x_i] is not known; we can estimate it by fitting a cubic polynomial to the data {xi2,xi1,xi,xi+1}\{x_{i-2},x_{i-1},x_i,x_{i+1}\} using polyfit and finding the maximum value of its second derivative at xi1,xix_{i-1},x_i to estimate HiH_i. P = polyfit(x,f,3) returns a polynomial in the form

P[0]x3+P[1]x2+P[2]x+P[3]P[0]*x^3 + P[1]*x^2 + P[2]*x + P[3]

whose second derivative is

6P[0]x+2P[1]6*P[0]*x + 2*P[1]

We can start with a uniform grid of NN intervals and apply the above idea as follows.

6.9.2Piecewise quadratic interpolation

Consider a grid

a=x0<x1<<xN=ba = x_0 < x_1 < \ldots < x_N = b

and define the mid-points of the intervals

xi12=12(xi1+xi),i=1,2,,Nx_\imh = \half(x_{i-1} + x_i), \qquad i=1,2,\ldots,N

We are given the information

yi=f(xi),i=0,1,,Nyi12=f(xi12),i=1,2,,N\begin{align} y_i &= f(x_i), \quad & i=0,1,\ldots,N \\ y_\imh &= f(x_\imh), \quad & i=1,2,\ldots,N \end{align}

In the interval Ii=[xi1,xi]I_i = [x_{i-1},x_i] we know three values

yi1,yi12,yiy_{i-1}, y_\imh, y_i

and we can determine a quadratic polynomial that interpolates these values

p(x)=yi1ϕ0(xxi1hi)+yi12ϕ1(xxi1hi)+yiϕ2(xxi1hi)p(x) = y_{i-1} \phi_0\left(\frac{x-x_{i-1}}{h_i}\right) + y_\imh \phi_1\left(\frac{x- x_{i-1}}{h_i}\right) + y_i \phi_2\left(\frac{x-x_{i-1}}{h_i}\right)

where ϕ0,ϕ1,ϕ2\phi_0,\phi_1,\phi_2 are Lagrange polynomials given by

ϕ0(ξ)=2(ξ12)(ξ1),ϕ1(ξ)=4ξ(1ξ),ϕ2(ξ)=2ξ(ξ12)\phi_0(\xi) = 2(\xi - \shalf)(\xi - 1), \qquad \phi_1(\xi) = 4\xi(1 - \xi), \qquad \phi_2(\xi) = 2\xi(\xi-\shalf)

Note that the quadratic interpolation is continuous but may not be differentiable.

Here is the piecewise quadratic approximation of f(x)=sin(4πx)f(x) = \sin(4 \pi x).

Source
fun = lambda x: sin(4*pi*x)
xmin, xmax = 0.0, 1.0

N = 7         # no of elements
m = 2*N + 1   # no of data
x = linspace(xmin,xmax,m)
f = fun(x)

xe = linspace(xmin,xmax,500)
fe = fun(xe)

plot(x,f,'o',label='Data')
plot(x,0*f,'o')
plot(xe,fe,'k--',lw=1,label='Function')

# Local coordinates
xi = linspace(0,1,10)

phi0 = lambda x: 2*(x - 0.5)*(x - 1.0)
phi1 = lambda x: 4*x*(1.0 - x)
phi2 = lambda x: 2*x*(x - 0.5)

j = 0
for i in range(N):
    z  = x[j:j+3]
    h  = z[-1] - z[0]
    y  = f[j:j+3]
    fu = y[0]*phi0(xi) + y[1]*phi1(xi) + y[2]*phi2(xi)
    plot(xi*h + z[0], fu, lw=2)
    j += 2
title('Piecewise quadratic with '+str(N)+' elements')
legend(), xticks(x[::2]), grid(True), xlabel('x');
<Figure size 640x480 with 1 Axes>

The vertical grid lines show the elements. The quadratic interpolant is shown in different color inside each element.

We can write the approximation in the form

p(x)=k=02NzkΦk(x),x[a,b]p(x) = \sum_{k=0}^{2N} z_k \Phi_k(x), \qquad x \in [a,b]

where

z=(y0,y12,y1,,yN1,yN1/2,yN)R2N+1z = (y_0, y_\half, y_1, \ldots, y_{N-1}, y_{N-1/2}, y_N) \in \re^{2N+1}

but this form should not be used in computations. For any x[xi1,xi]x \in [x_{i-1},x_i] only three terms in the sum survive reducing to the form (26).

6.9.3Piecewise degree kk interpolation

Consider a grid

a=x0<x1<<xN=ba = x_0 < x_1 < \ldots < x_N = b

In each interval [xi1,xi][x_{i-1},x_i] we want to construct a degree kk polynomial approximation, so we need to know k+1k+1 function values at k+1k+1 distinct points

xi1=xi,0<xi,1<<xi,k=xix_{i-1} = x_{i,0} < x_{i,1} < \ldots < x_{i,k} = x_i

Then the degree kk polynomial is given by

p(x)=j=0kf(xi,j)ϕj(xxi1hi)p(x) = \sum_{j=0}^k f(x_{i,j}) \phi_j\left( \frac{x-x_{i-1}}{h_i} \right)

where ϕj:[0,1]R\phi_j : [0,1] \to \re are Lagrange polynomials

ϕj(ξ)=l=0,ljkξξlξjξl,ξl=xi,lxi1hi\phi_j(\xi) = \prod_{l=0, l \ne j}^k \frac{\xi - \xi_l}{\xi_j - \xi_l}, \qquad \xi_l = \frac{x_{i,l} - x_{i-1}}{h_i}

with the property

ϕj(ξl)=δjl,0j,lk\phi_j(\xi_l) = \delta_{jl}, \quad 0 \le j,l \le k

Since we choose the nodes in each interval such that xi,0=xi1x_{i,0} = x_{i-1} and xi,k=xix_{i,k} = x_i, the piecewise polynomial is continuous, but may not be differentiable.

6.9.4Error estimate in Sobolev spaces^*

The estimate we have derived required functions to be differentiable. Here we use weaker smoothness properties.