Blame bspline/bspline.c

Packit 67cb25
/* bspline/bspline.c
Packit 67cb25
 *
Packit 67cb25
 * Copyright (C) 2006, 2007, 2008, 2009 Patrick Alken
Packit 67cb25
 * Copyright (C) 2008 Rhys Ulerich
Packit 67cb25
 *
Packit 67cb25
 * This program is free software; you can redistribute it and/or modify
Packit 67cb25
 * it under the terms of the GNU General Public License as published by
Packit 67cb25
 * the Free Software Foundation; either version 3 of the License, or (at
Packit 67cb25
 * your option) any later version.
Packit 67cb25
 *
Packit 67cb25
 * This program is distributed in the hope that it will be useful, but
Packit 67cb25
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
 * General Public License for more details.
Packit 67cb25
 *
Packit 67cb25
 * You should have received a copy of the GNU General Public License
Packit 67cb25
 * along with this program; if not, write to the Free Software
Packit 67cb25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <gsl/gsl_errno.h>
Packit 67cb25
#include <gsl/gsl_bspline.h>
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
 * This module contains routines related to calculating B-splines.
Packit 67cb25
 * The algorithms used are described in
Packit 67cb25
 *
Packit 67cb25
 * [1] Carl de Boor, "A Practical Guide to Splines", Springer
Packit 67cb25
 *     Verlag, 1978.
Packit 67cb25
 *
Packit 67cb25
 * The bspline_pppack_* internal routines contain code adapted from
Packit 67cb25
 *
Packit 67cb25
 * [2] "PPPACK - Piecewise Polynomial Package",
Packit 67cb25
 *     http://www.netlib.org/pppack/
Packit 67cb25
 *
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#include "bspline.h"
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_bspline_alloc()
Packit 67cb25
  Allocate space for a bspline workspace. The size of the
Packit 67cb25
workspace is O(5k + nbreak)
Packit 67cb25
Packit 67cb25
Inputs: k      - spline order (cubic = 4)
Packit 67cb25
        nbreak - number of breakpoints
Packit 67cb25
Packit 67cb25
Return: pointer to workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
gsl_bspline_workspace *
Packit 67cb25
gsl_bspline_alloc (const size_t k, const size_t nbreak)
Packit 67cb25
{
Packit 67cb25
  if (k == 0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_NULL ("k must be at least 1", GSL_EINVAL);
Packit 67cb25
    }
Packit 67cb25
  else if (nbreak < 2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_NULL ("nbreak must be at least 2", GSL_EINVAL);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      gsl_bspline_workspace *w;
Packit 67cb25
Packit 67cb25
      w = calloc (1, sizeof (gsl_bspline_workspace));
Packit 67cb25
Packit 67cb25
      if (w == 0)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR_NULL ("failed to allocate space for workspace",
Packit 67cb25
            GSL_ENOMEM);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      w->k = k;
Packit 67cb25
      w->km1 = k - 1;
Packit 67cb25
      w->nbreak = nbreak;
Packit 67cb25
      w->l = nbreak - 1;
Packit 67cb25
      w->n = w->l + k - 1;
Packit 67cb25
Packit 67cb25
      w->knots = gsl_vector_alloc (w->n + k);
Packit 67cb25
      if (w->knots == 0)
Packit 67cb25
        {
Packit 67cb25
          gsl_bspline_free (w);
Packit 67cb25
          GSL_ERROR_NULL ("failed to allocate space for knots vector",
Packit 67cb25
            GSL_ENOMEM);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      w->deltal = gsl_vector_alloc (k);
Packit 67cb25
      if (w->deltal == 0)
Packit 67cb25
        {
Packit 67cb25
          gsl_bspline_free (w);
Packit 67cb25
          GSL_ERROR_NULL ("failed to allocate space for deltal vector",
Packit 67cb25
            GSL_ENOMEM);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      w->deltar = gsl_vector_alloc (k);
Packit 67cb25
      if (w->deltar == 0)
Packit 67cb25
        {
Packit 67cb25
          gsl_bspline_free (w);
Packit 67cb25
          GSL_ERROR_NULL ("failed to allocate space for deltar vector",
Packit 67cb25
            GSL_ENOMEM);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      w->B = gsl_vector_alloc (k);
Packit 67cb25
      if (w->B == 0)
Packit 67cb25
        {
Packit 67cb25
          gsl_bspline_free (w);
Packit 67cb25
          GSL_ERROR_NULL
Packit 67cb25
            ("failed to allocate space for temporary spline vector",
Packit 67cb25
             GSL_ENOMEM);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      w->A = gsl_matrix_alloc (k, k);
Packit 67cb25
      if (w->A == 0)
Packit 67cb25
        {
Packit 67cb25
          gsl_bspline_free (w);
Packit 67cb25
          GSL_ERROR_NULL
Packit 67cb25
            ("failed to allocate space for derivative work matrix",
Packit 67cb25
             GSL_ENOMEM);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      w->dB = gsl_matrix_alloc (k, k + 1);
Packit 67cb25
      if (w->dB == 0)
Packit 67cb25
        {
Packit 67cb25
          gsl_bspline_free (w);
Packit 67cb25
          GSL_ERROR_NULL
Packit 67cb25
            ("failed to allocate space for temporary derivative matrix",
Packit 67cb25
             GSL_ENOMEM);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return w;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_bspline_alloc() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_bspline_free()
Packit 67cb25
  Free a gsl_bspline_workspace.
Packit 67cb25
Packit 67cb25
Inputs: w - workspace to free
Packit 67cb25
Packit 67cb25
Return: none
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
void
Packit 67cb25
gsl_bspline_free (gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  RETURN_IF_NULL (w);
Packit 67cb25
Packit 67cb25
  if (w->knots)
Packit 67cb25
    gsl_vector_free (w->knots);
Packit 67cb25
Packit 67cb25
  if (w->deltal)
Packit 67cb25
    gsl_vector_free (w->deltal);
Packit 67cb25
Packit 67cb25
  if (w->deltar)
Packit 67cb25
    gsl_vector_free (w->deltar);
Packit 67cb25
Packit 67cb25
  if (w->B)
Packit 67cb25
    gsl_vector_free (w->B);
Packit 67cb25
Packit 67cb25
  if (w->A)
Packit 67cb25
    gsl_matrix_free(w->A);
Packit 67cb25
Packit 67cb25
  if (w->dB)
Packit 67cb25
    gsl_matrix_free(w->dB);
Packit 67cb25
Packit 67cb25
  free (w);
Packit 67cb25
} /* gsl_bspline_free() */
Packit 67cb25
Packit 67cb25
Packit 67cb25
/* Return number of coefficients */
Packit 67cb25
size_t
Packit 67cb25
gsl_bspline_ncoeffs (gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  return w->n;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* Return order */
Packit 67cb25
size_t
Packit 67cb25
gsl_bspline_order (gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  return w->k;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* Return number of breakpoints */
Packit 67cb25
size_t
Packit 67cb25
gsl_bspline_nbreak (gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  return w->nbreak;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* Return the location of the i-th breakpoint*/
Packit 67cb25
double
Packit 67cb25
gsl_bspline_breakpoint (size_t i, gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  size_t j = i + w->k - 1;
Packit 67cb25
  return gsl_vector_get (w->knots, j);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_bspline_knots()
Packit 67cb25
  Compute the knots from the given breakpoints:
Packit 67cb25
Packit 67cb25
   knots(1:k) = breakpts(1)
Packit 67cb25
   knots(k+1:k+l-1) = breakpts(i), i = 2 .. l
Packit 67cb25
   knots(n+1:n+k) = breakpts(l + 1)
Packit 67cb25
Packit 67cb25
where l is the number of polynomial pieces (l = nbreak - 1) and
Packit 67cb25
   n = k + l - 1
Packit 67cb25
(using matlab syntax for the arrays)
Packit 67cb25
Packit 67cb25
The repeated knots at the beginning and end of the interval
Packit 67cb25
correspond to the continuity condition there. See pg. 119
Packit 67cb25
of [1].
Packit 67cb25
Packit 67cb25
Inputs: breakpts - breakpoints
Packit 67cb25
        w        - bspline workspace
Packit 67cb25
Packit 67cb25
Return: success or error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_bspline_knots (const gsl_vector * breakpts, gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (breakpts->size != w->nbreak)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("breakpts vector has wrong size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i; /* looping */
Packit 67cb25
Packit 67cb25
      for (i = 0; i < w->k; i++)
Packit 67cb25
        gsl_vector_set (w->knots, i, gsl_vector_get (breakpts, 0));
Packit 67cb25
Packit 67cb25
      for (i = 1; i < w->l; i++)
Packit 67cb25
        {
Packit 67cb25
          gsl_vector_set (w->knots, w->k - 1 + i,
Packit 67cb25
          gsl_vector_get (breakpts, i));
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      for (i = w->n; i < w->n + w->k; i++)
Packit 67cb25
        gsl_vector_set (w->knots, i, gsl_vector_get (breakpts, w->l));
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_bspline_knots() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_bspline_knots_uniform()
Packit 67cb25
  Construct uniformly spaced knots on the interval [a,b] using
Packit 67cb25
the previously specified number of breakpoints. 'a' is the position
Packit 67cb25
of the first breakpoint and 'b' is the position of the last
Packit 67cb25
breakpoint.
Packit 67cb25
Packit 67cb25
Inputs: a - left side of interval
Packit 67cb25
        b - right side of interval
Packit 67cb25
        w - bspline workspace
Packit 67cb25
Packit 67cb25
Return: success or error
Packit 67cb25
Packit 67cb25
Notes: 1) w->knots is modified to contain the uniformly spaced
Packit 67cb25
          knots
Packit 67cb25
Packit 67cb25
       2) The knots vector is set up as follows (using octave syntax):
Packit 67cb25
Packit 67cb25
          knots(1:k) = a
Packit 67cb25
          knots(k+1:k+l-1) = a + i*delta, i = 1 .. l - 1
Packit 67cb25
          knots(n+1:n+k) = b
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_bspline_knots_uniform (const double a, const double b,
Packit 67cb25
                           gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  size_t i;     /* looping */
Packit 67cb25
  double delta; /* interval spacing */
Packit 67cb25
  double x;
Packit 67cb25
Packit 67cb25
  delta = (b - a) / (double) w->l;
Packit 67cb25
Packit 67cb25
  for (i = 0; i < w->k; i++)
Packit 67cb25
    gsl_vector_set (w->knots, i, a);
Packit 67cb25
Packit 67cb25
  x = a + delta;
Packit 67cb25
  for (i = 0; i < w->l - 1; i++)
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_set (w->knots, w->k + i, x);
Packit 67cb25
      x += delta;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  for (i = w->n; i < w->n + w->k; i++)
Packit 67cb25
    gsl_vector_set (w->knots, i, b);
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
} /* gsl_bspline_knots_uniform() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_bspline_eval()
Packit 67cb25
  Evaluate the basis functions B_i(x) for all i. This is
Packit 67cb25
a wrapper function for gsl_bspline_eval_nonzero() which
Packit 67cb25
formats the output in a nice way.
Packit 67cb25
Packit 67cb25
Inputs: x - point for evaluation
Packit 67cb25
        B - (output) where to store B_i(x) values
Packit 67cb25
            the length of this vector is
Packit 67cb25
            n = nbreak + k - 2 = l + k - 1 = w->n
Packit 67cb25
        w - bspline workspace
Packit 67cb25
Packit 67cb25
Return: success or error
Packit 67cb25
Packit 67cb25
Notes: The w->knots vector must be initialized prior to calling
Packit 67cb25
       this function (see gsl_bspline_knots())
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_bspline_eval (const double x, gsl_vector * B, gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (B->size != w->n)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("vector B not of length n", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i;			/* looping */
Packit 67cb25
      size_t istart;		/* first non-zero spline for x */
Packit 67cb25
      size_t iend;		/* last non-zero spline for x, knot for x */
Packit 67cb25
      int error;		/* error handling */
Packit 67cb25
Packit 67cb25
      /* find all non-zero B_i(x) values */
Packit 67cb25
      error = gsl_bspline_eval_nonzero (x, w->B, &istart, &iend, w);
Packit 67cb25
      if (error)
Packit 67cb25
        return error;
Packit 67cb25
Packit 67cb25
      /* store values in appropriate part of given vector */
Packit 67cb25
      for (i = 0; i < istart; i++)
Packit 67cb25
        gsl_vector_set (B, i, 0.0);
Packit 67cb25
Packit 67cb25
      for (i = istart; i <= iend; i++)
Packit 67cb25
        gsl_vector_set (B, i, gsl_vector_get (w->B, i - istart));
Packit 67cb25
Packit 67cb25
      for (i = iend + 1; i < w->n; i++)
Packit 67cb25
        gsl_vector_set (B, i, 0.0);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_bspline_eval() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_bspline_eval_nonzero()
Packit 67cb25
  Evaluate all non-zero B-spline functions at point x.
Packit 67cb25
These are the B_i(x) for i in [istart, iend].
Packit 67cb25
Always B_i(x) = 0 for i < istart and for i > iend.
Packit 67cb25
Packit 67cb25
Inputs: x      - point at which to evaluate splines
Packit 67cb25
        Bk     - (output) where to store B-spline values (length k)
Packit 67cb25
        istart - (output) B-spline function index of
Packit 67cb25
                 first non-zero basis for given x
Packit 67cb25
        iend   - (output) B-spline function index of
Packit 67cb25
                 last non-zero basis for given x.
Packit 67cb25
                 This is also the knot index corresponding to x.
Packit 67cb25
        w      - bspline workspace
Packit 67cb25
Packit 67cb25
Return: success or error
Packit 67cb25
Packit 67cb25
Notes: 1) the w->knots vector must be initialized before calling
Packit 67cb25
          this function
Packit 67cb25
Packit 67cb25
       2) On output, B contains
Packit 67cb25
Packit 67cb25
             [B_{istart,k}, B_{istart+1,k},
Packit 67cb25
             ..., B_{iend-1,k}, B_{iend,k}]
Packit 67cb25
Packit 67cb25
          evaluated at the given x.
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_bspline_eval_nonzero (const double x, gsl_vector * Bk, size_t * istart,
Packit 67cb25
                          size_t * iend, gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (Bk->size != w->k)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("Bk vector length does not match order k", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i;			/* spline index */
Packit 67cb25
      size_t j;			/* looping */
Packit 67cb25
      int flag = 0;		/* interval search flag */
Packit 67cb25
      int error = 0;		/* error flag */
Packit 67cb25
Packit 67cb25
      i = bspline_find_interval (x, &flag, w);
Packit 67cb25
      error = bspline_process_interval_for_eval (x, &i, flag, w);
Packit 67cb25
      if (error)
Packit 67cb25
        return error;
Packit 67cb25
Packit 67cb25
      *istart = i - w->k + 1;
Packit 67cb25
      *iend = i;
Packit 67cb25
Packit 67cb25
      bspline_pppack_bsplvb (w->knots, w->k, 1, x, *iend, &j, w->deltal,
Packit 67cb25
                             w->deltar, Bk);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_bspline_eval_nonzero() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_bspline_deriv_eval()
Packit 67cb25
  Evaluate d^j/dx^j B_i(x) for all i, 0 <= j <= nderiv.
Packit 67cb25
This is a wrapper function for gsl_bspline_deriv_eval_nonzero()
Packit 67cb25
which formats the output in a nice way.
Packit 67cb25
Packit 67cb25
Inputs: x      - point for evaluation
Packit 67cb25
        nderiv - number of derivatives to compute, inclusive.
Packit 67cb25
        dB     - (output) where to store d^j/dx^j B_i(x)
Packit 67cb25
                 values. the size of this matrix is
Packit 67cb25
                 (n = nbreak + k - 2 = l + k - 1 = w->n)
Packit 67cb25
                 by (nderiv + 1)
Packit 67cb25
        w      - bspline derivative workspace
Packit 67cb25
Packit 67cb25
Return: success or error
Packit 67cb25
Packit 67cb25
Notes: 1) The w->knots vector must be initialized prior to calling
Packit 67cb25
          this function (see gsl_bspline_knots())
Packit 67cb25
Packit 67cb25
       2) based on PPPACK's bsplvd
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_bspline_deriv_eval (const double x, const size_t nderiv,
Packit 67cb25
                        gsl_matrix * dB, gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (dB->size1 != w->n)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("dB matrix first dimension not of length n", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (dB->size2 < nderiv + 1)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR
Packit 67cb25
        ("dB matrix second dimension must be at least length nderiv+1",
Packit 67cb25
         GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i;			/* looping */
Packit 67cb25
      size_t j;			/* looping */
Packit 67cb25
      size_t istart;		/* first non-zero spline for x */
Packit 67cb25
      size_t iend;		/* last non-zero spline for x, knot for x */
Packit 67cb25
      int error;		/* error handling */
Packit 67cb25
Packit 67cb25
      /* find all non-zero d^j/dx^j B_i(x) values */
Packit 67cb25
      error =
Packit 67cb25
        gsl_bspline_deriv_eval_nonzero (x, nderiv, w->dB, &istart, &iend, w);
Packit 67cb25
      if (error)
Packit 67cb25
        return error;
Packit 67cb25
Packit 67cb25
      /* store values in appropriate part of given matrix */
Packit 67cb25
      for (j = 0; j <= nderiv; j++)
Packit 67cb25
        {
Packit 67cb25
          for (i = 0; i < istart; i++)
Packit 67cb25
          gsl_matrix_set (dB, i, j, 0.0);
Packit 67cb25
Packit 67cb25
          for (i = istart; i <= iend; i++)
Packit 67cb25
            gsl_matrix_set (dB, i, j, gsl_matrix_get (w->dB, i - istart, j));
Packit 67cb25
Packit 67cb25
          for (i = iend + 1; i < w->n; i++)
Packit 67cb25
            gsl_matrix_set (dB, i, j, 0.0);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_bspline_deriv_eval() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_bspline_deriv_eval_nonzero()
Packit 67cb25
  At point x evaluate all requested, non-zero B-spline function
Packit 67cb25
derivatives and store them in dB.  These are the
Packit 67cb25
d^j/dx^j B_i(x) with i in [istart, iend] and j in [0, nderiv].
Packit 67cb25
Always d^j/dx^j B_i(x) = 0 for i < istart and for i > iend.
Packit 67cb25
Packit 67cb25
Inputs: x      - point at which to evaluate splines
Packit 67cb25
        nderiv - number of derivatives to request, inclusive
Packit 67cb25
        dB     - (output) where to store dB-spline derivatives
Packit 67cb25
                 (size k by nderiv + 1)
Packit 67cb25
        istart - (output) B-spline function index of
Packit 67cb25
                 first non-zero basis for given x
Packit 67cb25
        iend   - (output) B-spline function index of
Packit 67cb25
                 last non-zero basis for given x.
Packit 67cb25
                 This is also the knot index corresponding to x.
Packit 67cb25
        w      - bspline derivative workspace
Packit 67cb25
Packit 67cb25
Return: success or error
Packit 67cb25
Packit 67cb25
Notes: 1) the w->knots vector must be initialized before calling
Packit 67cb25
          this function
Packit 67cb25
Packit 67cb25
       2) On output, dB contains
Packit 67cb25
Packit 67cb25
            [[B_{istart,  k}, ..., d^nderiv/dx^nderiv B_{istart  ,k}],
Packit 67cb25
             [B_{istart+1,k}, ..., d^nderiv/dx^nderiv B_{istart+1,k}],
Packit 67cb25
             ...
Packit 67cb25
             [B_{iend-1,  k}, ..., d^nderiv/dx^nderiv B_{iend-1,  k}],
Packit 67cb25
             [B_{iend,    k}, ..., d^nderiv/dx^nderiv B_{iend,    k}]]
Packit 67cb25
Packit 67cb25
          evaluated at x.  B_{istart, k} is stored in dB(0,0).
Packit 67cb25
          Each additional column contains an additional derivative.
Packit 67cb25
Packit 67cb25
       3) Note that the zero-th column of the result contains the
Packit 67cb25
          0th derivative, which is simply a function evaluation.
Packit 67cb25
Packit 67cb25
       4) based on PPPACK's bsplvd
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_bspline_deriv_eval_nonzero (const double x, const size_t nderiv,
Packit 67cb25
                                gsl_matrix * dB, size_t * istart,
Packit 67cb25
                                size_t * iend, gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (dB->size1 != w->k)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("dB matrix first dimension not of length k", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (dB->size2 < nderiv + 1)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR
Packit 67cb25
        ("dB matrix second dimension must be at least length nderiv+1",
Packit 67cb25
         GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i;			/* spline index */
Packit 67cb25
      size_t j;			/* looping */
Packit 67cb25
      int flag = 0;		/* interval search flag */
Packit 67cb25
      int error = 0;		/* error flag */
Packit 67cb25
      size_t min_nderivk;
Packit 67cb25
Packit 67cb25
      i = bspline_find_interval (x, &flag, w);
Packit 67cb25
      error = bspline_process_interval_for_eval (x, &i, flag, w);
Packit 67cb25
      if (error)
Packit 67cb25
        return error;
Packit 67cb25
Packit 67cb25
      *istart = i - w->k + 1;
Packit 67cb25
      *iend = i;
Packit 67cb25
Packit 67cb25
      bspline_pppack_bsplvd (w->knots, w->k, x, *iend,
Packit 67cb25
                             w->deltal, w->deltar, w->A, dB, nderiv);
Packit 67cb25
Packit 67cb25
      /* An order k b-spline has at most k-1 nonzero derivatives
Packit 67cb25
         so we need to zero all requested higher order derivatives */
Packit 67cb25
      min_nderivk = GSL_MIN_INT (nderiv, w->k - 1);
Packit 67cb25
      for (j = min_nderivk + 1; j <= nderiv; j++)
Packit 67cb25
        {
Packit 67cb25
          for (i = 0; i < w->k; i++)
Packit 67cb25
            gsl_matrix_set (dB, i, j, 0.0);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_bspline_deriv_eval_nonzero() */
Packit 67cb25
Packit 67cb25
/****************************************
Packit 67cb25
 *          INTERNAL ROUTINES           *
Packit 67cb25
 ****************************************/
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
bspline_find_interval()
Packit 67cb25
  Find knot interval such that t_i <= x < t_{i + 1}
Packit 67cb25
where the t_i are knot values.
Packit 67cb25
Packit 67cb25
Inputs: x    - x value
Packit 67cb25
        flag - (output) error flag
Packit 67cb25
        w    - bspline workspace
Packit 67cb25
Packit 67cb25
Return: i (index in w->knots corresponding to left limit of interval)
Packit 67cb25
Packit 67cb25
Notes: The error conditions are reported as follows:
Packit 67cb25
Packit 67cb25
       Condition                        Return value        Flag
Packit 67cb25
       ---------                        ------------        ----
Packit 67cb25
       x < t_0                               0               -1
Packit 67cb25
       t_i <= x < t_{i+1}                    i                0
Packit 67cb25
       t_i < x = t_{i+1} = t_{n+k-1}         i                0
Packit 67cb25
       t_{n+k-1} < x                       l+k-1             +1
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static inline size_t
Packit 67cb25
bspline_find_interval (const double x, int *flag, gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  size_t i;
Packit 67cb25
Packit 67cb25
  if (x < gsl_vector_get (w->knots, 0))
Packit 67cb25
    {
Packit 67cb25
      *flag = -1;
Packit 67cb25
      return 0;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* find i such that t_i <= x < t_{i+1} */
Packit 67cb25
  for (i = w->k - 1; i < w->k + w->l - 1; i++)
Packit 67cb25
    {
Packit 67cb25
      const double ti = gsl_vector_get (w->knots, i);
Packit 67cb25
      const double tip1 = gsl_vector_get (w->knots, i + 1);
Packit 67cb25
Packit 67cb25
      if (tip1 < ti)
Packit 67cb25
	{
Packit 67cb25
	  GSL_ERROR ("knots vector is not increasing", GSL_EINVAL);
Packit 67cb25
	}
Packit 67cb25
Packit 67cb25
      if (ti <= x && x < tip1)
Packit 67cb25
	break;
Packit 67cb25
Packit 67cb25
      if (ti < x && x == tip1 && tip1 == gsl_vector_get (w->knots, w->k + w->l
Packit 67cb25
							 - 1))
Packit 67cb25
	break;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (i == w->k + w->l - 1)
Packit 67cb25
    *flag = 1;
Packit 67cb25
  else
Packit 67cb25
    *flag = 0;
Packit 67cb25
Packit 67cb25
  return i;
Packit 67cb25
}				/* bspline_find_interval() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
bspline_process_interval_for_eval()
Packit 67cb25
  Consumes an x location, left knot from bspline_find_interval, flag
Packit 67cb25
from bspline_find_interval, and a workspace.  Checks that x lies within
Packit 67cb25
the splines' knots, enforces some endpoint continuity requirements, and
Packit 67cb25
avoids divide by zero errors in the underlying bspline_pppack_* functions.
Packit 67cb25
*/
Packit 67cb25
static inline int
Packit 67cb25
bspline_process_interval_for_eval (const double x, size_t * i, const int flag,
Packit 67cb25
				   gsl_bspline_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (flag == -1)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("x outside of knot interval", GSL_EINVAL);
Packit 67cb25
    }
Packit 67cb25
  else if (flag == 1)
Packit 67cb25
    {
Packit 67cb25
      if (x <= gsl_vector_get (w->knots, *i) + GSL_DBL_EPSILON)
Packit 67cb25
	{
Packit 67cb25
	  *i -= 1;
Packit 67cb25
	}
Packit 67cb25
      else
Packit 67cb25
	{
Packit 67cb25
	  GSL_ERROR ("x outside of knot interval", GSL_EINVAL);
Packit 67cb25
	}
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (gsl_vector_get (w->knots, *i) == gsl_vector_get (w->knots, *i + 1))
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("knot(i) = knot(i+1) will result in division by zero",
Packit 67cb25
		 GSL_EINVAL);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}				/* bspline_process_interval_for_eval */
Packit 67cb25
Packit 67cb25
/********************************************************************
Packit 67cb25
 * PPPACK ROUTINES
Packit 67cb25
 *
Packit 67cb25
 * The routines herein deliberately avoid using the bspline workspace,
Packit 67cb25
 * choosing instead to pass all work areas explicitly.  This allows
Packit 67cb25
 * others to more easily adapt these routines to low memory or
Packit 67cb25
 * parallel scenarios.
Packit 67cb25
 ********************************************************************/
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
bspline_pppack_bsplvb()
Packit 67cb25
  calculates the value of all possibly nonzero b-splines at x of order
Packit 67cb25
jout = max( jhigh , (j+1)*(index-1) ) with knot sequence t.
Packit 67cb25
Packit 67cb25
Parameters:
Packit 67cb25
   t      - knot sequence, of length left + jout , assumed to be
Packit 67cb25
            nondecreasing.  assumption t(left).lt.t(left + 1).
Packit 67cb25
            division by zero  will result if t(left) = t(left+1)
Packit 67cb25
   jhigh  -
Packit 67cb25
   index  - integers which determine the order jout = max(jhigh,
Packit 67cb25
            (j+1)*(index-1))  of the b-splines whose values at x
Packit 67cb25
            are to be returned.  index  is used to avoid
Packit 67cb25
            recalculations when several columns of the triangular
Packit 67cb25
            array of b-spline values are needed (e.g., in  bsplpp
Packit 67cb25
            or in  bsplvd ).  precisely,
Packit 67cb25
Packit 67cb25
            if  index = 1 ,
Packit 67cb25
               the calculation starts from scratch and the entire
Packit 67cb25
               triangular array of b-spline values of orders
Packit 67cb25
               1,2,...,jhigh  is generated order by order , i.e.,
Packit 67cb25
               column by column .
Packit 67cb25
Packit 67cb25
            if  index = 2 ,
Packit 67cb25
               only the b-spline values of order j+1, j+2, ..., jout
Packit 67cb25
               are generated, the assumption being that biatx, j,
Packit 67cb25
               deltal, deltar are, on entry, as they were on exit
Packit 67cb25
               at the previous call.
Packit 67cb25
Packit 67cb25
            in particular, if jhigh = 0, then jout = j+1, i.e.,
Packit 67cb25
            just the next column of b-spline values is generated.
Packit 67cb25
   x      - the point at which the b-splines are to be evaluated.
Packit 67cb25
   left   - an integer chosen (usually) so that
Packit 67cb25
            t(left) .le. x .le. t(left+1).
Packit 67cb25
   j      - (output) a working scalar for indexing
Packit 67cb25
   deltal - (output) a working area which must be of length at least jout
Packit 67cb25
   deltar - (output) a working area which must be of length at least jout
Packit 67cb25
   biatx  - (output) array of length jout, with  biatx(i)
Packit 67cb25
            containing the value at  x  of the polynomial of order
Packit 67cb25
            jout which agrees with the b-spline b(left-jout+i,jout,t)
Packit 67cb25
            on the interval (t(left), t(left+1)) .
Packit 67cb25
Packit 67cb25
Method:
Packit 67cb25
   the recurrence relation
Packit 67cb25
Packit 67cb25
                      x - t(i)              t(i+j+1) - x
Packit 67cb25
      b(i,j+1)(x) = -----------b(i,j)(x) + ---------------b(i+1,j)(x)
Packit 67cb25
                    t(i+j)-t(i)            t(i+j+1)-t(i+1)
Packit 67cb25
Packit 67cb25
   is used (repeatedly) to generate the (j+1)-vector  b(left-j,j+1)(x),
Packit 67cb25
   ...,b(left,j+1)(x)  from the j-vector  b(left-j+1,j)(x),...,
Packit 67cb25
   b(left,j)(x), storing the new values in  biatx  over the old. the
Packit 67cb25
   facts that
Packit 67cb25
Packit 67cb25
      b(i,1) = 1  if  t(i) .le. x .lt. t(i+1)
Packit 67cb25
Packit 67cb25
   and that
Packit 67cb25
Packit 67cb25
      b(i,j)(x) = 0  unless  t(i) .le. x .lt. t(i+j)
Packit 67cb25
Packit 67cb25
   are used. the particular organization of the calculations follows
Packit 67cb25
   algorithm (8) in chapter x of [1].
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
Packit 67cb25
   (1) This is a direct translation of PPPACK's bsplvb routine with
Packit 67cb25
       j, deltal, deltar rewritten as input parameters and
Packit 67cb25
       utilizing zero-based indexing.
Packit 67cb25
Packit 67cb25
   (2) This routine contains no error checking.  Please use routines
Packit 67cb25
       like gsl_bspline_eval().
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
bspline_pppack_bsplvb (const gsl_vector * t,
Packit 67cb25
		       const size_t jhigh,
Packit 67cb25
		       const size_t index,
Packit 67cb25
		       const double x,
Packit 67cb25
		       const size_t left,
Packit 67cb25
		       size_t * j,
Packit 67cb25
		       gsl_vector * deltal,
Packit 67cb25
		       gsl_vector * deltar, gsl_vector * biatx)
Packit 67cb25
{
Packit 67cb25
  size_t i;			/* looping */
Packit 67cb25
  double saved;
Packit 67cb25
  double term;
Packit 67cb25
Packit 67cb25
  if (index == 1)
Packit 67cb25
    {
Packit 67cb25
      *j = 0;
Packit 67cb25
      gsl_vector_set (biatx, 0, 1.0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  for ( /* NOP */ ; *j < jhigh - 1; *j += 1)
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_set (deltar, *j, gsl_vector_get (t, left + *j + 1) - x);
Packit 67cb25
      gsl_vector_set (deltal, *j, x - gsl_vector_get (t, left - *j));
Packit 67cb25
Packit 67cb25
      saved = 0.0;
Packit 67cb25
Packit 67cb25
      for (i = 0; i <= *j; i++)
Packit 67cb25
	{
Packit 67cb25
	  term = gsl_vector_get (biatx, i) / (gsl_vector_get (deltar, i)
Packit 67cb25
					      + gsl_vector_get (deltal,
Packit 67cb25
								*j - i));
Packit 67cb25
Packit 67cb25
	  gsl_vector_set (biatx, i,
Packit 67cb25
			  saved + gsl_vector_get (deltar, i) * term);
Packit 67cb25
Packit 67cb25
	  saved = gsl_vector_get (deltal, *j - i) * term;
Packit 67cb25
	}
Packit 67cb25
Packit 67cb25
      gsl_vector_set (biatx, *j + 1, saved);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return;
Packit 67cb25
}				/* gsl_bspline_pppack_bsplvb */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
bspline_pppack_bsplvd()
Packit 67cb25
  calculates value and derivs of all b-splines which do not vanish at x
Packit 67cb25
Packit 67cb25
Parameters:
Packit 67cb25
   t      - the knot array, of length left+k (at least)
Packit 67cb25
   k      - the order of the b-splines to be evaluated
Packit 67cb25
   x      - the point at which these values are sought
Packit 67cb25
   left   - an integer indicating the left endpoint of the interval
Packit 67cb25
            of interest. the k b-splines whose support contains the
Packit 67cb25
            interval (t(left), t(left+1)) are to be considered.
Packit 67cb25
            it is assumed that t(left) .lt. t(left+1)
Packit 67cb25
            division by zero will result otherwise (in  bsplvb).
Packit 67cb25
            also, the output is as advertised only if
Packit 67cb25
            t(left) .le. x .le. t(left+1) .
Packit 67cb25
   deltal - a working area which must be of length at least k
Packit 67cb25
   deltar - a working area which must be of length at least k
Packit 67cb25
   a      - an array of order (k,k), to contain b-coeffs of the
Packit 67cb25
            derivatives of a certain order of the k b-splines
Packit 67cb25
            of interest.
Packit 67cb25
   dbiatx - an array of order (k,nderiv). its entry (i,m) contains
Packit 67cb25
            value of (m)th derivative of (left-k+i)-th b-spline
Packit 67cb25
            of order k for knot sequence  t, i=1,...,k, m=0,...,nderiv.
Packit 67cb25
   nderiv - an integer indicating that values of b-splines and
Packit 67cb25
            their derivatives up to AND INCLUDING the nderiv-th
Packit 67cb25
            are asked for. (nderiv is replaced internally by the
Packit 67cb25
            integer mhigh in (1,k) closest to it.)
Packit 67cb25
Packit 67cb25
Method:
Packit 67cb25
   values at x of all the relevant b-splines of order k,k-1,..., k+1-nderiv
Packit 67cb25
   are generated via bsplvb and stored temporarily in dbiatx.  then, the
Packit 67cb25
   b-coeffs of the required derivatives of the b-splines of interest are
Packit 67cb25
   generated by differencing, each from the preceeding one of lower order,
Packit 67cb25
   and combined with the values of b-splines of corresponding order in
Packit 67cb25
   dbiatx  to produce the desired values .
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
Packit 67cb25
   (1) This is a direct translation of PPPACK's bsplvd routine with
Packit 67cb25
       deltal, deltar rewritten as input parameters (to later feed them
Packit 67cb25
       to bspline_pppack_bsplvb) and utilizing zero-based indexing.
Packit 67cb25
Packit 67cb25
   (2) This routine contains no error checking.
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
bspline_pppack_bsplvd (const gsl_vector * t,
Packit 67cb25
		       const size_t k,
Packit 67cb25
		       const double x,
Packit 67cb25
		       const size_t left,
Packit 67cb25
		       gsl_vector * deltal,
Packit 67cb25
		       gsl_vector * deltar,
Packit 67cb25
		       gsl_matrix * a,
Packit 67cb25
		       gsl_matrix * dbiatx, const size_t nderiv)
Packit 67cb25
{
Packit 67cb25
  int i, ideriv, il, j, jlow, jp1mid, kmm, ldummy, m, mhigh;
Packit 67cb25
  double factor, fkmm, sum;
Packit 67cb25
Packit 67cb25
  size_t bsplvb_j;
Packit 67cb25
  gsl_vector_view dbcol = gsl_matrix_column (dbiatx, 0);
Packit 67cb25
Packit 67cb25
  mhigh = GSL_MIN_INT (nderiv, k - 1);
Packit 67cb25
  bspline_pppack_bsplvb (t, k - mhigh, 1, x, left, &bsplvb_j, deltal, deltar,
Packit 67cb25
			 &dbcol.vector);
Packit 67cb25
  if (mhigh > 0)
Packit 67cb25
    {
Packit 67cb25
      /* the first column of dbiatx always contains the b-spline
Packit 67cb25
         values for the current order. these are stored in column
Packit 67cb25
         k-current order before bsplvb is called to put values
Packit 67cb25
         for the next higher order on top of it.  */
Packit 67cb25
      ideriv = mhigh;
Packit 67cb25
      for (m = 1; m <= mhigh; m++)
Packit 67cb25
	{
Packit 67cb25
	  for (j = ideriv, jp1mid = 0; j < (int) k; j++, jp1mid++)
Packit 67cb25
	    {
Packit 67cb25
	      gsl_matrix_set (dbiatx, j, ideriv,
Packit 67cb25
			      gsl_matrix_get (dbiatx, jp1mid, 0));
Packit 67cb25
	    }
Packit 67cb25
	  ideriv--;
Packit 67cb25
	  bspline_pppack_bsplvb (t, k - ideriv, 2, x, left, &bsplvb_j, deltal,
Packit 67cb25
				 deltar, &dbcol.vector);
Packit 67cb25
	}
Packit 67cb25
Packit 67cb25
      /* at this point,  b(left-k+i, k+1-j)(x) is in  dbiatx(i,j)
Packit 67cb25
         for i=j,...,k-1 and j=0,...,mhigh. in particular, the
Packit 67cb25
         first column of dbiatx is already in final form. to obtain
Packit 67cb25
         corresponding derivatives of b-splines in subsequent columns,
Packit 67cb25
         generate their b-repr. by differencing, then evaluate at x. */
Packit 67cb25
      jlow = 0;
Packit 67cb25
      for (i = 0; i < (int) k; i++)
Packit 67cb25
	{
Packit 67cb25
	  for (j = jlow; j < (int) k; j++)
Packit 67cb25
	    {
Packit 67cb25
	      gsl_matrix_set (a, j, i, 0.0);
Packit 67cb25
	    }
Packit 67cb25
	  jlow = i;
Packit 67cb25
	  gsl_matrix_set (a, i, i, 1.0);
Packit 67cb25
	}
Packit 67cb25
Packit 67cb25
      /* at this point, a(.,j) contains the b-coeffs for the j-th of the
Packit 67cb25
         k b-splines of interest here. */
Packit 67cb25
      for (m = 1; m <= mhigh; m++)
Packit 67cb25
	{
Packit 67cb25
	  kmm = k - m;
Packit 67cb25
	  fkmm = (float) kmm;
Packit 67cb25
	  il = left;
Packit 67cb25
	  i = k - 1;
Packit 67cb25
Packit 67cb25
	  /* for j=1,...,k, construct b-coeffs of (m)th  derivative
Packit 67cb25
	     of b-splines from those for preceding derivative by
Packit 67cb25
	     differencing and store again in  a(.,j) . the fact that
Packit 67cb25
	     a(i,j) = 0  for i .lt. j  is used. */
Packit 67cb25
	  for (ldummy = 0; ldummy < kmm; ldummy++)
Packit 67cb25
	    {
Packit 67cb25
	      factor =
Packit 67cb25
		fkmm / (gsl_vector_get (t, il + kmm) -
Packit 67cb25
			gsl_vector_get (t, il));
Packit 67cb25
	      /* the assumption that t(left).lt.t(left+1) makes
Packit 67cb25
	         denominator in factor nonzero. */
Packit 67cb25
	      for (j = 0; j <= i; j++)
Packit 67cb25
		{
Packit 67cb25
		  gsl_matrix_set (a, i, j, factor * (gsl_matrix_get (a, i, j)
Packit 67cb25
						     - gsl_matrix_get (a,
Packit 67cb25
								       i - 1,
Packit 67cb25
								       j)));
Packit 67cb25
		}
Packit 67cb25
	      il--;
Packit 67cb25
	      i--;
Packit 67cb25
	    }
Packit 67cb25
Packit 67cb25
	  /* for i=1,...,k, combine b-coeffs a(.,i) with b-spline values
Packit 67cb25
	     stored in dbiatx(.,m) to get value of (m)th  derivative
Packit 67cb25
	     of i-th b-spline (of interest here) at x, and store in
Packit 67cb25
	     dbiatx(i,m). storage of this value over the value of a
Packit 67cb25
	     b-spline of order m there is safe since the remaining
Packit 67cb25
	     b-spline derivatives of the same order do not use this
Packit 67cb25
	     value due to the fact that a(j,i) = 0 for j .lt. i . */
Packit 67cb25
	  for (i = 0; i < (int) k; i++)
Packit 67cb25
	    {
Packit 67cb25
	      sum = 0;
Packit 67cb25
	      jlow = GSL_MAX_INT (i, m);
Packit 67cb25
	      for (j = jlow; j < (int) k; j++)
Packit 67cb25
		{
Packit 67cb25
		  sum +=
Packit 67cb25
		    gsl_matrix_get (a, j, i) * gsl_matrix_get (dbiatx, j, m);
Packit 67cb25
		}
Packit 67cb25
	      gsl_matrix_set (dbiatx, i, m, sum);
Packit 67cb25
	    }
Packit 67cb25
	}
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return;
Packit 67cb25
Packit 67cb25
}				/* bspline_pppack_bsplvd */