hypergeometric_pFq.hpp 8.3 KB

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