LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
Public Member Functions | Protected Member Functions | Protected Attributes | List of all members
lsst::afw::math::detail::SmoothedSpline Class Reference

#include <Spline.h>

Inheritance diagram for lsst::afw::math::detail::SmoothedSpline:
lsst::afw::math::detail::Spline

Public Member Functions

 SmoothedSpline (std::vector< double > const &x, std::vector< double > const &y, std::vector< double > const &dy, double s, double *chisq=nullptr, std::vector< double > *errs=nullptr)
 Cubic spline data smoother. More...
 
void interpolate (std::vector< double > const &x, std::vector< double > &y) const
 Interpolate a Spline. More...
 
void derivative (std::vector< double > const &x, std::vector< double > &dydx) const
 Find the derivative of a Spline. More...
 
std::vector< double > roots (double const value, double const x0, double const x1) const
 Find the roots of Spline - val = 0 in the range [x0, x1). More...
 

Protected Member Functions

void _allocateSpline (int const nknot)
 Allocate the storage a Spline needs. More...
 

Protected Attributes

std::vector< double > _knots
 
std::vector< std::vector< double > > _coeffs
 

Detailed Description

Definition at line 159 of file Spline.h.

Constructor & Destructor Documentation

◆ SmoothedSpline()

lsst::afw::math::detail::SmoothedSpline::SmoothedSpline ( std::vector< double > const &  x,
std::vector< double > const &  y,
std::vector< double > const &  dy,
double  s,
double *  chisq = nullptr,
std::vector< double > *  errs = nullptr 
)

Cubic spline data smoother.

Algorithm 642 collected algorithms from ACM. Algorithm appeared in Acm-Trans. Math. Software, vol.12, no. 2, Jun., 1986, p. 150.

Translated from fortran by a combination of f2c and RHL.

    Author              - M.F.Hutchinson
                          CSIRO Division of Mathematics and Statistics
                          P.O. Box 1965
                          Canberra, ACT 2601
                          Australia

latest revision - 15 August 1985

Parameters
[in]xarray of length n containing the abscissae of the n data points (x(i),f(i)) i=0..n-1. x must be ordered so that x(i) < x(i+1)
[in]yvector of length >= 3 containing the ordinates (or function values) of the data points
[in]dyvector of standard deviations of y the error associated with the data point; each dy[] must be positive.
[in]sdesired chisq
[out]chisqfinal chisq (if non-NULL)
[out]errserror estimates, (if non-NULL). You'll need to delete it
Note
y,c: spline coefficients (output). y is an array of length n; c is an n-1 by 3 matrix. The value of the spline approximation at t is s(t) = c[2][i]*d^3 + c[1][i]*d^2 + c[0][i]*d + y[i] where x[i] <= t < x[i+1] and d = t - x[i].
var: error variance. If var is negative (i.e. unknown) then the smoothing parameter is determined by minimizing the generalized cross validation and an estimate of the error variance is returned. If var is non-negative (i.e. known) then the smoothing parameter is determined to minimize an estimate, which depends on var, of the true mean square error. In particular, if var is zero, then an interpolating natural cubic spline is calculated. Set var to 1 if absolute standard deviations have been provided in dy (see above).
Additional information on the fit is available in the stat array. on normal exit the values are assigned as follows: stat[0] = smoothing parameter (= rho/(rho + 1)) stat[1] = estimate of the number of degrees of freedom of the residual sum of squares; this reduces to the usual value of n-2 when a least squares regression line is calculated. stat[2] = generalized cross validation stat[3] = mean square residual stat[4] = estimate of the true mean square error at the data points stat[5] = estimate of the error variance; chi^2/nu in the case of linear regression
If stat[0]==0 (rho==0) an interpolating natural cubic spline has been calculated; if stat[0]==1 (rho==infinite) a least squares regression line has been calculated.
Returns stat[4], an estimate of the true rms error
precision/hardware - double (originally VAX double)
the number of arithmetic operations required by the subroutine is proportional to n. The subroutine uses an algorithm developed by M.F. Hutchinson and F.R. de Hoog, 'Smoothing Noisy Data with Spline Functions', Numer. Math. 47 p.99 (1985)

Definition at line 534 of file Spline.cc.

