cardinal_quadratic_b_spline_detail.hpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Copyright Nick Thompson, 2019
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_MATH_INTERPOLATORS_CARDINAL_QUADRATIC_B_SPLINE_DETAIL_HPP
  7. #define BOOST_MATH_INTERPOLATORS_CARDINAL_QUADRATIC_B_SPLINE_DETAIL_HPP
  8. #include <vector>
  9. namespace boost{ namespace math{ namespace interpolators{ namespace detail{
  10. template <class Real>
  11. Real b2_spline(Real x) {
  12. using std::abs;
  13. Real absx = abs(x);
  14. if (absx < 1/Real(2))
  15. {
  16. Real y = absx - 1/Real(2);
  17. Real z = absx + 1/Real(2);
  18. return (2-y*y-z*z)/2;
  19. }
  20. if (absx < Real(3)/Real(2))
  21. {
  22. Real y = absx - Real(3)/Real(2);
  23. return y*y/2;
  24. }
  25. return (Real) 0;
  26. }
  27. template <class Real>
  28. Real b2_spline_prime(Real x) {
  29. if (x < 0) {
  30. return -b2_spline_prime(-x);
  31. }
  32. if (x < 1/Real(2))
  33. {
  34. return -2*x;
  35. }
  36. if (x < Real(3)/Real(2))
  37. {
  38. return x - Real(3)/Real(2);
  39. }
  40. return (Real) 0;
  41. }
  42. template <class Real>
  43. class cardinal_quadratic_b_spline_detail
  44. {
  45. public:
  46. // If you don't know the value of the derivative at the endpoints, leave them as nans and the routine will estimate them.
  47. // y[0] = y(a), y[n -1] = y(b), step_size = (b - a)/(n -1).
  48. cardinal_quadratic_b_spline_detail(const Real* const y,
  49. size_t n,
  50. Real t0 /* initial time, left endpoint */,
  51. Real h /*spacing, stepsize*/,
  52. Real left_endpoint_derivative = std::numeric_limits<Real>::quiet_NaN(),
  53. Real right_endpoint_derivative = std::numeric_limits<Real>::quiet_NaN())
  54. {
  55. if (h <= 0) {
  56. throw std::logic_error("Spacing must be > 0.");
  57. }
  58. m_inv_h = 1/h;
  59. m_t0 = t0;
  60. if (n < 3) {
  61. throw std::logic_error("The interpolator requires at least 3 points.");
  62. }
  63. using std::isnan;
  64. Real a;
  65. if (isnan(left_endpoint_derivative)) {
  66. // http://web.media.mit.edu/~crtaylor/calculator.html
  67. a = -3*y[0] + 4*y[1] - y[2];
  68. }
  69. else {
  70. a = 2*h*left_endpoint_derivative;
  71. }
  72. Real b;
  73. if (isnan(right_endpoint_derivative)) {
  74. b = 3*y[n-1] - 4*y[n-2] + y[n-3];
  75. }
  76. else {
  77. b = 2*h*right_endpoint_derivative;
  78. }
  79. m_alpha.resize(n + 2);
  80. // Begin row reduction:
  81. std::vector<Real> rhs(n + 2, std::numeric_limits<Real>::quiet_NaN());
  82. std::vector<Real> super_diagonal(n + 2, std::numeric_limits<Real>::quiet_NaN());
  83. rhs[0] = -a;
  84. rhs[rhs.size() - 1] = b;
  85. super_diagonal[0] = 0;
  86. for(size_t i = 1; i < rhs.size() - 1; ++i) {
  87. rhs[i] = 8*y[i - 1];
  88. super_diagonal[i] = 1;
  89. }
  90. // Patch up 5-diagonal problem:
  91. rhs[1] = (rhs[1] - rhs[0])/6;
  92. super_diagonal[1] = Real(1)/Real(3);
  93. // First two rows are now:
  94. // 1 0 -1 | -2hy0'
  95. // 0 1 1/3| (8y0+2hy0')/6
  96. // Start traditional tridiagonal row reduction:
  97. for (size_t i = 2; i < rhs.size() - 1; ++i) {
  98. Real diagonal = 6 - super_diagonal[i - 1];
  99. rhs[i] = (rhs[i] - rhs[i - 1])/diagonal;
  100. super_diagonal[i] /= diagonal;
  101. }
  102. // 1 sd[n-1] 0 | rhs[n-1]
  103. // 0 1 sd[n] | rhs[n]
  104. // -1 0 1 | rhs[n+1]
  105. rhs[n+1] = rhs[n+1] + rhs[n-1];
  106. Real bottom_subdiagonal = super_diagonal[n-1];
  107. // We're here:
  108. // 1 sd[n-1] 0 | rhs[n-1]
  109. // 0 1 sd[n] | rhs[n]
  110. // 0 bs 1 | rhs[n+1]
  111. rhs[n+1] = (rhs[n+1]-bottom_subdiagonal*rhs[n])/(1-bottom_subdiagonal*super_diagonal[n]);
  112. m_alpha[n+1] = rhs[n+1];
  113. for (size_t i = n; i > 0; --i) {
  114. m_alpha[i] = rhs[i] - m_alpha[i+1]*super_diagonal[i];
  115. }
  116. m_alpha[0] = m_alpha[2] + rhs[0];
  117. }
  118. Real operator()(Real t) const {
  119. if (t < m_t0 || t > m_t0 + (m_alpha.size()-2)/m_inv_h) {
  120. const char* err_msg = "Tried to evaluate the cardinal quadratic b-spline outside the domain of of interpolation; extrapolation does not work.";
  121. throw std::domain_error(err_msg);
  122. }
  123. // Let k, gamma be defined via t = t0 + kh + gamma * h.
  124. // Now find all j: |k-j+1+gamma|< 3/2, or, in other words
  125. // j_min = ceil((t-t0)/h - 1/2)
  126. // j_max = floor(t-t0)/h + 5/2)
  127. using std::floor;
  128. using std::ceil;
  129. Real x = (t-m_t0)*m_inv_h;
  130. size_t j_min = ceil(x - Real(1)/Real(2));
  131. size_t j_max = ceil(x + Real(5)/Real(2));
  132. if (j_max >= m_alpha.size()) {
  133. j_max = m_alpha.size() - 1;
  134. }
  135. Real y = 0;
  136. x += 1;
  137. for (size_t j = j_min; j <= j_max; ++j) {
  138. y += m_alpha[j]*detail::b2_spline(x - j);
  139. }
  140. return y;
  141. }
  142. Real prime(Real t) const {
  143. if (t < m_t0 || t > m_t0 + (m_alpha.size()-2)/m_inv_h) {
  144. const char* err_msg = "Tried to evaluate the cardinal quadratic b-spline outside the domain of of interpolation; extrapolation does not work.";
  145. throw std::domain_error(err_msg);
  146. }
  147. // Let k, gamma be defined via t = t0 + kh + gamma * h.
  148. // Now find all j: |k-j+1+gamma|< 3/2, or, in other words
  149. // j_min = ceil((t-t0)/h - 1/2)
  150. // j_max = floor(t-t0)/h + 5/2)
  151. using std::floor;
  152. using std::ceil;
  153. Real x = (t-m_t0)*m_inv_h;
  154. size_t j_min = ceil(x - Real(1)/Real(2));
  155. size_t j_max = ceil(x + Real(5)/Real(2));
  156. if (j_max >= m_alpha.size()) {
  157. j_max = m_alpha.size() - 1;
  158. }
  159. Real y = 0;
  160. x += 1;
  161. for (size_t j = j_min; j <= j_max; ++j) {
  162. y += m_alpha[j]*detail::b2_spline_prime(x - j);
  163. }
  164. return y*m_inv_h;
  165. }
  166. Real t_max() const {
  167. return m_t0 + (m_alpha.size()-3)/m_inv_h;
  168. }
  169. private:
  170. std::vector<Real> m_alpha;
  171. Real m_inv_h;
  172. Real m_t0;
  173. };
  174. }}}}
  175. #endif