hypergeometric_1F1_map_neg_b_fwd_recurrence.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // Copyright John Maddock 2006.
  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. #define BOOST_ENABLE_ASSERT_HANDLER
  6. #define BOOST_MATH_MAX_SERIES_ITERATION_POLICY INT_MAX
  7. // for consistent behaviour across compilers/platforms:
  8. #define BOOST_MATH_PROMOTE_DOUBLE_POLICY false
  9. // overflow to infinity is OK, we treat these as zero error as long as the sign is correct!
  10. #define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error
  11. #include <iostream>
  12. #include <ctime>
  13. #include <boost/multiprecision/mpfr.hpp>
  14. #include <boost/multiprecision/cpp_bin_float.hpp>
  15. #include <boost/math/special_functions/hypergeometric_1F1.hpp>
  16. #include <boost/math/special_functions/hypergeometric_pFq.hpp>
  17. #include <boost/math/special_functions/relative_difference.hpp>
  18. #include <boost/random.hpp>
  19. #include <set>
  20. #include <fstream>
  21. #include <boost/iostreams/tee.hpp>
  22. #include <boost/iostreams/stream.hpp>
  23. using boost::multiprecision::mpfr_float;
  24. namespace boost {
  25. //
  26. // We convert assertions into exceptions, so we can log them and continue:
  27. //
  28. void assertion_failed(char const * expr, char const *, char const * file, long line)
  29. {
  30. std::ostringstream oss;
  31. oss << file << ":" << line << " Assertion failed: " << expr;
  32. throw std::runtime_error(oss.str());
  33. }
  34. }
  35. typedef boost::multiprecision::cpp_bin_float_quad test_type;
  36. int main()
  37. {
  38. using std::floor;
  39. using std::ceil;
  40. try {
  41. test_type a_start, a_end;
  42. test_type b_start, b_end;
  43. test_type a_mult, b_mult;
  44. std::cout << "Enter range for paramater a: ";
  45. std::cin >> a_start >> a_end;
  46. std::cout << "Enter range for paramater b: ";
  47. std::cin >> b_start >> b_end;
  48. std::cout << "Enter multiplier for a parameter: ";
  49. std::cin >> a_mult;
  50. std::cout << "Enter multiplier for b parameter: ";
  51. std::cin >> b_mult;
  52. double error_limit = 200;
  53. double time_limit = 10.0;
  54. for (test_type a = a_start; a < a_end; a_start < 0 ? a /= a_mult : a *= a_mult)
  55. {
  56. for (test_type b = b_start; b < b_end; b_start < 0 ? b /= b_mult : b *= b_mult)
  57. {
  58. test_type z_mult = 2;
  59. test_type last_good = 0;
  60. test_type bad = 0;
  61. try {
  62. for (test_type z = 1; z < 1e10; z *= z_mult, z_mult *= 2)
  63. {
  64. // std::cout << "z = " << z << std::endl;
  65. boost::uintmax_t max_iter = 1000;
  66. test_type calc = boost::math::tools::function_ratio_from_forwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients<test_type>(a, b, z), std::numeric_limits<test_type>::epsilon() * 2, max_iter);
  67. test_type reference = (test_type)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a) }, { mpfr_float(b) }, mpfr_float(z), 50, time_limit) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a + 1) }, { mpfr_float(b + 1) }, mpfr_float(z), std::numeric_limits<test_type>::digits10 * 2, time_limit));
  68. double err = (double)boost::math::epsilon_difference(reference, calc);
  69. if (err < error_limit)
  70. {
  71. last_good = z;
  72. break;
  73. }
  74. else
  75. {
  76. bad = z;
  77. }
  78. }
  79. }
  80. catch (const std::exception& e)
  81. {
  82. std::cout << "Unexpected exception: " << e.what() << std::endl;
  83. std::cout << "For a = " << a << " b = " << b << " z = " << bad * z_mult / 2 << std::endl;
  84. }
  85. test_type z_limit;
  86. if (0 == bad)
  87. z_limit = 1; // Any z is large enough
  88. else if (0 == last_good)
  89. z_limit = std::numeric_limits<test_type > ::infinity();
  90. else
  91. {
  92. //
  93. // At this stage last_good and bad should bracket the edge of the domain, bisect to narrow things down:
  94. //
  95. z_limit = last_good == 0 ? 0 : boost::math::tools::bisect([&a, b, error_limit, time_limit](test_type z)
  96. {
  97. boost::uintmax_t max_iter = 1000;
  98. test_type calc = boost::math::tools::function_ratio_from_forwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients<test_type>(a, b, z), std::numeric_limits<test_type>::epsilon() * 2, max_iter);
  99. test_type reference = (test_type)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a) }, { mpfr_float(b) }, mpfr_float(z), 50, time_limit + 20) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a + 1) }, { mpfr_float(b + 1) }, mpfr_float(z), std::numeric_limits<test_type>::digits10 * 2, time_limit + 20));
  100. test_type err = boost::math::epsilon_difference(reference, calc);
  101. return err < error_limit ? 1 : -1;
  102. }, bad, last_good, boost::math::tools::equal_floor()).first;
  103. z_limit = floor(z_limit + 2); // Give ourselves some headroom!
  104. }
  105. // std::cout << "z_limit = " << z_limit << std::endl;
  106. //
  107. // Now over again for backwards recurrence domain at the same points:
  108. //
  109. bad = z_limit > 1e10 ? 1e10 : z_limit;
  110. last_good = 0;
  111. z_mult = 1.1;
  112. for (test_type z = bad; z > 1; z /= z_mult, z_mult *= 2)
  113. {
  114. // std::cout << "z = " << z << std::endl;
  115. try {
  116. boost::uintmax_t max_iter = 1000;
  117. test_type calc = boost::math::tools::function_ratio_from_backwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients<test_type>(a, b, z), std::numeric_limits<test_type>::epsilon() * 2, max_iter);
  118. test_type reference = (test_type)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a) }, { mpfr_float(b) }, mpfr_float(z), 50, time_limit) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a - 1) }, { mpfr_float(b - 1) }, mpfr_float(z), std::numeric_limits<test_type>::digits10 * 2, time_limit));
  119. test_type err = boost::math::epsilon_difference(reference, calc);
  120. if (err < error_limit)
  121. {
  122. last_good = z;
  123. break;
  124. }
  125. else
  126. {
  127. bad = z;
  128. }
  129. }
  130. catch (const std::exception& e)
  131. {
  132. bad = z;
  133. std::cout << "Unexpected exception: " << e.what() << std::endl;
  134. std::cout << "For a = " << a << " b = " << b << " z = " << z << std::endl;
  135. }
  136. }
  137. test_type lower_z_limit;
  138. if (last_good < 1)
  139. lower_z_limit = 0;
  140. else if (last_good >= bad)
  141. {
  142. boost::uintmax_t max_iter = 1000;
  143. test_type z = bad;
  144. test_type calc = boost::math::tools::function_ratio_from_forwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients<test_type>(a, b, z), std::numeric_limits<test_type>::epsilon() * 2, max_iter);
  145. test_type reference = (test_type)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a) }, { mpfr_float(b) }, mpfr_float(z), 50, time_limit) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a + 1) }, { mpfr_float(b + 1) }, mpfr_float(z), std::numeric_limits<test_type>::digits10 * 2, time_limit));
  146. test_type err = boost::math::epsilon_difference(reference, calc);
  147. if (err < error_limit)
  148. {
  149. lower_z_limit = bad; // Both forwards and backwards iteration work!!!
  150. }
  151. else
  152. throw std::runtime_error("Internal logic failed!");
  153. }
  154. else
  155. {
  156. //
  157. // At this stage last_good and bad should bracket the edge of the domain, bisect to narrow things down:
  158. //
  159. lower_z_limit = last_good == 0 ? 0 : boost::math::tools::bisect([&a, b, error_limit, time_limit](test_type z)
  160. {
  161. boost::uintmax_t max_iter = 1000;
  162. test_type calc = boost::math::tools::function_ratio_from_backwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients<test_type>(a, b, z), std::numeric_limits<test_type>::epsilon() * 2, max_iter);
  163. test_type reference = (test_type)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a) }, { mpfr_float(b) }, mpfr_float(z), 50, time_limit + 20) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a - 1) }, { mpfr_float(b - 1) }, mpfr_float(z), std::numeric_limits<test_type>::digits10 * 2, time_limit + 20));
  164. test_type err = boost::math::epsilon_difference(reference, calc);
  165. return err < error_limit ? 1 : -1;
  166. }, last_good, bad, boost::math::tools::equal_floor()).first;
  167. z_limit = ceil(z_limit - 2); // Give ourselves some headroom!
  168. }
  169. std::cout << std::setprecision(std::numeric_limits<test_type>::max_digits10) << "{ " << a << ", " << b << ", " << lower_z_limit << ", " << z_limit << "}," << std::endl;
  170. }
  171. }
  172. }
  173. catch (const std::exception& e)
  174. {
  175. std::cout << "Unexpected exception: " << e.what() << std::endl;
  176. }
  177. return 0;
  178. }