536  {
537  float var = 1; // i.e. df is the absolute s.d. N.B. ADD GCV Variant with var=-1
538  int const n = x.size();
539  double const ratio = 2.0;
540  double const tau = 1.618033989; /* golden ratio */
541  double avdf, avar, stat[6];
542  double p, q, delta, r1, r2, r3, r4;
543  double gf1, gf2, gf3, gf4, avh, err;
544  /*
545  * allocate scratch space
546  */
547  _allocateSpline(n);
548 
549  double *y = &_coeffs[0][0];
550  double *c[3];
551  c[0] = &_coeffs[1][0];
552  c[1] = &_coeffs[2][0];
553  c[2] = &_coeffs[3][0];
554 
555  std::vector<double> scratch(7 * (n + 2)); // scratch space
556 
557  double *r[3];
558  r[0] = &scratch[0] + 1; // we want indices -1..n
559  r[1] = r[0] + (n + 2);
560  r[2] = r[1] + (n + 2);
561  double *t[2];
562  t[0] = r[2] + (n + 2);
563  t[1] = t[0] + (n + 2);
564  double *u = t[1] + (n + 2);
565  double *v = u + (n + 2);
566  /*
567  * and so to work.
568  */
569  std::vector<double> sdf = df; // scaled values of df
570 
571  avdf = spint1(&x[0], &avh, &f[0], &sdf[0], n, y, c, r, t);
572  avar = var;
573  if (var > 0) {
574  avar *= avdf * avdf;
575  }
576 
577  if (var == 0) { /* simply find a natural cubic spline*/
578  r1 = 0;
579 
580  gf1 = spfit1(&x[0], avh, &sdf[0], n, r1, &p, &q, avar, stat, y, c, r, t, u, v);
581  } else { /* Find local minimum of gcv or the
582  expected mean square error */
583  r1 = 1;
584  r2 = ratio * r1;
585  gf2 = spfit1(&x[0], avh, &sdf[0], n, r2, &p, &q, avar, stat, y, c, r, t, u, v);
586  bool set_r3 = false; // was r3 set?
587  for (;;) {
588  gf1 = spfit1(&x[0], avh, &sdf[0], n, r1, &p, &q, avar, stat, y, c, r, t, u, v);
589  if (gf1 > gf2) {
590  break;
591  }
592 
593  if (p <= 0) {
594  break;
595  }
596  r2 = r1;
597  gf2 = gf1;
598  r1 /= ratio;
599  }
600 
601  if (p <= 0) {
602  set_r3 = false;
603  r3 = 0; /* placate compiler */
604  } else {
605  r3 = ratio * r2;
606  set_r3 = true;
607 
608  for (;;) {
609  gf3 = spfit1(&x[0], avh, &sdf[0], n, r3, &p, &q, avar, stat, y, c, r, t, u, v);
610  if (gf3 >= gf2) {
611  break;
612  }
613 
614  if (q <= 0) {
615  break;
616  }
617  r2 = r3;
618  gf2 = gf3;
619  r3 = ratio * r3;
620  }
621  }
622 
623  if (p > 0 && q > 0) {
624  assert(set_r3);
625  r2 = r3;
626  gf2 = gf3;
627  delta = (r2 - r1) / tau;
628  r4 = r1 + delta;
629  r3 = r2 - delta;
630  gf3 = spfit1(&x[0], avh, &sdf[0], n, r3, &p, &q, avar, stat, y, c, r, t, u, v);
631  gf4 = spfit1(&x[0], avh, &sdf[0], n, r4, &p, &q, avar, stat, y, c, r, t, u, v);
632  /*
633  * Golden section search for local minimum
634  */
635  do {
636  if (gf3 <= gf4) {
637  r2 = r4;
638  gf2 = gf4;
639  r4 = r3;
640  gf4 = gf3;
641  delta /= tau;
642  r3 = r2 - delta;
643  gf3 = spfit1(&x[0], avh, &sdf[0], n, r3, &p, &q, avar, stat, y, c, r, t, u, v);
644  } else {
645  r1 = r3;
646  gf1 = gf3;
647  r3 = r4;
648  gf3 = gf4;
649  delta /= tau;
650  r4 = r1 + delta;
651  gf4 = spfit1(&x[0], avh, &sdf[0], n, r4, &p, &q, avar, stat, y, c, r, t, u, v);
652  }
653 
654  err = (r2 - r1) / (r1 + r2);
655  } while (err * err + 1 > 1 && err > 1e-6);
656 
657  r1 = (r1 + r2) * .5;
658  gf1 = spfit1(&x[0], avh, &sdf[0], n, r1, &p, &q, avar, stat, y, c, r, t, u, v);
659  }
660  }
661  /*
662  * Calculate spline coefficients
663  */
664  spcof1(&x[0], avh, &f[0], &sdf[0], n, p, q, y, c, u, v);
665 
666  stat[2] /= avdf * avdf; /* undo scaling */
667  stat[3] /= avdf * avdf;
668  stat[4] /= avdf * avdf;
669  /*
670  * Optionally calculate standard error estimates
671  */
672  if (errs != nullptr) {
673  sperr1(&x[0], avh, &sdf[0], n, r, p, avar, errs);
674  }
675  /*
676  * clean up
677  */
678  if (chisq != nullptr) {
679  *chisq = n * stat[4];
680  }
681 }
double x
int y
Definition: SpanSet.cc:48
void _allocateSpline(int const nknot)
Allocate the storage a Spline needs.
Definition: Spline.cc:21
std::vector< std::vector< double > > _coeffs
Definition: Spline.h:56

