In the last two chapters, we explored the power and peril of using a single, high-degree polynomial to fit a set of points. We saw that with a clever choice of nodes, the Chebyshev points, we could achieve remarkable accuracy. However, this approach has two major weaknesses. First, we are not always free to choose our data points; they are often given to us, perhaps from an experiment at evenly spaced time intervals. Second, a single global polynomial is a rigid tool. A single sharp corner or discontinuity anywhere in the function can send ruinous oscillations across the entire domain.

This calls for a more flexible, robust, and practical philosophy: divide and conquer.

Instead of trying to fit one complex, high-degree curve to all the data, we will fit many simple, low-degree curves to small sections of it. We will break our interval into smaller subintervals and use a low-degree polynomial on each piece. This is the world of piecewise polynomial interpolation.

The error formula from Chapter 2 provides the mathematical motivation. For a polynomial of degree on an interval of length , the error is roughly:

This formula tells us we can control the error in two ways: by increasing the polynomial degree or by decreasing the interval size . The global polynomial approach focuses on increasing , which we saw can be dangerous. The piecewise approach keeps small (typically 1 or 3) and focuses on making small. This strategy is far more stable and adaptable.

Piecewise Linear Interpolation: Connecting the Dots

The simplest idea is to just connect the dots with straight lines. This creates a function that is continuous, but not smooth.

Given a set of data points , which we now call knots or breakpoints, we define a separate linear function on each subinterval . Let be the length of the subinterval. The formula for the line segment is a simple weighted average:

This can be understood more formally by defining a linear “hat function” on a reference interval , . Then, by mapping each subinterval to via , our interpolant becomes:

The resulting global function, , is continuous (we say ), but its derivative is discontinuous at the knots, the function has sharp “kinks.” This lack of smoothness is its primary drawback. It’s simple and robust, but it’s not elegant. To do better, we need to control the derivatives.

Cubic Hermite Interpolation: Building Smoothness

A function appears smooth if its slope doesn’t change abruptly. To achieve this, we need to build an interpolant whose first derivative is also continuous, making it a function.

The idea of Hermite interpolation is to match not only the function values at the knots, but also the derivative values. Let’s assume that for each knot , we are given both a value and a slope .

On a single interval , we now have four pieces of information: . This is exactly the right amount of information to uniquely determine a cubic polynomial (which has four coefficients, ).

How do we construct this cubic? We can define a set of basis functions on the reference interval . We need four functions that handle our four constraints:

  • Two for the values: and
  • Two for the slopes: and

These basis polynomials are chosen to isolate each condition:

  • : Must be 1 at , 0 at , and have zero slope at both ends. The unique cubic is .
  • : Must be 0 at , 1 at , and have zero slope at both ends. The unique cubic is .
  • : Must be 0 at both ends, have slope 1 at , and slope 0 at . The unique cubic is .
  • : Must be 0 at both ends, have slope 0 at , and slope 1 at . The unique cubic is .

With these building blocks, the cubic interpolant on the interval is a combination that satisfies all four conditions:

The factors of are crucial; they come from the chain rule when converting derivatives with respect to to derivatives with respect to .

By construction, the resulting piecewise cubic function will be smooth. But this raises a crucial question: where do the slope values come from?

Case 1: Slopes are Known (True Hermite Interpolation)

If the derivatives of the underlying function are known, we can use them directly. In this ideal case, the error is very small.

Theorem 4.2.2: Error of Cubic Hermite Interpolation If and is the piecewise cubic Hermite interpolant using the exact derivatives , then the error is bounded by:

where . The function value converges as and the derivative as . This is excellent convergence.

Case 2: Slopes are Estimated (PCHIP)

In most real-world problems, we only have the data values . We must estimate the slopes. Different estimation schemes lead to different types of cubic interpolants. One of the most useful is the Piecewise Cubic Hermite Interpolating Polynomial (PCHIP).

PCHIP estimates the slope at a knot by looking at the slopes of the lines connecting its neighbors. It uses a carefully chosen weighted average designed to prevent overshoots and preserve the shape of the data.

The Power of Shape Preservation

The key feature of PCHIP is that it is monotonicity-preserving. If your data points are monotonically increasing, the PCHIP interpolant will also be monotonically increasing. It will not introduce new “bumps” or wiggles where none should exist. This is an invaluable property when interpolating physical data that is known to be non-oscillatory.

The price for this robustness is a lower order of accuracy compared to using the true derivatives, but its shape-preserving nature often makes it the right tool for the job.

