hypergeometric_pFq.hpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright 2018 John Maddock
  3. // Distributed under the Boost
  4. // Software License, Version 1.0. (See accompanying file
  5. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_MATH_HYPERGEOMETRIC_PFQ_HPP
  7. #define BOOST_MATH_HYPERGEOMETRIC_PFQ_HPP
  8. #include <boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp>
  9. #include <boost/math/tools/throw_exception.hpp>
  10. #include <chrono>
  11. #include <initializer_list>
  12. namespace boost {
  13. namespace math {
  14. namespace detail {
  15. struct pFq_termination_exception : public std::runtime_error
  16. {
  17. pFq_termination_exception(const char* p) : std::runtime_error(p) {}
  18. };
  19. struct timed_iteration_terminator
  20. {
  21. timed_iteration_terminator(std::uintmax_t i, double t) : max_iter(i), max_time(t), start_time(std::chrono::system_clock::now()) {}
  22. bool operator()(std::uintmax_t iter)const
  23. {
  24. if (iter > max_iter)
  25. BOOST_MATH_THROW_EXCEPTION(boost::math::detail::pFq_termination_exception("pFq exceeded maximum permitted iterations."));
  26. if (std::chrono::duration<double>(std::chrono::system_clock::now() - start_time).count() > max_time)
  27. BOOST_MATH_THROW_EXCEPTION(boost::math::detail::pFq_termination_exception("pFq exceeded maximum permitted evaluation time."));
  28. return false;
  29. }
  30. std::uintmax_t max_iter;
  31. double max_time;
  32. std::chrono::system_clock::time_point start_time;
  33. };
  34. }
  35. template <class Seq, class Real, class Policy>
  36. inline typename tools::promote_args<Real, typename Seq::value_type>::type hypergeometric_pFq(const Seq& aj, const Seq& bj, const Real& z, Real* p_abs_error, const Policy& pol)
  37. {
  38. typedef typename tools::promote_args<Real, typename Seq::value_type>::type result_type;
  39. typedef typename policies::evaluation<result_type, Policy>::type value_type;
  40. typedef typename policies::normalise<
  41. Policy,
  42. policies::promote_float<false>,
  43. policies::promote_double<false>,
  44. policies::discrete_quantile<>,
  45. policies::assert_undefined<> >::type forwarding_policy;
  46. BOOST_MATH_STD_USING
  47. long long scale = 0;
  48. std::pair<value_type, value_type> r = boost::math::detail::hypergeometric_pFq_checked_series_impl(aj, bj, value_type(z), pol, boost::math::detail::iteration_terminator(boost::math::policies::get_max_series_iterations<forwarding_policy>()), scale);
  49. r.first *= exp(Real(scale));
  50. r.second *= exp(Real(scale));
  51. if (p_abs_error)
  52. *p_abs_error = static_cast<Real>(r.second) * boost::math::tools::epsilon<Real>();
  53. return policies::checked_narrowing_cast<result_type, Policy>(r.first, "boost::math::hypergeometric_pFq<%1%>(%1%,%1%,%1%)");
  54. }
  55. template <class Seq, class Real>
  56. inline typename tools::promote_args<Real, typename Seq::value_type>::type hypergeometric_pFq(const Seq& aj, const Seq& bj, const Real& z, Real* p_abs_error = 0)
  57. {
  58. return hypergeometric_pFq(aj, bj, z, p_abs_error, boost::math::policies::policy<>());
  59. }
  60. template <class R, class Real, class Policy>
  61. inline typename tools::promote_args<Real, R>::type hypergeometric_pFq(const std::initializer_list<R>& aj, const std::initializer_list<R>& bj, const Real& z, Real* p_abs_error, const Policy& pol)
  62. {
  63. return hypergeometric_pFq<std::initializer_list<R>, Real, Policy>(aj, bj, z, p_abs_error, pol);
  64. }
  65. template <class R, class Real>
  66. inline typename tools::promote_args<Real, R>::type hypergeometric_pFq(const std::initializer_list<R>& aj, const std::initializer_list<R>& bj, const Real& z, Real* p_abs_error = nullptr)
  67. {
  68. return hypergeometric_pFq<std::initializer_list<R>, Real>(aj, bj, z, p_abs_error);
  69. }
  70. #ifndef BOOST_MATH_NO_EXCEPTIONS
  71. template <class T>
  72. struct scoped_precision
  73. {
  74. scoped_precision(unsigned p)
  75. {
  76. old_p = T::default_precision();
  77. T::default_precision(p);
  78. }
  79. ~scoped_precision()
  80. {
  81. T::default_precision(old_p);
  82. }
  83. unsigned old_p;
  84. };
  85. template <class Seq, class Real, class Policy>
  86. Real hypergeometric_pFq_precision(const Seq& aj, const Seq& bj, Real z, unsigned digits10, double timeout, const Policy& pol)
  87. {
  88. unsigned current_precision = digits10 + 5;
  89. for (auto ai = aj.begin(); ai != aj.end(); ++ai)
  90. {
  91. current_precision = (std::max)(current_precision, ai->precision());
  92. }
  93. for (auto bi = bj.begin(); bi != bj.end(); ++bi)
  94. {
  95. current_precision = (std::max)(current_precision, bi->precision());
  96. }
  97. current_precision = (std::max)(current_precision, z.precision());
  98. Real r, norm;
  99. std::vector<Real> aa(aj), bb(bj);
  100. do
  101. {
  102. scoped_precision<Real> p(current_precision);
  103. for (auto ai = aa.begin(); ai != aa.end(); ++ai)
  104. ai->precision(current_precision);
  105. for (auto bi = bb.begin(); bi != bb.end(); ++bi)
  106. bi->precision(current_precision);
  107. z.precision(current_precision);
  108. try
  109. {
  110. long long scale = 0;
  111. std::pair<Real, Real> rp = boost::math::detail::hypergeometric_pFq_checked_series_impl(aa, bb, z, pol, boost::math::detail::timed_iteration_terminator(boost::math::policies::get_max_series_iterations<Policy>(), timeout), scale);
  112. rp.first *= exp(Real(scale));
  113. rp.second *= exp(Real(scale));
  114. r = rp.first;
  115. norm = rp.second;
  116. unsigned cancellation;
  117. try {
  118. cancellation = itrunc(log10(abs(norm / r)));
  119. }
  120. catch (const boost::math::rounding_error&)
  121. {
  122. // Happens when r is near enough zero:
  123. cancellation = UINT_MAX;
  124. }
  125. if (cancellation >= current_precision - 1)
  126. {
  127. current_precision *= 2;
  128. continue;
  129. }
  130. unsigned precision_obtained = current_precision - 1 - cancellation;
  131. if (precision_obtained < digits10)
  132. {
  133. current_precision += digits10 - precision_obtained + 5;
  134. }
  135. else
  136. break;
  137. }
  138. catch (const boost::math::evaluation_error&)
  139. {
  140. current_precision *= 2;
  141. }
  142. catch (const detail::pFq_termination_exception& e)
  143. {
  144. //
  145. // Either we have exhausted the number of series iterations, or the timeout.
  146. // Either way we quit now.
  147. throw boost::math::evaluation_error(e.what());
  148. }
  149. } while (true);
  150. return r;
  151. }
  152. template <class Seq, class Real>
  153. Real hypergeometric_pFq_precision(const Seq& aj, const Seq& bj, const Real& z, unsigned digits10, double timeout = 0.5)
  154. {
  155. return hypergeometric_pFq_precision(aj, bj, z, digits10, timeout, boost::math::policies::policy<>());
  156. }
  157. template <class Real, class Policy>
  158. Real hypergeometric_pFq_precision(const std::initializer_list<Real>& aj, const std::initializer_list<Real>& bj, const Real& z, unsigned digits10, double timeout, const Policy& pol)
  159. {
  160. return hypergeometric_pFq_precision< std::initializer_list<Real>, Real>(aj, bj, z, digits10, timeout, pol);
  161. }
  162. template <class Real>
  163. Real hypergeometric_pFq_precision(const std::initializer_list<Real>& aj, const std::initializer_list<Real>& bj, const Real& z, unsigned digits10, double timeout = 0.5)
  164. {
  165. return hypergeometric_pFq_precision< std::initializer_list<Real>, Real>(aj, bj, z, digits10, timeout, boost::math::policies::policy<>());
  166. }
  167. #endif
  168. }
  169. } // namespaces
  170. #endif // BOOST_MATH_BESSEL_ITERATORS_HPP