Member Function Documentation

◆ _allocateSpline()

void lsst::afw::math::detail::Spline::_allocateSpline ( int const  nknot)
protectedinherited

Allocate the storage a Spline needs.

Definition at line 21 of file Spline.cc.

21  {
22  _knots.resize(nknot);
23  _coeffs.resize(4);
24  for (unsigned int i = 0; i != _coeffs.size(); ++i) {
25  _coeffs[i].reserve(nknot);
26  }
27 }
std::vector< double > _knots
Definition: Spline.h:55
T reserve(T... args)
T resize(T... args)
T size(T... args)

◆ derivative()

void lsst::afw::math::detail::Spline::derivative ( std::vector< double > const &  x,
std::vector< double > &  dydx 
) const
inherited

Find the derivative of a Spline.

Parameters
[in]xpoints to evaluate derivative at
[out]dydxderivatives at x

Definition at line 57 of file Spline.cc.

57  {
58  int const nknot = _knots.size();
59  int const n = x.size();
60 
61  dydx.resize(n); // may default-construct elements which is a little inefficient
62  /*
63  * For _knots[i] <= x <= _knots[i+1], the * interpolant has the form
64  * val = _coeff[0][i] +dx*(_coeff[1][i] + dx*(_coeff[2][i]/2 + dx*_coeff[3][i]/6))
65  * with
66  * dx = x - knots[i]
67  * so the derivative is
68  * val = _coeff[1][i] + dx*(_coeff[2][i] + dx*_coeff[3][i]/2))
69  */
70 
71  int ind = -1; // no idea initially
72  for (int i = 0; i != n; ++i) {
73  ind = search_array(x[i], &_knots[0], nknot, ind);
74 
75  if (ind < 0) { // off bottom
76  ind = 0;
77  } else if (ind >= nknot) { // off top
78  ind = nknot - 1;
79  }
80 
81  double const dx = x[i] - _knots[ind];
82  dydx[i] = _coeffs[1][ind] + dx * (_coeffs[2][ind] + dx * _coeffs[3][ind] / 2);
83  }
84 }

◆ interpolate()

void lsst::afw::math::detail::Spline::interpolate ( std::vector< double > const &  x,
std::vector< double > &  y 
) const
inherited

Interpolate a Spline.

Parameters
[in]xpoints to interpolate at
[out]yvalues of spline interpolation at x

Definition at line 29 of file Spline.cc.

29  {
30  int const nknot = _knots.size();
31  int const n = x.size();
32 
33  y.resize(n); // may default-construct elements which is a little inefficient
34  /*
35  * For _knots[i] <= x <= _knots[i+1], the interpolant
36  * has the form
37  * val = _coeff[0][i] +dx*(_coeff[1][i] + dx*(_coeff[2][i]/2 + dx*_coeff[3][i]/6))
38  * with
39  * dx = x - knots[i]
40  */
41  int ind = -1; // no idea initially
42  for (int i = 0; i != n; ++i) {
43  ind = search_array(x[i], &_knots[0], nknot, ind);
44 
45  if (ind < 0) { // off bottom
46  ind = 0;
47  } else if (ind >= nknot) { // off top
48  ind = nknot - 1;
49  }
50 
51  double const dx = x[i] - _knots[ind];
52  y[i] = _coeffs[0][ind] +
53  dx * (_coeffs[1][ind] + dx * (_coeffs[2][ind] / 2 + dx * _coeffs[3][ind] / 6));
54  }
55 }

