Interpolation by Natural splines

For the spline interpolation one interpolation function is calculated for each interval between two supporting points.

If we have for instance a set of 6 supporting points, the interpolation would look like this:

Interpolation

To this 6 supporting points we get 5 different functions f1(x), f2(x)..f5(x). For x1 =< x < x2 y = f1(x), for x2 =< x < x3 y = f2(x) and so on.

For each of these intervals one cubic polynomial function is calculated like:

Interpolation

for the interval starting at xi and ending at xi+1 and x as the interpolation variable. That means in opposite to the other interpolation algorithms we do not get just one interpolation function but N-1 for N supporting points. And we finally have to switch these functions when we build the interpolation graph.

To make sure that the wave shapes of all this intervals fit together and build a smooth wave shape, there are some conditions to be fulfilled:

a) The graph of the functions shall meet all the supporting points
 
pi (xi ) = yi for i = 1 to N

b) The graph shall not have a jump discontinuity at the supporting points

pi (xi ) = pi-1 (xi ) for i = 2 to N-1
 

c) There shouldn’t be any break-points at the supporting points

p'i (xi ) = p'i-1 (xi ) for i = 2 to N-1
 

d) The bending of the graph shall be the same on both sides of the supporting points

p''i (xi ) = p''i-1 (xi ) for i = 2 to N-1


With N as the number of supporting points.

From these conditions and the substitution hi = xi+1 - xi the parameters a, b, c and d can be derived:

From condition a we get ai = yi. If x = xi, then x – xi = 0 and pi(x) = ai = yi

With the second deductions

Interpolation


and the substitution

Interpolation


inserted in the formulation of b

Interpolation


This resolved for di-1

Interpolation


And with this we get from b

Interpolation


And both put together

Interpolation


Resolved for bi-1:

Interpolation


Or bi:

Interpolation


This will be the equation to compute bi

And we’ll use further down



From c we get

Interpolation

(with the same substitution for di-1 as above)

Interpolation

Now we remember the 2 equations from above

Interpolation

And use them

Interpolation

Here we bring all the c terms on one side

Interpolation


Now, there is a boundary condition for natural spines: The interpolated graph shall continue in a straight line on both ends. That means

Interpolation


and

Interpolation


That means c1 = 0 for natural splines.

With this the equation leads to a matrix equation of the order N-2 for ci with  i = 2 ..n-1 and the matrix.

Interpolation

and the solution vector

Interpolation

This matrix equation solved yields c2 to cN-1. The first and the last c both are set as 0.

Now there is only di left and this we get from condition d

Interpolation

With these parameters each of the intervals between the supporting points can be interpolated now and at the end the pieces have to be put together to one graph.


The algorithm to calculate the parameters looks like:

public bool CalcParameters()
{
     int i, k;
     for (i = 0; i < order; i++)  // calculate a parameters
     {
         a[i] = y_k[i];
     }
     for (i = 0; i < order - 1; i++)
     {
         h[i] = x_k[i + 1] - x_k[i];
     }
 
     // empty matrix for c parameters
     for (i = 0; i < order - 2; i++)
     {
         for (k = 0; k < order - 2; k++)
         {
              m[i, k] = 0.0;
              y[i] = 0.0;
              x[i] = 0.0;
         }
     }
 
     // fill matrix for c parameters
     for (i = 0; i < order - 2; i++)
     {
         if (i == 0)
         {
              m[i, 0] = 2.0 * (h[0] + h[1]);
              m[i, 1] = h[1];
         }
         else
         {
              m[i, i - 1] = h[i];
              m[i, i] = 2.0 * (h[i] + h[i + 1]);
              if (i < order - 3)
                   m[i, i + 1] = h[i + 1];
         }
         if ((h[i] != 0.0) && (h[i + 1] != 0.0))
y[i] = ((a[i + 2] - a[i + 1]) / h[i + 1] - (a[i + 1] - a[i]) / h[i]) * 3.0;
         else
              y[i] = 0.0;
     }
     if (gauss.Eliminate())
     {
         gauss.Solve();
         c[0] = 0.0;  // boundry condition for natural splines
         for (i = 1; i < order - 1; i++)
              c[i] = x[i - 1];
         for (i = 0; i < order - 1; i++) // calculate b and d parameters
         {
              if (h[i] != 0.0)
              {
d[i] = 1.0 / 3.0 / h[i] * (c[i + 1] - c[i]);
b[i] = 1.0 / h[i] * (a[i + 1] - a[i]) - h[i] / 3.0 * (c[i + 1] + 2 * c[i]);
              }
         }
         return true;
     }
     else
         return false;
}


The part for the interpolation is

public void Interpolate(int order, int resolution)
{
     int i, k;
     double timestamp;
     for (i = 0; i < order - 1; i++)
     {
         for (k = 0; k < resolution; k++)
         {
timestamp = (double)(k) / (double)(resolution) * h[i];
yp[i * resolution + k] = a[i] + b[i] * (timestamp) + c[i] * Math.Pow(timestamp, 2) + d[i] * Math.Pow(timestamp, 3);
xp[i * resolution + k] = timestamp + x_k[i];
         }
     }
}



