Lagrange Polynomials

This notebook uses the scipy lagrange function to compute the Lagrange polynomial.

    import numpy as np
    from numpy.polynomial.polynomial import Polynomial
    from scipy.interpolate import lagrange
    import matplotlib.pyplot as plt
    from IPython.display import Markdown as md
    plt.style.use('seaborn-poster')

For some points $x$, define some data $y$ and create the Lagrange polynomial $f$, which has coefficients $p$

    x = [0.0, 1.0, 2.0]
    y = [1.0, 3.0, 2.0]
    f = lagrange(x, y)
    p = Polynomial(f.coef[::-1]).coef
    z = ['' if i==0 else ('x' if (i==1) else 'x^{}'.format(i)) for i in np.arange(np.size(x))]
    string= [str(p[i]) + str(z[i]) for i in np.arange(np.size(x))]
    print(string)
    md("Coefficients of the interpolating polynomial: {}".format(p))
['1.0', '3.5x', '-1.5x^2']

Coefficients of the interpolating polynomial: [ 1.0, 3.5, -1.5]

def lagrange_basis(x_int, y_int, x_new):
    """
    This function takes pairs of points (x_int, y_int) and, from a set of points x_new
    computes the Lagrange polynomial to return the interpolated values y_new
    """
    l = np.zeros((np.size(x_int), np.size(x_new)), dtype=np.float32)
    i = int(0)
    for xi, yi in zip(x_int, y_int):
        l[i,:] = np.prod( [(x_new - xj) / (xi - xj) for xj in x_int if xi != xj], axis=0)
        i=i+1
    return l

Plot the data and the individual components of the polynomial

    x_new = np.arange(-1.0, 3.1, 0.1)

    test = lagrange_basis(x, y, x_new)
    fig = plt.figure()
    fig.subplots_adjust(top=1.3, hspace=0.4)
    ax=fig.add_subplot(3,1,1)
    ax.axvline(x=0.0, color="grey", linestyle="-", linewidth=1.75)
    ax.plot(x_new, f(x_new), 'b', x, y, 'ro')
    ax.set_title(r'Lagrange Interpolation', fontsize=12)
    ax.grid()
    ax.set_ylabel(r'$y$')
    ax.tick_params(axis = 'both', which = 'major', labelsize = 12)
    ax1=fig.add_subplot(3,1,2)
    ax1.axhline(y=0.0, color="grey", linestyle="-", linewidth=1.75)
    ax1.axhline(y=1.0, color="grey", linestyle="-", linewidth=1.75)
    ax1.scatter(np.array(x), np.array(y), c=['tab:blue', 'tab:orange', 'tab:green'])
    ax1.plot(x_new, test[0,:], color='tab:blue', label='$l_0(x)$' )
    ax1.plot(x_new, test[1,:], color='tab:orange', label='$l_1(x)$' )
    ax1.plot(x_new, test[2,:], color='tab:green', label='$l_2(x)$' )
    ax1.set_ylim([-0.5, 6.5])
    ax1.grid()
    ax1.set_ylabel(r'$y$')
    ax1.set_title(r'Lagrange Basis Functions', fontsize=12)
    ax1.legend(prop={'size': 12})
    ax1.tick_params(axis = 'both', which = 'major', labelsize = 12)

    ax2=fig.add_subplot(3,1,3)
    ax2.grid()
    ax2.axhline(y=0.0, color="grey", linestyle="-", linewidth=1.75)
    ax2.axhline(y=1.0, color="grey", linestyle="-", linewidth=1.75)
    ax2.scatter(np.array(x), np.array(y), c=['tab:blue', 'tab:orange', 'tab:green'])
    ax2.plot(x_new, y[0]*test[0,:], color='tab:blue', label='$y_0 l_0(x)$')
    ax2.plot(x_new, y[1]*test[1,:], color='tab:orange', label='$y_1 l_1(x)$' )
    ax2.plot(x_new, y[2]*test[2,:], color='tab:green', label='$y_2 l_2(x)$' )
    ax2.set_xlabel(r'$x$', fontsize=12)
    ax2.set_ylabel(r'$y$', fontsize=12)
    ax2.set_title(r'Scaled Lagrange Basis Functions', fontsize=12)
    ax2.legend(prop={'size': 12})
    ax2.tick_params(axis = 'both', which = 'major', labelsize = 12)

    plt.show()

Cubic Lagrange basis functions

Now use a different set of points, defined by a function $x \log(x)$.

g = lambda x: x * np.log(x)

We can compute the polynomials of different orders and the errors.

m = np.array([3,8])
x0 = 0.1
x1 = 2.0
dx = 0.01
x_new = np.arange(x0, x1, dx)
err = np.zeros( (m.size, x_new.size) )
y_new = np.zeros( (m.size, x_new.size) )
fig1 = plt.figure()
fig1.subplots_adjust(wspace=0.3)
for i in np.arange(m.size):
    z = np.linspace(x0, x1, m[i])
    y = g(z)
    f = lagrange(z, y)
    p = Polynomial(f.coef[::-1]).coef
    err[i,:] = g(x_new) - f(x_new)
    md("Coefficients of the interpolating polynomial: {}".format(p))
    ax=fig1.add_subplot(1,m.size,i+1)
    clrs = 'tab:orange' if (i==0) else 'tab:green'
    ax.plot(x_new, f(x_new), '-', c=clrs, label="interpolation")
    ax.plot(z, y, 'ro', label="data")
    ax.plot(x_new, np.log(x_new) * x_new, linestyle='--', label="function")
    ax.set_title(r'Lagrange Interpolation of order {}'.format(m[i]))
    ax.legend()
    ax.grid()
    ax.set_xlabel(r'$x$')
    ax.set_ylabel(r'$y$')
plt.show()

png

Plot the errors

fig2=plt.figure()
for i in np.arange(m.size):
    ax=fig2.add_subplot(m.size,1,i+1)
    clrs = 'tab:orange' if (i==0) else 'tab:green'
    ax.plot(x_new, err[i], c=clrs, label="Error Order {}".format(m[i]))
    ax.legend()
    ax.grid()
    ax.set_xlabel(r'$x$')
    ax.set_ylabel(r'$y$')
plt.show()

png

Compute the bounds on the errors.

fig3=plt.figure()
for i in np.arange(m.size):
    z = np.linspace(x0, x1, m[i])
    bounds1 = np.prod( [(x_new - z[i])  for i in np.arange(0,m[i])], axis=0)
    bounds2 = (m[i]-2) * (-1)**(m[i]-2) / ( np.math.factorial(m[i]) * x_new**(m[i]-1) )
    ax=fig3.add_subplot(m.size,1,i+1)
    b=np.max(np.abs(bounds1 * bounds2))
    ax.plot(x_new, err[i], c=clrs, label="Error Order {}".format(m[i]))
    ax.axhline(y=b)
    ax.axhline(y=-b)
    ax.legend()
    ax.grid()
    ax.set_xlabel(r'$x$')
    ax.set_ylabel(r'$y$')
plt.show()

png