◆ roots()

std::vector< double > lsst::afw::math::detail::Spline::roots ( double const  value,
double const  x0,
double const  x1 
) const
inherited

Find the roots of Spline - val = 0 in the range [x0, x1).

Return a vector of all the roots found

Parameters
valuedesired value
x0,x1specify desired range is [x0,x1)

Definition at line 1226 of file Spline.cc.

1226  {
1227  /*
1228  * Strategy: we know that the interpolant has the form
1229  * val = coef[0][i] +dx*(coef[1][i] + dx*(coef[2][i]/2 + dx*coef[3][i]/6))
1230  * so we can use the usual analytic solution for a cubic. Note that the
1231  * cubic quoted above returns dx, the distance from the previous knot,
1232  * rather than x itself
1233  */
1234  std::vector<double> roots; /* the roots found */
1235  double x0 = a; // lower end of current range
1236  double const x1 = b;
1237  int const nknot = _knots.size();
1238 
1239  int i0 = search_array(x0, &_knots[0], nknot, -1);
1240  int const i1 = search_array(x1, &_knots[0], nknot, i0);
1241  assert(i1 >= i0 && i1 <= nknot - 1);
1242 
1243  std::vector<double> newRoots; // the roots we find in some interval
1244  /*
1245  * Deal with special case that x0 may be off one end or the other of
1246  * the array of knots.
1247  */
1248  if (i0 < 0) { /* off bottom */
1249  i0 = 0;
1250  do_cubic(_coeffs[3][i0] / 6, _coeffs[2][i0] / 2, _coeffs[1][i0], _coeffs[0][i0] - value, newRoots);
1251  //
1252  // Could use
1253  // std::transform(newRoots.begin(), newRoots.end(), newRoots.begin(),
1254  // std::bind(std::plus<double>(), _1, _knots[i0]));
1255  // but let's not
1256  //
1257  for (unsigned int j = 0; j != newRoots.size(); ++j) {
1258  newRoots[j] += _knots[i0];
1259  }
1260  keep_valid_roots(roots, newRoots, x0, _knots[i0]);
1261 
1262  x0 = _knots[i0];
1263  } else if (i0 >= nknot) { /* off top */
1264  i0 = nknot - 1;
1265  assert(i0 >= 0);
1266  do_cubic(_coeffs[3][i0] / 6, _coeffs[2][i0] / 2, _coeffs[1][i0], _coeffs[0][i0] - value, newRoots);
1267 
1268  for (unsigned int j = 0; j != newRoots.size(); ++j) {
1269  newRoots[j] += _knots[i0];
1270  }
1271  keep_valid_roots(roots, newRoots, x0, x1);
1272 
1273  return roots;
1274  }
1275  /*
1276  * OK, now search in main body of spline. Note that i1 may be nknot - 1, and
1277  * in any case the right hand limit of the last segment is at x1, not a knot
1278  */
1279  for (int i = i0; i <= i1; i++) {
1280  do_cubic(_coeffs[3][i] / 6, _coeffs[2][i] / 2, _coeffs[1][i], _coeffs[0][i] - value, newRoots);
1281 
1282  for (unsigned int j = 0; j != newRoots.size(); ++j) {
1283  newRoots[j] += _knots[i];
1284  }
1285  keep_valid_roots(roots, newRoots, ((i == i0) ? x0 : _knots[i]), ((i == i1) ? x1 : _knots[i + 1]));
1286  }
1287 
1288  return roots;
1289 }
table::Key< int > b
table::Key< int > a
std::vector< double > roots(double const value, double const x0, double const x1) const
Find the roots of Spline - val = 0 in the range [x0, x1).
Definition: Spline.cc:1226

Member Data Documentation

◆ _coeffs

std::vector<std::vector<double> > lsst::afw::math::detail::Spline::_coeffs
protectedinherited

Definition at line 56 of file Spline.h.

◆ _knots

std::vector<double> lsst::afw::math::detail::Spline::_knots
protectedinherited

Definition at line 55 of file Spline.h.


The documentation for this class was generated from the following files: