Copyright (C) 2020 Andreas Kloeckner
import numpy as np
import numpy.linalg as la
import matplotlib.pyplot as pt
Here are a few functions:
if 1:
def f(x):
return np.sin(5*x)
def df(x):
return 5*np.cos(5*x)
elif 0:
gamma = 0.15
def f(x):
return np.sin(1/(gamma+x))
def df(x):
return -np.cos(1/(gamma+x))/(gamma+x)**2
else:
def f(x):
return np.abs(x-0.5)
def df(x):
# Well...
return -1 + 2*(x<=0.5).astype(np.float)
x_01 = np.linspace(0, 1, 1000)
pt.plot(x_01, f(x_01))
[<matplotlib.lines.Line2D at 0x7f5f98d5dd30>]
degree = 4
h = 1
nodes = 0.5 + np.linspace(-h/2, h/2, degree+1)
nodes
array([0. , 0.25, 0.5 , 0.75, 1. ])
Build the gen. Vandermonde matrix and find the coefficients:
V = np.array([
nodes**i
for i in range(degree+1)
]).T
coeffs = la.solve(V, f(nodes))
Evaluate the interpolant:
x_0h = 0.5+np.linspace(-h/2, h/2, 1000)
interp_0h = 0*x_0h
for i in range(degree+1):
interp_0h += coeffs[i] * x_0h**i
pt.plot(x_01, f(x_01), "--", color="gray", label="$f$")
pt.plot(x_0h, interp_0h, color="red", label="Interpolant")
pt.plot(nodes, f(nodes), "or")
pt.legend(loc="best")
<matplotlib.legend.Legend at 0x7f5f98d180f0>
Now build the gen. Vandermonde matrix $V'=$Vprime
of the derivatives:
def monomial_deriv(i, x):
if i == 0:
return 0*x
else:
return i*nodes**(i-1)
Vprime = np.array([
monomial_deriv(i, nodes)
for i in range(degree+1)
]).T
Compute the value of the derivative at the nodes as fderiv
:
fderiv = Vprime @ la.inv(V) @ f(nodes)
And plot vs df
, the exact derivative:
pt.plot(x_01, df(x_01), "--", color="gray", label="$f$")
pt.plot(nodes, fderiv, "or")
pt.legend(loc="best")
<matplotlib.legend.Legend at 0x7f5f98bc9d68>
print(np.max(np.abs(df(nodes) - fderiv)))
1.8560489763862222
(
Vprime @ la.inv(V)
).round(3)
array([[ -8.333, 16. , -12. , 5.333, -1. ], [ -1. , -3.333, 6. , -2. , 0.333], [ 0.333, -2.667, 0. , 2.667, -0.333], [ -0.333, 2. , -6. , 3.333, 1. ], [ 1. , -5.333, 12. , -16. , 8.333]])
nodes
array([0. , 0.25, 0.5 , 0.75, 1. ])