airy.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. // Copyright John Maddock 2012.
  2. // Copyright Matt Borland 2024.
  3. // Use, modification and distribution are subject to the
  4. // Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt
  6. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. #ifndef BOOST_MATH_AIRY_HPP
  8. #define BOOST_MATH_AIRY_HPP
  9. #include <boost/math/tools/config.hpp>
  10. #include <boost/math/tools/numeric_limits.hpp>
  11. #include <boost/math/tools/precision.hpp>
  12. #include <boost/math/tools/cstdint.hpp>
  13. #include <boost/math/special_functions/math_fwd.hpp>
  14. #include <boost/math/special_functions/bessel.hpp>
  15. #include <boost/math/special_functions/cbrt.hpp>
  16. #include <boost/math/special_functions/detail/airy_ai_bi_zero.hpp>
  17. #include <boost/math/tools/roots.hpp>
  18. #include <boost/math/policies/error_handling.hpp>
  19. #include <boost/math/constants/constants.hpp>
  20. namespace boost{ namespace math{
  21. namespace detail{
  22. template <class T, class Policy>
  23. BOOST_MATH_GPU_ENABLED T airy_ai_imp(T x, const Policy& pol)
  24. {
  25. BOOST_MATH_STD_USING
  26. if(x < 0)
  27. {
  28. T p = (-x * sqrt(-x) * 2) / 3;
  29. T v = T(1) / 3;
  30. T j1 = boost::math::cyl_bessel_j(v, p, pol);
  31. T j2 = boost::math::cyl_bessel_j(-v, p, pol);
  32. T ai = sqrt(-x) * (j1 + j2) / 3;
  33. //T bi = sqrt(-x / 3) * (j2 - j1);
  34. return ai;
  35. }
  36. else if(fabs(x * x * x) / 6 < tools::epsilon<T>())
  37. {
  38. T tg = boost::math::tgamma(constants::twothirds<T>(), pol);
  39. T ai = 1 / (pow(T(3), constants::twothirds<T>()) * tg);
  40. //T bi = 1 / (sqrt(boost::math::cbrt(T(3))) * tg);
  41. return ai;
  42. }
  43. else
  44. {
  45. T p = 2 * x * sqrt(x) / 3;
  46. T v = T(1) / 3;
  47. //T j1 = boost::math::cyl_bessel_i(-v, p, pol);
  48. //T j2 = boost::math::cyl_bessel_i(v, p, pol);
  49. //
  50. // Note that although we can calculate ai from j1 and j2, the accuracy is horrible
  51. // as we're subtracting two very large values, so use the Bessel K relation instead:
  52. //
  53. T ai = cyl_bessel_k(v, p, pol) * sqrt(x / 3) / boost::math::constants::pi<T>(); //sqrt(x) * (j1 - j2) / 3;
  54. //T bi = sqrt(x / 3) * (j1 + j2);
  55. return ai;
  56. }
  57. }
  58. template <class T, class Policy>
  59. BOOST_MATH_GPU_ENABLED T airy_bi_imp(T x, const Policy& pol)
  60. {
  61. BOOST_MATH_STD_USING
  62. if(x < 0)
  63. {
  64. T p = (-x * sqrt(-x) * 2) / 3;
  65. T v = T(1) / 3;
  66. T j1 = boost::math::cyl_bessel_j(v, p, pol);
  67. T j2 = boost::math::cyl_bessel_j(-v, p, pol);
  68. //T ai = sqrt(-x) * (j1 + j2) / 3;
  69. T bi = sqrt(-x / 3) * (j2 - j1);
  70. return bi;
  71. }
  72. else if(fabs(x * x * x) / 6 < tools::epsilon<T>())
  73. {
  74. T tg = boost::math::tgamma(constants::twothirds<T>(), pol);
  75. //T ai = 1 / (pow(T(3), constants::twothirds<T>()) * tg);
  76. T bi = 1 / (sqrt(boost::math::cbrt(T(3), pol)) * tg);
  77. return bi;
  78. }
  79. else
  80. {
  81. T p = 2 * x * sqrt(x) / 3;
  82. T v = T(1) / 3;
  83. T j1 = boost::math::cyl_bessel_i(-v, p, pol);
  84. T j2 = boost::math::cyl_bessel_i(v, p, pol);
  85. T bi = sqrt(x / 3) * (j1 + j2);
  86. return bi;
  87. }
  88. }
  89. template <class T, class Policy>
  90. BOOST_MATH_GPU_ENABLED T airy_ai_prime_imp(T x, const Policy& pol)
  91. {
  92. BOOST_MATH_STD_USING
  93. if(x < 0)
  94. {
  95. T p = (-x * sqrt(-x) * 2) / 3;
  96. T v = T(2) / 3;
  97. T j1 = boost::math::cyl_bessel_j(v, p, pol);
  98. T j2 = boost::math::cyl_bessel_j(-v, p, pol);
  99. T aip = -x * (j1 - j2) / 3;
  100. return aip;
  101. }
  102. else if(fabs(x * x) / 2 < tools::epsilon<T>())
  103. {
  104. T tg = boost::math::tgamma(constants::third<T>(), pol);
  105. T aip = 1 / (boost::math::cbrt(T(3), pol) * tg);
  106. return -aip;
  107. }
  108. else
  109. {
  110. T p = 2 * x * sqrt(x) / 3;
  111. T v = T(2) / 3;
  112. //T j1 = boost::math::cyl_bessel_i(-v, p, pol);
  113. //T j2 = boost::math::cyl_bessel_i(v, p, pol);
  114. //
  115. // Note that although we can calculate ai from j1 and j2, the accuracy is horrible
  116. // as we're subtracting two very large values, so use the Bessel K relation instead:
  117. //
  118. T aip = -cyl_bessel_k(v, p, pol) * x / (boost::math::constants::root_three<T>() * boost::math::constants::pi<T>());
  119. return aip;
  120. }
  121. }
  122. template <class T, class Policy>
  123. BOOST_MATH_GPU_ENABLED T airy_bi_prime_imp(T x, const Policy& pol)
  124. {
  125. BOOST_MATH_STD_USING
  126. if(x < 0)
  127. {
  128. T p = (-x * sqrt(-x) * 2) / 3;
  129. T v = T(2) / 3;
  130. T j1 = boost::math::cyl_bessel_j(v, p, pol);
  131. T j2 = boost::math::cyl_bessel_j(-v, p, pol);
  132. T aip = -x * (j1 + j2) / constants::root_three<T>();
  133. return aip;
  134. }
  135. else if(fabs(x * x) / 2 < tools::epsilon<T>())
  136. {
  137. T tg = boost::math::tgamma(constants::third<T>(), pol);
  138. T bip = sqrt(boost::math::cbrt(T(3), pol)) / tg;
  139. return bip;
  140. }
  141. else
  142. {
  143. T p = 2 * x * sqrt(x) / 3;
  144. T v = T(2) / 3;
  145. T j1 = boost::math::cyl_bessel_i(-v, p, pol);
  146. T j2 = boost::math::cyl_bessel_i(v, p, pol);
  147. T aip = x * (j1 + j2) / boost::math::constants::root_three<T>();
  148. return aip;
  149. }
  150. }
  151. template <class T, class Policy>
  152. BOOST_MATH_GPU_ENABLED T airy_ai_zero_imp(int m, const Policy& pol)
  153. {
  154. BOOST_MATH_STD_USING // ADL of std names, needed for log, sqrt.
  155. // Handle cases when a negative zero (negative rank) is requested.
  156. if(m < 0)
  157. {
  158. return policies::raise_domain_error<T>("boost::math::airy_ai_zero<%1%>(%1%, int)",
  159. "Requested the %1%'th zero, but the rank must be 1 or more !", static_cast<T>(m), pol);
  160. }
  161. // Handle case when the zero'th zero is requested.
  162. if(m == 0U)
  163. {
  164. return policies::raise_domain_error<T>("boost::math::airy_ai_zero<%1%>(%1%,%1%)",
  165. "The requested rank of the zero is %1%, but must be 1 or more !", static_cast<T>(m), pol);
  166. }
  167. // Set up the initial guess for the upcoming root-finding.
  168. const T guess_root = boost::math::detail::airy_zero::airy_ai_zero_detail::initial_guess<T>(m, pol);
  169. // Select the maximum allowed iterations based on the number
  170. // of decimal digits in the numeric type T, being at least 12.
  171. const int my_digits10 = static_cast<int>(static_cast<float>(policies::digits<T, Policy>() * 0.301F));
  172. const std::uintmax_t iterations_allowed = static_cast<std::uintmax_t>(BOOST_MATH_GPU_SAFE_MAX(12, my_digits10 * 2));
  173. std::uintmax_t iterations_used = iterations_allowed;
  174. // Use a dynamic tolerance because the roots get closer the higher m gets.
  175. T tolerance; // LCOV_EXCL_LINE
  176. if (m <= 10) { tolerance = T(0.3F); }
  177. else if(m <= 100) { tolerance = T(0.1F); }
  178. else if(m <= 1000) { tolerance = T(0.05F); }
  179. else { tolerance = T(1) / sqrt(T(m)); }
  180. // Perform the root-finding using Newton-Raphson iteration from Boost.Math.
  181. const T am =
  182. boost::math::tools::newton_raphson_iterate(
  183. boost::math::detail::airy_zero::airy_ai_zero_detail::function_object_ai_and_ai_prime<T, Policy>(pol),
  184. guess_root,
  185. T(guess_root - tolerance),
  186. T(guess_root + tolerance),
  187. policies::digits<T, Policy>(),
  188. iterations_used);
  189. static_cast<void>(iterations_used);
  190. return am;
  191. }
  192. template <class T, class Policy>
  193. BOOST_MATH_GPU_ENABLED T airy_bi_zero_imp(int m, const Policy& pol)
  194. {
  195. BOOST_MATH_STD_USING // ADL of std names, needed for log, sqrt.
  196. // Handle cases when a negative zero (negative rank) is requested.
  197. if(m < 0)
  198. {
  199. return policies::raise_domain_error<T>("boost::math::airy_bi_zero<%1%>(%1%, int)",
  200. "Requested the %1%'th zero, but the rank must 1 or more !", static_cast<T>(m), pol);
  201. }
  202. // Handle case when the zero'th zero is requested.
  203. if(m == 0U)
  204. {
  205. return policies::raise_domain_error<T>("boost::math::airy_bi_zero<%1%>(%1%,%1%)",
  206. "The requested rank of the zero is %1%, but must be 1 or more !", static_cast<T>(m), pol);
  207. }
  208. // Set up the initial guess for the upcoming root-finding.
  209. const T guess_root = boost::math::detail::airy_zero::airy_bi_zero_detail::initial_guess<T>(m, pol);
  210. // Select the maximum allowed iterations based on the number
  211. // of decimal digits in the numeric type T, being at least 12.
  212. const int my_digits10 = static_cast<int>(static_cast<float>(policies::digits<T, Policy>() * 0.301F));
  213. const std::uintmax_t iterations_allowed = static_cast<std::uintmax_t>(BOOST_MATH_GPU_SAFE_MAX(12, my_digits10 * 2));
  214. std::uintmax_t iterations_used = iterations_allowed;
  215. // Use a dynamic tolerance because the roots get closer the higher m gets.
  216. T tolerance; // LCOV_EXCL_LINE
  217. if (m <= 10) { tolerance = T(0.3F); }
  218. else if(m <= 100) { tolerance = T(0.1F); }
  219. else if(m <= 1000) { tolerance = T(0.05F); }
  220. else { tolerance = T(1) / sqrt(T(m)); }
  221. // Perform the root-finding using Newton-Raphson iteration from Boost.Math.
  222. const T bm =
  223. boost::math::tools::newton_raphson_iterate(
  224. boost::math::detail::airy_zero::airy_bi_zero_detail::function_object_bi_and_bi_prime<T, Policy>(pol),
  225. guess_root,
  226. T(guess_root - tolerance),
  227. T(guess_root + tolerance),
  228. policies::digits<T, Policy>(),
  229. iterations_used);
  230. static_cast<void>(iterations_used);
  231. return bm;
  232. }
  233. } // namespace detail
  234. template <class T, class Policy>
  235. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type airy_ai(T x, const Policy&)
  236. {
  237. BOOST_FPU_EXCEPTION_GUARD
  238. typedef typename tools::promote_args<T>::type result_type;
  239. typedef typename policies::evaluation<result_type, Policy>::type value_type;
  240. typedef typename policies::normalise<
  241. Policy,
  242. policies::promote_float<false>,
  243. policies::promote_double<false>,
  244. policies::discrete_quantile<>,
  245. policies::assert_undefined<> >::type forwarding_policy;
  246. return policies::checked_narrowing_cast<result_type, Policy>(detail::airy_ai_imp<value_type>(static_cast<value_type>(x), forwarding_policy()), "boost::math::airy<%1%>(%1%)");
  247. }
  248. template <class T>
  249. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type airy_ai(T x)
  250. {
  251. return airy_ai(x, policies::policy<>());
  252. }
  253. template <class T, class Policy>
  254. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type airy_bi(T x, const Policy&)
  255. {
  256. BOOST_FPU_EXCEPTION_GUARD
  257. typedef typename tools::promote_args<T>::type result_type;
  258. typedef typename policies::evaluation<result_type, Policy>::type value_type;
  259. typedef typename policies::normalise<
  260. Policy,
  261. policies::promote_float<false>,
  262. policies::promote_double<false>,
  263. policies::discrete_quantile<>,
  264. policies::assert_undefined<> >::type forwarding_policy;
  265. return policies::checked_narrowing_cast<result_type, Policy>(detail::airy_bi_imp<value_type>(static_cast<value_type>(x), forwarding_policy()), "boost::math::airy<%1%>(%1%)");
  266. }
  267. template <class T>
  268. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type airy_bi(T x)
  269. {
  270. return airy_bi(x, policies::policy<>());
  271. }
  272. template <class T, class Policy>
  273. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type airy_ai_prime(T x, const Policy&)
  274. {
  275. BOOST_FPU_EXCEPTION_GUARD
  276. typedef typename tools::promote_args<T>::type result_type;
  277. typedef typename policies::evaluation<result_type, Policy>::type value_type;
  278. typedef typename policies::normalise<
  279. Policy,
  280. policies::promote_float<false>,
  281. policies::promote_double<false>,
  282. policies::discrete_quantile<>,
  283. policies::assert_undefined<> >::type forwarding_policy;
  284. return policies::checked_narrowing_cast<result_type, Policy>(detail::airy_ai_prime_imp<value_type>(static_cast<value_type>(x), forwarding_policy()), "boost::math::airy<%1%>(%1%)");
  285. }
  286. template <class T>
  287. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type airy_ai_prime(T x)
  288. {
  289. return airy_ai_prime(x, policies::policy<>());
  290. }
  291. template <class T, class Policy>
  292. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type airy_bi_prime(T x, const Policy&)
  293. {
  294. BOOST_FPU_EXCEPTION_GUARD
  295. typedef typename tools::promote_args<T>::type result_type;
  296. typedef typename policies::evaluation<result_type, Policy>::type value_type;
  297. typedef typename policies::normalise<
  298. Policy,
  299. policies::promote_float<false>,
  300. policies::promote_double<false>,
  301. policies::discrete_quantile<>,
  302. policies::assert_undefined<> >::type forwarding_policy;
  303. return policies::checked_narrowing_cast<result_type, Policy>(detail::airy_bi_prime_imp<value_type>(static_cast<value_type>(x), forwarding_policy()), "boost::math::airy<%1%>(%1%)");
  304. }
  305. template <class T>
  306. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type airy_bi_prime(T x)
  307. {
  308. return airy_bi_prime(x, policies::policy<>());
  309. }
  310. template <class T, class Policy>
  311. BOOST_MATH_GPU_ENABLED inline T airy_ai_zero(int m, const Policy& /*pol*/)
  312. {
  313. BOOST_FPU_EXCEPTION_GUARD
  314. typedef typename policies::evaluation<T, Policy>::type value_type;
  315. typedef typename policies::normalise<
  316. Policy,
  317. policies::promote_float<false>,
  318. policies::promote_double<false>,
  319. policies::discrete_quantile<>,
  320. policies::assert_undefined<> >::type forwarding_policy;
  321. static_assert( false == std::numeric_limits<T>::is_specialized
  322. || ( true == std::numeric_limits<T>::is_specialized
  323. && false == std::numeric_limits<T>::is_integer),
  324. "Airy value type must be a floating-point type.");
  325. return policies::checked_narrowing_cast<T, Policy>(detail::airy_ai_zero_imp<value_type>(m, forwarding_policy()), "boost::math::airy_ai_zero<%1%>(unsigned)");
  326. }
  327. template <class T>
  328. BOOST_MATH_GPU_ENABLED inline T airy_ai_zero(int m)
  329. {
  330. return airy_ai_zero<T>(m, policies::policy<>());
  331. }
  332. template <class T, class OutputIterator, class Policy>
  333. BOOST_MATH_GPU_ENABLED inline OutputIterator airy_ai_zero(
  334. int start_index,
  335. unsigned number_of_zeros,
  336. OutputIterator out_it,
  337. const Policy& pol)
  338. {
  339. typedef T result_type;
  340. static_assert( false == std::numeric_limits<T>::is_specialized
  341. || ( true == std::numeric_limits<T>::is_specialized
  342. && false == std::numeric_limits<T>::is_integer),
  343. "Airy value type must be a floating-point type.");
  344. for(unsigned i = 0; i < number_of_zeros; ++i)
  345. {
  346. *out_it = boost::math::airy_ai_zero<result_type>(start_index + i, pol);
  347. ++out_it;
  348. }
  349. return out_it;
  350. }
  351. template <class T, class OutputIterator>
  352. BOOST_MATH_GPU_ENABLED inline OutputIterator airy_ai_zero(
  353. int start_index,
  354. unsigned number_of_zeros,
  355. OutputIterator out_it)
  356. {
  357. return airy_ai_zero<T>(start_index, number_of_zeros, out_it, policies::policy<>());
  358. }
  359. template <class T, class Policy>
  360. BOOST_MATH_GPU_ENABLED inline T airy_bi_zero(int m, const Policy& /*pol*/)
  361. {
  362. BOOST_FPU_EXCEPTION_GUARD
  363. typedef typename policies::evaluation<T, Policy>::type value_type;
  364. typedef typename policies::normalise<
  365. Policy,
  366. policies::promote_float<false>,
  367. policies::promote_double<false>,
  368. policies::discrete_quantile<>,
  369. policies::assert_undefined<> >::type forwarding_policy;
  370. static_assert( false == std::numeric_limits<T>::is_specialized
  371. || ( true == std::numeric_limits<T>::is_specialized
  372. && false == std::numeric_limits<T>::is_integer),
  373. "Airy value type must be a floating-point type.");
  374. return policies::checked_narrowing_cast<T, Policy>(detail::airy_bi_zero_imp<value_type>(m, forwarding_policy()), "boost::math::airy_bi_zero<%1%>(unsigned)");
  375. }
  376. template <typename T>
  377. BOOST_MATH_GPU_ENABLED inline T airy_bi_zero(int m)
  378. {
  379. return airy_bi_zero<T>(m, policies::policy<>());
  380. }
  381. template <class T, class OutputIterator, class Policy>
  382. BOOST_MATH_GPU_ENABLED inline OutputIterator airy_bi_zero(
  383. int start_index,
  384. unsigned number_of_zeros,
  385. OutputIterator out_it,
  386. const Policy& pol)
  387. {
  388. typedef T result_type;
  389. static_assert( false == std::numeric_limits<T>::is_specialized
  390. || ( true == std::numeric_limits<T>::is_specialized
  391. && false == std::numeric_limits<T>::is_integer),
  392. "Airy value type must be a floating-point type.");
  393. for(unsigned i = 0; i < number_of_zeros; ++i)
  394. {
  395. *out_it = boost::math::airy_bi_zero<result_type>(start_index + i, pol);
  396. ++out_it;
  397. }
  398. return out_it;
  399. }
  400. template <class T, class OutputIterator>
  401. BOOST_MATH_GPU_ENABLED inline OutputIterator airy_bi_zero(
  402. int start_index,
  403. unsigned number_of_zeros,
  404. OutputIterator out_it)
  405. {
  406. return airy_bi_zero<T>(start_index, number_of_zeros, out_it, policies::policy<>());
  407. }
  408. }} // namespaces
  409. #endif // BOOST_MATH_AIRY_HPP