annotate DEPENDENCIES/generic/include/boost/math/special_functions/detail/bessel_ik.hpp @ 125:34e428693f5d vext

Vext -> Repoint
author Chris Cannam
date Thu, 14 Jun 2018 11:15:39 +0100
parents c530137014c0
children
rev   line source
Chris@16 1 // Copyright (c) 2006 Xiaogang Zhang
Chris@16 2 // Use, modification and distribution are subject to the
Chris@16 3 // Boost Software License, Version 1.0. (See accompanying file
Chris@16 4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Chris@16 5
Chris@16 6 #ifndef BOOST_MATH_BESSEL_IK_HPP
Chris@16 7 #define BOOST_MATH_BESSEL_IK_HPP
Chris@16 8
Chris@16 9 #ifdef _MSC_VER
Chris@16 10 #pragma once
Chris@16 11 #endif
Chris@16 12
Chris@16 13 #include <boost/math/special_functions/round.hpp>
Chris@16 14 #include <boost/math/special_functions/gamma.hpp>
Chris@16 15 #include <boost/math/special_functions/sin_pi.hpp>
Chris@16 16 #include <boost/math/constants/constants.hpp>
Chris@16 17 #include <boost/math/policies/error_handling.hpp>
Chris@16 18 #include <boost/math/tools/config.hpp>
Chris@16 19
Chris@16 20 // Modified Bessel functions of the first and second kind of fractional order
Chris@16 21
Chris@16 22 namespace boost { namespace math {
Chris@16 23
Chris@16 24 namespace detail {
Chris@16 25
Chris@16 26 template <class T, class Policy>
Chris@16 27 struct cyl_bessel_i_small_z
Chris@16 28 {
Chris@16 29 typedef T result_type;
Chris@16 30
Chris@16 31 cyl_bessel_i_small_z(T v_, T z_) : k(0), v(v_), mult(z_*z_/4)
Chris@16 32 {
Chris@16 33 BOOST_MATH_STD_USING
Chris@16 34 term = 1;
Chris@16 35 }
Chris@16 36
Chris@16 37 T operator()()
Chris@16 38 {
Chris@16 39 T result = term;
Chris@16 40 ++k;
Chris@16 41 term *= mult / k;
Chris@16 42 term /= k + v;
Chris@16 43 return result;
Chris@16 44 }
Chris@16 45 private:
Chris@16 46 unsigned k;
Chris@16 47 T v;
Chris@16 48 T term;
Chris@16 49 T mult;
Chris@16 50 };
Chris@16 51
Chris@16 52 template <class T, class Policy>
Chris@16 53 inline T bessel_i_small_z_series(T v, T x, const Policy& pol)
Chris@16 54 {
Chris@16 55 BOOST_MATH_STD_USING
Chris@16 56 T prefix;
Chris@16 57 if(v < max_factorial<T>::value)
Chris@16 58 {
Chris@16 59 prefix = pow(x / 2, v) / boost::math::tgamma(v + 1, pol);
Chris@16 60 }
Chris@16 61 else
Chris@16 62 {
Chris@16 63 prefix = v * log(x / 2) - boost::math::lgamma(v + 1, pol);
Chris@16 64 prefix = exp(prefix);
Chris@16 65 }
Chris@16 66 if(prefix == 0)
Chris@16 67 return prefix;
Chris@16 68
Chris@16 69 cyl_bessel_i_small_z<T, Policy> s(v, x);
Chris@16 70 boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
Chris@16 71 #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
Chris@16 72 T zero = 0;
Chris@16 73 T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter, zero);
Chris@16 74 #else
Chris@16 75 T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
Chris@16 76 #endif
Chris@16 77 policies::check_series_iterations<T>("boost::math::bessel_j_small_z_series<%1%>(%1%,%1%)", max_iter, pol);
Chris@16 78 return prefix * result;
Chris@16 79 }
Chris@16 80
Chris@16 81 // Calculate K(v, x) and K(v+1, x) by method analogous to
Chris@16 82 // Temme, Journal of Computational Physics, vol 21, 343 (1976)
Chris@16 83 template <typename T, typename Policy>
Chris@16 84 int temme_ik(T v, T x, T* K, T* K1, const Policy& pol)
Chris@16 85 {
Chris@16 86 T f, h, p, q, coef, sum, sum1, tolerance;
Chris@16 87 T a, b, c, d, sigma, gamma1, gamma2;
Chris@16 88 unsigned long k;
Chris@16 89
Chris@16 90 BOOST_MATH_STD_USING
Chris@16 91 using namespace boost::math::tools;
Chris@16 92 using namespace boost::math::constants;
Chris@16 93
Chris@16 94
Chris@16 95 // |x| <= 2, Temme series converge rapidly
Chris@16 96 // |x| > 2, the larger the |x|, the slower the convergence
Chris@16 97 BOOST_ASSERT(abs(x) <= 2);
Chris@16 98 BOOST_ASSERT(abs(v) <= 0.5f);
Chris@16 99
Chris@16 100 T gp = boost::math::tgamma1pm1(v, pol);
Chris@16 101 T gm = boost::math::tgamma1pm1(-v, pol);
Chris@16 102
Chris@16 103 a = log(x / 2);
Chris@16 104 b = exp(v * a);
Chris@16 105 sigma = -a * v;
Chris@16 106 c = abs(v) < tools::epsilon<T>() ?
Chris@16 107 T(1) : T(boost::math::sin_pi(v) / (v * pi<T>()));
Chris@16 108 d = abs(sigma) < tools::epsilon<T>() ?
Chris@16 109 T(1) : T(sinh(sigma) / sigma);
Chris@16 110 gamma1 = abs(v) < tools::epsilon<T>() ?
Chris@16 111 T(-euler<T>()) : T((0.5f / v) * (gp - gm) * c);
Chris@16 112 gamma2 = (2 + gp + gm) * c / 2;
Chris@16 113
Chris@16 114 // initial values
Chris@16 115 p = (gp + 1) / (2 * b);
Chris@16 116 q = (1 + gm) * b / 2;
Chris@16 117 f = (cosh(sigma) * gamma1 + d * (-a) * gamma2) / c;
Chris@16 118 h = p;
Chris@16 119 coef = 1;
Chris@16 120 sum = coef * f;
Chris@16 121 sum1 = coef * h;
Chris@16 122
Chris@16 123 BOOST_MATH_INSTRUMENT_VARIABLE(p);
Chris@16 124 BOOST_MATH_INSTRUMENT_VARIABLE(q);
Chris@16 125 BOOST_MATH_INSTRUMENT_VARIABLE(f);
Chris@16 126 BOOST_MATH_INSTRUMENT_VARIABLE(sigma);
Chris@16 127 BOOST_MATH_INSTRUMENT_CODE(sinh(sigma));
Chris@16 128 BOOST_MATH_INSTRUMENT_VARIABLE(gamma1);
Chris@16 129 BOOST_MATH_INSTRUMENT_VARIABLE(gamma2);
Chris@16 130 BOOST_MATH_INSTRUMENT_VARIABLE(c);
Chris@16 131 BOOST_MATH_INSTRUMENT_VARIABLE(d);
Chris@16 132 BOOST_MATH_INSTRUMENT_VARIABLE(a);
Chris@16 133
Chris@16 134 // series summation
Chris@16 135 tolerance = tools::epsilon<T>();
Chris@16 136 for (k = 1; k < policies::get_max_series_iterations<Policy>(); k++)
Chris@16 137 {
Chris@16 138 f = (k * f + p + q) / (k*k - v*v);
Chris@16 139 p /= k - v;
Chris@16 140 q /= k + v;
Chris@16 141 h = p - k * f;
Chris@16 142 coef *= x * x / (4 * k);
Chris@16 143 sum += coef * f;
Chris@16 144 sum1 += coef * h;
Chris@16 145 if (abs(coef * f) < abs(sum) * tolerance)
Chris@16 146 {
Chris@16 147 break;
Chris@16 148 }
Chris@16 149 }
Chris@16 150 policies::check_series_iterations<T>("boost::math::bessel_ik<%1%>(%1%,%1%) in temme_ik", k, pol);
Chris@16 151
Chris@16 152 *K = sum;
Chris@16 153 *K1 = 2 * sum1 / x;
Chris@16 154
Chris@16 155 return 0;
Chris@16 156 }
Chris@16 157
Chris@16 158 // Evaluate continued fraction fv = I_(v+1) / I_v, derived from
Chris@16 159 // Abramowitz and Stegun, Handbook of Mathematical Functions, 1972, 9.1.73
Chris@16 160 template <typename T, typename Policy>
Chris@16 161 int CF1_ik(T v, T x, T* fv, const Policy& pol)
Chris@16 162 {
Chris@16 163 T C, D, f, a, b, delta, tiny, tolerance;
Chris@16 164 unsigned long k;
Chris@16 165
Chris@16 166 BOOST_MATH_STD_USING
Chris@16 167
Chris@16 168 // |x| <= |v|, CF1_ik converges rapidly
Chris@16 169 // |x| > |v|, CF1_ik needs O(|x|) iterations to converge
Chris@16 170
Chris@16 171 // modified Lentz's method, see
Chris@16 172 // Lentz, Applied Optics, vol 15, 668 (1976)
Chris@16 173 tolerance = 2 * tools::epsilon<T>();
Chris@16 174 BOOST_MATH_INSTRUMENT_VARIABLE(tolerance);
Chris@16 175 tiny = sqrt(tools::min_value<T>());
Chris@16 176 BOOST_MATH_INSTRUMENT_VARIABLE(tiny);
Chris@16 177 C = f = tiny; // b0 = 0, replace with tiny
Chris@16 178 D = 0;
Chris@16 179 for (k = 1; k < policies::get_max_series_iterations<Policy>(); k++)
Chris@16 180 {
Chris@16 181 a = 1;
Chris@16 182 b = 2 * (v + k) / x;
Chris@16 183 C = b + a / C;
Chris@16 184 D = b + a * D;
Chris@16 185 if (C == 0) { C = tiny; }
Chris@16 186 if (D == 0) { D = tiny; }
Chris@16 187 D = 1 / D;
Chris@16 188 delta = C * D;
Chris@16 189 f *= delta;
Chris@16 190 BOOST_MATH_INSTRUMENT_VARIABLE(delta-1);
Chris@16 191 if (abs(delta - 1) <= tolerance)
Chris@16 192 {
Chris@16 193 break;
Chris@16 194 }
Chris@16 195 }
Chris@16 196 BOOST_MATH_INSTRUMENT_VARIABLE(k);
Chris@16 197 policies::check_series_iterations<T>("boost::math::bessel_ik<%1%>(%1%,%1%) in CF1_ik", k, pol);
Chris@16 198
Chris@16 199 *fv = f;
Chris@16 200
Chris@16 201 return 0;
Chris@16 202 }
Chris@16 203
Chris@16 204 // Calculate K(v, x) and K(v+1, x) by evaluating continued fraction
Chris@16 205 // z1 / z0 = U(v+1.5, 2v+1, 2x) / U(v+0.5, 2v+1, 2x), see
Chris@16 206 // Thompson and Barnett, Computer Physics Communications, vol 47, 245 (1987)
Chris@16 207 template <typename T, typename Policy>
Chris@16 208 int CF2_ik(T v, T x, T* Kv, T* Kv1, const Policy& pol)
Chris@16 209 {
Chris@16 210 BOOST_MATH_STD_USING
Chris@16 211 using namespace boost::math::constants;
Chris@16 212
Chris@16 213 T S, C, Q, D, f, a, b, q, delta, tolerance, current, prev;
Chris@16 214 unsigned long k;
Chris@16 215
Chris@16 216 // |x| >= |v|, CF2_ik converges rapidly
Chris@16 217 // |x| -> 0, CF2_ik fails to converge
Chris@16 218
Chris@16 219 BOOST_ASSERT(abs(x) > 1);
Chris@16 220
Chris@16 221 // Steed's algorithm, see Thompson and Barnett,
Chris@16 222 // Journal of Computational Physics, vol 64, 490 (1986)
Chris@16 223 tolerance = tools::epsilon<T>();
Chris@16 224 a = v * v - 0.25f;
Chris@16 225 b = 2 * (x + 1); // b1
Chris@16 226 D = 1 / b; // D1 = 1 / b1
Chris@16 227 f = delta = D; // f1 = delta1 = D1, coincidence
Chris@16 228 prev = 0; // q0
Chris@16 229 current = 1; // q1
Chris@16 230 Q = C = -a; // Q1 = C1 because q1 = 1
Chris@16 231 S = 1 + Q * delta; // S1
Chris@16 232 BOOST_MATH_INSTRUMENT_VARIABLE(tolerance);
Chris@16 233 BOOST_MATH_INSTRUMENT_VARIABLE(a);
Chris@16 234 BOOST_MATH_INSTRUMENT_VARIABLE(b);
Chris@16 235 BOOST_MATH_INSTRUMENT_VARIABLE(D);
Chris@16 236 BOOST_MATH_INSTRUMENT_VARIABLE(f);
Chris@16 237
Chris@16 238 for (k = 2; k < policies::get_max_series_iterations<Policy>(); k++) // starting from 2
Chris@16 239 {
Chris@16 240 // continued fraction f = z1 / z0
Chris@16 241 a -= 2 * (k - 1);
Chris@16 242 b += 2;
Chris@16 243 D = 1 / (b + a * D);
Chris@16 244 delta *= b * D - 1;
Chris@16 245 f += delta;
Chris@16 246
Chris@16 247 // series summation S = 1 + \sum_{n=1}^{\infty} C_n * z_n / z_0
Chris@16 248 q = (prev - (b - 2) * current) / a;
Chris@16 249 prev = current;
Chris@16 250 current = q; // forward recurrence for q
Chris@16 251 C *= -a / k;
Chris@16 252 Q += C * q;
Chris@16 253 S += Q * delta;
Chris@16 254 //
Chris@16 255 // Under some circumstances q can grow very small and C very
Chris@16 256 // large, leading to under/overflow. This is particularly an
Chris@16 257 // issue for types which have many digits precision but a narrow
Chris@16 258 // exponent range. A typical example being a "double double" type.
Chris@16 259 // To avoid this situation we can normalise q (and related prev/current)
Chris@16 260 // and C. All other variables remain unchanged in value. A typical
Chris@16 261 // test case occurs when x is close to 2, for example cyl_bessel_k(9.125, 2.125).
Chris@16 262 //
Chris@16 263 if(q < tools::epsilon<T>())
Chris@16 264 {
Chris@16 265 C *= q;
Chris@16 266 prev /= q;
Chris@16 267 current /= q;
Chris@16 268 q = 1;
Chris@16 269 }
Chris@16 270
Chris@16 271 // S converges slower than f
Chris@16 272 BOOST_MATH_INSTRUMENT_VARIABLE(Q * delta);
Chris@16 273 BOOST_MATH_INSTRUMENT_VARIABLE(abs(S) * tolerance);
Chris@101 274 BOOST_MATH_INSTRUMENT_VARIABLE(S);
Chris@16 275 if (abs(Q * delta) < abs(S) * tolerance)
Chris@16 276 {
Chris@16 277 break;
Chris@16 278 }
Chris@16 279 }
Chris@16 280 policies::check_series_iterations<T>("boost::math::bessel_ik<%1%>(%1%,%1%) in CF2_ik", k, pol);
Chris@16 281
Chris@101 282 if(x >= tools::log_max_value<T>())
Chris@101 283 *Kv = exp(0.5f * log(pi<T>() / (2 * x)) - x - log(S));
Chris@101 284 else
Chris@101 285 *Kv = sqrt(pi<T>() / (2 * x)) * exp(-x) / S;
Chris@16 286 *Kv1 = *Kv * (0.5f + v + x + (v * v - 0.25f) * f) / x;
Chris@16 287 BOOST_MATH_INSTRUMENT_VARIABLE(*Kv);
Chris@16 288 BOOST_MATH_INSTRUMENT_VARIABLE(*Kv1);
Chris@16 289
Chris@16 290 return 0;
Chris@16 291 }
Chris@16 292
Chris@16 293 enum{
Chris@16 294 need_i = 1,
Chris@16 295 need_k = 2
Chris@16 296 };
Chris@16 297
Chris@16 298 // Compute I(v, x) and K(v, x) simultaneously by Temme's method, see
Chris@16 299 // Temme, Journal of Computational Physics, vol 19, 324 (1975)
Chris@16 300 template <typename T, typename Policy>
Chris@16 301 int bessel_ik(T v, T x, T* I, T* K, int kind, const Policy& pol)
Chris@16 302 {
Chris@16 303 // Kv1 = K_(v+1), fv = I_(v+1) / I_v
Chris@16 304 // Ku1 = K_(u+1), fu = I_(u+1) / I_u
Chris@16 305 T u, Iv, Kv, Kv1, Ku, Ku1, fv;
Chris@16 306 T W, current, prev, next;
Chris@16 307 bool reflect = false;
Chris@16 308 unsigned n, k;
Chris@16 309 int org_kind = kind;
Chris@16 310 BOOST_MATH_INSTRUMENT_VARIABLE(v);
Chris@16 311 BOOST_MATH_INSTRUMENT_VARIABLE(x);
Chris@16 312 BOOST_MATH_INSTRUMENT_VARIABLE(kind);
Chris@16 313
Chris@16 314 BOOST_MATH_STD_USING
Chris@16 315 using namespace boost::math::tools;
Chris@16 316 using namespace boost::math::constants;
Chris@16 317
Chris@16 318 static const char* function = "boost::math::bessel_ik<%1%>(%1%,%1%)";
Chris@16 319
Chris@16 320 if (v < 0)
Chris@16 321 {
Chris@16 322 reflect = true;
Chris@16 323 v = -v; // v is non-negative from here
Chris@16 324 kind |= need_k;
Chris@16 325 }
Chris@16 326 n = iround(v, pol);
Chris@16 327 u = v - n; // -1/2 <= u < 1/2
Chris@16 328 BOOST_MATH_INSTRUMENT_VARIABLE(n);
Chris@16 329 BOOST_MATH_INSTRUMENT_VARIABLE(u);
Chris@16 330
Chris@16 331 if (x < 0)
Chris@16 332 {
Chris@16 333 *I = *K = policies::raise_domain_error<T>(function,
Chris@16 334 "Got x = %1% but real argument x must be non-negative, complex number result not supported.", x, pol);
Chris@16 335 return 1;
Chris@16 336 }
Chris@16 337 if (x == 0)
Chris@16 338 {
Chris@16 339 Iv = (v == 0) ? static_cast<T>(1) : static_cast<T>(0);
Chris@16 340 if(kind & need_k)
Chris@16 341 {
Chris@16 342 Kv = policies::raise_overflow_error<T>(function, 0, pol);
Chris@16 343 }
Chris@16 344 else
Chris@16 345 {
Chris@16 346 Kv = std::numeric_limits<T>::quiet_NaN(); // any value will do
Chris@16 347 }
Chris@16 348
Chris@16 349 if(reflect && (kind & need_i))
Chris@16 350 {
Chris@16 351 T z = (u + n % 2);
Chris@16 352 Iv = boost::math::sin_pi(z, pol) == 0 ?
Chris@16 353 Iv :
Chris@16 354 policies::raise_overflow_error<T>(function, 0, pol); // reflection formula
Chris@16 355 }
Chris@16 356
Chris@16 357 *I = Iv;
Chris@16 358 *K = Kv;
Chris@16 359 return 0;
Chris@16 360 }
Chris@16 361
Chris@16 362 // x is positive until reflection
Chris@16 363 W = 1 / x; // Wronskian
Chris@16 364 if (x <= 2) // x in (0, 2]
Chris@16 365 {
Chris@16 366 temme_ik(u, x, &Ku, &Ku1, pol); // Temme series
Chris@16 367 }
Chris@16 368 else // x in (2, \infty)
Chris@16 369 {
Chris@16 370 CF2_ik(u, x, &Ku, &Ku1, pol); // continued fraction CF2_ik
Chris@16 371 }
Chris@16 372 BOOST_MATH_INSTRUMENT_VARIABLE(Ku);
Chris@16 373 BOOST_MATH_INSTRUMENT_VARIABLE(Ku1);
Chris@16 374 prev = Ku;
Chris@16 375 current = Ku1;
Chris@16 376 T scale = 1;
Chris@16 377 for (k = 1; k <= n; k++) // forward recurrence for K
Chris@16 378 {
Chris@16 379 T fact = 2 * (u + k) / x;
Chris@16 380 if((tools::max_value<T>() - fabs(prev)) / fact < fabs(current))
Chris@16 381 {
Chris@16 382 prev /= current;
Chris@16 383 scale /= current;
Chris@16 384 current = 1;
Chris@16 385 }
Chris@16 386 next = fact * current + prev;
Chris@16 387 prev = current;
Chris@16 388 current = next;
Chris@16 389 }
Chris@16 390 Kv = prev;
Chris@16 391 Kv1 = current;
Chris@16 392 BOOST_MATH_INSTRUMENT_VARIABLE(Kv);
Chris@16 393 BOOST_MATH_INSTRUMENT_VARIABLE(Kv1);
Chris@16 394 if(kind & need_i)
Chris@16 395 {
Chris@16 396 T lim = (4 * v * v + 10) / (8 * x);
Chris@16 397 lim *= lim;
Chris@16 398 lim *= lim;
Chris@16 399 lim /= 24;
Chris@16 400 if((lim < tools::epsilon<T>() * 10) && (x > 100))
Chris@16 401 {
Chris@16 402 // x is huge compared to v, CF1 may be very slow
Chris@16 403 // to converge so use asymptotic expansion for large
Chris@16 404 // x case instead. Note that the asymptotic expansion
Chris@16 405 // isn't very accurate - so it's deliberately very hard
Chris@16 406 // to get here - probably we're going to overflow:
Chris@16 407 Iv = asymptotic_bessel_i_large_x(v, x, pol);
Chris@16 408 }
Chris@16 409 else if((v > 0) && (x / v < 0.25))
Chris@16 410 {
Chris@16 411 Iv = bessel_i_small_z_series(v, x, pol);
Chris@16 412 }
Chris@16 413 else
Chris@16 414 {
Chris@16 415 CF1_ik(v, x, &fv, pol); // continued fraction CF1_ik
Chris@16 416 Iv = scale * W / (Kv * fv + Kv1); // Wronskian relation
Chris@16 417 }
Chris@16 418 }
Chris@16 419 else
Chris@16 420 Iv = std::numeric_limits<T>::quiet_NaN(); // any value will do
Chris@16 421
Chris@16 422 if (reflect)
Chris@16 423 {
Chris@16 424 T z = (u + n % 2);
Chris@16 425 T fact = (2 / pi<T>()) * (boost::math::sin_pi(z) * Kv);
Chris@16 426 if(fact == 0)
Chris@16 427 *I = Iv;
Chris@16 428 else if(tools::max_value<T>() * scale < fact)
Chris@16 429 *I = (org_kind & need_i) ? T(sign(fact) * sign(scale) * policies::raise_overflow_error<T>(function, 0, pol)) : T(0);
Chris@16 430 else
Chris@16 431 *I = Iv + fact / scale; // reflection formula
Chris@16 432 }
Chris@16 433 else
Chris@16 434 {
Chris@16 435 *I = Iv;
Chris@16 436 }
Chris@16 437 if(tools::max_value<T>() * scale < Kv)
Chris@16 438 *K = (org_kind & need_k) ? T(sign(Kv) * sign(scale) * policies::raise_overflow_error<T>(function, 0, pol)) : T(0);
Chris@16 439 else
Chris@16 440 *K = Kv / scale;
Chris@16 441 BOOST_MATH_INSTRUMENT_VARIABLE(*I);
Chris@16 442 BOOST_MATH_INSTRUMENT_VARIABLE(*K);
Chris@16 443 return 0;
Chris@16 444 }
Chris@16 445
Chris@16 446 }}} // namespaces
Chris@16 447
Chris@16 448 #endif // BOOST_MATH_BESSEL_IK_HPP
Chris@16 449