ATTPCROOT  0.3.0-alpha
A ROOT-based framework for analyzing data from active target detectors
AtSpline.cxx
Go to the documentation of this file.
1 #include "AtSpline.h"
2 // IWYU pragma: no_include <ext/alloc_traits.h>
3 
4 #include <cmath> // for fabs, sqrt, cos, acos, pow, M_PI
5 #include <memory> // for allocator_traits<>::value_type
6 
7 namespace tk {
8 
9 // ---------------------------------------------------------------------
10 // implementation part, which could be separated into a cpp file
11 // ---------------------------------------------------------------------
12 
13 // spline implementation
14 // -----------------------
15 
16 void spline::set_boundary(spline::bd_type left, double left_value, spline::bd_type right, double right_value)
17 {
18  assert(m_x.size() == 0); // set_points() must not have happened yet
19  m_left = left;
20  m_right = right;
21  m_left_value = left_value;
22  m_right_value = right_value;
23 }
24 
26 {
27  assert(m_x.size() == m_y.size());
28  assert(m_x.size() == m_b.size());
29  assert(m_x.size() > 2);
30  size_t n = m_b.size();
31  if (m_c.size() != n)
32  m_c.resize(n);
33  if (m_d.size() != n)
34  m_d.resize(n);
35 
36  for (size_t i = 0; i < n - 1; i++) {
37  const double h = m_x[i + 1] - m_x[i];
38  // from continuity and differentiability condition
39  m_c[i] = (3.0 * (m_y[i + 1] - m_y[i]) / h - (2.0 * m_b[i] + m_b[i + 1])) / h;
40  // from differentiability condition
41  m_d[i] = ((m_b[i + 1] - m_b[i]) / (3.0 * h) - 2.0 / 3.0 * m_c[i]) / h;
42  }
43 
44  // for left extrapolation coefficients
45  m_c0 = (m_left == first_deriv) ? 0.0 : m_c[0];
46 }
47 
49 {
50  int n = (int)m_x.size();
51  // linear interpolation
52  m_d.resize(n);
53  m_c.resize(n);
54  m_b.resize(n);
55  for (int i = 0; i < n - 1; i++) {
56  m_d[i] = 0.0;
57  m_c[i] = 0.0;
58  m_b[i] = (m_y[i + 1] - m_y[i]) / (m_x[i + 1] - m_x[i]);
59  }
60  // ignore boundary conditions, set slope equal to the last segment
61  m_b[n - 1] = m_b[n - 2];
62  m_c[n - 1] = 0.0;
63  m_d[n - 1] = 0.0;
64 }
65 
67 {
68  int n = (int)m_x.size();
69  // classical cubic splines which are C^2 (twice cont differentiable)
70  // this requires solving an equation system
71 
72  // setting up the matrix and right hand side of the equation system
73  // for the parameters b[]
74  int n_upper = (m_left == spline::not_a_knot) ? 2 : 1;
75  int n_lower = (m_right == spline::not_a_knot) ? 2 : 1;
76  internal::band_matrix A(n, n_upper, n_lower);
77  std::vector<double> rhs(n);
78  for (int i = 1; i < n - 1; i++) {
79  A(i, i - 1) = 1.0 / 3.0 * (m_x[i] - m_x[i - 1]);
80  A(i, i) = 2.0 / 3.0 * (m_x[i + 1] - m_x[i - 1]);
81  A(i, i + 1) = 1.0 / 3.0 * (m_x[i + 1] - m_x[i]);
82  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]);
83  }
84  // boundary conditions
86  // 2*c[0] = f''
87  A(0, 0) = 2.0;
88  A(0, 1) = 0.0;
89  rhs[0] = m_left_value;
90  } else if (m_left == spline::first_deriv) {
91  // b[0] = f', needs to be re-expressed in terms of c:
92  // (2c[0]+c[1])(m_x[1]-m_x[0]) = 3 ((m_y[1]-m_y[0])/(m_x[1]-m_x[0]) - f')
93  A(0, 0) = 2.0 * (m_x[1] - m_x[0]);
94  A(0, 1) = 1.0 * (m_x[1] - m_x[0]);
95  rhs[0] = 3.0 * ((m_y[1] - m_y[0]) / (m_x[1] - m_x[0]) - m_left_value);
96  } else if (m_left == spline::not_a_knot) {
97  // f'''(m_x[1]) exists, i.e. d[0]=d[1], or re-expressed in c:
98  // -h1*c[0] + (h0+h1)*c[1] - h0*c[2] = 0
99  A(0, 0) = -(m_x[2] - m_x[1]);
100  A(0, 1) = m_x[2] - m_x[0];
101  A(0, 2) = -(m_x[1] - m_x[0]);
102  rhs[0] = 0.0;
103  } else {
104  assert(false);
105  }
106  if (m_right == spline::second_deriv) {
107  // 2*c[n-1] = f''
108  A(n - 1, n - 1) = 2.0;
109  A(n - 1, n - 2) = 0.0;
110  rhs[n - 1] = m_right_value;
111  } else if (m_right == spline::first_deriv) {
112  // b[n-1] = f', needs to be re-expressed in terms of c:
113  // (c[n-2]+2c[n-1])(m_x[n-1]-m_x[n-2])
114  // = 3 (f' - (m_y[n-1]-m_y[n-2])/(m_x[n-1]-m_x[n-2]))
115  A(n - 1, n - 1) = 2.0 * (m_x[n - 1] - m_x[n - 2]);
116  A(n - 1, n - 2) = 1.0 * (m_x[n - 1] - m_x[n - 2]);
117  rhs[n - 1] = 3.0 * (m_right_value - (m_y[n - 1] - m_y[n - 2]) / (m_x[n - 1] - m_x[n - 2]));
118  } else if (m_right == spline::not_a_knot) {
119  // f'''(m_x[n-2]) exists, i.e. d[n-3]=d[n-2], or re-expressed in c:
120  // -h_{n-2}*c[n-3] + (h_{n-3}+h_{n-2})*c[n-2] - h_{n-3}*c[n-1] = 0
121  A(n - 1, n - 3) = -(m_x[n - 1] - m_x[n - 2]);
122  A(n - 1, n - 2) = m_x[n - 1] - m_x[n - 3];
123  A(n - 1, n - 1) = -(m_x[n - 2] - m_x[n - 3]);
124  rhs[0] = 0.0;
125  } else {
126  assert(false);
127  }
128 
129  // solve the equation system to obtain the parameters c[]
130  m_c = A.lu_solve(rhs);
131 
132  // calculate parameters b[] and d[] based on c[]. Also calculate the integral
133  m_d.resize(n);
134  m_b.resize(n);
135 
136  for (int i = 0; i < n - 1; i++) {
137  m_d[i] = 1.0 / 3.0 * (m_c[i + 1] - m_c[i]) / (m_x[i + 1] - m_x[i]);
138  m_b[i] = (m_y[i + 1] - m_y[i]) / (m_x[i + 1] - m_x[i]) -
139  1.0 / 3.0 * (2.0 * m_c[i] + m_c[i + 1]) * (m_x[i + 1] - m_x[i]);
140  }
141 
142  // for the right extrapolation coefficients (zero cubic term)
143  // f_{n-1}(x) = y_{n-1} + b*(x-x_{n-1}) + c*(x-x_{n-1})^2
144  double h = m_x[n - 1] - m_x[n - 2];
145  // m_c[n-1] is determined by the boundary condition
146  m_d[n - 1] = 0.0;
147  m_b[n - 1] = 3.0 * m_d[n - 2] * h * h + 2.0 * m_c[n - 2] * h + m_b[n - 2]; // = f'_{n-2}(x_{n-1})
148  if (m_right == first_deriv)
149  m_c[n - 1] = 0.0; // force linear extrapolation
150 }
151 
153 {
154  int n = (int)m_x.size();
155  // hermite cubic splines which are C^1 (cont. differentiable)
156  // and derivatives are specified on each grid point
157  // (here we use 3-point finite differences)
158  m_b.resize(n);
159  m_c.resize(n);
160  m_d.resize(n);
161  // set b to match 1st order derivative finite difference
162  for (int i = 1; i < n - 1; i++) {
163  const double h = m_x[i + 1] - m_x[i];
164  const double hl = m_x[i] - m_x[i - 1];
165  m_b[i] = -h / (hl * (hl + h)) * m_y[i - 1] + (h - hl) / (hl * h) * m_y[i] + hl / (h * (hl + h)) * m_y[i + 1];
166  }
167  // boundary conditions determine b[0] and b[n-1]
168  if (m_left == first_deriv) {
169  m_b[0] = m_left_value;
170  } else if (m_left == second_deriv) {
171  const double h = m_x[1] - m_x[0];
172  m_b[0] = 0.5 * (-m_b[1] - 0.5 * m_left_value * h + 3.0 * (m_y[1] - m_y[0]) / h);
173  } else if (m_left == not_a_knot) {
174  // f''' continuous at x[1]
175  const double h0 = m_x[1] - m_x[0];
176  const double h1 = m_x[2] - m_x[1];
177  m_b[0] = -m_b[1] + 2.0 * (m_y[1] - m_y[0]) / h0 +
178  h0 * h0 / (h1 * h1) * (m_b[1] + m_b[2] - 2.0 * (m_y[2] - m_y[1]) / h1);
179  } else {
180  assert(false);
181  }
182  if (m_right == first_deriv) {
183  m_b[n - 1] = m_right_value;
184  m_c[n - 1] = 0.0;
185  } else if (m_right == second_deriv) {
186  const double h = m_x[n - 1] - m_x[n - 2];
187  m_b[n - 1] = 0.5 * (-m_b[n - 2] + 0.5 * m_right_value * h + 3.0 * (m_y[n - 1] - m_y[n - 2]) / h);
188  m_c[n - 1] = 0.5 * m_right_value;
189  } else if (m_right == not_a_knot) {
190  // f''' continuous at x[n-2]
191  const double h0 = m_x[n - 2] - m_x[n - 3];
192  const double h1 = m_x[n - 1] - m_x[n - 2];
193  m_b[n - 1] = -m_b[n - 2] + 2.0 * (m_y[n - 1] - m_y[n - 2]) / h1 +
194  h1 * h1 / (h0 * h0) * (m_b[n - 3] + m_b[n - 2] - 2.0 * (m_y[n - 2] - m_y[n - 3]) / h0);
195  // f'' continuous at x[n-1]: c[n-1] = 3*d[n-2]*h[n-2] + c[n-1]
196  m_c[n - 1] = (m_b[n - 2] + 2.0 * m_b[n - 1]) / h1 - 3.0 * (m_y[n - 1] - m_y[n - 2]) / (h1 * h1);
197  } else {
198  assert(false);
199  }
200  m_d[n - 1] = 0.0;
201 
202  // parameters c and d are determined by continuity and differentiability
204 }
205 
207 {
208  int n = (int)m_x.size();
209  // Integrate spline using Simpson's rule (exact for cubics)
210  // Simson's: h/3 * (f(a) + f(a+h) + f(b))
211  m_integral.resize(n);
212  m_integral[0] = 0;
213  for (int i = 1; i < n - 1; ++i) {
214  double h = (m_x[i] - m_x[i - 1]) / 2.;
215  m_integral[i] = h / 3. * (m_y[i - 1] + 4 * (*this)(m_x[i - 1] + h) + m_y[i]);
216  m_integral[i] += m_integral[i - 1];
217  }
218 }
219 
220 void spline::set_points(const std::vector<double> &x, const std::vector<double> &y, spline_type type)
221 {
222  assert(x.size() == y.size());
223  assert(x.size() >= 3);
224  // not-a-knot with 3 points has many solutions
225  if (m_left == not_a_knot || m_right == not_a_knot)
226  assert(x.size() >= 4);
227  m_type = type;
228  m_made_monotonic = false;
229  m_x = x;
230  m_y = y;
231  int n = (int)x.size();
232  // check strict monotonicity of input vector x
233  for (int i = 0; i < n - 1; i++) {
234  assert(m_x[i] < m_x[i + 1]);
235  }
236 
237  if (type == linear) {
239  } else if (type == cspline) {
241  } else if (type == cspline_hermite) {
243  } else {
244  assert(false);
245  }
246  // for left extrapolation coefficients
247  m_c0 = (m_left == first_deriv) ? 0.0 : m_c[0];
248  set_integral();
249 }
250 
252 {
253  assert(m_x.size() == m_y.size());
254  assert(m_x.size() == m_b.size());
255  assert(m_x.size() > 2);
256  bool modified = false;
257  const int n = (int)m_x.size();
258  // make sure: input data monotonic increasing --> b_i>=0
259  // input data monotonic decreasing --> b_i<=0
260  for (int i = 0; i < n; i++) {
261  int im1 = std::max(i - 1, 0);
262  int ip1 = std::min(i + 1, n - 1);
263  if (((m_y[im1] <= m_y[i]) && (m_y[i] <= m_y[ip1]) && m_b[i] < 0.0) ||
264  ((m_y[im1] >= m_y[i]) && (m_y[i] >= m_y[ip1]) && m_b[i] > 0.0)) {
265  modified = true;
266  m_b[i] = 0.0;
267  }
268  }
269  // if input data is monotonic (b[i], b[i+1], avg have all the same sign)
270  // ensure a sufficient criteria for monotonicity is satisfied:
271  // sqrt(b[i]^2+b[i+1]^2) <= 3 |avg|, with avg=(y[i+1]-y[i])/h,
272  for (int i = 0; i < n - 1; i++) {
273  double h = m_x[i + 1] - m_x[i];
274  double avg = (m_y[i + 1] - m_y[i]) / h;
275  if (avg == 0.0 && (m_b[i] != 0.0 || m_b[i + 1] != 0.0)) {
276  modified = true;
277  m_b[i] = 0.0;
278  m_b[i + 1] = 0.0;
279  } else if ((m_b[i] >= 0.0 && m_b[i + 1] >= 0.0 && avg > 0.0) ||
280  (m_b[i] <= 0.0 && m_b[i + 1] <= 0.0 && avg < 0.0)) {
281  // input data is monotonic
282  double r = sqrt(m_b[i] * m_b[i] + m_b[i + 1] * m_b[i + 1]) / std::fabs(avg);
283  if (r > 3.0) {
284  // sufficient criteria for monotonicity: r<=3
285  // adjust b[i] and b[i+1]
286  modified = true;
287  m_b[i] *= (3.0 / r);
288  m_b[i + 1] *= (3.0 / r);
289  }
290  }
291  }
292 
293  if (modified == true) {
295  set_integral();
296  m_made_monotonic = true;
297  }
298 
299  return modified;
300 }
301 
302 // return the closest idx so that m_x[idx] <= x (return 0 if x<m_x[0])
303 size_t spline::find_closest(double x) const
304 {
305  std::vector<double>::const_iterator it;
306  it = std::upper_bound(m_x.begin(), m_x.end(), x); // *it > x
307  size_t idx = std::max(int(it - m_x.begin()) - 1, 0); // m_x[idx] <= x
308  return idx;
309 }
310 
311 double spline::operator()(double x) const
312 {
313  // polynomial evaluation using Horner's scheme
314  // TODO: consider more numerically accurate algorithms, e.g.:
315  // - Clenshaw
316  // - Even-Odd method by A.C.R. Newbery
317  // - Compensated Horner Scheme
318  size_t n = m_x.size();
319  size_t idx = find_closest(x);
320 
321  double h = x - m_x[idx];
322  double interpol;
323  if (x < m_x[0]) {
324  // extrapolation to the left
325  interpol = (m_c0 * h + m_b[0]) * h + m_y[0];
326  } else if (x > m_x[n - 1]) {
327  // extrapolation to the right
328  interpol = (m_c[n - 1] * h + m_b[n - 1]) * h + m_y[n - 1];
329  } else {
330  // interpolation
331  interpol = ((m_d[idx] * h + m_c[idx]) * h + m_b[idx]) * h + m_y[idx];
332  }
333  return interpol;
334 }
335 
336 double spline::deriv(int order, double x) const
337 {
338  assert(order > 0);
339  size_t n = m_x.size();
340  size_t idx = find_closest(x);
341 
342  double h = x - m_x[idx];
343  double interpol;
344  if (x < m_x[0]) {
345  // extrapolation to the left
346  switch (order) {
347  case 1: interpol = 2.0 * m_c0 * h + m_b[0]; break;
348  case 2: interpol = 2.0 * m_c0; break;
349  default: interpol = 0.0; break;
350  }
351  } else if (x > m_x[n - 1]) {
352  // extrapolation to the right
353  switch (order) {
354  case 1: interpol = 2.0 * m_c[n - 1] * h + m_b[n - 1]; break;
355  case 2: interpol = 2.0 * m_c[n - 1]; break;
356  default: interpol = 0.0; break;
357  }
358  } else {
359  // interpolation
360  switch (order) {
361  case 1: interpol = (3.0 * m_d[idx] * h + 2.0 * m_c[idx]) * h + m_b[idx]; break;
362  case 2: interpol = 6.0 * m_d[idx] * h + 2.0 * m_c[idx]; break;
363  case 3: interpol = 6.0 * m_d[idx]; break;
364  default: interpol = 0.0; break;
365  }
366  }
367  return interpol;
368 }
369 
370 double spline::integrate(double x0, double x1) const
371 {
372  if (x0 == x1)
373  return 0;
374 
375  assert(x0 >= get_x_min());
376  assert(x1 <= get_x_max());
377  assert(x0 <= x1);
378 
379  // Get the rough integral
380  auto idx_0 = find_closest(x0);
381  auto idx_1 = find_closest(x1);
382  double integral = m_integral[idx_1] - m_integral[idx_0];
383 
384  // Subtract out the integral from m_x[idx_0] to x0
385  double h = (x0 - m_x[idx_0]) / 2;
386  integral -= h / 3. * (m_y[idx_0] + 4 * (*this)(m_x[idx_0] + h) + (*this)(x0));
387 
388  // Add in the integral from m_x[idx_1] to x1
389  h = (x1 - m_x[idx_1]) / 2;
390  integral += h / 3. * (m_y[idx_1] + 4 * (*this)(m_x[idx_1] + h) + (*this)(x1));
391 
392  return integral;
393 };
394 
395 std::vector<double> spline::solve(double y, bool ignore_extrapolation) const
396 {
397  std::vector<double> x; // roots for the entire spline
398  std::vector<double> root; // roots for each piecewise cubic
399  const size_t n = m_x.size();
400 
401  // left extrapolation
402  if (ignore_extrapolation == false) {
403  root = internal::solve_cubic(m_y[0] - y, m_b[0], m_c0, 0.0, 1);
404  for (double j : root) {
405  if (j < 0.0) {
406  x.push_back(m_x[0] + j);
407  }
408  }
409  }
410 
411  // brute force check if piecewise cubic has roots in their resp. segment
412  // TODO: make more efficient
413  for (size_t i = 0; i < n - 1; i++) {
414  root = internal::solve_cubic(m_y[i] - y, m_b[i], m_c[i], m_d[i], 1);
415  for (size_t j = 0; j < root.size(); j++) { // NOLINT
416  double h = (i > 0) ? (m_x[i] - m_x[i - 1]) : 0.0;
417  double eps = internal::get_eps() * 512.0 * std::min(h, 1.0);
418  if ((-eps <= root[j]) && (root[j] < m_x[i + 1] - m_x[i])) {
419  double new_root = m_x[i] + root[j];
420  if (x.size() > 0 && x.back() + eps > new_root) {
421  x.back() = new_root; // avoid spurious duplicate roots
422  } else {
423  x.push_back(new_root);
424  }
425  }
426  }
427  }
428 
429  // right extrapolation
430  if (ignore_extrapolation == false) {
431  root = internal::solve_cubic(m_y[n - 1] - y, m_b[n - 1], m_c[n - 1], 0.0, 1);
432  for (size_t j = 0; j < root.size(); j++) { // NOLINT
433  if (0.0 <= root[j]) {
434  x.push_back(m_x[n - 1] + root[j]);
435  }
436  }
437  }
438 
439  return x;
440 };
441 
442 #ifdef HAVE_SSTREAM
443 std::string spline::info() const
444 {
445  std::stringstream ss;
446  ss << "type " << m_type << ", left boundary deriv " << m_left << " = ";
447  ss << m_left_value << ", right boundary deriv " << m_right << " = ";
448  ss << m_right_value << std::endl;
449  if (m_made_monotonic) {
450  ss << "(spline has been adjusted for piece-wise monotonicity)";
451  }
452  return ss.str();
453 }
454 #endif // HAVE_SSTREAM
455 
456 namespace internal {
457 
458 // band_matrix implementation
459 // -------------------------
460 
461 band_matrix::band_matrix(int dim, int n_u, int n_l)
462 {
463  resize(dim, n_u, n_l);
464 }
465 void band_matrix::resize(int dim, int n_u, int n_l)
466 {
467  assert(dim > 0);
468  assert(n_u >= 0);
469  assert(n_l >= 0);
470  m_upper.resize(n_u + 1);
471  m_lower.resize(n_l + 1);
472  for (size_t i = 0; i < m_upper.size(); i++) { // NOLINT
473  m_upper[i].resize(dim);
474  }
475  for (size_t i = 0; i < m_lower.size(); i++) { // NOLINT
476  m_lower[i].resize(dim);
477  }
478 }
479 int band_matrix::dim() const
480 {
481  if (m_upper.size() > 0) {
482  return m_upper[0].size();
483  } else {
484  return 0;
485  }
486 }
487 
488 // defines the new operator (), so that we can access the elements
489 // by A(i,j), index going from i=0,...,dim()-1
490 double &band_matrix::operator()(int i, int j)
491 {
492  int k = j - i; // what band is the entry
493  assert((i >= 0) && (i < dim()) && (j >= 0) && (j < dim()));
494  assert((-num_lower() <= k) && (k <= num_upper()));
495  // k=0 -> diagonal, k<0 lower left part, k>0 upper right part
496  if (k >= 0)
497  return m_upper[k][i];
498  else
499  return m_lower[-k][i];
500 }
501 double band_matrix::operator()(int i, int j) const
502 {
503  int k = j - i; // what band is the entry
504  assert((i >= 0) && (i < dim()) && (j >= 0) && (j < dim()));
505  assert((-num_lower() <= k) && (k <= num_upper()));
506  // k=0 -> diagonal, k<0 lower left part, k>0 upper right part
507  if (k >= 0)
508  return m_upper[k][i];
509  else
510  return m_lower[-k][i];
511 }
512 // second diag (used in LU decomposition), saved in m_lower
513 double band_matrix::saved_diag(int i) const
514 {
515  assert((i >= 0) && (i < dim()));
516  return m_lower[0][i];
517 }
519 {
520  assert((i >= 0) && (i < dim()));
521  return m_lower[0][i];
522 }
523 
524 // LR-Decomposition of a band matrix
526 {
527  int i_max, j_max;
528  int j_min;
529  double x;
530 
531  // preconditioning
532  // normalize column i so that a_ii=1
533  for (int i = 0; i < this->dim(); i++) {
534  assert(this->operator()(i, i) != 0.0);
535  this->saved_diag(i) = 1.0 / this->operator()(i, i);
536  j_min = std::max(0, i - this->num_lower());
537  j_max = std::min(this->dim() - 1, i + this->num_upper());
538  for (int j = j_min; j <= j_max; j++) {
539  this->operator()(i, j) *= this->saved_diag(i);
540  }
541  this->operator()(i, i) = 1.0; // prevents rounding errors
542  }
543 
544  // Gauss LR-Decomposition
545  for (int k = 0; k < this->dim(); k++) {
546  i_max = std::min(this->dim() - 1, k + this->num_lower()); // num_lower not a mistake!
547  for (int i = k + 1; i <= i_max; i++) {
548  assert(this->operator()(k, k) != 0.0);
549  x = -this->operator()(i, k) / this->operator()(k, k);
550  this->operator()(i, k) = -x; // assembly part of L
551  j_max = std::min(this->dim() - 1, k + this->num_upper());
552  for (int j = k + 1; j <= j_max; j++) {
553  // assembly part of R
554  this->operator()(i, j) = this->operator()(i, j) + x * this->operator()(k, j);
555  }
556  }
557  }
558 }
559 // solves Ly=b
560 std::vector<double> band_matrix::l_solve(const std::vector<double> &b) const
561 {
562  assert(this->dim() == (int)b.size());
563  std::vector<double> x(this->dim());
564  int j_start;
565  double sum;
566  for (int i = 0; i < this->dim(); i++) {
567  sum = 0;
568  j_start = std::max(0, i - this->num_lower());
569  for (int j = j_start; j < i; j++)
570  sum += this->operator()(i, j) * x[j];
571  x[i] = (b[i] * this->saved_diag(i)) - sum;
572  }
573  return x;
574 }
575 // solves Rx=y
576 std::vector<double> band_matrix::r_solve(const std::vector<double> &b) const
577 {
578  assert(this->dim() == (int)b.size());
579  std::vector<double> x(this->dim());
580  int j_stop;
581  double sum;
582  for (int i = this->dim() - 1; i >= 0; i--) {
583  sum = 0;
584  j_stop = std::min(this->dim() - 1, i + this->num_upper());
585  for (int j = i + 1; j <= j_stop; j++)
586  sum += this->operator()(i, j) * x[j];
587  x[i] = (b[i] - sum) / this->operator()(i, i);
588  }
589  return x;
590 }
591 
592 std::vector<double> band_matrix::lu_solve(const std::vector<double> &b, bool is_lu_decomposed)
593 {
594  assert(this->dim() == (int)b.size());
595  std::vector<double> x, y;
596  if (is_lu_decomposed == false) {
597  this->lu_decompose();
598  }
599  y = this->l_solve(b);
600  x = this->r_solve(y);
601  return x;
602 }
603 
604 // machine precision of a double, i.e. the successor of 1 is 1+eps
605 double get_eps()
606 {
607  // return std::numeric_limits<double>::epsilon(); // __DBL_EPSILON__
608  return 2.2204460492503131e-16; // 2^-52
609 }
610 
611 // solutions for a + b*x = 0
612 std::vector<double> solve_linear(double a, double b)
613 {
614  std::vector<double> x; // roots
615  if (b == 0.0) {
616  if (a == 0.0) {
617  // 0*x = 0
618  x.resize(1);
619  x[0] = 0.0; // any x solves it but we need to pick one
620  return x;
621  } else {
622  // 0*x + ... = 0, no solution
623  return x;
624  }
625  } else {
626  x.resize(1);
627  x[0] = -a / b;
628  return x;
629  }
630 }
631 
632 // solutions for a + b*x + c*x^2 = 0
633 std::vector<double> solve_quadratic(double a, double b, double c, int newton_iter = 0)
634 {
635  if (c == 0.0) {
636  return solve_linear(a, b);
637  }
638  // rescale so that we solve x^2 + 2p x + q = (x+p)^2 + q - p^2 = 0
639  double p = 0.5 * b / c;
640  double q = a / c;
641  double discr = p * p - q;
642  const double eps = 0.5 * internal::get_eps();
643  double discr_err = (6.0 * (p * p) + 3.0 * fabs(q) + fabs(discr)) * eps;
644 
645  std::vector<double> x; // roots
646  if (fabs(discr) <= discr_err) {
647  // discriminant is zero --> one root
648  x.resize(1);
649  x[0] = -p;
650  } else if (discr < 0) {
651  // no root
652  } else {
653  // two roots
654  x.resize(2);
655  x[0] = -p - sqrt(discr);
656  x[1] = -p + sqrt(discr);
657  }
658 
659  // improve solution via newton steps
660  for (size_t i = 0; i < x.size(); i++) { // NOLINT
661  for (int k = 0; k < newton_iter; k++) {
662  double f = (c * x[i] + b) * x[i] + a;
663  double f1 = 2.0 * c * x[i] + b;
664  // only adjust if slope is large enough
665  if (fabs(f1) > 1e-8) {
666  x[i] -= f / f1;
667  }
668  }
669  }
670 
671  return x;
672 }
673 
674 // solutions for the cubic equation: a + b*x +c*x^2 + d*x^3 = 0
675 // this is a naive implementation of the analytic solution without
676 // optimisation for speed or numerical accuracy
677 // newton_iter: number of newton iterations to improve analytical solution
678 // see also
679 // gsl: gsl_poly_solve_cubic() in solve_cubic.c
680 // octave: roots.m - via eigenvalues of the Frobenius companion matrix
681 std::vector<double> solve_cubic(double a, double b, double c, double d, int newton_iter)
682 {
683  if (d == 0.0) {
684  return solve_quadratic(a, b, c, newton_iter);
685  }
686 
687  // convert to normalised form: a + bx + cx^2 + x^3 = 0
688  if (d != 1.0) {
689  a /= d;
690  b /= d;
691  c /= d;
692  }
693 
694  // convert to depressed cubic: z^3 - 3pz - 2q = 0
695  // via substitution: z = x + c/3
696  std::vector<double> z; // roots of the depressed cubic
697  double p = -(1.0 / 3.0) * b + (1.0 / 9.0) * (c * c);
698  double r = 2.0 * (c * c) - 9.0 * b;
699  double q = -0.5 * a - (1.0 / 54.0) * (c * r);
700  double discr = p * p * p - q * q; // discriminant
701  // calculating numerical round-off errors with assumptions:
702  // - each operation is precise but each intermediate result x
703  // when stored has max error of x*eps
704  // - only multiplication with a power of 2 introduces no new error
705  // - a,b,c,d and some fractions (e.g. 1/3) have rounding errors eps
706  // - p_err << |p|, q_err << |q|, ... (this is violated in rare cases)
707  // would be more elegant to use boost::numeric::interval<double>
708  const double eps = internal::get_eps();
709  double p_err = eps * ((3.0 / 3.0) * fabs(b) + (4.0 / 9.0) * (c * c) + fabs(p));
710  double r_err = eps * (6.0 * (c * c) + 18.0 * fabs(b) + fabs(r));
711  double q_err = 0.5 * fabs(a) * eps + (1.0 / 54.0) * fabs(c) * (r_err + fabs(r) * 3.0 * eps) + fabs(q) * eps;
712  double discr_err =
713  (p * p) * (3.0 * p_err + fabs(p) * 2.0 * eps) + fabs(q) * (2.0 * q_err + fabs(q) * eps) + fabs(discr) * eps;
714 
715  // depending on the discriminant we get different solutions
716  if (fabs(discr) <= discr_err) {
717  // discriminant zero: one or two real roots
718  if (fabs(p) <= p_err) {
719  // p and q are zero: single root
720  z.resize(1);
721  z[0] = 0.0; // triple root
722  } else {
723  z.resize(2);
724  z[0] = 2.0 * q / p; // single root
725  z[1] = -0.5 * z[0]; // double root
726  }
727  } else if (discr > 0) {
728  // three real roots: via trigonometric solution
729  z.resize(3);
730  double ac = (1.0 / 3.0) * acos(q / (p * sqrt(p)));
731  double sq = 2.0 * sqrt(p);
732  z[0] = sq * cos(ac);
733  z[1] = sq * cos(ac - 2.0 * M_PI / 3.0);
734  z[2] = sq * cos(ac - 4.0 * M_PI / 3.0);
735  } else if (discr < 0.0) {
736  // single real root: via Cardano's fromula
737  z.resize(1);
738  double sgnq = (q >= 0 ? 1 : -1);
739  double basis = fabs(q) + sqrt(-discr);
740  double C = sgnq * pow(basis, 1.0 / 3.0); // c++11 has std::cbrt()
741  z[0] = C + p / C;
742  }
743  for (size_t i = 0; i < z.size(); i++) { // NOLINT
744  // convert depressed cubic roots to original cubic: x = z - c/3
745  z[i] -= (1.0 / 3.0) * c;
746  // improve solution via newton steps
747  for (int k = 0; k < newton_iter; k++) {
748  double f = ((z[i] + c) * z[i] + b) * z[i] + a;
749  double f1 = (3.0 * z[i] + 2.0 * c) * z[i] + b;
750  // only adjust if slope is large enough
751  if (fabs(f1) > 1e-8) {
752  z[i] -= f / f1;
753  }
754  }
755  }
756  // ensure if a=0 we get exactly x=0 as root
757  // TODO: remove this fudge
758  if (a == 0.0) {
759  assert(z.size() > 0); // cubic should always have at least one root
760  double xmin = fabs(z[0]);
761  size_t imin = 0;
762  for (size_t i = 1; i < z.size(); i++) {
763  if (xmin > fabs(z[i])) {
764  xmin = fabs(z[i]);
765  imin = i;
766  }
767  }
768  z[imin] = 0.0; // replace the smallest absolute value with 0
769  }
770  std::sort(z.begin(), z.end());
771  return z;
772 }
773 
774 } // namespace internal
775 } // namespace tk
tk::spline::m_left
bd_type m_left
Definition: AtSpline.h:63
tk::spline::cspline
@ cspline
Definition: AtSpline.h:48
tk::spline::set_points_linear
void set_points_linear()
Definition: AtSpline.cxx:48
tk::spline::set_integral
void set_integral()
Definition: AtSpline.cxx:206
tk::internal::band_matrix::resize
void resize(int dim, int n_u, int n_l)
Definition: AtSpline.cxx:465
tk::spline::linear
@ linear
Definition: AtSpline.h:47
tk::spline::make_monotonic
bool make_monotonic()
Definition: AtSpline.cxx:251
AtSpline.h
tk::spline::first_deriv
@ first_deriv
Definition: AtSpline.h:53
tk::spline::cspline_hermite
@ cspline_hermite
Definition: AtSpline.h:49
tk::spline::m_x
std::vector< double > m_x
Definition: AtSpline.h:56
tk::internal::band_matrix::lu_decompose
void lu_decompose()
Definition: AtSpline.cxx:525
tk::spline::m_type
spline_type m_type
Definition: AtSpline.h:62
f
double(* f)(double t, const double *par)
Definition: lmcurve.cxx:21
tk::internal::band_matrix::operator()
double & operator()(int i, int j)
Definition: AtSpline.cxx:490
tk::spline::set_boundary
void set_boundary(bd_type left, double left_value, bd_type right, double right_value)
Definition: AtSpline.cxx:16
tk::spline::m_y
std::vector< double > m_y
Definition: AtSpline.h:56
tk::spline::m_c
std::vector< double > m_c
Definition: AtSpline.h:60
tk::spline::spline_type
spline_type
Definition: AtSpline.h:46
tk::internal::solve_linear
std::vector< double > solve_linear(double a, double b)
Definition: AtSpline.cxx:612
tk::spline::m_left_value
double m_left_value
Definition: AtSpline.h:64
tk::spline::set_points_cspline
void set_points_cspline()
Definition: AtSpline.cxx:66
tk::internal::solve_cubic
std::vector< double > solve_cubic(double a, double b, double c, double d, int newton_iter)
Definition: AtSpline.cxx:681
tk::spline::m_d
std::vector< double > m_d
Definition: AtSpline.h:60
tk::spline::set_points_cspline_hermite
void set_points_cspline_hermite()
Definition: AtSpline.cxx:152
tk
Definition: AtSpline.cxx:7
tk::spline::solve
std::vector< double > solve(double y, bool ignore_extrapolation=true) const
Definition: AtSpline.cxx:395
tk::spline::integrate
double integrate(double x0, double x1) const
Definition: AtSpline.cxx:370
tk::internal::band_matrix::band_matrix
band_matrix()
Definition: AtSpline.h:148
tk::spline::set_points
void set_points(const std::vector< double > &x, const std::vector< double > &y, spline_type type=cspline)
Definition: AtSpline.cxx:220
tk::spline::m_c0
double m_c0
Definition: AtSpline.h:61
tk::spline::deriv
double deriv(int order, double x) const
Definition: AtSpline.cxx:336
tk::internal::band_matrix::num_lower
int num_lower() const
Definition: AtSpline.h:154
tk::internal::band_matrix::l_solve
std::vector< double > l_solve(const std::vector< double > &b) const
Definition: AtSpline.cxx:560
tk::internal::get_eps
double get_eps()
Definition: AtSpline.cxx:605
tk::spline::second_deriv
@ second_deriv
Definition: AtSpline.h:53
tk::spline::m_b
std::vector< double > m_b
Definition: AtSpline.h:60
tk::spline::get_x_min
double get_x_min() const
Definition: AtSpline.h:123
tk::internal::band_matrix::r_solve
std::vector< double > r_solve(const std::vector< double > &b) const
Definition: AtSpline.cxx:576
tk::internal::solve_quadratic
std::vector< double > solve_quadratic(double a, double b, double c, int newton_iter=0)
Definition: AtSpline.cxx:633
y
const double * y
Definition: lmcurve.cxx:20
tk::spline::m_right
bd_type m_right
Definition: AtSpline.h:63
tk::internal::band_matrix::num_upper
int num_upper() const
Definition: AtSpline.h:153
tk::internal::band_matrix::dim
int dim() const
Definition: AtSpline.cxx:479
tk::spline::not_a_knot
@ not_a_knot
Definition: AtSpline.h:53
tk::spline::m_integral
std::vector< double > m_integral
Definition: AtSpline.h:67
tk::spline::operator()
double operator()(double x) const
Definition: AtSpline.cxx:311
tk::spline::find_closest
size_t find_closest(double x) const
Definition: AtSpline.cxx:303
tk::spline::get_x_max
double get_x_max() const
Definition: AtSpline.h:128
tk::spline::set_coeffs_from_b
void set_coeffs_from_b()
Definition: AtSpline.cxx:25
tk::internal::band_matrix::saved_diag
double & saved_diag(int i)
Definition: AtSpline.cxx:518
tk::spline::m_right_value
double m_right_value
Definition: AtSpline.h:64
tk::internal::band_matrix::lu_solve
std::vector< double > lu_solve(const std::vector< double > &b, bool is_lu_decomposed=false)
Definition: AtSpline.cxx:592
tk::spline::m_made_monotonic
bool m_made_monotonic
Definition: AtSpline.h:65
c
constexpr auto c
Definition: AtRadialChargeModel.cxx:20
tk::internal::band_matrix
Definition: AtSpline.h:143
tk::spline::bd_type
bd_type
Definition: AtSpline.h:53