bessel_jy_derivatives_series.hpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // Copyright (c) 2013 Anton Bikineev
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_BESSEL_JY_DERIVATIVES_SERIES_HPP
  6. #define BOOST_MATH_BESSEL_JY_DERIVATIVES_SERIES_HPP
  7. #ifdef _MSC_VER
  8. #pragma once
  9. #endif
  10. #include <cmath>
  11. #include <cstdint>
  12. namespace boost{ namespace math{ namespace detail{
  13. template <class T, class Policy>
  14. struct bessel_j_derivative_small_z_series_term
  15. {
  16. typedef T result_type;
  17. bessel_j_derivative_small_z_series_term(T v_, T x)
  18. : N(0), v(v_), term(1), mult(x / 2)
  19. {
  20. mult *= -mult;
  21. // iterate if v == 0; otherwise result of
  22. // first term is 0 and tools::sum_series stops
  23. if (v == 0)
  24. iterate();
  25. }
  26. T operator()()
  27. {
  28. T r = term * (v + 2 * N);
  29. iterate();
  30. return r;
  31. }
  32. private:
  33. void iterate()
  34. {
  35. ++N;
  36. term *= mult / (N * (N + v));
  37. }
  38. unsigned N;
  39. T v;
  40. T term;
  41. T mult;
  42. };
  43. //
  44. // Series evaluation for BesselJ'(v, z) as z -> 0.
  45. // It's derivative of http://functions.wolfram.com/Bessel-TypeFunctions/BesselJ/06/01/04/01/01/0003/
  46. // Converges rapidly for all z << v.
  47. //
  48. template <class T, class Policy>
  49. inline T bessel_j_derivative_small_z_series(T v, T x, const Policy& pol)
  50. {
  51. BOOST_MATH_STD_USING
  52. T prefix;
  53. if (v < boost::math::max_factorial<T>::value)
  54. {
  55. prefix = pow(x / 2, v - 1) / 2 / boost::math::tgamma(v + 1, pol);
  56. }
  57. else
  58. {
  59. prefix = (v - 1) * log(x / 2) - constants::ln_two<T>() - boost::math::lgamma(v + 1, pol);
  60. prefix = exp(prefix);
  61. }
  62. if (0 == prefix)
  63. return prefix;
  64. bessel_j_derivative_small_z_series_term<T, Policy> s(v, x);
  65. std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  66. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  67. boost::math::policies::check_series_iterations<T>("boost::math::bessel_j_derivative_small_z_series<%1%>(%1%,%1%)", max_iter, pol);
  68. return prefix * result;
  69. }
  70. template <class T, class Policy>
  71. struct bessel_y_derivative_small_z_series_term_a
  72. {
  73. typedef T result_type;
  74. bessel_y_derivative_small_z_series_term_a(T v_, T x)
  75. : N(0), v(v_)
  76. {
  77. mult = x / 2;
  78. mult *= -mult;
  79. term = 1;
  80. }
  81. T operator()()
  82. {
  83. T r = term * (-v + 2 * N);
  84. ++N;
  85. term *= mult / (N * (N - v));
  86. return r;
  87. }
  88. private:
  89. unsigned N;
  90. T v;
  91. T mult;
  92. T term;
  93. };
  94. template <class T, class Policy>
  95. struct bessel_y_derivative_small_z_series_term_b
  96. {
  97. typedef T result_type;
  98. bessel_y_derivative_small_z_series_term_b(T v_, T x)
  99. : N(0), v(v_)
  100. {
  101. mult = x / 2;
  102. mult *= -mult;
  103. term = 1;
  104. }
  105. T operator()()
  106. {
  107. T r = term * (v + 2 * N);
  108. ++N;
  109. term *= mult / (N * (N + v));
  110. return r;
  111. }
  112. private:
  113. unsigned N;
  114. T v;
  115. T mult;
  116. T term;
  117. };
  118. //
  119. // Series form for BesselY' as z -> 0,
  120. // It's derivative of http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/01/0003/
  121. // This series is only useful when the second term is small compared to the first
  122. // otherwise we get catastrophic cancellation errors.
  123. //
  124. // Approximating tgamma(v) by v^v, and assuming |tgamma(-z)| < eps we end up requiring:
  125. // eps/2 * v^v(x/2)^-v > (x/2)^v or log(eps/2) > v log((x/2)^2/v)
  126. //
  127. template <class T, class Policy>
  128. inline T bessel_y_derivative_small_z_series(T v, T x, const Policy& pol)
  129. {
  130. BOOST_MATH_STD_USING
  131. static const char* function = "bessel_y_derivative_small_z_series<%1%>(%1%,%1%)";
  132. T prefix;
  133. T gam;
  134. T p = log(x / 2);
  135. T scale = 1;
  136. bool need_logs = (v >= boost::math::max_factorial<T>::value) || (boost::math::tools::log_max_value<T>() / v < fabs(p));
  137. if (!need_logs)
  138. {
  139. gam = boost::math::tgamma(v, pol);
  140. p = pow(x / 2, v + 1) * 2;
  141. if (boost::math::tools::max_value<T>() * p < gam)
  142. {
  143. scale /= gam;
  144. gam = 1;
  145. if (boost::math::tools::max_value<T>() * p < gam)
  146. {
  147. // This term will overflow to -INF, when combined with the series below it becomes +INF:
  148. return boost::math::policies::raise_overflow_error<T>(function, nullptr, pol);
  149. }
  150. }
  151. prefix = -gam / (boost::math::constants::pi<T>() * p);
  152. }
  153. else
  154. {
  155. gam = boost::math::lgamma(v, pol);
  156. p = (v + 1) * p + constants::ln_two<T>();
  157. prefix = gam - log(boost::math::constants::pi<T>()) - p;
  158. if (boost::math::tools::log_max_value<T>() < prefix)
  159. {
  160. prefix -= log(boost::math::tools::max_value<T>() / 4);
  161. scale /= (boost::math::tools::max_value<T>() / 4);
  162. if (boost::math::tools::log_max_value<T>() < prefix)
  163. {
  164. return boost::math::policies::raise_overflow_error<T>(function, nullptr, pol);
  165. }
  166. }
  167. prefix = -exp(prefix);
  168. }
  169. bessel_y_derivative_small_z_series_term_a<T, Policy> s(v, x);
  170. std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  171. T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  172. boost::math::policies::check_series_iterations<T>("boost::math::bessel_y_derivative_small_z_series<%1%>(%1%,%1%)", max_iter, pol);
  173. result *= prefix;
  174. p = pow(x / 2, v - 1) / 2;
  175. if (!need_logs)
  176. {
  177. prefix = boost::math::tgamma(-v, pol) * boost::math::cos_pi(v, pol) * p / boost::math::constants::pi<T>();
  178. }
  179. else
  180. {
  181. int sgn {};
  182. prefix = boost::math::lgamma(-v, &sgn, pol) + (v - 1) * log(x / 2) - constants::ln_two<T>();
  183. prefix = exp(prefix) * sgn / boost::math::constants::pi<T>();
  184. }
  185. bessel_y_derivative_small_z_series_term_b<T, Policy> s2(v, x);
  186. max_iter = boost::math::policies::get_max_series_iterations<Policy>();
  187. T b = boost::math::tools::sum_series(s2, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
  188. result += scale * prefix * b;
  189. if(scale * tools::max_value<T>() < result)
  190. return boost::math::policies::raise_overflow_error<T>(function, nullptr, pol);
  191. return result / scale;
  192. }
  193. // Calculating of BesselY'(v,x) with small x (x < epsilon) and integer x using derivatives
  194. // of formulas in http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/02/
  195. // seems to lose precision. Instead using linear combination of regular Bessel is preferred.
  196. }}} // namespaces
  197. #endif // BOOST_MATH_BESSEL_JY_DERIVATIVES_SERIES_HPP