|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: basis splines, B-splines
|
|
Packit |
67cb25 |
single: splines, basis
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. _chap_basis-splines:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
*************
|
|
Packit |
67cb25 |
Basis Splines
|
|
Packit |
67cb25 |
*************
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This chapter describes functions for the computation of smoothing
|
|
Packit |
67cb25 |
basis splines (B-splines). A smoothing spline differs from an
|
|
Packit |
67cb25 |
interpolating spline in that the resulting curve is not required to
|
|
Packit |
67cb25 |
pass through each datapoint. For information about
|
|
Packit |
67cb25 |
interpolating splines, see :ref:`sec_interpolation`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The header file :file:`gsl_bspline.h` contains the prototypes for the
|
|
Packit |
67cb25 |
bspline functions and related declarations.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: basis splines, overview
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Overview
|
|
Packit |
67cb25 |
========
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
B-splines are commonly used as basis functions to fit smoothing
|
|
Packit |
67cb25 |
curves to large data sets. To do this, the abscissa axis is
|
|
Packit |
67cb25 |
broken up into some number of intervals, where the endpoints
|
|
Packit |
67cb25 |
of each interval are called *breakpoints*. These breakpoints
|
|
Packit |
67cb25 |
are then converted to *knots* by imposing various continuity
|
|
Packit |
67cb25 |
and smoothness conditions at each interface. Given a nondecreasing
|
|
Packit |
67cb25 |
knot vector
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: t = \{t_0, t_1, \dots, t_{n+k-1}\}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
the :math:`n` basis splines of order :math:`k` are defined by
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: not texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
B_{i,1}(x) &=
|
|
Packit |
67cb25 |
\left\{
|
|
Packit |
67cb25 |
\begin{array}{cc}
|
|
Packit |
67cb25 |
1, & t_i \le x < t_{i+1} \\
|
|
Packit |
67cb25 |
0, & else
|
|
Packit |
67cb25 |
\end{array}
|
|
Packit |
67cb25 |
\right. \\
|
|
Packit |
67cb25 |
B_{i,k}(x) &= {(x - t_i) \over (t_{i+k-1} - t_i)} B_{i,k-1}(x) +
|
|
Packit |
67cb25 |
{(t_{i+k} - x) \over (t_{i+k} - t_{i+1})} B_{i+1,k-1}(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. only:: texinfo
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
B_(i,1)(x) = (1, t_i <= x < t_(i+1)
|
|
Packit |
67cb25 |
(0, else
|
|
Packit |
67cb25 |
B_(i,k)(x) = [(x - t_i)/(t_(i+k-1) - t_i)] B_(i,k-1)(x)
|
|
Packit |
67cb25 |
+ [(t_(i+k) - x)/(t_(i+k) - t_(i+1))] B_(i+1,k-1)(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for :math:`i = 0, \ldots, n-1`. The common case of cubic B-splines
|
|
Packit |
67cb25 |
is given by :math:`k = 4`. The above recurrence relation can be
|
|
Packit |
67cb25 |
evaluated in a numerically stable way by the de Boor algorithm.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
If we define appropriate knots on an interval :math:`[a,b]` then
|
|
Packit |
67cb25 |
the B-spline basis functions form a complete set on that interval.
|
|
Packit |
67cb25 |
Therefore we can expand a smoothing function as
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. math:: f(x) = \sum_{i=0}^{n-1} c_i B_{i,k}(x)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
given enough :math:`(x_j, f(x_j))` data pairs. The coefficients
|
|
Packit |
67cb25 |
:math:`c_i` can be readily obtained from a least-squares fit.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: basis splines, initializing
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Initializing the B-splines solver
|
|
Packit |
67cb25 |
=================================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. type:: gsl_bspline_workspace
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The computation of B-spline functions requires a preallocated
|
|
Packit |
67cb25 |
workspace.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: gsl_bspline_workspace * gsl_bspline_alloc (const size_t k, const size_t nbreak)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function allocates a workspace for computing B-splines of order
|
|
Packit |
67cb25 |
:data:`k`. The number of breakpoints is given by :data:`nbreak`. This
|
|
Packit |
67cb25 |
leads to :math:`n = nbreak + k - 2` basis functions. Cubic B-splines
|
|
Packit |
67cb25 |
are specified by :math:`k = 4`. The size of the workspace is
|
|
Packit |
67cb25 |
:math:`O(2k^2 + 5k + nbreak)`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: void gsl_bspline_free (gsl_bspline_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function frees the memory associated with the workspace :data:`w`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: knots, basis splines
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Constructing the knots vector
|
|
Packit |
67cb25 |
=============================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_bspline_knots (const gsl_vector * breakpts, gsl_bspline_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function computes the knots associated with the given breakpoints
|
|
Packit |
67cb25 |
and stores them internally in :code:`w->knots`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_bspline_knots_uniform (const double a, const double b, gsl_bspline_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function assumes uniformly spaced breakpoints on :math:`[a,b]`
|
|
Packit |
67cb25 |
and constructs the corresponding knot vector using the previously
|
|
Packit |
67cb25 |
specified :data:`nbreak` parameter. The knots are stored in
|
|
Packit |
67cb25 |
:code:`w->knots`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: basis splines, evaluation
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Evaluation of B-splines
|
|
Packit |
67cb25 |
=======================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_bspline_eval (const double x, gsl_vector * B, gsl_bspline_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function evaluates all B-spline basis functions at the position
|
|
Packit |
67cb25 |
:data:`x` and stores them in the vector :data:`B`, so that the :math:`i`-th element
|
|
Packit |
67cb25 |
is :math:`B_i(x)`. The vector :data:`B` must be of length
|
|
Packit |
67cb25 |
:math:`n = nbreak + k - 2`. This value may also be obtained by calling
|
|
Packit |
67cb25 |
:func:`gsl_bspline_ncoeffs`.
|
|
Packit |
67cb25 |
Computing all the basis functions at once is more efficient than
|
|
Packit |
67cb25 |
computing them individually, due to the nature of the defining
|
|
Packit |
67cb25 |
recurrence relation.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_bspline_eval_nonzero (const double x, gsl_vector * Bk, size_t * istart, size_t * iend, gsl_bspline_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function evaluates all potentially nonzero B-spline basis
|
|
Packit |
67cb25 |
functions at the position :data:`x` and stores them in the vector :data:`Bk`, so
|
|
Packit |
67cb25 |
that the :math:`i`-th element is :math:`B_{(istart+i)}(x)`.
|
|
Packit |
67cb25 |
The last element of :data:`Bk` is :math:`B_{iend}(x)`.
|
|
Packit |
67cb25 |
The vector :data:`Bk` must be
|
|
Packit |
67cb25 |
of length :math:`k`. By returning only the nonzero basis functions,
|
|
Packit |
67cb25 |
this function
|
|
Packit |
67cb25 |
allows quantities involving linear combinations of the :math:`B_i(x)`
|
|
Packit |
67cb25 |
to be computed without unnecessary terms
|
|
Packit |
67cb25 |
(such linear combinations occur, for example,
|
|
Packit |
67cb25 |
when evaluating an interpolated function).
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: size_t gsl_bspline_ncoeffs (gsl_bspline_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function returns the number of B-spline coefficients given by
|
|
Packit |
67cb25 |
:math:`n = nbreak + k - 2`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: basis splines, derivatives
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Evaluation of B-spline derivatives
|
|
Packit |
67cb25 |
==================================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_bspline_deriv_eval (const double x, const size_t nderiv, gsl_matrix * dB, gsl_bspline_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function evaluates all B-spline basis function derivatives of orders
|
|
Packit |
67cb25 |
:math:`0` through :data:`nderiv` (inclusive) at the position :data:`x`
|
|
Packit |
67cb25 |
and stores them in the matrix :data:`dB`. The :math:`(i,j)`-th element of :data:`dB`
|
|
Packit |
67cb25 |
is :math:`d^jB_i(x)/dx^j`. The matrix :data:`dB` must be
|
|
Packit |
67cb25 |
of size :math:`n = nbreak + k - 2` by :math:`nderiv + 1`.
|
|
Packit |
67cb25 |
The value :math:`n` may also be obtained
|
|
Packit |
67cb25 |
by calling :func:`gsl_bspline_ncoeffs`. Note that function evaluations
|
|
Packit |
67cb25 |
are included as the zeroth order derivatives in :data:`dB`.
|
|
Packit |
67cb25 |
Computing all the basis function derivatives at once is more efficient
|
|
Packit |
67cb25 |
than computing them individually, due to the nature of the defining
|
|
Packit |
67cb25 |
recurrence relation.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: int gsl_bspline_deriv_eval_nonzero (const double x, const size_t nderiv, gsl_matrix * dB, size_t * istart, size_t * iend, gsl_bspline_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This function evaluates all potentially nonzero B-spline basis function
|
|
Packit |
67cb25 |
derivatives of orders :math:`0` through :data:`nderiv` (inclusive) at
|
|
Packit |
67cb25 |
the position :data:`x` and stores them in the matrix :data:`dB`. The
|
|
Packit |
67cb25 |
:math:`(i,j)`-th element of :data:`dB` is :math:`d^jB_{(istart+i)}(x)/dx^j`.
|
|
Packit |
67cb25 |
The last row of :data:`dB` contains :math:`d^jB_{iend}(x)/dx^j`.
|
|
Packit |
67cb25 |
The matrix :data:`dB` must be
|
|
Packit |
67cb25 |
of size :math:`k` by at least :math:`nderiv + 1`. Note that function
|
|
Packit |
67cb25 |
evaluations are included as the zeroth order derivatives in :data:`dB`.
|
|
Packit |
67cb25 |
By returning only the nonzero basis functions, this function allows
|
|
Packit |
67cb25 |
quantities involving linear combinations of the :math:`B_i(x)` and
|
|
Packit |
67cb25 |
their derivatives to be computed without unnecessary terms.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: basis splines, Greville abscissae
|
|
Packit |
67cb25 |
single: basis splines, Marsden-Schoenberg points
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Working with the Greville abscissae
|
|
Packit |
67cb25 |
===================================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The Greville abscissae are defined to be the mean location of :math:`k-1`
|
|
Packit |
67cb25 |
consecutive knots in the knot vector for each basis spline function of order
|
|
Packit |
67cb25 |
:math:`k`. With the first and last knots in the :type:`gsl_bspline_workspace`
|
|
Packit |
67cb25 |
knot vector excluded, there are :func:`gsl_bspline_ncoeffs` Greville abscissae
|
|
Packit |
67cb25 |
for any given B-spline basis. These values are often used in B-spline
|
|
Packit |
67cb25 |
collocation applications and may also be called Marsden-Schoenberg points.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. function:: double gsl_bspline_greville_abscissa (size_t i, gsl_bspline_workspace * w)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Returns the location of the :math:`i`-th Greville abscissa for the given
|
|
Packit |
67cb25 |
B-spline basis. For the ill-defined case when :math:`k = 1`, the implementation
|
|
Packit |
67cb25 |
chooses to return breakpoint interval midpoints.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. See https://savannah.gnu.org/bugs/index.php?34361
|
|
Packit |
67cb25 |
.. @deftypefun int gsl_bspline_knots_greville (const gsl_vector * abscissae, gsl_bspline_workspace * w, double * abserr);
|
|
Packit |
67cb25 |
.. Given target Greville abscissae values in :data:`abscissae` and a workspace
|
|
Packit |
67cb25 |
.. :data:`w` where @code{abscissae->size == gsl_bspline_ncoeffs(w)}, this functions
|
|
Packit |
67cb25 |
.. computes and stores the knots required for the workspace to best approximate
|
|
Packit |
67cb25 |
.. the target abscissae. The approximation is optimal in that the first and last
|
|
Packit |
67cb25 |
.. values in :data:`abscissae` are preserved exactly while the 2-norm of the error
|
|
Packit |
67cb25 |
.. in any other abscissae is minimized. If not-@code{NULL}, the sum of the
|
|
Packit |
67cb25 |
.. absolute approximation errors over each abscissa is returned in :data:`abserr`.
|
|
Packit |
67cb25 |
..
|
|
Packit |
67cb25 |
.. The workspace order must satisfy :math:`k > 1` and :data:`abscissae` should be
|
|
Packit |
67cb25 |
.. monotonically increasing. Beware that when @code{w->nbreak} is small relative
|
|
Packit |
67cb25 |
.. to @code{w->k} the best approximation may still be of poor quality for
|
|
Packit |
67cb25 |
.. non-uniformly spaced :data:`abscissae`. This function has memory and runtime
|
|
Packit |
67cb25 |
.. overhead that scales like a QR-based linear least squares solution on a
|
|
Packit |
67cb25 |
.. @code{(abscissae->size - 2)} by @code{(w->nbreak - 2)} problem.
|
|
Packit |
67cb25 |
.. @end deftypefun
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. index::
|
|
Packit |
67cb25 |
single: basis splines, examples
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Examples
|
|
Packit |
67cb25 |
========
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The following program computes a linear least squares fit to data using
|
|
Packit |
67cb25 |
cubic B-spline basis functions with uniform breakpoints. The data is
|
|
Packit |
67cb25 |
generated from the curve :math:`y(x) = \cos{(x)} \exp{(-x/10)}` on
|
|
Packit |
67cb25 |
the interval :math:`[0, 15]` with Gaussian noise added.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. include:: examples/bspline.c
|
|
Packit |
67cb25 |
:code:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The output is shown below::
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
$ ./a.out > bspline.txt
|
|
Packit |
67cb25 |
chisq/dof = 1.118217e+00, Rsq = 0.989771
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The data and fitted model are shown in :numref:`fig_bspline`.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. _fig_bspline:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
.. figure:: /images/bspline.png
|
|
Packit |
67cb25 |
:scale: 60%
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Data (black) and fitted model (red)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
References and Further Reading
|
|
Packit |
67cb25 |
==============================
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Further information on the algorithms described in this section can be
|
|
Packit |
67cb25 |
found in the following book,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* C. de Boor, *A Practical Guide to Splines* (1978), Springer-Verlag,
|
|
Packit |
67cb25 |
ISBN 0-387-90356-9.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Further information of Greville abscissae and B-spline collocation
|
|
Packit |
67cb25 |
can be found in the following paper,
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* Richard W. Johnson, Higher order B-spline collocation at the Greville
|
|
Packit |
67cb25 |
abscissae. *Applied Numerical Mathematics*. vol.: 52, 2005, 63--75.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
A large collection of B-spline routines is available in the
|
|
Packit |
67cb25 |
PPPACK library available at http://www.netlib.org/pppack,
|
|
Packit |
67cb25 |
which is also part of SLATEC.
|