libpspm
cubic_spline.h
Go to the documentation of this file.
1 #ifndef PSPM_CUBIC_SPLINE_H_
2 #define PSPM_CUBIC_SPLINE_H_
3 
4 #include <vector>
5 #include <cassert>
6 #include <iomanip>
7 
8 typedef double Float;
9 
10 
11 
12 
13 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 // Thomas Algorithm to solve Tridiagonal system in O(N) time
15 //
16 // n is the number of unknowns = matrix size
17 // The system solved is as follows:
18 //
19 // b0 c0 0 0 0 0 0 x0 d0
20 // a1 b1 c1 0 0 0 0 x1 d1
21 // 0 a2 b2 c2 0 0 0 x2 d2
22 // 0 0 a3 b3 c3 0 0 * x3 = d3
23 // 0 0 0 a4 b4 c4 0 x4 d4
24 // 0 0 0 0 a5 b5 c5 x5 d5
25 // 0 0 0 0 0 a6 b6 x6 d6
26 //
27 // When storing the diagonal (b), upper (c), and lower (a) arrays,
28 // array index corresponds to row number in the matrix.
29 // Thus, a[0] and c[n-1] are never used.
30 //
31 // Written by Keivan Moradi, 2014
32 // See:
33 // https://en.wikibooks.org/wiki/Algorithm_Implementation/Linear_Algebra/Tridiagonal_matrix_algorithm
34 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35 inline void thomas_solve(double* a, double* b, double* c, double* d, int n) {
36  n--; // since we start from x0 (not x1) JAI: Therefore last valid index is n for a,b, n-1 for c
37  c[0] /= b[0];
38  d[0] /= b[0];
39 
40  for (int i = 1; i < n; i++) {
41  c[i] /= b[i] - a[i]*c[i-1];
42  d[i] = (d[i] - a[i]*d[i-1]) / (b[i] - a[i]*c[i-1]);
43  }
44 
45  d[n] = (d[n] - a[n]*d[n-1]) / (b[n] - a[n]*c[n-1]);
46 
47  for (int i = n; i-- > 0;) {
48  d[i] -= c[i]*d[i+1]; // JAI: note, i is in [0, n-1]
49  }
50 }
51 
52 
53 
54 
55 
56 // This is a custom implementation of std::lower_bound() for arrays
57 // The implementation can be found here:
58 // http://www.cplusplus.com/reference/algorithm/lower_bound/
59 // This function returns an the index of the first element
60 // in the range [0,arrlen) which does not compare less than val.
61 // i.e. index of first element >= val.
62 template <typename T>
63 int my_lower_bound(const T& val, const T * arr, const int arrlen)
64 {
65  int it, first=0, last=arrlen;
66  int count, step;
67  count = arrlen;
68  while (count>0)
69  {
70  it = first; step=count/2;
71  it += step;
72  if (arr[it] < val) {
73  first=++it;
74  count-=step+1;
75  }
76  else count=step;
77  }
78  return first;
79 }
80 
81 // This is a custom implementation of std::upper_bound() for arrays
82 // The implementation can be found here:
83 // http://www.cplusplus.com/reference/algorithm/upper_bound/
84 // This function returns an the index of the first element
85 // in the range [0,arrlen) which compares greater than val.
86 // i.e. index of first element > val.
87 template <typename T>
88 int my_upper_bound(const T& val, T * arr, int arrlen)
89 {
90  int it, first=0, last=arrlen;
91  int count, step;
92  count = arrlen;
93  while (count>0)
94  {
95  it = first; step=count/2;
96  it += step;
97  if (!(val<arr[it])) {
98  first=++it;
99  count-=step+1;
100  }
101  else count=step;
102  }
103  return first;
104 }
105 
106 
107 
108 // This spline interpolation follows the method from
109 // https://kluge.in-chemnitz.de/opensource/spline/
110 
111 // In interval [x{i} - x{i+1}], the function is
112 // fi(x) = ai*(x-xi)^3 + bi*(x-xi)^2 + ci*(x-xi) + yi
113 // Applying constraints gives:
114 // 1. ai = (b{i+1} - b{i})/3/hi hi = x{i+1}-xi
115 // 2. ci = (y{i+1}-y{i})/hi - 1/3*(2bi + b{i+1})hi
116 // 3. Tridiagonal system for b: for i = [1,n-2]
117 // h{i-1}b{i-1}/3 + 2(h{i-1}+h{i})bi/3 + hib{i+1}/3 = (y{i+1}-y{i})/hi - (y{i}-y{i-1})/h{i-1}
118 // 4. Boundary conditions: (NEED TO BETTER UNDERSTAND)
119 //
120 
121 class Spline{
122  public:
123  int npoints;
124 
129 
130 // bool extrapolate_constant = true;
131 // bool extrapolate_off = true;
132 
133  protected:
134  std::vector <Float> m_a, m_b, m_c;
135  std::vector <Float> m_x, m_y; // Need random-access containers here
136 
137  public:
138  template <typename Container>
139  void set_points(const Container &x, const Container &y){ // construct should be able to take any containers, as the points will be copied to the Spline's own storage anyway
140  assert(x.size() == y.size());
141  m_x.assign(x.begin(), x.end());
142  m_y.assign(y.begin(), y.end());
143 
144  npoints = x.size();
145  m_a.resize(npoints);
146  m_b.resize(npoints);
147  m_c.resize(npoints);
148 
149  // Check that x is in increasing order
150  for(int i=0; i<npoints-1; i++) {
151  assert(m_x[i]<m_x[i+1]);
152  }
153 
154  if( splineType == CUBIC) { // cubic spline interpolation
156  }
157  else if (splineType == LINEAR) { // linear interpolation
159  }
160  else if (splineType == CONSTRAINED_CUBIC){
162  }
163 
164  }
165 
166 
167  inline void solve_coeffs_linear(){
168  int n = npoints;
169  for(int i=0; i<n-1; i++) {
170  m_a[i]=0.0;
171  m_b[i]=0.0;
172  m_c[i]=(m_y[i+1]-m_y[i])/(m_x[i+1]-m_x[i]);
173  }
174  m_a[n-1] = m_b[n-1] = 0.0;
175  m_c[n-1] = m_c[n-2]; // fn-1 is same as fn-2 - same line extends right for extrapolation
176  }
177 
178 
179  inline void solve_coeffs_cubic(){
180  int n = npoints;
181  // setting up the matrix and right hand side of the equation system
182  // for the parameters b[]
183  //band_matrix A(n,1,1);
184  std::vector <Float> l(n,0), m(n,0), u(n,0); // lower, middle, and upper diagonals of thr matrix. These hold coefficients of b{i-1}, b{i}, b{i+1} respectively
185  std::vector<double> rhs(n);
186  for(int i=1; i<n-1; i++) {
187  l[i] =1.0/3.0*(m_x[i]-m_x[i-1]);
188  m[i] =2.0/3.0*(m_x[i+1]-m_x[i-1]);
189  u[i] =1.0/3.0*(m_x[i+1]-m_x[i]);
190  rhs[i]=(m_y[i+1]-m_y[i])/(m_x[i+1]-m_x[i]) - (m_y[i]-m_y[i-1])/(m_x[i]-m_x[i-1]);
191  }
192  // boundary conditions, zero curvature b[0]=b[n-1]=0
193  m[0] = 2.0;
194  u[0] = 0.0;
195  rhs[0] = 0.0;
196  m[n-1] = 2.0;
197  l[n-1] = 0.0;
198  rhs[n-1] = 0.0;
199 
200  // solve the equation system to obtain the parameters b[]
201  thomas_solve(l.data(),m.data(),u.data(),rhs.data(),n);
202  m_b = rhs;
203 
204  // calculate parameters a[] and c[] based on b[]
205  for(int i=0; i<n-1; i++) {
206  m_a[i]=1.0/3.0*(m_b[i+1]-m_b[i])/(m_x[i+1]-m_x[i]);
207  m_c[i]=(m_y[i+1]-m_y[i])/(m_x[i+1]-m_x[i])
208  - 1.0/3.0*(2.0*m_b[i]+m_b[i+1])*(m_x[i+1]-m_x[i]);
209  }
210  // for the right boundary we define
211  // f_{n-1}(x) = b*(x-x_{n-1})^2 + c*(x-x_{n-1}) + y_{n-1}
212  double h=m_x[n-1]-m_x[n-2];
213  // m_b[n-1] is determined by the boundary condition (turns out to be 0; = 0 for 0 curvature at xn-1)
214  m_a[n-1] = 0.0;
215  m_c[n-1]=3.0*m_a[n-2]*h*h+2.0*m_b[n-2]*h+m_c[n-2]; // = f'_{n-2}(x_{n-1})
216  }
217 
218 
220  int n = npoints;
221 
222  std::vector <double> m(n);
223  for (int i=1; i<n-1; ++i){
224  double fplus = (m_y[i+1]-m_y[i])/(m_x[i+1]-m_x[i]);
225  double fminus = (m_y[i]-m_y[i-1])/(m_x[i]-m_x[i-1]);
226  if (fplus*fminus < 0) m[i] = 0;
227  else m[i] = 2/(1/fplus + 1/fminus); // harmonic mean will tend towards smaller derivative
228  //else m[i] = (fplus+fminus)/2;
229  }
230  // Boundary conditions: zero curvature at x0 and xN-1
231  m[0] = 3*(m_y[1]-m_y[0])/(m_x[1]-m_x[0])/2 - m[1]/2;
232  m[n-1] = 3*(m_y[n-1]-m_y[n-2])/(m_x[n-1]-m_x[n-2])/2 - m[n-2]/2;
233 
234  for (int i=0; i<n-1; ++i){
235  m_c[i] = m[i];
236  double hi = m_x[i+1]-m_x[i];
237  m_b[i] = ( 3*(m_y[i+1]-m_y[i] - m[i]*hi) - hi*(m[i+1]-m[i]))/hi/hi;
238  m_a[i] = (-2*(m_y[i+1]-m_y[i] - m[i]*hi) + hi*(m[i+1]-m[i]))/hi/hi/hi;
239  }
240  m_b[n-1] = 0;
241  m_c[n-1] = m[n-1];
242  }
243 
244 
245  template <typename Container>
246  void constructAndReset(const Container &x, const Container &y){ // construct should be able to take any containers, as the points will be copied to the Spline's own storage anyway
247  set_points(x,y);
248  }
249 
250  inline Float extrapolate_left(double x) const{
251  double h=x-m_x[0];
252  // extrapolation to the left
253  if (extrapolate == ZERO) return 0; // zero
254  else if (extrapolate == CONSTANT) return m_y[0]; // constant
255  else return ((m_b[0])*h + m_c[0])*h + m_y[0]; // quadratic
256  }
257 
258  inline Float extrapolate_right(double x) const{
259  size_t n=m_x.size();
260  double h=x-m_x[n-1];
261  // extrapolation to the right
262  if (extrapolate == ZERO) return 0; // zero
263  else if (extrapolate == CONSTANT) return m_y[n-1]; // constant
264  else return ((m_b[n-1])*h + m_c[n-1])*h + m_y[n-1]; // quadratic
265 
266  }
267 
268  inline Float eval(Float x) const {
269  size_t n=m_x.size();
270 
271  if(x<m_x[0]) return extrapolate_left(x);
272  else if(x>=m_x[n-1]) return extrapolate_right(x);
273  else {
274  // find the closest point m_x[idx] < x
275 // std::vector<double>::const_iterator it;
276 // it=std::lower_bound(m_x.begin(),m_x.end(),x);
277 // int idx=std::max( int(it-m_x.begin())-1, 0);
278  int it = my_lower_bound(x, m_x.data(), m_x.size());
279  int idx=std::max(it-1,0); //std::max(it-1, 0);
280 // int it_ub = my_upper_bound(x, m_x.data(), m_x.size());
281 // int idx_ub=it_ub-1; //std::max(it_ub-1, 0);
282 // std::cout << "x: " << x << " " << idx << " " << idx_ub << endl;
283 
284  return eval(x,idx);
285  }
286  }
287 
288  inline Float eval(Float x, int idx) const{
289  double h=x-m_x[idx];
290  return ((m_a[idx]*h + m_b[idx])*h + m_c[idx])*h + m_y[idx];
291  }
292 
293  inline void print(){
294  std::cout << "Spline: \n";
295  for (int i=0; i<npoints; ++i){
296  std::cout << std::right << std::setw(10) << std::setfill(' ') << m_x[i] << " "
297  << std::right << std::setw(10) << std::setfill(' ') << m_y[i] << " "
298  << std::right << std::setw(10) << std::setfill(' ') << m_a[i] << " "
299  << std::right << std::setw(10) << std::setfill(' ') << m_b[i] << " "
300  << std::right << std::setw(10) << std::setfill(' ') << m_c[i] << " "
301  << "\n";
302  }
303  std:: cout << std::endl;
304  }
305 };
306 
307 
308 
309 // When storing the diagonal, upper, and lower arrays, array
310 // index corresponds to row number. Thus, a[0] and c[N-1] are
311 // never used
312 
314  public:
315  int N;
316 
317  std::vector <Float> upper; // element N-1 is absent
318  std::vector <Float> diag;
319  std::vector <Float> lower; // element 0 is absent
320 
321  inline BandMatrix(int size){
322  N = size;
323  upper.resize(N);
324  lower.resize(N);
325  diag.resize(N);
326  }
327 
328  inline BandMatrix(std::vector<Float> _lower, std::vector<Float> _diag, std::vector<Float> _upper){
329  N = _upper.size();
330  upper = _upper;
331  lower = _lower;
332  diag = _diag;
333  }
334 
335  inline void print(){
336  for (int i=0; i<N; ++i){
337  for (int z=0; z<i-1;++z) std::cout << 0 << "\t";
338  if (i > 0) std::cout << lower[i] << "\t";
339  std::cout << diag[i] << "\t";
340  if (i < N-1) std::cout << upper[i] << "\t";
341  for (int z=i+2; z<N; ++z) std::cout << 0 << "\t";
342  std::cout << "\n";
343  }
344  }
345 
346  inline void solve(std::vector<Float>&y){
347  thomas_solve(lower.data(), diag.data(), upper.data(), y.data(), N);
348  }
349 };
350 
351 
352 
353 #endif
354 
void print()
Definition: cubic_spline.h:335
void solve_coeffs_linear()
Definition: cubic_spline.h:167
int npoints
Definition: cubic_spline.h:123
Float extrapolate_right(double x) const
Definition: cubic_spline.h:258
Float eval(Float x, int idx) const
Definition: cubic_spline.h:288
std::vector< Float > m_x
Definition: cubic_spline.h:135
Float extrapolate_left(double x) const
Definition: cubic_spline.h:250
void solve_coeffs_constrained_cubic()
Definition: cubic_spline.h:219
int my_lower_bound(const T &val, const T *arr, const int arrlen)
Definition: cubic_spline.h:63
void set_points(const Container &x, const Container &y)
Definition: cubic_spline.h:139
Extr extrapolate
Definition: cubic_spline.h:128
void solve_coeffs_cubic()
Definition: cubic_spline.h:179
std::vector< Float > upper
Definition: cubic_spline.h:317
std::vector< Float > m_a
Definition: cubic_spline.h:134
BandMatrix(std::vector< Float > _lower, std::vector< Float > _diag, std::vector< Float > _upper)
Definition: cubic_spline.h:328
Float eval(Float x) const
Definition: cubic_spline.h:268
std::vector< Float > m_y
Definition: cubic_spline.h:135
void thomas_solve(double *a, double *b, double *c, double *d, int n)
Definition: cubic_spline.h:35
double Float
Definition: cubic_spline.h:8
void print()
Definition: cubic_spline.h:293
Type splineType
Definition: cubic_spline.h:126
void solve(std::vector< Float > &y)
Definition: cubic_spline.h:346
int my_upper_bound(const T &val, T *arr, int arrlen)
Definition: cubic_spline.h:88
std::vector< Float > lower
Definition: cubic_spline.h:319
std::vector< Float > diag
Definition: cubic_spline.h:318
std::vector< Float > m_b
Definition: cubic_spline.h:134
std::vector< Float > m_c
Definition: cubic_spline.h:134
BandMatrix(int size)
Definition: cubic_spline.h:321
void constructAndReset(const Container &x, const Container &y)
Definition: cubic_spline.h:246