from scipy.interpolate import pchip_interpolate
# PchipInterpolator is also available for an object-oriented approach
 
# Data that is monotonic
x_data = np.array([0, 1, 2, 4, 5, 6])
y_data = np.array([0, 0, 1, 1, 2, 2])
 
x_plot = np.linspace(0, 6, 400)
y_pchip = pchip_interpolate(x_data, y_data, x_plot)
 
plt.plot(x_data, y_data, 'ro', label='Data')
plt.plot(x_plot, y_pchip, 'b-', label='PCHIP Interpolant')
plt.title("PCHIP Preserves Monotonicity")
plt.legend(); plt.grid(True); plt.show()

Cubic Splines: The Gold Standard of Smoothness

PCHIP gives us a function, which has a continuous slope. But if you look closely, the curvature (the second derivative) can still jump at the knots. For applications in computer graphics, engineering design, or simply creating a visually perfect curve, we often want an even higher degree of smoothness. We want a function that is twice continuously differentiable, or .

This is the domain of the cubic spline.

The idea is beautiful. We start, as before, with the Hermite cubic form on each interval, but now the slopes are unknown variables. We will determine them all at once by enforcing a global smoothness condition.

At each of the interior knots, we demand that the second derivative of the cubic piece on the left is equal to the second derivative of the cubic piece on the right:

After some calculus (taking two derivatives of the Hermite basis form and applying the chain rule), this condition simplifies into a linear relationship between three neighboring slopes: .

This gives us equations for our unknown slopes. We are two equations short! To get a unique solution, we must specify two additional boundary conditions.

Types of Spline Boundary Conditions

  1. Clamped Spline: We explicitly specify the slopes at the endpoints, and . This is useful if we know the derivative of the underlying function at the boundaries.
  2. Natural Spline: This is the most common choice. It assumes the second derivative is zero at the endpoints: . This corresponds physically to a flexible draftsman’s ruler (a “spline”) being held straight at its ends.
  3. “Not-a-Knot” Spline: A clever condition that forces the third derivative to be continuous at the first and last interior knots ( and ). Since our pieces are cubics, this forces the first two cubic pieces (and the last two) to be the same polynomial. This is the default in many software packages, including scipy.interpolate.CubicSpline.
  4. Periodic Spline: If the function is known to be periodic, we enforce and .

Once we’ve chosen our boundary conditions, we have a system of linear equations for the slopes. The matrix for this system is tridiagonal (or nearly so for periodic splines). This is fantastic news, because a tridiagonal system can be solved with a specialized, fast algorithm in just operations, not the usual for a dense matrix.

from scipy.interpolate import CubicSpline
 
x_data = np.array([0, 1, 3, 4, 6])
y_data = np.array([1, 4, 2, 2, 3])
 
# Create the spline objects with different boundary conditions
cs_notaknot = CubicSpline(x_data, y_data, bc_type='not-a-knot')
cs_natural = CubicSpline(x_data, y_data, bc_type='natural')
cs_clamped = CubicSpline(x_data, y_data, bc_type='clamped', extrapolate=False) # Clamped to zero slope
 
x_plot = np.linspace(0, 6, 400)
 
plt.plot(x_data, y_data, 'ro', label='Data Points')
plt.plot(x_plot, cs_notaknot(x_plot), label='Not-a-Knot (default)')
plt.plot(x_plot, cs_natural(x_plot), '--', label='Natural Spline')
plt.plot(x_plot, cs_clamped(x_plot), ':', label='Clamped Spline')
plt.title("Comparison of Cubic Spline Boundary Conditions")
plt.legend(); plt.grid(True); plt.show()

The Optimality of Splines

The natural cubic spline has a beautiful optimality property. Among all functions that are twice-differentiable and pass through the data points, the natural spline is the one that minimizes the total “bending energy,” defined as . It is, in a very precise mathematical sense, the “smoothest” possible interpolant.

Convergence Comparison

The choice of method matters. For smooth functions, the higher-order smoothness of splines pays off in faster convergence.

The plots show that for smooth functions, both clamped splines and true Hermite splines achieve the excellent convergence rate. PCHIP, which prioritizes shape over smoothness, converges more slowly but is more robust against oscillations. The “not-a-knot” spline also achieves convergence.

The overarching lesson is powerful. While a single, global polynomial is a delicate and sometimes dangerous tool, a chain of low-degree piecewise polynomials is robust, flexible, and forms the foundation of modern interpolation, approximation, and computer-aided design.