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