123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- // (C) Copyright Anton Bikineev 2014
- // Use, modification and distribution are subject to the
- // Boost Software License, Version 1.0. (See accompanying file
- // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_MATH_TOOLS_RECURRENCE_HPP_
- #define BOOST_MATH_TOOLS_RECURRENCE_HPP_
- #include <type_traits>
- #include <tuple>
- #include <utility>
- #include <boost/math/tools/config.hpp>
- #include <boost/math/tools/precision.hpp>
- #include <boost/math/tools/tuple.hpp>
- #include <boost/math/tools/fraction.hpp>
- #include <boost/math/tools/cxx03_warn.hpp>
- #include <boost/math/tools/assert.hpp>
- #include <boost/math/special_functions/fpclassify.hpp>
- #include <boost/math/policies/error_handling.hpp>
- namespace boost {
- namespace math {
- namespace tools {
- namespace detail{
- //
- // Function ratios directly from recurrence relations:
- // H. Shintan, Note on Miller's recurrence algorithm, J. Sci. Hiroshima Univ. Ser. A-I
- // Math., 29 (1965), pp. 121 - 133.
- // and:
- // COMPUTATIONAL ASPECTS OF THREE-TERM RECURRENCE RELATIONS
- // WALTER GAUTSCHI
- // SIAM REVIEW Vol. 9, No. 1, January, 1967
- //
- template <class Recurrence>
- struct function_ratio_from_backwards_recurrence_fraction
- {
- typedef typename std::remove_reference<decltype(std::get<0>(std::declval<Recurrence&>()(0)))>::type value_type;
- typedef std::pair<value_type, value_type> result_type;
- function_ratio_from_backwards_recurrence_fraction(const Recurrence& r) : r(r), k(0) {}
- result_type operator()()
- {
- value_type a, b, c;
- std::tie(a, b, c) = r(k);
- ++k;
- // an and bn defined as per Gauchi 1.16, not the same
- // as the usual continued fraction a' and b's.
- value_type bn = a / c;
- value_type an = b / c;
- return result_type(-bn, an);
- }
- private:
- function_ratio_from_backwards_recurrence_fraction operator=(const function_ratio_from_backwards_recurrence_fraction&) = delete;
- Recurrence r;
- int k;
- };
- template <class R, class T>
- struct recurrence_reverser
- {
- recurrence_reverser(const R& r) : r(r) {}
- std::tuple<T, T, T> operator()(int i)
- {
- using std::swap;
- std::tuple<T, T, T> t = r(-i);
- swap(std::get<0>(t), std::get<2>(t));
- return t;
- }
- R r;
- };
- template <class Recurrence>
- struct recurrence_offsetter
- {
- typedef decltype(std::declval<Recurrence&>()(0)) result_type;
- recurrence_offsetter(Recurrence const& rr, int offset) : r(rr), k(offset) {}
- result_type operator()(int i)
- {
- return r(i + k);
- }
- private:
- Recurrence r;
- int k;
- };
- } // namespace detail
- //
- // Given a stable backwards recurrence relation:
- // a f_n-1 + b f_n + c f_n+1 = 0
- // returns the ratio f_n / f_n-1
- //
- // Recurrence: a functor that returns a tuple of the factors (a,b,c).
- // factor: Convergence criteria, should be no less than machine epsilon.
- // max_iter: Maximum iterations to use solving the continued fraction.
- //
- template <class Recurrence, class T>
- T function_ratio_from_backwards_recurrence(const Recurrence& r, const T& factor, std::uintmax_t& max_iter)
- {
- detail::function_ratio_from_backwards_recurrence_fraction<Recurrence> f(r);
- return boost::math::tools::continued_fraction_a(f, factor, max_iter);
- }
- //
- // Given a stable forwards recurrence relation:
- // a f_n-1 + b f_n + c f_n+1 = 0
- // returns the ratio f_n / f_n+1
- //
- // Note that in most situations where this would be used, we're relying on
- // pseudo-convergence, as in most cases f_n will not be minimal as N -> -INF
- // as long as we reach convergence on the continued-fraction before f_n
- // switches behaviour, we should be fine.
- //
- // Recurrence: a functor that returns a tuple of the factors (a,b,c).
- // factor: Convergence criteria, should be no less than machine epsilon.
- // max_iter: Maximum iterations to use solving the continued fraction.
- //
- template <class Recurrence, class T>
- T function_ratio_from_forwards_recurrence(const Recurrence& r, const T& factor, std::uintmax_t& max_iter)
- {
- boost::math::tools::detail::function_ratio_from_backwards_recurrence_fraction<boost::math::tools::detail::recurrence_reverser<Recurrence, T> > f(r);
- return boost::math::tools::continued_fraction_a(f, factor, max_iter);
- }
- // solves usual recurrence relation for homogeneous
- // difference equation in stable forward direction
- // a(n)w(n-1) + b(n)w(n) + c(n)w(n+1) = 0
- //
- // Params:
- // get_coefs: functor returning a tuple, where
- // get<0>() is a(n); get<1>() is b(n); get<2>() is c(n);
- // last_index: index N to be found;
- // first: w(-1);
- // second: w(0);
- //
- template <class NextCoefs, class T>
- 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)
- {
- BOOST_MATH_STD_USING
- using std::tuple;
- using std::get;
- using std::swap;
- T third;
- T a, b, c;
- for (unsigned k = 0; k < number_of_steps; ++k)
- {
- tie(a, b, c) = get_coefs(k);
- if ((log_scaling) &&
- ((fabs(tools::max_value<T>() * (c / (a * 2048))) < fabs(first))
- || (fabs(tools::max_value<T>() * (c / (b * 2048))) < fabs(second))
- || (fabs(tools::min_value<T>() * (c * 2048 / a)) > fabs(first))
- || (fabs(tools::min_value<T>() * (c * 2048 / b)) > fabs(second))
- ))
- {
- // Rescale everything:
- long long log_scale = lltrunc(log(fabs(second)));
- T scale = exp(T(-log_scale));
- second *= scale;
- first *= scale;
- *log_scaling += log_scale;
- }
- // scale each part separately to avoid spurious overflow:
- third = (a / -c) * first + (b / -c) * second;
- BOOST_MATH_ASSERT((boost::math::isfinite)(third));
- swap(first, second);
- swap(second, third);
- }
- if (previous)
- *previous = first;
- return second;
- }
- // solves usual recurrence relation for homogeneous
- // difference equation in stable backward direction
- // a(n)w(n-1) + b(n)w(n) + c(n)w(n+1) = 0
- //
- // Params:
- // get_coefs: functor returning a tuple, where
- // get<0>() is a(n); get<1>() is b(n); get<2>() is c(n);
- // number_of_steps: index N to be found;
- // first: w(1);
- // second: w(0);
- //
- template <class T, class NextCoefs>
- 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)
- {
- BOOST_MATH_STD_USING
- using std::tuple;
- using std::get;
- using std::swap;
- T next;
- T a, b, c;
- for (unsigned k = 0; k < number_of_steps; ++k)
- {
- tie(a, b, c) = get_coefs(-static_cast<int>(k));
- if ((log_scaling) && (second != 0) &&
- ( (fabs(tools::max_value<T>() * (a / b) / 2048) < fabs(second))
- || (fabs(tools::max_value<T>() * (a / c) / 2048) < fabs(first))
- || (fabs(tools::min_value<T>() * (a / b) * 2048) > fabs(second))
- || (fabs(tools::min_value<T>() * (a / c) * 2048) > fabs(first))
- ))
- {
- // Rescale everything:
- int log_scale = itrunc(log(fabs(second)));
- T scale = exp(T(-log_scale));
- second *= scale;
- first *= scale;
- *log_scaling += log_scale;
- }
- // scale each part separately to avoid spurious overflow:
- next = (b / -a) * second + (c / -a) * first;
- BOOST_MATH_ASSERT((boost::math::isfinite)(next));
- swap(first, second);
- swap(second, next);
- }
- if (previous)
- *previous = first;
- return second;
- }
- template <class Recurrence>
- struct forward_recurrence_iterator
- {
- typedef typename std::remove_reference<decltype(std::get<0>(std::declval<Recurrence&>()(0)))>::type value_type;
- forward_recurrence_iterator(const Recurrence& r, value_type f_n_minus_1, value_type f_n)
- : f_n_minus_1(f_n_minus_1), f_n(f_n), coef(r), k(0) {}
- forward_recurrence_iterator(const Recurrence& r, value_type f_n)
- : f_n(f_n), coef(r), k(0)
- {
- std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<boost::math::policies::policy<> >();
- 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);
- boost::math::policies::check_series_iterations<value_type>("forward_recurrence_iterator<>::forward_recurrence_iterator", max_iter, boost::math::policies::policy<>());
- }
- forward_recurrence_iterator& operator++()
- {
- using std::swap;
- value_type a, b, c;
- std::tie(a, b, c) = coef(k);
- value_type f_n_plus_1 = a * f_n_minus_1 / -c + b * f_n / -c;
- swap(f_n_minus_1, f_n);
- swap(f_n, f_n_plus_1);
- ++k;
- return *this;
- }
- forward_recurrence_iterator operator++(int)
- {
- forward_recurrence_iterator t(*this);
- ++(*this);
- return t;
- }
- value_type operator*() { return f_n; }
- value_type f_n_minus_1, f_n;
- Recurrence coef;
- int k;
- };
- template <class Recurrence>
- struct backward_recurrence_iterator
- {
- typedef typename std::remove_reference<decltype(std::get<0>(std::declval<Recurrence&>()(0)))>::type value_type;
- backward_recurrence_iterator(const Recurrence& r, value_type f_n_plus_1, value_type f_n)
- : f_n_plus_1(f_n_plus_1), f_n(f_n), coef(r), k(0) {}
- backward_recurrence_iterator(const Recurrence& r, value_type f_n)
- : f_n(f_n), coef(r), k(0)
- {
- std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<boost::math::policies::policy<> >();
- 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);
- boost::math::policies::check_series_iterations<value_type>("backward_recurrence_iterator<>::backward_recurrence_iterator", max_iter, boost::math::policies::policy<>());
- }
- backward_recurrence_iterator& operator++()
- {
- using std::swap;
- value_type a, b, c;
- std::tie(a, b, c) = coef(k);
- value_type f_n_minus_1 = c * f_n_plus_1 / -a + b * f_n / -a;
- swap(f_n_plus_1, f_n);
- swap(f_n, f_n_minus_1);
- --k;
- return *this;
- }
- backward_recurrence_iterator operator++(int)
- {
- backward_recurrence_iterator t(*this);
- ++(*this);
- return t;
- }
- value_type operator*() { return f_n; }
- value_type f_n_plus_1, f_n;
- Recurrence coef;
- int k;
- };
- }
- }
- } // namespaces
- #endif // BOOST_MATH_TOOLS_RECURRENCE_HPP_
|