hypergeometric_series.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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_DETAIL_HYPERGEOMETRIC_SERIES_HPP
  11. #define BOOST_MATH_DETAIL_HYPERGEOMETRIC_SERIES_HPP
  12. #include <cmath>
  13. #include <cstdint>
  14. #include <boost/math/tools/series.hpp>
  15. #include <boost/math/special_functions/gamma.hpp>
  16. #include <boost/math/special_functions/trunc.hpp>
  17. #include <boost/math/policies/error_handling.hpp>
  18. namespace boost { namespace math { namespace detail {
  19. // primary template for term of Taylor series
  20. template <class T, unsigned p, unsigned q>
  21. struct hypergeometric_pFq_generic_series_term;
  22. // partial specialization for 0F1
  23. template <class T>
  24. struct hypergeometric_pFq_generic_series_term<T, 0u, 1u>
  25. {
  26. typedef T result_type;
  27. hypergeometric_pFq_generic_series_term(const T& b, const T& z)
  28. : n(0), term(1), b(b), z(z)
  29. {
  30. }
  31. T operator()()
  32. {
  33. BOOST_MATH_STD_USING
  34. const T r = term;
  35. term *= ((1 / ((b + n) * (n + 1))) * z);
  36. ++n;
  37. return r;
  38. }
  39. private:
  40. unsigned n;
  41. T term;
  42. const T b, z;
  43. };
  44. // partial specialization for 1F0
  45. template <class T>
  46. struct hypergeometric_pFq_generic_series_term<T, 1u, 0u>
  47. {
  48. typedef T result_type;
  49. hypergeometric_pFq_generic_series_term(const T& a, const T& z)
  50. : n(0), term(1), a(a), z(z)
  51. {
  52. }
  53. T operator()()
  54. {
  55. BOOST_MATH_STD_USING
  56. const T r = term;
  57. term *= (((a + n) / (n + 1)) * z);
  58. ++n;
  59. return r;
  60. }
  61. private:
  62. unsigned n;
  63. T term;
  64. const T a, z;
  65. };
  66. // partial specialization for 1F1
  67. template <class T>
  68. struct hypergeometric_pFq_generic_series_term<T, 1u, 1u>
  69. {
  70. typedef T result_type;
  71. hypergeometric_pFq_generic_series_term(const T& a, const T& b, const T& z)
  72. : n(0), term(1), a(a), b(b), z(z)
  73. {
  74. }
  75. T operator()()
  76. {
  77. BOOST_MATH_STD_USING
  78. const T r = term;
  79. term *= (((a + n) / ((b + n) * (n + 1))) * z);
  80. ++n;
  81. return r;
  82. }
  83. private:
  84. unsigned n;
  85. T term;
  86. const T a, b, z;
  87. };
  88. // partial specialization for 1F2
  89. template <class T>
  90. struct hypergeometric_pFq_generic_series_term<T, 1u, 2u>
  91. {
  92. typedef T result_type;
  93. hypergeometric_pFq_generic_series_term(const T& a, const T& b1, const T& b2, const T& z)
  94. : n(0), term(1), a(a), b1(b1), b2(b2), z(z)
  95. {
  96. }
  97. T operator()()
  98. {
  99. BOOST_MATH_STD_USING
  100. const T r = term;
  101. term *= (((a + n) / ((b1 + n) * (b2 + n) * (n + 1))) * z);
  102. ++n;
  103. return r;
  104. }
  105. private:
  106. unsigned n;
  107. T term;
  108. const T a, b1, b2, z;
  109. };
  110. // partial specialization for 2F0
  111. template <class T>
  112. struct hypergeometric_pFq_generic_series_term<T, 2u, 0u>
  113. {
  114. typedef T result_type;
  115. hypergeometric_pFq_generic_series_term(const T& a1, const T& a2, const T& z)
  116. : n(0), term(1), a1(a1), a2(a2), z(z)
  117. {
  118. }
  119. T operator()()
  120. {
  121. BOOST_MATH_STD_USING
  122. const T r = term;
  123. term *= (((a1 + n) * (a2 + n) / (n + 1)) * z);
  124. ++n;
  125. return r;
  126. }
  127. private:
  128. unsigned n;
  129. T term;
  130. const T a1, a2, z;
  131. };
  132. // partial specialization for 2F1
  133. template <class T>
  134. struct hypergeometric_pFq_generic_series_term<T, 2u, 1u>
  135. {
  136. typedef T result_type;
  137. hypergeometric_pFq_generic_series_term(const T& a1, const T& a2, const T& b, const T& z)
  138. : n(0), term(1), a1(a1), a2(a2), b(b), z(z)
  139. {
  140. }
  141. T operator()()
  142. {
  143. BOOST_MATH_STD_USING
  144. const T r = term;
  145. term *= (((a1 + n) * (a2 + n) / ((b + n) * (n + 1))) * z);
  146. ++n;
  147. return r;
  148. }
  149. private:
  150. unsigned n;
  151. T term;
  152. const T a1, a2, b, z;
  153. };
  154. // we don't need to define extra check and make a polinom from
  155. // series, when p(i) and q(i) are negative integers and p(i) >= q(i)
  156. // as described in functions.wolfram.alpha, because we always
  157. // stop summation when result (in this case numerator) is zero.
  158. template <class T, unsigned p, unsigned q, class Policy>
  159. inline T sum_pFq_series(detail::hypergeometric_pFq_generic_series_term<T, p, q>& term, const Policy& pol)
  160. {
  161. BOOST_MATH_STD_USING
  162. std::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
  163. const T result = boost::math::tools::sum_series(term, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  164. policies::check_series_iterations<T>("boost::math::hypergeometric_pFq_generic_series<%1%>(%1%,%1%,%1%)", max_iter, pol);
  165. return result;
  166. }
  167. template <class T, class Policy>
  168. inline T hypergeometric_0F1_generic_series(const T& b, const T& z, const Policy& pol)
  169. {
  170. detail::hypergeometric_pFq_generic_series_term<T, 0u, 1u> s(b, z);
  171. return detail::sum_pFq_series(s, pol);
  172. }
  173. template <class T, class Policy>
  174. inline T hypergeometric_1F0_generic_series(const T& a, const T& z, const Policy& pol)
  175. {
  176. detail::hypergeometric_pFq_generic_series_term<T, 1u, 0u> s(a, z);
  177. return detail::sum_pFq_series(s, pol);
  178. }
  179. template <class T, class Policy>
  180. inline T log_pochhammer(T z, unsigned n, const Policy pol, int* s = nullptr)
  181. {
  182. BOOST_MATH_STD_USING
  183. #if 0
  184. if (z < 0)
  185. {
  186. if (n < -z)
  187. {
  188. if(s)
  189. *s = (n & 1 ? -1 : 1);
  190. return log_pochhammer(T(-z + (1 - (int)n)), n, pol);
  191. }
  192. else
  193. {
  194. int cross = itrunc(ceil(-z));
  195. return log_pochhammer(T(-z + (1 - cross)), cross, pol, s) + log_pochhammer(T(cross + z), n - cross, pol);
  196. }
  197. }
  198. else
  199. #endif
  200. {
  201. if (z + n < 0)
  202. {
  203. T r = log_pochhammer(T(-z - n + 1), n, pol, s);
  204. if (s)
  205. *s *= (n & 1 ? -1 : 1);
  206. return r;
  207. }
  208. int s1, s2;
  209. auto r = static_cast<T>(boost::math::lgamma(T(z + n), &s1, pol) - boost::math::lgamma(z, &s2, pol));
  210. if(s)
  211. *s = s1 * s2;
  212. return r;
  213. }
  214. }
  215. template <class T, class Policy>
  216. inline T hypergeometric_1F1_generic_series(const T& a, const T& b, const T& z, const Policy& pol, long long& log_scaling, const char* function)
  217. {
  218. BOOST_MATH_STD_USING
  219. T sum(0), term(1), upper_limit(sqrt(boost::math::tools::max_value<T>())), diff;
  220. T lower_limit(1 / upper_limit);
  221. unsigned n = 0;
  222. long long log_scaling_factor = lltrunc(boost::math::tools::log_max_value<T>()) - 2;
  223. T scaling_factor = exp(T(log_scaling_factor));
  224. T term_m1 = 0;
  225. long long local_scaling = 0;
  226. //
  227. // When a is very small, then (a+n)/n => 1 faster than
  228. // z / (b+n) => 1, as a result the series starts off
  229. // converging, then at some unspecified time very gradually
  230. // starts to diverge, potentially resulting in some very large
  231. // values being missed. As a result we need a check for small
  232. // a in the convergence criteria. Note that this issue occurs
  233. // even when all the terms are positive.
  234. //
  235. bool small_a = fabs(a) < 0.25;
  236. unsigned summit_location = 0;
  237. bool have_minima = false;
  238. T sq = 4 * a * z + b * b - 2 * b * z + z * z;
  239. if (sq >= 0)
  240. {
  241. T t = (-sqrt(sq) - b + z) / 2;
  242. if (t > 1) // Don't worry about a minima between 0 and 1.
  243. have_minima = true;
  244. t = (sqrt(sq) - b + z) / 2;
  245. if (t > 0)
  246. summit_location = itrunc(t);
  247. }
  248. if (summit_location > boost::math::policies::get_max_series_iterations<Policy>() / 4)
  249. {
  250. //
  251. // Skip forward to the location of the largest term in the series and
  252. // evaluate outwards from there:
  253. //
  254. int s1, s2;
  255. term = log_pochhammer(a, summit_location, pol, &s1) + summit_location * log(z) - log_pochhammer(b, summit_location, pol, &s2) - lgamma(T(summit_location + 1), pol);
  256. //std::cout << term << " " << log_pochhammer(boost::multiprecision::mpfr_float(a), summit_location, pol, &s1) + summit_location * log(boost::multiprecision::mpfr_float(z)) - log_pochhammer(boost::multiprecision::mpfr_float(b), summit_location, pol, &s2) - lgamma(boost::multiprecision::mpfr_float(summit_location + 1), pol) << std::endl;
  257. local_scaling = lltrunc(term);
  258. log_scaling += local_scaling;
  259. term = s1 * s2 * exp(term - local_scaling);
  260. //std::cout << term << " " << exp(log_pochhammer(boost::multiprecision::mpfr_float(a), summit_location, pol, &s1) + summit_location * log(boost::multiprecision::mpfr_float(z)) - log_pochhammer(boost::multiprecision::mpfr_float(b), summit_location, pol, &s2) - lgamma(boost::multiprecision::mpfr_float(summit_location + 1), pol) - local_scaling) << std::endl;
  261. n = summit_location;
  262. }
  263. else
  264. summit_location = 0;
  265. T saved_term = term;
  266. long long saved_scale = local_scaling;
  267. do
  268. {
  269. sum += term;
  270. //std::cout << n << " " << term * exp(boost::multiprecision::mpfr_float(local_scaling)) << " " << rising_factorial(boost::multiprecision::mpfr_float(a), n) * pow(boost::multiprecision::mpfr_float(z), n) / (rising_factorial(boost::multiprecision::mpfr_float(b), n) * factorial<boost::multiprecision::mpfr_float>(n)) << std::endl;
  271. if (fabs(sum) >= upper_limit)
  272. {
  273. sum /= scaling_factor;
  274. term /= scaling_factor;
  275. log_scaling += log_scaling_factor;
  276. local_scaling += log_scaling_factor;
  277. }
  278. if (fabs(sum) < lower_limit)
  279. {
  280. sum *= scaling_factor;
  281. term *= scaling_factor;
  282. log_scaling -= log_scaling_factor;
  283. local_scaling -= log_scaling_factor;
  284. }
  285. term_m1 = term;
  286. term *= (((a + n) / ((b + n) * (n + 1))) * z);
  287. if (n - summit_location > boost::math::policies::get_max_series_iterations<Policy>())
  288. return boost::math::policies::raise_evaluation_error(function, "Series did not converge, best value is %1%", sum, pol);
  289. ++n;
  290. diff = fabs(term / sum);
  291. } while ((diff > boost::math::policies::get_epsilon<T, Policy>()) || (fabs(term_m1) < fabs(term)) || (small_a && n < 10));
  292. //
  293. // See if we need to go backwards as well:
  294. //
  295. if (summit_location)
  296. {
  297. //
  298. // Backup state:
  299. //
  300. term = saved_term * exp(T(local_scaling - saved_scale));
  301. n = summit_location;
  302. term *= (b + (n - 1)) * n / ((a + (n - 1)) * z);
  303. --n;
  304. do
  305. {
  306. sum += term;
  307. //std::cout << n << " " << term * exp(boost::multiprecision::mpfr_float(local_scaling)) << " " << rising_factorial(boost::multiprecision::mpfr_float(a), n) * pow(boost::multiprecision::mpfr_float(z), n) / (rising_factorial(boost::multiprecision::mpfr_float(b), n) * factorial<boost::multiprecision::mpfr_float>(n)) << std::endl;
  308. if (n == 0)
  309. break;
  310. if (fabs(sum) >= upper_limit)
  311. {
  312. sum /= scaling_factor;
  313. term /= scaling_factor;
  314. log_scaling += log_scaling_factor;
  315. local_scaling += log_scaling_factor;
  316. }
  317. if (fabs(sum) < lower_limit)
  318. {
  319. sum *= scaling_factor;
  320. term *= scaling_factor;
  321. log_scaling -= log_scaling_factor;
  322. local_scaling -= log_scaling_factor;
  323. }
  324. term_m1 = term;
  325. term *= (b + (n - 1)) * n / ((a + (n - 1)) * z);
  326. if (summit_location - n > boost::math::policies::get_max_series_iterations<Policy>())
  327. return boost::math::policies::raise_evaluation_error(function, "Series did not converge, best value is %1%", sum, pol);
  328. --n;
  329. diff = fabs(term / sum);
  330. } while ((diff > boost::math::policies::get_epsilon<T, Policy>()) || (fabs(term_m1) < fabs(term)));
  331. }
  332. if (have_minima && n && summit_location)
  333. {
  334. //
  335. // There are a few terms starting at n == 0 which
  336. // haven't been accounted for yet...
  337. //
  338. unsigned backstop = n;
  339. n = 0;
  340. term = exp(T(-local_scaling));
  341. do
  342. {
  343. sum += term;
  344. //std::cout << n << " " << term << " " << sum << std::endl;
  345. if (fabs(sum) >= upper_limit)
  346. {
  347. sum /= scaling_factor;
  348. term /= scaling_factor;
  349. log_scaling += log_scaling_factor;
  350. }
  351. if (fabs(sum) < lower_limit)
  352. {
  353. sum *= scaling_factor;
  354. term *= scaling_factor;
  355. log_scaling -= log_scaling_factor;
  356. }
  357. //term_m1 = term;
  358. term *= (((a + n) / ((b + n) * (n + 1))) * z);
  359. if (n > boost::math::policies::get_max_series_iterations<Policy>())
  360. return boost::math::policies::raise_evaluation_error(function, "Series did not converge, best value is %1%", sum, pol);
  361. if (++n == backstop)
  362. break; // we've caught up with ourselves.
  363. diff = fabs(term / sum);
  364. } while ((diff > boost::math::policies::get_epsilon<T, Policy>())/* || (fabs(term_m1) < fabs(term))*/);
  365. }
  366. //std::cout << sum << std::endl;
  367. return sum;
  368. }
  369. template <class T, class Policy>
  370. inline T hypergeometric_1F2_generic_series(const T& a, const T& b1, const T& b2, const T& z, const Policy& pol)
  371. {
  372. detail::hypergeometric_pFq_generic_series_term<T, 1u, 2u> s(a, b1, b2, z);
  373. return detail::sum_pFq_series(s, pol);
  374. }
  375. template <class T, class Policy>
  376. inline T hypergeometric_2F0_generic_series(const T& a1, const T& a2, const T& z, const Policy& pol)
  377. {
  378. detail::hypergeometric_pFq_generic_series_term<T, 2u, 0u> s(a1, a2, z);
  379. return detail::sum_pFq_series(s, pol);
  380. }
  381. template <class T, class Policy>
  382. inline T hypergeometric_2F1_generic_series(const T& a1, const T& a2, const T& b, const T& z, const Policy& pol)
  383. {
  384. detail::hypergeometric_pFq_generic_series_term<T, 2u, 1u> s(a1, a2, b, z);
  385. return detail::sum_pFq_series(s, pol);
  386. }
  387. } } } // namespaces
  388. #endif // BOOST_MATH_DETAIL_HYPERGEOMETRIC_SERIES_HPP