hypergeometric_1F1_bessel.hpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  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_1F1_BESSEL_HPP
  11. #define BOOST_MATH_HYPERGEOMETRIC_1F1_BESSEL_HPP
  12. #include <boost/math/tools/series.hpp>
  13. #include <boost/math/special_functions/bessel.hpp>
  14. #include <boost/math/special_functions/laguerre.hpp>
  15. #include <boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp>
  16. #include <boost/math/special_functions/bessel_iterators.hpp>
  17. namespace boost { namespace math { namespace detail {
  18. template <class T, class Policy>
  19. T hypergeometric_1F1_divergent_fallback(const T& a, const T& b, const T& z, const Policy& pol, long long& log_scaling);
  20. template <class T>
  21. bool hypergeometric_1F1_is_tricomi_viable_positive_b(const T& a, const T& b, const T& z)
  22. {
  23. BOOST_MATH_STD_USING
  24. if ((z < b) && (a > -50))
  25. return false; // might as well fall through to recursion
  26. if (b <= 100)
  27. return true;
  28. // Even though we're in a reasonable domain for Tricomi's approximation,
  29. // the arguments to the Bessel functions may be so large that we can't
  30. // actually evaluate them:
  31. T x = sqrt(fabs(2 * z * b - 4 * a * z));
  32. T v = b - 1;
  33. return log(boost::math::constants::e<T>() * x / (2 * v)) * v > tools::log_min_value<T>();
  34. }
  35. //
  36. // Returns an arbitrarily small value compared to "target" for use as a seed
  37. // value for Bessel recurrences. Note that we'd better not make it too small
  38. // or underflow may occur resulting in either one of the terms in the
  39. // recurrence being zero, or else the result being zero. Using 1/epsilon
  40. // as a safety factor ensures that if we do underflow to zero, all of the digits
  41. // will have been cancelled out anyway:
  42. //
  43. template <class T>
  44. T arbitrary_small_value(const T& target)
  45. {
  46. using std::fabs;
  47. return (tools::min_value<T>() / tools::epsilon<T>()) * (fabs(target) > 1 ? target : 1);
  48. }
  49. template <class T, class Policy>
  50. struct hypergeometric_1F1_AS_13_3_7_tricomi_series
  51. {
  52. typedef T result_type;
  53. enum { cache_size = 64 };
  54. hypergeometric_1F1_AS_13_3_7_tricomi_series(const T& a, const T& b, const T& z, const Policy& pol_)
  55. : A_minus_2(1), A_minus_1(0), A(b / 2), mult(z / 2), term(1), b_minus_1_plus_n(b - 1),
  56. bessel_arg((b / 2 - a) * z),
  57. two_a_minus_b(2 * a - b), pol(pol_), n(2)
  58. {
  59. BOOST_MATH_STD_USING
  60. term /= pow(fabs(bessel_arg), b_minus_1_plus_n / 2);
  61. mult /= sqrt(fabs(bessel_arg));
  62. bessel_cache[cache_size - 1] = bessel_arg > 0 ? boost::math::cyl_bessel_j(b_minus_1_plus_n - 1, 2 * sqrt(bessel_arg), pol) : boost::math::cyl_bessel_i(b_minus_1_plus_n - 1, 2 * sqrt(-bessel_arg), pol);
  63. if (fabs(bessel_cache[cache_size - 1]) < tools::min_value<T>() / tools::epsilon<T>())
  64. {
  65. // We get very limited precision due to rapid denormalisation/underflow of the Bessel values, raise an exception and try something else:
  66. policies::raise_evaluation_error("hypergeometric_1F1_AS_13_3_7_tricomi_series<%1%>", "Underflow in Bessel functions", bessel_cache[cache_size - 1], pol);
  67. }
  68. if ((term * bessel_cache[cache_size - 1] < tools::min_value<T>() / (tools::epsilon<T>() * tools::epsilon<T>())) || !(boost::math::isfinite)(term) || (!std::numeric_limits<T>::has_infinity && (fabs(term) > tools::max_value<T>())))
  69. {
  70. term = -log(fabs(bessel_arg)) * b_minus_1_plus_n / 2;
  71. log_scale = lltrunc(term);
  72. term -= log_scale;
  73. term = exp(term);
  74. }
  75. else
  76. log_scale = 0;
  77. #ifndef BOOST_MATH_NO_CXX17_IF_CONSTEXPR
  78. if constexpr (std::numeric_limits<T>::has_infinity)
  79. {
  80. if (!(boost::math::isfinite)(bessel_cache[cache_size - 1]))
  81. policies::raise_evaluation_error("hypergeometric_1F1_AS_13_3_7_tricomi_series<%1%>", "Expected finite Bessel function result but got %1%", bessel_cache[cache_size - 1], pol);
  82. }
  83. else
  84. if ((boost::math::isnan)(bessel_cache[cache_size - 1]) || (fabs(bessel_cache[cache_size - 1]) >= tools::max_value<T>()))
  85. policies::raise_evaluation_error("hypergeometric_1F1_AS_13_3_7_tricomi_series<%1%>", "Expected finite Bessel function result but got %1%", bessel_cache[cache_size - 1], pol);
  86. #else
  87. if ((std::numeric_limits<T>::has_infinity && !(boost::math::isfinite)(bessel_cache[cache_size - 1]))
  88. || (!std::numeric_limits<T>::has_infinity && ((boost::math::isnan)(bessel_cache[cache_size - 1]) || (fabs(bessel_cache[cache_size - 1]) >= tools::max_value<T>()))))
  89. policies::raise_evaluation_error("hypergeometric_1F1_AS_13_3_7_tricomi_series<%1%>", "Expected finite Bessel function result but got %1%", bessel_cache[cache_size - 1], pol);
  90. #endif
  91. cache_offset = -cache_size;
  92. refill_cache();
  93. }
  94. T operator()()
  95. {
  96. //
  97. // We return the n-2 term, and do 2 terms at once as every other term can be
  98. // very small (or zero) when b == 2a:
  99. //
  100. BOOST_MATH_STD_USING
  101. if(n - 2 - cache_offset >= cache_size)
  102. refill_cache();
  103. T result = A_minus_2 * term * bessel_cache[n - 2 - cache_offset];
  104. term *= mult;
  105. ++n;
  106. T A_next = ((b_minus_1_plus_n + 2) * A_minus_1 + two_a_minus_b * A_minus_2) / n;
  107. b_minus_1_plus_n += 1;
  108. A_minus_2 = A_minus_1;
  109. A_minus_1 = A;
  110. A = A_next;
  111. if (A_minus_2 != 0)
  112. {
  113. if (n - 2 - cache_offset >= cache_size)
  114. refill_cache();
  115. result += A_minus_2 * term * bessel_cache[n - 2 - cache_offset];
  116. }
  117. term *= mult;
  118. ++n;
  119. A_next = ((b_minus_1_plus_n + 2) * A_minus_1 + two_a_minus_b * A_minus_2) / n;
  120. b_minus_1_plus_n += 1;
  121. A_minus_2 = A_minus_1;
  122. A_minus_1 = A;
  123. A = A_next;
  124. return result;
  125. }
  126. long long scale()const
  127. {
  128. return log_scale;
  129. }
  130. private:
  131. T A_minus_2, A_minus_1, A, mult, term, b_minus_1_plus_n, bessel_arg, two_a_minus_b;
  132. std::array<T, cache_size> bessel_cache;
  133. const Policy& pol;
  134. int n, cache_offset;
  135. long long log_scale;
  136. hypergeometric_1F1_AS_13_3_7_tricomi_series operator=(const hypergeometric_1F1_AS_13_3_7_tricomi_series&) = delete;
  137. void refill_cache()
  138. {
  139. BOOST_MATH_STD_USING
  140. //
  141. // We don't calculate a new bessel I/J value: instead start our iterator off
  142. // with an arbitrary small value, then when we get back to the last value in the previous cache
  143. // calculate the ratio and use it to renormalise all the new values. This is more efficient, but
  144. // also avoids problems with J_v(x) or I_v(x) underflowing to zero.
  145. //
  146. cache_offset += cache_size;
  147. T last_value = bessel_cache.back();
  148. T ratio;
  149. if (bessel_arg > 0)
  150. {
  151. //
  152. // We will be calculating Bessel J.
  153. // We need a different recurrence strategy for positive and negative orders:
  154. //
  155. if (b_minus_1_plus_n > 0)
  156. {
  157. bessel_j_backwards_iterator<T, Policy> i(b_minus_1_plus_n + (int)cache_size - 1, 2 * sqrt(bessel_arg), arbitrary_small_value(last_value));
  158. for (int j = cache_size - 1; j >= 0; --j, ++i)
  159. {
  160. bessel_cache[j] = *i;
  161. //
  162. // Depending on the value of bessel_arg, the values stored in the cache can grow so
  163. // large as to overflow, if that looks likely then we need to rescale all the
  164. // existing terms (most of which will then underflow to zero). In this situation
  165. // it's likely that our series will only need 1 or 2 terms of the series but we
  166. // can't be sure of that:
  167. //
  168. if ((j < cache_size - 2) && (tools::max_value<T>() / fabs(64 * bessel_cache[j] / bessel_cache[j + 1]) < fabs(bessel_cache[j])))
  169. {
  170. T rescale = static_cast<T>(pow(fabs(bessel_cache[j] / bessel_cache[j + 1]), T(j + 1)) * 2);
  171. if (!((boost::math::isfinite)(rescale)))
  172. rescale = tools::max_value<T>();
  173. for (int k = j; k < cache_size; ++k)
  174. bessel_cache[k] /= rescale;
  175. bessel_j_backwards_iterator<T, Policy> ti(b_minus_1_plus_n + j, 2 * sqrt(bessel_arg), bessel_cache[j + 1], bessel_cache[j]);
  176. i = ti;
  177. }
  178. }
  179. ratio = last_value / *i;
  180. }
  181. else
  182. {
  183. //
  184. // Negative order is difficult: the J_v(x) recurrence relations are unstable
  185. // *in both directions* for v < 0, except as v -> -INF when we have
  186. // J_-v(x) ~= -sin(pi.v)Y_v(x).
  187. // For small v what we can do is compute every other Bessel function and
  188. // then fill in the gaps using the recurrence relation. This *is* stable
  189. // provided that v is not so negative that the above approximation holds.
  190. //
  191. bessel_cache[1] = cyl_bessel_j(b_minus_1_plus_n + 1, 2 * sqrt(bessel_arg), pol);
  192. bessel_cache[0] = (last_value + bessel_cache[1]) / (b_minus_1_plus_n / sqrt(bessel_arg));
  193. int pos = 2;
  194. while ((pos < cache_size - 1) && (b_minus_1_plus_n + pos < 0))
  195. {
  196. bessel_cache[pos + 1] = cyl_bessel_j(b_minus_1_plus_n + pos + 1, 2 * sqrt(bessel_arg), pol);
  197. bessel_cache[pos] = (bessel_cache[pos-1] + bessel_cache[pos+1]) / ((b_minus_1_plus_n + pos) / sqrt(bessel_arg));
  198. pos += 2;
  199. }
  200. if (pos < cache_size)
  201. {
  202. //
  203. // We have crossed over into the region where backward recursion is the stable direction
  204. // start from arbitrary value and recurse down to "pos" and normalise:
  205. //
  206. bessel_j_backwards_iterator<T, Policy> i2(b_minus_1_plus_n + (int)cache_size - 1, 2 * sqrt(bessel_arg), arbitrary_small_value(bessel_cache[pos-1]));
  207. for (int loc = cache_size - 1; loc >= pos; --loc)
  208. bessel_cache[loc] = *i2++;
  209. ratio = bessel_cache[pos - 1] / *i2;
  210. //
  211. // Sanity check, if we normalised to an unusually small value then it was likely
  212. // to be near a root and the calculated ratio is garbage, if so perform one
  213. // more J_v(x) evaluation at position and normalise again:
  214. //
  215. if (fabs(bessel_cache[pos] * ratio / bessel_cache[pos - 1]) > 5)
  216. ratio = cyl_bessel_j(b_minus_1_plus_n + pos, 2 * sqrt(bessel_arg), pol) / bessel_cache[pos];
  217. while (pos < cache_size)
  218. bessel_cache[pos++] *= ratio;
  219. }
  220. ratio = 1;
  221. }
  222. }
  223. else
  224. {
  225. //
  226. // Bessel I.
  227. // We need a different recurrence strategy for positive and negative orders:
  228. //
  229. if (b_minus_1_plus_n > 0)
  230. {
  231. bessel_i_backwards_iterator<T, Policy> i(b_minus_1_plus_n + (int)cache_size - 1, 2 * sqrt(-bessel_arg), arbitrary_small_value(last_value));
  232. for (int j = cache_size - 1; j >= 0; --j, ++i)
  233. {
  234. bessel_cache[j] = *i;
  235. //
  236. // Depending on the value of bessel_arg, the values stored in the cache can grow so
  237. // large as to overflow, if that looks likely then we need to rescale all the
  238. // existing terms (most of which will then underflow to zero). In this situation
  239. // it's likely that our series will only need 1 or 2 terms of the series but we
  240. // can't be sure of that:
  241. //
  242. if ((j < cache_size - 2) && (tools::max_value<T>() / fabs(64 * bessel_cache[j] / bessel_cache[j + 1]) < fabs(bessel_cache[j])))
  243. {
  244. T rescale = static_cast<T>(pow(fabs(bessel_cache[j] / bessel_cache[j + 1]), T(j + 1)) * 2);
  245. if (!((boost::math::isfinite)(rescale)))
  246. rescale = tools::max_value<T>();
  247. for (int k = j; k < cache_size; ++k)
  248. bessel_cache[k] /= rescale;
  249. i = bessel_i_backwards_iterator<T, Policy>(b_minus_1_plus_n + j, 2 * sqrt(-bessel_arg), bessel_cache[j + 1], bessel_cache[j]);
  250. }
  251. }
  252. ratio = last_value / *i;
  253. }
  254. else
  255. {
  256. //
  257. // Forwards iteration is stable:
  258. //
  259. bessel_i_forwards_iterator<T, Policy> i(b_minus_1_plus_n, 2 * sqrt(-bessel_arg));
  260. int pos = 0;
  261. while ((pos < cache_size) && (b_minus_1_plus_n + pos < 0.5))
  262. {
  263. bessel_cache[pos++] = *i++;
  264. }
  265. if (pos < cache_size)
  266. {
  267. //
  268. // We have crossed over into the region where backward recursion is the stable direction
  269. // start from arbitrary value and recurse down to "pos" and normalise:
  270. //
  271. bessel_i_backwards_iterator<T, Policy> i2(b_minus_1_plus_n + (int)cache_size - 1, 2 * sqrt(-bessel_arg), arbitrary_small_value(last_value));
  272. for (int loc = cache_size - 1; loc >= pos; --loc)
  273. bessel_cache[loc] = *i2++;
  274. ratio = bessel_cache[pos - 1] / *i2;
  275. while (pos < cache_size)
  276. bessel_cache[pos++] *= ratio;
  277. }
  278. ratio = 1;
  279. }
  280. }
  281. if(ratio != 1)
  282. for (auto j = bessel_cache.begin(); j != bessel_cache.end(); ++j)
  283. *j *= ratio;
  284. //
  285. // Very occasionally our normalisation fails because the normalisztion value
  286. // is sitting right on top of a root (or very close to it). When that happens
  287. // best to calculate a fresh Bessel evaluation and normalise again.
  288. //
  289. if (fabs(bessel_cache[0] / last_value) > 5)
  290. {
  291. ratio = (bessel_arg < 0 ? cyl_bessel_i(b_minus_1_plus_n, 2 * sqrt(-bessel_arg), pol) : cyl_bessel_j(b_minus_1_plus_n, 2 * sqrt(bessel_arg), pol)) / bessel_cache[0];
  292. if (ratio != 1)
  293. for (auto j = bessel_cache.begin(); j != bessel_cache.end(); ++j)
  294. *j *= ratio;
  295. }
  296. }
  297. };
  298. template <class T, class Policy>
  299. T hypergeometric_1F1_AS_13_3_7_tricomi(const T& a, const T& b, const T& z, const Policy& pol, long long& log_scale)
  300. {
  301. BOOST_MATH_STD_USING
  302. //
  303. // Works for a < 0, b < 0, z > 0.
  304. //
  305. // For convergence we require A * term to be converging otherwise we get
  306. // a divergent alternating series. It's actually really hard to analyse this
  307. // and the best purely heuristic policy we've found is
  308. // z < fabs((2 * a - b) / (sqrt(fabs(a)))) ; b > 0 or:
  309. // z < fabs((2 * a - b) / (sqrt(fabs(ab)))) ; b < 0
  310. //
  311. T prefix(0);
  312. int prefix_sgn(0);
  313. bool use_logs = false;
  314. long long scale = 0;
  315. //
  316. // We can actually support the b == 2a case within here, but we haven't
  317. // as we appear never to get here in practice. Which means this get out
  318. // clause is a bit of defensive programming....
  319. //
  320. if(b == 2 * a)
  321. return hypergeometric_1F1_divergent_fallback(a, b, z, pol, log_scale);
  322. #ifndef BOOST_MATH_NO_EXCEPTIONS
  323. try
  324. #endif
  325. {
  326. prefix = boost::math::tgamma(b, pol);
  327. prefix *= exp(z / 2);
  328. }
  329. #ifndef BOOST_MATH_NO_EXCEPTIONS
  330. catch (const std::runtime_error&)
  331. {
  332. use_logs = true;
  333. }
  334. #endif
  335. if (use_logs || (prefix == 0) || !(boost::math::isfinite)(prefix) || (!std::numeric_limits<T>::has_infinity && (fabs(prefix) >= tools::max_value<T>())))
  336. {
  337. use_logs = true;
  338. prefix = boost::math::lgamma(b, &prefix_sgn, pol) + z / 2;
  339. scale = lltrunc(prefix);
  340. log_scale += scale;
  341. prefix -= scale;
  342. }
  343. T result(0);
  344. std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  345. bool retry = false;
  346. long long series_scale = 0;
  347. #ifndef BOOST_MATH_NO_EXCEPTIONS
  348. try
  349. #endif
  350. {
  351. hypergeometric_1F1_AS_13_3_7_tricomi_series<T, Policy> s(a, b, z, pol);
  352. series_scale = s.scale();
  353. log_scale += s.scale();
  354. #ifndef BOOST_MATH_NO_EXCEPTIONS
  355. try
  356. #endif
  357. {
  358. T norm = 0;
  359. result = 0;
  360. if((a < 0) && (b < 0))
  361. result = boost::math::tools::checked_sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter, result, norm);
  362. else
  363. result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter, result);
  364. if (!(boost::math::isfinite)(result) || (result == 0) || (!std::numeric_limits<T>::has_infinity && (fabs(result) >= tools::max_value<T>())))
  365. retry = true;
  366. if (norm / fabs(result) > 1 / boost::math::tools::root_epsilon<T>())
  367. retry = true; // fatal cancellation
  368. }
  369. #ifndef BOOST_MATH_NO_EXCEPTIONS
  370. catch (const std::overflow_error&)
  371. {
  372. retry = true;
  373. }
  374. catch (const boost::math::evaluation_error&)
  375. {
  376. retry = true;
  377. }
  378. #endif
  379. }
  380. #ifndef BOOST_MATH_NO_EXCEPTIONS
  381. catch (const std::overflow_error&)
  382. {
  383. log_scale -= scale;
  384. return hypergeometric_1F1_divergent_fallback(a, b, z, pol, log_scale);
  385. }
  386. catch (const boost::math::evaluation_error&)
  387. {
  388. log_scale -= scale;
  389. return hypergeometric_1F1_divergent_fallback(a, b, z, pol, log_scale);
  390. }
  391. #endif
  392. if (retry)
  393. {
  394. log_scale -= scale;
  395. log_scale -= series_scale;
  396. return hypergeometric_1F1_divergent_fallback(a, b, z, pol, log_scale);
  397. }
  398. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1F1_AS_13_3_7<%1%>(%1%,%1%,%1%)", max_iter, pol);
  399. if (use_logs)
  400. {
  401. int sgn = boost::math::sign(result);
  402. prefix += log(fabs(result));
  403. result = sgn * prefix_sgn * exp(prefix);
  404. }
  405. else
  406. {
  407. if ((fabs(result) > 1) && (fabs(prefix) > 1) && (tools::max_value<T>() / fabs(result) < fabs(prefix)))
  408. {
  409. // Overflow:
  410. scale = lltrunc(tools::log_max_value<T>()) - 10;
  411. log_scale += scale;
  412. result /= exp(T(scale));
  413. }
  414. result *= prefix;
  415. }
  416. return result;
  417. }
  418. template <class T>
  419. struct cyl_bessel_i_large_x_sum
  420. {
  421. typedef T result_type;
  422. cyl_bessel_i_large_x_sum(const T& v, const T& x) : v(v), z(x), term(1), k(0) {}
  423. T operator()()
  424. {
  425. T result = term;
  426. ++k;
  427. term *= -(4 * v * v - (2 * k - 1) * (2 * k - 1)) / (8 * k * z);
  428. return result;
  429. }
  430. T v, z, term;
  431. int k;
  432. };
  433. template <class T, class Policy>
  434. T cyl_bessel_i_large_x_scaled(const T& v, const T& x, long long& log_scaling, const Policy& pol)
  435. {
  436. BOOST_MATH_STD_USING
  437. cyl_bessel_i_large_x_sum<T> s(v, x);
  438. std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  439. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  440. boost::math::policies::check_series_iterations<T>("boost::math::cyl_bessel_i_large_x<%1%>(%1%,%1%)", max_iter, pol);
  441. long long scale = boost::math::lltrunc(x);
  442. log_scaling += scale;
  443. return result * exp(x - scale) / sqrt(boost::math::constants::two_pi<T>() * x);
  444. }
  445. template <class T, class Policy>
  446. struct hypergeometric_1F1_AS_13_3_6_series
  447. {
  448. typedef T result_type;
  449. enum { cache_size = 64 };
  450. //
  451. // This series is only convergent/useful for a and b approximately equal
  452. // (ideally |a-b| < 1). The series can also go divergent after a while
  453. // when b < 0, which limits precision to around that of double. In that
  454. // situation we return 0 to terminate the series as otherwise the divergent
  455. // terms will destroy all the bits in our result before they do eventually
  456. // converge again. One important use case for this series is for z < 0
  457. // and |a| << |b| so that either b-a == b or at least most of the digits in a
  458. // are lost in the subtraction. Note that while you can easily convince yourself
  459. // that the result should be unity when b-a == b, in fact this is not (quite)
  460. // the case for large z.
  461. //
  462. hypergeometric_1F1_AS_13_3_6_series(const T& a, const T& b, const T& z, const T& b_minus_a, const Policy& pol_)
  463. : b_minus_a(b_minus_a), half_z(z / 2), poch_1(2 * b_minus_a - 1), poch_2(b_minus_a - a), b_poch(b), term(1), last_result(1), sign(1), n(0), cache_offset(-cache_size), scale(0), pol(pol_)
  464. {
  465. bessel_i_cache[cache_size - 1] = half_z > tools::log_max_value<T>() ?
  466. cyl_bessel_i_large_x_scaled(T(b_minus_a - 1.5f), half_z, scale, pol) : boost::math::cyl_bessel_i(b_minus_a - 1.5f, half_z, pol);
  467. refill_cache();
  468. }
  469. T operator()()
  470. {
  471. BOOST_MATH_STD_USING
  472. if(n - cache_offset >= cache_size)
  473. refill_cache();
  474. T result = term * (b_minus_a - 0.5f + n) * sign * bessel_i_cache[n - cache_offset];
  475. ++n;
  476. term *= poch_1;
  477. poch_1 = (n == 1) ? T(2 * b_minus_a) : T(poch_1 + 1);
  478. term *= poch_2;
  479. poch_2 += 1;
  480. term /= n;
  481. term /= b_poch;
  482. b_poch += 1;
  483. sign = -sign;
  484. if ((fabs(result) > fabs(last_result)) && (n > 100))
  485. return 0; // series has gone divergent!
  486. last_result = result;
  487. return result;
  488. }
  489. long long scaling()const
  490. {
  491. return scale;
  492. }
  493. private:
  494. T b_minus_a, half_z, poch_1, poch_2, b_poch, term, last_result;
  495. int sign;
  496. int n, cache_offset;
  497. long long scale;
  498. const Policy& pol;
  499. std::array<T, cache_size> bessel_i_cache;
  500. void refill_cache()
  501. {
  502. BOOST_MATH_STD_USING
  503. //
  504. // We don't calculate a new bessel I value: instead start our iterator off
  505. // with an arbitrary small value, then when we get back to the last value in the previous cache
  506. // calculate the ratio and use it to renormalise all the values. This is more efficient, but
  507. // also avoids problems with I_v(x) underflowing to zero.
  508. //
  509. cache_offset += cache_size;
  510. T last_value = bessel_i_cache.back();
  511. bessel_i_backwards_iterator<T, Policy> i(b_minus_a + cache_offset + (int)cache_size - 1.5f, half_z, tools::min_value<T>() * (fabs(last_value) > 1 ? last_value : 1) / tools::epsilon<T>());
  512. for (int j = cache_size - 1; j >= 0; --j, ++i)
  513. {
  514. bessel_i_cache[j] = *i;
  515. //
  516. // Depending on the value of half_z, the values stored in the cache can grow so
  517. // large as to overflow, if that looks likely then we need to rescale all the
  518. // existing terms (most of which will then underflow to zero). In this situation
  519. // it's likely that our series will only need 1 or 2 terms of the series but we
  520. // can't be sure of that:
  521. //
  522. if((j < cache_size - 2) && (bessel_i_cache[j + 1] != 0) && (tools::max_value<T>() / fabs(64 * bessel_i_cache[j] / bessel_i_cache[j + 1]) < fabs(bessel_i_cache[j])))
  523. {
  524. T rescale = static_cast<T>(pow(fabs(bessel_i_cache[j] / bessel_i_cache[j + 1]), T(j + 1)) * 2);
  525. if (rescale > tools::max_value<T>())
  526. rescale = tools::max_value<T>();
  527. for (int k = j; k < cache_size; ++k)
  528. bessel_i_cache[k] /= rescale;
  529. i = bessel_i_backwards_iterator<T, Policy>(b_minus_a -0.5f + cache_offset + j, half_z, bessel_i_cache[j + 1], bessel_i_cache[j]);
  530. }
  531. }
  532. T ratio = last_value / *i;
  533. for (auto j = bessel_i_cache.begin(); j != bessel_i_cache.end(); ++j)
  534. *j *= ratio;
  535. }
  536. hypergeometric_1F1_AS_13_3_6_series() = delete;
  537. hypergeometric_1F1_AS_13_3_6_series(const hypergeometric_1F1_AS_13_3_6_series&) = delete;
  538. hypergeometric_1F1_AS_13_3_6_series& operator=(const hypergeometric_1F1_AS_13_3_6_series&) = delete;
  539. };
  540. template <class T, class Policy>
  541. T hypergeometric_1F1_AS_13_3_6(const T& a, const T& b, const T& z, const T& b_minus_a, const Policy& pol, long long& log_scaling)
  542. {
  543. BOOST_MATH_STD_USING
  544. if(b_minus_a == 0)
  545. {
  546. // special case: M(a,a,z) == exp(z)
  547. long long scale = lltrunc(z, pol);
  548. log_scaling += scale;
  549. return exp(z - scale);
  550. }
  551. hypergeometric_1F1_AS_13_3_6_series<T, Policy> s(a, b, z, b_minus_a, pol);
  552. std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  553. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  554. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1F1_AS_13_3_6<%1%>(%1%,%1%,%1%)", max_iter, pol);
  555. result *= boost::math::tgamma(b_minus_a - 0.5f, pol) * pow(z / 4, -b_minus_a + T(0.5f));
  556. long long scale = lltrunc(z / 2);
  557. log_scaling += scale;
  558. log_scaling += s.scaling();
  559. result *= exp(z / 2 - scale);
  560. return result;
  561. }
  562. /****************************************************************************************************************/
  563. //
  564. // The following are not used at present and are commented out for that reason:
  565. //
  566. /****************************************************************************************************************/
  567. #if 0
  568. template <class T, class Policy>
  569. struct hypergeometric_1F1_AS_13_3_8_series
  570. {
  571. //
  572. // TODO: store and cache Bessel function evaluations via backwards recurrence.
  573. //
  574. // The C term grows by at least an order of magnitude with each iteration, and
  575. // rate of growth is largely independent of the arguments. Free parameter h
  576. // seems to give accurate results for small values (almost zero) or h=1.
  577. // Convergence and accuracy, only when -a/z > 100, this appears to have no
  578. // or little benefit over 13.3.7 as it generally requires more iterations?
  579. //
  580. hypergeometric_1F1_AS_13_3_8_series(const T& a, const T& b, const T& z, const T& h, const Policy& pol_)
  581. : C_minus_2(1), C_minus_1(-b * h), C(b * (b + 1) * h * h / 2 - (2 * h - 1) * a / 2),
  582. bessel_arg(2 * sqrt(-a * z)), bessel_order(b - 1), power_term(std::pow(-a * z, (1 - b) / 2)), mult(z / std::sqrt(-a * z)),
  583. a_(a), b_(b), z_(z), h_(h), n(2), pol(pol_)
  584. {
  585. }
  586. T operator()()
  587. {
  588. // we actually return the n-2 term:
  589. T result = C_minus_2 * power_term * boost::math::cyl_bessel_j(bessel_order, bessel_arg, pol);
  590. bessel_order += 1;
  591. power_term *= mult;
  592. ++n;
  593. T C_next = ((1 - 2 * h_) * (n - 1) - b_ * h_) * C
  594. + ((1 - 2 * h_) * a_ - h_ * (h_ - 1) *(b_ + n - 2)) * C_minus_1
  595. - h_ * (h_ - 1) * a_ * C_minus_2;
  596. C_next /= n;
  597. C_minus_2 = C_minus_1;
  598. C_minus_1 = C;
  599. C = C_next;
  600. return result;
  601. }
  602. T C, C_minus_1, C_minus_2, bessel_arg, bessel_order, power_term, mult, a_, b_, z_, h_;
  603. const Policy& pol;
  604. int n;
  605. typedef T result_type;
  606. };
  607. template <class T, class Policy>
  608. T hypergeometric_1F1_AS_13_3_8(const T& a, const T& b, const T& z, const T& h, const Policy& pol)
  609. {
  610. BOOST_MATH_STD_USING
  611. T prefix = exp(h * z) * boost::math::tgamma(b);
  612. hypergeometric_1F1_AS_13_3_8_series<T, Policy> s(a, b, z, h, pol);
  613. std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  614. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  615. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1F1_AS_13_3_8<%1%>(%1%,%1%,%1%)", max_iter, pol);
  616. result *= prefix;
  617. return result;
  618. }
  619. //
  620. // This is the series from https://dlmf.nist.gov/13.11
  621. // It appears to be unusable for a,z < 0, and for
  622. // b < 0 appears to never be better than the defining series
  623. // for 1F1.
  624. //
  625. template <class T, class Policy>
  626. struct hypergeometric_1f1_13_11_1_series
  627. {
  628. typedef T result_type;
  629. hypergeometric_1f1_13_11_1_series(const T& a, const T& b, const T& z, const Policy& pol_)
  630. : term(1), two_a_minus_1_plus_s(2 * a - 1), two_a_minus_b_plus_s(2 * a - b), b_plus_s(b), a_minus_half_plus_s(a - 0.5f), half_z(z / 2), s(0), pol(pol_)
  631. {
  632. }
  633. T operator()()
  634. {
  635. T result = term * a_minus_half_plus_s * boost::math::cyl_bessel_i(a_minus_half_plus_s, half_z, pol);
  636. term *= two_a_minus_1_plus_s * two_a_minus_b_plus_s / (b_plus_s * ++s);
  637. two_a_minus_1_plus_s += 1;
  638. a_minus_half_plus_s += 1;
  639. two_a_minus_b_plus_s += 1;
  640. b_plus_s += 1;
  641. return result;
  642. }
  643. T term, two_a_minus_1_plus_s, two_a_minus_b_plus_s, b_plus_s, a_minus_half_plus_s, half_z;
  644. long long s;
  645. const Policy& pol;
  646. };
  647. template <class T, class Policy>
  648. T hypergeometric_1f1_13_11_1(const T& a, const T& b, const T& z, const Policy& pol, long long& log_scaling)
  649. {
  650. BOOST_MATH_STD_USING
  651. bool use_logs = false;
  652. T prefix;
  653. int prefix_sgn = 1;
  654. if (true/*(a < boost::math::max_factorial<T>::value) && (a > 0)*/)
  655. prefix = boost::math::tgamma(a - 0.5f, pol);
  656. else
  657. {
  658. prefix = boost::math::lgamma(a - 0.5f, &prefix_sgn, pol);
  659. use_logs = true;
  660. }
  661. if (use_logs)
  662. {
  663. prefix += z / 2;
  664. prefix += log(z / 4) * (0.5f - a);
  665. }
  666. else if (z > 0)
  667. {
  668. prefix *= pow(z / 4, 0.5f - a);
  669. prefix *= exp(z / 2);
  670. }
  671. else
  672. {
  673. prefix *= exp(z / 2);
  674. prefix *= pow(z / 4, 0.5f - a);
  675. }
  676. hypergeometric_1f1_13_11_1_series<T, Policy> s(a, b, z, pol);
  677. std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  678. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  679. boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1f1_13_11_1<%1%>(%1%,%1%,%1%)", max_iter, pol);
  680. if (use_logs)
  681. {
  682. long long scaling = lltrunc(prefix);
  683. log_scaling += scaling;
  684. prefix -= scaling;
  685. result *= exp(prefix) * prefix_sgn;
  686. }
  687. else
  688. result *= prefix;
  689. return result;
  690. }
  691. #endif
  692. } } } // namespaces
  693. #endif // BOOST_MATH_HYPERGEOMETRIC_1F1_BESSEL_HPP