hypergeometric_1F1_addition_theorems_on_z.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. //
  7. #ifndef BOOST_MATH_HYPERGEOMETRIC_1F1_ADDITION_THEOREMS_ON_Z_HPP
  8. #define BOOST_MATH_HYPERGEOMETRIC_1F1_ADDITION_THEOREMS_ON_Z_HPP
  9. #include <boost/math/tools/series.hpp>
  10. //
  11. // This file implements the addition theorems for 1F1 on z, specifically
  12. // each function returns 1F1[a, b, z + k] for some integer k - there's
  13. // no particular reason why k needs to be an integer, but no reason why
  14. // it shouldn't be either.
  15. //
  16. // The functions are named hypergeometric_1f1_recurrence_on_z_[plus|minus|zero]_[plus|minus|zero]
  17. // where a "plus" indicates forward recurrence, minus backwards recurrence, and zero no recurrence.
  18. // So for example hypergeometric_1f1_recurrence_on_z_zero_plus uses forward recurrence on b and
  19. // hypergeometric_1f1_recurrence_on_z_minus_minus uses backwards recurrence on both a and b.
  20. //
  21. // See https://dlmf.nist.gov/13.13
  22. //
  23. namespace boost { namespace math { namespace detail {
  24. //
  25. // This works moderately well for a < 0, but has some very strange behaviour with
  26. // strings of values of the same sign followed by a sign switch then another
  27. // series all the same sign and so on.... doesn't converge smoothly either
  28. // but rises and falls in wave-like behaviour.... very slow to converge...
  29. //
  30. template <class T, class Policy>
  31. struct hypergeometric_1f1_recurrence_on_z_minus_zero_series
  32. {
  33. typedef T result_type;
  34. hypergeometric_1f1_recurrence_on_z_minus_zero_series(const T& a, const T& b, const T& z, int k_, const Policy& pol)
  35. : term(1), b_minus_a_plus_n(b - a), a_(a), b_(b), z_(z), n(0), k(k_)
  36. {
  37. BOOST_MATH_STD_USING
  38. int scale1(0), scale2(0);
  39. M = boost::math::detail::hypergeometric_1F1_imp(a, b, z, pol, scale1);
  40. M_next = boost::math::detail::hypergeometric_1F1_imp(T(a - 1), b, z, pol, scale2);
  41. if (scale1 != scale2)
  42. M_next *= exp(scale2 - scale1);
  43. if (M > 1e10f)
  44. {
  45. // rescale:
  46. int rescale = itrunc(log(fabs(M)));
  47. M *= exp(T(-rescale));
  48. M_next *= exp(T(-rescale));
  49. scale1 += rescale;
  50. }
  51. scaling = scale1;
  52. }
  53. T operator()()
  54. {
  55. T result = term * M;
  56. term *= b_minus_a_plus_n * k / ((z_ + k) * ++n);
  57. b_minus_a_plus_n += 1;
  58. T M2 = -((2 * (a_ - n) - b_ + z_) * M_next - (a_ - n) * M) / (b_ - (a_ - n));
  59. M = M_next;
  60. M_next = M2;
  61. return result;
  62. }
  63. int scale()const { return scaling; }
  64. private:
  65. T term, b_minus_a_plus_n, M, M_next, a_, b_, z_;
  66. int n, k, scaling;
  67. };
  68. template <class T, class Policy>
  69. T hypergeometric_1f1_recurrence_on_z_minus_zero(const T& a, const T& b, const T& z, int k, const Policy& pol, int& log_scaling)
  70. {
  71. BOOST_MATH_STD_USING
  72. BOOST_ASSERT((z + k) / z > 0.5f);
  73. hypergeometric_1f1_recurrence_on_z_minus_zero_series<T, Policy> s(a, b, z, k, pol);
  74. boost::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  75. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  76. log_scaling += s.scale();
  77. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1f1_recurrence_on_z_plus_plus<%1%>(%1%,%1%,%1%)", max_iter, pol);
  78. return result * exp(T(k)) * pow(z / (z + k), b - a);
  79. }
  80. #if 0
  81. //
  82. // These are commented out as they are currently unused, but may find use in the future:
  83. //
  84. template <class T, class Policy>
  85. struct hypergeometric_1f1_recurrence_on_z_plus_plus_series
  86. {
  87. typedef T result_type;
  88. hypergeometric_1f1_recurrence_on_z_plus_plus_series(const T& a, const T& b, const T& z, int k_, const Policy& pol)
  89. : term(1), a_plus_n(a), b_plus_n(b), z_(z), n(0), k(k_)
  90. {
  91. M = boost::math::detail::hypergeometric_1F1_imp(a, b, z, pol);
  92. M_next = boost::math::detail::hypergeometric_1F1_imp(a + 1, b + 1, z, pol);
  93. }
  94. T operator()()
  95. {
  96. T result = term * M;
  97. term *= a_plus_n * k / (b_plus_n * ++n);
  98. a_plus_n += 1;
  99. b_plus_n += 1;
  100. // The a_plus_n == 0 case below isn't actually correct, but doesn't matter as that term will be zero
  101. // anyway, we just need to not divde by zero and end up with a NaN in the result.
  102. T M2 = (a_plus_n == -1) ? 1 : (a_plus_n == 0) ? 0 : (M_next * b_plus_n * (1 - b_plus_n + z_) + b_plus_n * (b_plus_n - 1) * M) / (a_plus_n * z_);
  103. M = M_next;
  104. M_next = M2;
  105. return result;
  106. }
  107. T term, a_plus_n, b_plus_n, M, M_next, z_;
  108. int n, k;
  109. };
  110. template <class T, class Policy>
  111. T hypergeometric_1f1_recurrence_on_z_plus_plus(const T& a, const T& b, const T& z, int k, const Policy& pol)
  112. {
  113. hypergeometric_1f1_recurrence_on_z_plus_plus_series<T, Policy> s(a, b, z, k, pol);
  114. boost::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  115. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  116. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1f1_recurrence_on_z_plus_plus<%1%>(%1%,%1%,%1%)", max_iter, pol);
  117. return result;
  118. }
  119. template <class T, class Policy>
  120. struct hypergeometric_1f1_recurrence_on_z_zero_minus_series
  121. {
  122. typedef T result_type;
  123. hypergeometric_1f1_recurrence_on_z_zero_minus_series(const T& a, const T& b, const T& z, int k_, const Policy& pol)
  124. : term(1), b_pochhammer(1 - b), x_k_power(-k_ / z), b_minus_n(b), a_(a), z_(z), b_(b), n(0), k(k_)
  125. {
  126. M = boost::math::detail::hypergeometric_1F1_imp(a, b, z, pol);
  127. M_next = boost::math::detail::hypergeometric_1F1_imp(a, b - 1, z, pol);
  128. }
  129. T operator()()
  130. {
  131. BOOST_MATH_STD_USING
  132. T result = term * M;
  133. term *= b_pochhammer * x_k_power / ++n;
  134. b_pochhammer += 1;
  135. b_minus_n -= 1;
  136. T M2 = (M_next * b_minus_n * (1 - b_minus_n - z_) + z_ * (b_minus_n - a_) * M) / (-b_minus_n * (b_minus_n - 1));
  137. M = M_next;
  138. M_next = M2;
  139. return result;
  140. }
  141. T term, b_pochhammer, x_k_power, M, M_next, b_minus_n, a_, z_, b_;
  142. int n, k;
  143. };
  144. template <class T, class Policy>
  145. T hypergeometric_1f1_recurrence_on_z_zero_minus(const T& a, const T& b, const T& z, int k, const Policy& pol)
  146. {
  147. BOOST_MATH_STD_USING
  148. BOOST_ASSERT(abs(k) < fabs(z));
  149. hypergeometric_1f1_recurrence_on_z_zero_minus_series<T, Policy> s(a, b, z, k, pol);
  150. boost::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  151. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  152. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1f1_recurrence_on_z_plus_plus<%1%>(%1%,%1%,%1%)", max_iter, pol);
  153. return result * pow((z + k) / z, 1 - b);
  154. }
  155. template <class T, class Policy>
  156. struct hypergeometric_1f1_recurrence_on_z_plus_zero_series
  157. {
  158. typedef T result_type;
  159. hypergeometric_1f1_recurrence_on_z_plus_zero_series(const T& a, const T& b, const T& z, int k_, const Policy& pol)
  160. : term(1), a_pochhammer(a), z_plus_k(z + k_), b_(b), a_(a), z_(z), n(0), k(k_)
  161. {
  162. M = boost::math::detail::hypergeometric_1F1_imp(a, b, z, pol);
  163. M_next = boost::math::detail::hypergeometric_1F1_imp(a + 1, b, z, pol);
  164. }
  165. T operator()()
  166. {
  167. T result = term * M;
  168. term *= a_pochhammer * k / (++n * z_plus_k);
  169. a_pochhammer += 1;
  170. T M2 = (a_pochhammer == -1) ? 1 : (a_pochhammer == 0) ? 0 : (M_next * (2 * a_pochhammer - b_ + z_) + (b_ - a_pochhammer) * M) / a_pochhammer;
  171. M = M_next;
  172. M_next = M2;
  173. return result;
  174. }
  175. T term, a_pochhammer, z_plus_k, M, M_next, b_minus_n, a_, b_, z_;
  176. int n, k;
  177. };
  178. template <class T, class Policy>
  179. T hypergeometric_1f1_recurrence_on_z_plus_zero(const T& a, const T& b, const T& z, int k, const Policy& pol)
  180. {
  181. BOOST_MATH_STD_USING
  182. BOOST_ASSERT(k / z > -0.5f);
  183. //BOOST_ASSERT(floor(a) != a || a > 0);
  184. hypergeometric_1f1_recurrence_on_z_plus_zero_series<T, Policy> s(a, b, z, k, pol);
  185. boost::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  186. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  187. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1f1_recurrence_on_z_plus_plus<%1%>(%1%,%1%,%1%)", max_iter, pol);
  188. return result * pow(z / (z + k), a);
  189. }
  190. template <class T, class Policy>
  191. struct hypergeometric_1f1_recurrence_on_z_zero_plus_series
  192. {
  193. typedef T result_type;
  194. hypergeometric_1f1_recurrence_on_z_zero_plus_series(const T& a, const T& b, const T& z, int k_, const Policy& pol)
  195. : term(1), b_minus_a_plus_n(b - a), b_plus_n(b), a_(a), z_(z), n(0), k(k_)
  196. {
  197. M = boost::math::detail::hypergeometric_1F1_imp(a, b, z, pol);
  198. M_next = boost::math::detail::hypergeometric_1F1_imp(a, b + 1, z, pol);
  199. }
  200. T operator()()
  201. {
  202. T result = term * M;
  203. term *= b_minus_a_plus_n * -k / (b_plus_n * ++n);
  204. b_minus_a_plus_n += 1;
  205. b_plus_n += 1;
  206. T M2 = (b_plus_n * (b_plus_n - 1) * M + b_plus_n * (1 - b_plus_n - z_) * M_next) / (-z_ * b_minus_a_plus_n);
  207. M = M_next;
  208. M_next = M2;
  209. return result;
  210. }
  211. T term, b_minus_a_plus_n, M, M_next, b_minus_n, a_, b_plus_n, z_;
  212. int n, k;
  213. };
  214. template <class T, class Policy>
  215. T hypergeometric_1f1_recurrence_on_z_zero_plus(const T& a, const T& b, const T& z, int k, const Policy& pol)
  216. {
  217. BOOST_MATH_STD_USING
  218. hypergeometric_1f1_recurrence_on_z_zero_plus_series<T, Policy> s(a, b, z, k, pol);
  219. boost::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  220. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  221. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1f1_recurrence_on_z_plus_plus<%1%>(%1%,%1%,%1%)", max_iter, pol);
  222. return result * exp(T(k));
  223. }
  224. //
  225. // I'm unable to find any situation where this series isn't divergent and therefore
  226. // is probably quite useless:
  227. //
  228. template <class T, class Policy>
  229. struct hypergeometric_1f1_recurrence_on_z_minus_minus_series
  230. {
  231. typedef T result_type;
  232. hypergeometric_1f1_recurrence_on_z_minus_minus_series(const T& a, const T& b, const T& z, int k_, const Policy& pol)
  233. : term(1), one_minus_b_plus_n(1 - b), a_(a), b_(b), z_(z), n(0), k(k_)
  234. {
  235. M = boost::math::detail::hypergeometric_1F1_imp(a, b, z, pol);
  236. M_next = boost::math::detail::hypergeometric_1F1_imp(a - 1, b - 1, z, pol);
  237. }
  238. T operator()()
  239. {
  240. T result = term * M;
  241. term *= one_minus_b_plus_n * k / (z_ * ++n);
  242. one_minus_b_plus_n += 1;
  243. T M2 = -((b_ - n) * (1 - b_ + n + z_) * M_next - (a_ - n) * z_ * M) / ((b_ - n) * (b_ - n - 1));
  244. M = M_next;
  245. M_next = M2;
  246. return result;
  247. }
  248. T term, one_minus_b_plus_n, M, M_next, a_, b_, z_;
  249. int n, k;
  250. };
  251. template <class T, class Policy>
  252. T hypergeometric_1f1_recurrence_on_z_minus_minus(const T& a, const T& b, const T& z, int k, const Policy& pol)
  253. {
  254. BOOST_MATH_STD_USING
  255. hypergeometric_1f1_recurrence_on_z_minus_minus_series<T, Policy> s(a, b, z, k, pol);
  256. boost::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  257. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  258. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1f1_recurrence_on_z_plus_plus<%1%>(%1%,%1%,%1%)", max_iter, pol);
  259. return result * exp(T(k)) * pow((z + k) / z, 1 - b);
  260. }
  261. #endif
  262. } } } // namespaces
  263. #endif // BOOST_MATH_HYPERGEOMETRIC_1F1_ADDITION_THEOREMS_ON_Z_HPP