bessel_jy_derivatives_series.hpp 6.8 KB

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