hypergeometric_1F1_bessel.hpp 32 KB

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