|
fitpack
Modern Fortran library for curve and surface fitting with splines
|
This tutorial explains how to use the fitpack_curve type for univariate spline smoothing and interpolation of the form \( y = s(x) \). It covers the mathematical background, basic and advanced usage, and practical guidance on choosing a smoothing parameter.
Use fitpack_curve when you have a single-valued relationship between an independent variable \( x \) and a dependent variable \( y \) — that is, each \( x_i \) maps to exactly one \( y_i \). The data must be sorted in strictly increasing order of \( x \).
Given \( m \) data points \( (x_i, y_i) \) with positive weights \( w_i \), FITPACK constructs a B-spline \( s(x) \) of degree \( k \) (default \( k = 3 \), cubic) by solving the constrained optimization problem
\[ \min \int_{x_1}^{x_m} \left[ s''(x) \right]^2 dx \quad \text{subject to} \quad \sum_{i=1}^{m} w_i^2 \left( y_i - s(x_i) \right)^2 \leq S \]
The smoothing parameter \( S \geq 0 \) controls the trade-off between roughness and fidelity to the data:
When the data satisfy \( y_i = g(x_i) + \varepsilon_i \) with independent errors of variance \( \sigma^2 \) and unit weights, the expected residual sum of squares for the true function is \( m \sigma^2 \). A natural starting point is therefore
\[ S \approx m \, \sigma^2 \]
If no noise estimate is available, \( S = m \) (assuming unit weights) is a reasonable first guess. The algorithm selects knots automatically: fewer knots for larger \( S \), more knots for smaller \( S \). After fitting, inspect curve\mse() and curve\knots to judge the result.
The following program fits a smoothing spline to noisy sine data, evaluates it, and prints the result.
use fitpack statement exposes the curve type, kind parameters (FP_REAL, FP_FLAG), error-handling helpers (FITPACK_SUCCESS, FITPACK_MESSAGE), and named constants (zero, one, half, pi). Note that two is not a public export — define it locally when needed.x must be strictly increasing. The corresponding values y may contain noise.curve\new_fit(x, y, smoothing=S) constructs the spline. It returns an integer(FP_FLAG) error code. Check it with FITPACK_SUCCESS(ierr).curve\eval(x, ierr) accepts a scalar or an array and returns the spline value(s).curve\destroy() releases all internal allocations.Supply per-point weights via the w argument to emphasize or de-emphasize individual observations. Points with larger weights are fitted more closely.
When weights are proportional to \( 1 / \sigma_i \), the smoothing constraint becomes \( \sum (y_i - s(x_i))^2 / \sigma_i^2 \leq S \), and \( S \approx m \) remains a natural choice.
Evaluate derivatives of the fitted spline up to order \( k \) (the spline degree, default 3):
The order parameter selects which derivative: 1 for \( s'(x) \), 2 for \( s''(x) \), 3 for \( s'''(x) \).
Compute the definite integral of the spline over an arbitrary sub-interval \( [a, b] \):
This evaluates \( \int_a^b s(x) \, dx \) analytically from the B-spline coefficients, so the result is exact for the fitted spline (no numerical quadrature error).
Find all zeros (roots) of a cubic spline within the data range:
The curve\zeros(ierr) function returns an allocatable array containing every root of \( s(x) = 0 \). This operation requires a cubic spline ( \( k = 3 \)).
For exact interpolation — a spline that passes through every data point — set smoothing=zero:
Alternatively, call the dedicated interpolation method:
Both produce the smoothest spline (minimum roughness integral) that satisfies \( s(x_i) = y_i \) for all \( i \).
When the appropriate smoothing level is unknown, try a sequence of \( S \) values on a logarithmic scale and inspect the resulting number of knots and residual:
A good fit typically shows a "knee" where adding more knots (decreasing \( S \)) yields diminishing improvement in the residual. Choose the \( S \) value at or just past this transition point.
| Method | Description |
|---|---|
curve\new_fit(x, y, w, smoothing, order) | Fit a smoothing spline; returns integer(FP_FLAG) |
curve\eval(x, ierr) | Evaluate \( s(x) \) at a scalar or array of points |
curve\dfdx(x, order, ierr) | Evaluate derivative of order 1, 2, or 3 |
curve\integral(from, to) | Definite integral \( \int_a^b s(x) \, dx \) |
curve\zeros(ierr) | All zeros of \( s(x) \) (cubic splines only) |
curve\interpolate() | Exact interpolation ( \( S = 0 \)) |
curve\knots | Number of knots in the current fit |
curve\mse() | Weighted residual sum of squares \( f_p \) |
curve\smoothing | Current smoothing parameter \( S \) |
curve\destroy() | Release all allocated memory |
A fully compilable program demonstrating smoothing, interpolation, derivatives, integration, root finding, and a smoothing sweep is provided in examples/example_curve.f90. Build and run it with:
All fitting and evaluation routines return an integer(FP_FLAG) error code. Use FITPACK_SUCCESS(ierr) to test for success and FITPACK_MESSAGE(ierr) to obtain a human-readable description:
Common return values:
| Flag | Meaning |
|---|---|
FITPACK_OK (0) | Success |
FITPACK_INTERPOLATING_OK ( \(-1\)) | Interpolating spline returned |
FITPACK_S_TOO_SMALL (1) | Smoothing parameter too small for the knot budget |
FITPACK_MAXIT (2) | Maximum iterations reached |
FITPACK_INPUT_ERROR (10) | Invalid input data |