This is the basic algorithm for Natural Splines.

But, that’s not all. The Matrix equation to calculate the h parameters contains many elements that are 0 and due to this fact there can by an improvement to solve this equation :-)



Improvement of calculation speed by LU decomposition

To calculate the h parameters we have to solve a matrix equation of following structure:

Interpolation

This is a tridiagonal matrix (at least as long as we don’t want to interpolate a periodic function). This special case of a matrix equation can be solved much quicker than by using the Gaussian elimination algorithm by using the LU decomposition of Durant Crout transforming the A matrix of the equation Ax = D into a lower triangular matrix and a upper triangular matrix as A = LU like.

Interpolation

Interpolation


Comparison of the parameters gives:

c2 = l1 * m1
c3 = l2 * m2
c4 = l3 * m3
a1 = m1
a2 = l1 * u1 + m1
a3 = l2 * u2 + m2
a4 = l3 * u3 + m3
b1 = u1
b2 = u2
b3 = u3
b4 = u4


And from this for the elements:

li = ci+1 / mi
mi+1 = ai+1 – li * bi
for i = 1 to N-1

To solve this equation we first extend the equation LUx = D by y and say LUxy = Dy (y is not the solution vector of the very top equation). This can be separated to Ly = D and Ux = y.

Ly = D looks like

Interpolation


And resolving Ly = D gives

y1 = d1
y1 * l1 + y2 = d2
y2 * l2 + y3 = d3
y3 * l3 + y4 = d4
=> y1 = d1
=> y2 = d2 - y1 * l1
=> y3 = d3 – y2 * l2
=> y4 = d4 – y3 * l3


With this we basically have to implement 3 loops to solve the tridiagonal Matrix equation:


First loop:

m1 = a1
For i = 1 to n-1
li = ci+1 / mi
mi+1 = ai+1 – li * bi


put into c code:

m_lu[0] = m.a[0, 0];
for (i = 0; i < order - 1; i++)
{
     if (Math.Abs(m_lu[i]) > 1E-10)
         l_lu[i] = m[i + 1, i] / m_lu[i];
     else
         calcError = true;
     m_lu[i + 1] = m[i + 1, i + 1] - l_lu[i] * m[i, i + 1];
}

 

 

Second loop:

y1 = d1

for i = 2 to n

yi = di – li-1 * yi-1

In c code:

y_lu[0] = m.y[0];
for (i = 1; i < order; i++)
{
     y_lu[i] = y[i] - l_lu[i - 1] * y_lu[i - 1];
}



Third loop:

xn = yn / mn
For I = n-1 to 1
xi = (yi - bi * xi+1)/mi


In c code:

if (Math.Abs(m_lr[order - 1]) > 1E-10)
     x[order - 1] = y_lu[order - 1] / m_lr[order - 1];
else
     calcError = true;
if (!calcError)
{
     for (i = order - 2; i >= 0; i--)
     {
         x[i] = (y_lu[i] - a[i, i + 1] * x[i + 1]) / m_lu[i];
     }
}



Now the first and second loop can be implemented in one if we separate the first term of the first loop and the last term of the second loop

m1 = a1
l1 = c2 / m1
m2 = a2 – l1 * b1
For i = 2 to n-1
li = ci+1 / mi
mi+1 = ai+1 – li * bi
yi = di – li-1 * yi-1
yN = dN – lN-1 * yN-1


So there remain 2 loops to solve the entire matrix equation and that takes a bit more than half as much calculation time as a Gaussian algorithm :-)


The final c code :


m_lu[0] = m.a[0, 0];
for (i = 0; i < order - 1; i++)
{
     if (Math.Abs(m_lu[i]) > 1E-10)
         l_lu[i] = m[i + 1, i] / m_lu[i];
     else
     calcError = true;
     m_lu[i + 1] = m[i + 1, i + 1] - l_lu[i] * m[i, i + 1];
}
y_lu[0] = m.y[0];
for (i = 1; i < order; i++)
{
     y_lu[i] = y[i] - l_lu[i - 1] * y_lu[i - 1];
}
if (Math.Abs(m_lr[order - 1]) > 1E-10)
     x[order - 1] = y_lu[order - 1] / m_lu[order - 1];
else
     calcError = true;
if (!calcError)
{
     for (i = order - 2; i >= 0; i--)
     {
         x[i] = (y_lu[i] - m[i, i + 1] * x[i + 1]) / m_lu[i];
     }
}


That's it :-)


For Periodic splines see periodic splines

For End slope splines see end slope splines



C# Demo Projects to natural splines
  • Spline.zip

  • Spline_LU.zip


  • Java Demo Projects to natural splines
  • Splines.zip

  • Spline_LU.zip