recurrence.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // (C) Copyright Anton Bikineev 2014
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_TOOLS_RECURRENCE_HPP_
  6. #define BOOST_MATH_TOOLS_RECURRENCE_HPP_
  7. #include <type_traits>
  8. #include <tuple>
  9. #include <utility>
  10. #include <boost/math/tools/config.hpp>
  11. #include <boost/math/tools/precision.hpp>
  12. #include <boost/math/tools/tuple.hpp>
  13. #include <boost/math/tools/fraction.hpp>
  14. #include <boost/math/tools/cxx03_warn.hpp>
  15. #include <boost/math/tools/assert.hpp>
  16. #include <boost/math/special_functions/fpclassify.hpp>
  17. #include <boost/math/policies/error_handling.hpp>
  18. namespace boost {
  19. namespace math {
  20. namespace tools {
  21. namespace detail{
  22. //
  23. // Function ratios directly from recurrence relations:
  24. // H. Shintan, Note on Miller's recurrence algorithm, J. Sci. Hiroshima Univ. Ser. A-I
  25. // Math., 29 (1965), pp. 121 - 133.
  26. // and:
  27. // COMPUTATIONAL ASPECTS OF THREE-TERM RECURRENCE RELATIONS
  28. // WALTER GAUTSCHI
  29. // SIAM REVIEW Vol. 9, No. 1, January, 1967
  30. //
  31. template <class Recurrence>
  32. struct function_ratio_from_backwards_recurrence_fraction
  33. {
  34. typedef typename std::remove_reference<decltype(std::get<0>(std::declval<Recurrence&>()(0)))>::type value_type;
  35. typedef std::pair<value_type, value_type> result_type;
  36. function_ratio_from_backwards_recurrence_fraction(const Recurrence& r) : r(r), k(0) {}
  37. result_type operator()()
  38. {
  39. value_type a, b, c;
  40. std::tie(a, b, c) = r(k);
  41. ++k;
  42. // an and bn defined as per Gauchi 1.16, not the same
  43. // as the usual continued fraction a' and b's.
  44. value_type bn = a / c;
  45. value_type an = b / c;
  46. return result_type(-bn, an);
  47. }
  48. private:
  49. function_ratio_from_backwards_recurrence_fraction operator=(const function_ratio_from_backwards_recurrence_fraction&) = delete;
  50. Recurrence r;
  51. int k;
  52. };
  53. template <class R, class T>
  54. struct recurrence_reverser
  55. {
  56. recurrence_reverser(const R& r) : r(r) {}
  57. std::tuple<T, T, T> operator()(int i)
  58. {
  59. using std::swap;
  60. std::tuple<T, T, T> t = r(-i);
  61. swap(std::get<0>(t), std::get<2>(t));
  62. return t;
  63. }
  64. R r;
  65. };
  66. template <class Recurrence>
  67. struct recurrence_offsetter
  68. {
  69. typedef decltype(std::declval<Recurrence&>()(0)) result_type;
  70. recurrence_offsetter(Recurrence const& rr, int offset) : r(rr), k(offset) {}
  71. result_type operator()(int i)
  72. {
  73. return r(i + k);
  74. }
  75. private:
  76. Recurrence r;
  77. int k;
  78. };
  79. } // namespace detail
  80. //
  81. // Given a stable backwards recurrence relation:
  82. // a f_n-1 + b f_n + c f_n+1 = 0
  83. // returns the ratio f_n / f_n-1
  84. //
  85. // Recurrence: a functor that returns a tuple of the factors (a,b,c).
  86. // factor: Convergence criteria, should be no less than machine epsilon.
  87. // max_iter: Maximum iterations to use solving the continued fraction.
  88. //
  89. template <class Recurrence, class T>
  90. T function_ratio_from_backwards_recurrence(const Recurrence& r, const T& factor, std::uintmax_t& max_iter)
  91. {
  92. detail::function_ratio_from_backwards_recurrence_fraction<Recurrence> f(r);
  93. return boost::math::tools::continued_fraction_a(f, factor, max_iter);
  94. }
  95. //
  96. // Given a stable forwards recurrence relation:
  97. // a f_n-1 + b f_n + c f_n+1 = 0
  98. // returns the ratio f_n / f_n+1
  99. //
  100. // Note that in most situations where this would be used, we're relying on
  101. // pseudo-convergence, as in most cases f_n will not be minimal as N -> -INF
  102. // as long as we reach convergence on the continued-fraction before f_n
  103. // switches behaviour, we should be fine.
  104. //
  105. // Recurrence: a functor that returns a tuple of the factors (a,b,c).
  106. // factor: Convergence criteria, should be no less than machine epsilon.
  107. // max_iter: Maximum iterations to use solving the continued fraction.
  108. //
  109. template <class Recurrence, class T>
  110. T function_ratio_from_forwards_recurrence(const Recurrence& r, const T& factor, std::uintmax_t& max_iter)
  111. {
  112. boost::math::tools::detail::function_ratio_from_backwards_recurrence_fraction<boost::math::tools::detail::recurrence_reverser<Recurrence, T> > f(r);
  113. return boost::math::tools::continued_fraction_a(f, factor, max_iter);
  114. }
  115. // solves usual recurrence relation for homogeneous
  116. // difference equation in stable forward direction
  117. // a(n)w(n-1) + b(n)w(n) + c(n)w(n+1) = 0
  118. //
  119. // Params:
  120. // get_coefs: functor returning a tuple, where
  121. // get<0>() is a(n); get<1>() is b(n); get<2>() is c(n);
  122. // last_index: index N to be found;
  123. // first: w(-1);
  124. // second: w(0);
  125. //
  126. template <class NextCoefs, class T>
  127. inline T apply_recurrence_relation_forward(const NextCoefs& get_coefs, unsigned number_of_steps, T first, T second, long long* log_scaling = nullptr, T* previous = nullptr)
  128. {
  129. BOOST_MATH_STD_USING
  130. using std::tuple;
  131. using std::get;
  132. using std::swap;
  133. T third;
  134. T a, b, c;
  135. for (unsigned k = 0; k < number_of_steps; ++k)
  136. {
  137. tie(a, b, c) = get_coefs(k);
  138. if ((log_scaling) &&
  139. ((fabs(tools::max_value<T>() * (c / (a * 2048))) < fabs(first))
  140. || (fabs(tools::max_value<T>() * (c / (b * 2048))) < fabs(second))
  141. || (fabs(tools::min_value<T>() * (c * 2048 / a)) > fabs(first))
  142. || (fabs(tools::min_value<T>() * (c * 2048 / b)) > fabs(second))
  143. ))
  144. {
  145. // Rescale everything:
  146. long long log_scale = lltrunc(log(fabs(second)));
  147. T scale = exp(T(-log_scale));
  148. second *= scale;
  149. first *= scale;
  150. *log_scaling += log_scale;
  151. }
  152. // scale each part separately to avoid spurious overflow:
  153. third = (a / -c) * first + (b / -c) * second;
  154. BOOST_MATH_ASSERT((boost::math::isfinite)(third));
  155. swap(first, second);
  156. swap(second, third);
  157. }
  158. if (previous)
  159. *previous = first;
  160. return second;
  161. }
  162. // solves usual recurrence relation for homogeneous
  163. // difference equation in stable backward direction
  164. // a(n)w(n-1) + b(n)w(n) + c(n)w(n+1) = 0
  165. //
  166. // Params:
  167. // get_coefs: functor returning a tuple, where
  168. // get<0>() is a(n); get<1>() is b(n); get<2>() is c(n);
  169. // number_of_steps: index N to be found;
  170. // first: w(1);
  171. // second: w(0);
  172. //
  173. template <class T, class NextCoefs>
  174. inline T apply_recurrence_relation_backward(const NextCoefs& get_coefs, unsigned number_of_steps, T first, T second, long long* log_scaling = nullptr, T* previous = nullptr)
  175. {
  176. BOOST_MATH_STD_USING
  177. using std::tuple;
  178. using std::get;
  179. using std::swap;
  180. T next;
  181. T a, b, c;
  182. for (unsigned k = 0; k < number_of_steps; ++k)
  183. {
  184. tie(a, b, c) = get_coefs(-static_cast<int>(k));
  185. if ((log_scaling) && (second != 0) &&
  186. ( (fabs(tools::max_value<T>() * (a / b) / 2048) < fabs(second))
  187. || (fabs(tools::max_value<T>() * (a / c) / 2048) < fabs(first))
  188. || (fabs(tools::min_value<T>() * (a / b) * 2048) > fabs(second))
  189. || (fabs(tools::min_value<T>() * (a / c) * 2048) > fabs(first))
  190. ))
  191. {
  192. // Rescale everything:
  193. int log_scale = itrunc(log(fabs(second)));
  194. T scale = exp(T(-log_scale));
  195. second *= scale;
  196. first *= scale;
  197. *log_scaling += log_scale;
  198. }
  199. // scale each part separately to avoid spurious overflow:
  200. next = (b / -a) * second + (c / -a) * first;
  201. BOOST_MATH_ASSERT((boost::math::isfinite)(next));
  202. swap(first, second);
  203. swap(second, next);
  204. }
  205. if (previous)
  206. *previous = first;
  207. return second;
  208. }
  209. template <class Recurrence>
  210. struct forward_recurrence_iterator
  211. {
  212. typedef typename std::remove_reference<decltype(std::get<0>(std::declval<Recurrence&>()(0)))>::type value_type;
  213. forward_recurrence_iterator(const Recurrence& r, value_type f_n_minus_1, value_type f_n)
  214. : f_n_minus_1(f_n_minus_1), f_n(f_n), coef(r), k(0) {}
  215. forward_recurrence_iterator(const Recurrence& r, value_type f_n)
  216. : f_n(f_n), coef(r), k(0)
  217. {
  218. std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<boost::math::policies::policy<> >();
  219. f_n_minus_1 = f_n * boost::math::tools::function_ratio_from_forwards_recurrence(detail::recurrence_offsetter<Recurrence>(r, -1), value_type(boost::math::tools::epsilon<value_type>() * 2), max_iter);
  220. boost::math::policies::check_series_iterations<value_type>("forward_recurrence_iterator<>::forward_recurrence_iterator", max_iter, boost::math::policies::policy<>());
  221. }
  222. forward_recurrence_iterator& operator++()
  223. {
  224. using std::swap;
  225. value_type a, b, c;
  226. std::tie(a, b, c) = coef(k);
  227. value_type f_n_plus_1 = a * f_n_minus_1 / -c + b * f_n / -c;
  228. swap(f_n_minus_1, f_n);
  229. swap(f_n, f_n_plus_1);
  230. ++k;
  231. return *this;
  232. }
  233. forward_recurrence_iterator operator++(int)
  234. {
  235. forward_recurrence_iterator t(*this);
  236. ++(*this);
  237. return t;
  238. }
  239. value_type operator*() { return f_n; }
  240. value_type f_n_minus_1, f_n;
  241. Recurrence coef;
  242. int k;
  243. };
  244. template <class Recurrence>
  245. struct backward_recurrence_iterator
  246. {
  247. typedef typename std::remove_reference<decltype(std::get<0>(std::declval<Recurrence&>()(0)))>::type value_type;
  248. backward_recurrence_iterator(const Recurrence& r, value_type f_n_plus_1, value_type f_n)
  249. : f_n_plus_1(f_n_plus_1), f_n(f_n), coef(r), k(0) {}
  250. backward_recurrence_iterator(const Recurrence& r, value_type f_n)
  251. : f_n(f_n), coef(r), k(0)
  252. {
  253. std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<boost::math::policies::policy<> >();
  254. f_n_plus_1 = f_n * boost::math::tools::function_ratio_from_backwards_recurrence(detail::recurrence_offsetter<Recurrence>(r, 1), value_type(boost::math::tools::epsilon<value_type>() * 2), max_iter);
  255. boost::math::policies::check_series_iterations<value_type>("backward_recurrence_iterator<>::backward_recurrence_iterator", max_iter, boost::math::policies::policy<>());
  256. }
  257. backward_recurrence_iterator& operator++()
  258. {
  259. using std::swap;
  260. value_type a, b, c;
  261. std::tie(a, b, c) = coef(k);
  262. value_type f_n_minus_1 = c * f_n_plus_1 / -a + b * f_n / -a;
  263. swap(f_n_plus_1, f_n);
  264. swap(f_n, f_n_minus_1);
  265. --k;
  266. return *this;
  267. }
  268. backward_recurrence_iterator operator++(int)
  269. {
  270. backward_recurrence_iterator t(*this);
  271. ++(*this);
  272. return t;
  273. }
  274. value_type operator*() { return f_n; }
  275. value_type f_n_plus_1, f_n;
  276. Recurrence coef;
  277. int k;
  278. };
  279. }
  280. }
  281. } // namespaces
  282. #endif // BOOST_MATH_TOOLS_RECURRENCE_HPP_