hypergeometric_asym.hpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright 2014 Anton Bikineev
  3. // Copyright 2014 Christopher Kormanyos
  4. // Copyright 2014 John Maddock
  5. // Copyright 2014 Paul Bristow
  6. // Distributed under the Boost
  7. // Software License, Version 1.0. (See accompanying file
  8. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef BOOST_MATH_HYPERGEOMETRIC_ASYM_HPP
  11. #define BOOST_MATH_HYPERGEOMETRIC_ASYM_HPP
  12. #include <boost/math/special_functions/gamma.hpp>
  13. #include <boost/math/special_functions/hypergeometric_2F0.hpp>
  14. #ifdef BOOST_MSVC
  15. #pragma warning(push)
  16. #pragma warning(disable:4127)
  17. #endif
  18. namespace boost { namespace math {
  19. namespace detail {
  20. //
  21. // Asymptotic series based on https://dlmf.nist.gov/13.7#E1
  22. //
  23. // Note that a and b must not be negative integers, in addition
  24. // we require z > 0 and so apply Kummer's relation for z < 0.
  25. //
  26. template <class T, class Policy>
  27. inline T hypergeometric_1F1_asym_large_z_series(T a, const T& b, T z, const Policy& pol, int& log_scaling)
  28. {
  29. BOOST_MATH_STD_USING
  30. static const char* function = "boost::math::hypergeometric_1F1_asym_large_z_series<%1%>(%1%, %1%, %1%)";
  31. T prefix;
  32. int e, s;
  33. if (z < 0)
  34. {
  35. a = b - a;
  36. z = -z;
  37. prefix = 1;
  38. }
  39. else
  40. {
  41. e = z > INT_MAX ? INT_MAX : itrunc(z, pol);
  42. log_scaling += e;
  43. prefix = exp(z - e);
  44. }
  45. if ((fabs(a) < 10) && (fabs(b) < 10))
  46. {
  47. prefix *= pow(z, a) * pow(z, -b) * boost::math::tgamma(b, pol) / boost::math::tgamma(a, pol);
  48. }
  49. else
  50. {
  51. T t = log(z) * (a - b);
  52. e = itrunc(t, pol);
  53. log_scaling += e;
  54. prefix *= exp(t - e);
  55. t = boost::math::lgamma(b, &s, pol);
  56. e = itrunc(t, pol);
  57. log_scaling += e;
  58. prefix *= s * exp(t - e);
  59. t = boost::math::lgamma(a, &s, pol);
  60. e = itrunc(t, pol);
  61. log_scaling -= e;
  62. prefix /= s * exp(t - e);
  63. }
  64. //
  65. // Checked 2F0:
  66. //
  67. unsigned k = 0;
  68. T a1_poch(1 - a);
  69. T a2_poch(b - a);
  70. T z_mult(1 / z);
  71. T sum = 0;
  72. T abs_sum = 0;
  73. T term = 1;
  74. T last_term = 0;
  75. do
  76. {
  77. sum += term;
  78. last_term = term;
  79. abs_sum += fabs(sum);
  80. term *= a1_poch * a2_poch * z_mult;
  81. term /= ++k;
  82. a1_poch += 1;
  83. a2_poch += 1;
  84. if (fabs(sum) * boost::math::policies::get_epsilon<T, Policy>() > fabs(term))
  85. break;
  86. if(fabs(sum) / abs_sum < boost::math::policies::get_epsilon<T, Policy>())
  87. return boost::math::policies::raise_evaluation_error<T>(function, "Large-z asymptotic approximation to 1F1 has destroyed all the digits in the result due to cancellation. Current best guess is %1%",
  88. prefix * sum, Policy());
  89. if(k > boost::math::policies::get_max_series_iterations<Policy>())
  90. return boost::math::policies::raise_evaluation_error<T>(function, "1F1: Unable to locate solution in a reasonable time:"
  91. " large-z asymptotic approximation. Current best guess is %1%", prefix * sum, Policy());
  92. if((k > 10) && (fabs(term) > fabs(last_term)))
  93. return boost::math::policies::raise_evaluation_error<T>(function, "Large-z asymptotic approximation to 1F1 is divergent. Current best guess is %1%", prefix * sum, Policy());
  94. } while (true);
  95. return prefix * sum;
  96. }
  97. // experimental range
  98. template <class T, class Policy>
  99. inline bool hypergeometric_1F1_asym_region(const T& a, const T& b, const T& z, const Policy&)
  100. {
  101. BOOST_MATH_STD_USING
  102. int half_digits = policies::digits<T, Policy>() / 2;
  103. bool in_region = false;
  104. if (fabs(a) < 0.001f)
  105. return false; // Haven't been able to make this work, why not? TODO!
  106. //
  107. // We use the following heuristic, if after we have had half_digits terms
  108. // of the 2F0 series, we require terms to be decreasing in size by a factor
  109. // of at least 0.7. Assuming the earlier terms were converging much faster
  110. // than this, then this should be enough to achieve convergence before the
  111. // series shoots off to infinity.
  112. //
  113. if (z > 0)
  114. {
  115. T one_minus_a = 1 - a;
  116. T b_minus_a = b - a;
  117. if (fabs((one_minus_a + half_digits) * (b_minus_a + half_digits) / (half_digits * z)) < 0.7)
  118. {
  119. in_region = true;
  120. //
  121. // double check that we are not divergent at the start if a,b < 0:
  122. //
  123. if ((one_minus_a < 0) || (b_minus_a < 0))
  124. {
  125. if (fabs(one_minus_a * b_minus_a / z) > 0.5)
  126. in_region = false;
  127. }
  128. }
  129. }
  130. else if (fabs((1 - (b - a) + half_digits) * (a + half_digits) / (half_digits * z)) < 0.7)
  131. {
  132. if ((floor(b - a) == (b - a)) && (b - a < 0))
  133. return false; // Can't have a negative integer b-a.
  134. in_region = true;
  135. //
  136. // double check that we are not divergent at the start if a,b < 0:
  137. //
  138. T a1 = 1 - (b - a);
  139. if ((a1 < 0) || (a < 0))
  140. {
  141. if (fabs(a1 * a / z) > 0.5)
  142. in_region = false;
  143. }
  144. }
  145. //
  146. // Check for a and b negative integers as these aren't supported by the approximation:
  147. //
  148. if (in_region)
  149. {
  150. if ((a < 0) && (floor(a) == a))
  151. in_region = false;
  152. if ((b < 0) && (floor(b) == b))
  153. in_region = false;
  154. if (fabs(z) < 40)
  155. in_region = false;
  156. }
  157. return in_region;
  158. }
  159. } } } // namespaces
  160. #ifdef BOOST_MSVC
  161. #pragma warning(pop)
  162. #endif
  163. #endif // BOOST_MATH_HYPERGEOMETRIC_ASYM_HPP