452 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			452 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | //  Copyright (c) 2006 Xiaogang Zhang | ||
|  | //  Use, modification and distribution are subject to the | ||
|  | //  Boost Software License, Version 1.0. (See accompanying file | ||
|  | //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|  | 
 | ||
|  | #ifndef BOOST_MATH_BESSEL_IK_HPP | ||
|  | #define BOOST_MATH_BESSEL_IK_HPP | ||
|  | 
 | ||
|  | #ifdef _MSC_VER | ||
|  | #pragma once | ||
|  | #endif | ||
|  | 
 | ||
|  | #include <boost/math/special_functions/round.hpp> | ||
|  | #include <boost/math/special_functions/gamma.hpp> | ||
|  | #include <boost/math/special_functions/sin_pi.hpp> | ||
|  | #include <boost/math/constants/constants.hpp> | ||
|  | #include <boost/math/policies/error_handling.hpp> | ||
|  | #include <boost/math/tools/config.hpp> | ||
|  | 
 | ||
|  | // Modified Bessel functions of the first and second kind of fractional order | ||
|  | 
 | ||
|  | namespace boost { namespace math { | ||
|  | 
 | ||
|  | namespace detail { | ||
|  | 
 | ||
|  | template <class T, class Policy> | ||
|  | struct cyl_bessel_i_small_z | ||
|  | { | ||
|  |    typedef T result_type; | ||
|  | 
 | ||
|  |    cyl_bessel_i_small_z(T v_, T z_) : k(0), v(v_), mult(z_*z_/4)  | ||
|  |    { | ||
|  |       BOOST_MATH_STD_USING | ||
|  |       term = 1; | ||
|  |    } | ||
|  | 
 | ||
|  |    T operator()() | ||
|  |    { | ||
|  |       T result = term; | ||
|  |       ++k; | ||
|  |       term *= mult / k; | ||
|  |       term /= k + v; | ||
|  |       return result; | ||
|  |    } | ||
|  | private: | ||
|  |    unsigned k; | ||
|  |    T v; | ||
|  |    T term; | ||
|  |    T mult; | ||
|  | }; | ||
|  | 
 | ||
|  | template <class T, class Policy> | ||
|  | inline T bessel_i_small_z_series(T v, T x, const Policy& pol) | ||
|  | { | ||
|  |    BOOST_MATH_STD_USING | ||
|  |    T prefix; | ||
|  |    if(v < max_factorial<T>::value) | ||
|  |    { | ||
|  |       prefix = pow(x / 2, v) / boost::math::tgamma(v + 1, pol); | ||
|  |    } | ||
|  |    else | ||
|  |    { | ||
|  |       prefix = v * log(x / 2) - boost::math::lgamma(v + 1, pol); | ||
|  |       prefix = exp(prefix); | ||
|  |    } | ||
|  |    if(prefix == 0) | ||
|  |       return prefix; | ||
|  | 
 | ||
|  |    cyl_bessel_i_small_z<T, Policy> s(v, x); | ||
|  |    boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); | ||
|  | #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) | ||
|  |    T zero = 0; | ||
|  |    T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter, zero); | ||
|  | #else | ||
|  |    T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter); | ||
|  | #endif | ||
|  |    policies::check_series_iterations<T>("boost::math::bessel_j_small_z_series<%1%>(%1%,%1%)", max_iter, pol); | ||
|  |    return prefix * result; | ||
|  | } | ||
|  | 
 | ||
|  | // Calculate K(v, x) and K(v+1, x) by method analogous to | ||
|  | // Temme, Journal of Computational Physics, vol 21, 343 (1976) | ||
|  | template <typename T, typename Policy> | ||
|  | int temme_ik(T v, T x, T* K, T* K1, const Policy& pol) | ||
|  | { | ||
|  |     T f, h, p, q, coef, sum, sum1, tolerance; | ||
|  |     T a, b, c, d, sigma, gamma1, gamma2; | ||
|  |     unsigned long k; | ||
|  | 
 | ||
|  |     BOOST_MATH_STD_USING | ||
|  |     using namespace boost::math::tools; | ||
|  |     using namespace boost::math::constants; | ||
|  | 
 | ||
|  | 
 | ||
|  |     // |x| <= 2, Temme series converge rapidly | ||
|  |     // |x| > 2, the larger the |x|, the slower the convergence | ||
|  |     BOOST_ASSERT(abs(x) <= 2); | ||
|  |     BOOST_ASSERT(abs(v) <= 0.5f); | ||
|  | 
 | ||
|  |     T gp = boost::math::tgamma1pm1(v, pol); | ||
|  |     T gm = boost::math::tgamma1pm1(-v, pol); | ||
|  | 
 | ||
|  |     a = log(x / 2); | ||
|  |     b = exp(v * a); | ||
|  |     sigma = -a * v; | ||
|  |     c = abs(v) < tools::epsilon<T>() ? | ||
|  |        T(1) : T(boost::math::sin_pi(v) / (v * pi<T>())); | ||
|  |     d = abs(sigma) < tools::epsilon<T>() ? | ||
|  |         T(1) : T(sinh(sigma) / sigma); | ||
|  |     gamma1 = abs(v) < tools::epsilon<T>() ? | ||
|  |         T(-euler<T>()) : T((0.5f / v) * (gp - gm) * c); | ||
|  |     gamma2 = (2 + gp + gm) * c / 2; | ||
|  | 
 | ||
|  |     // initial values | ||
|  |     p = (gp + 1) / (2 * b); | ||
|  |     q = (1 + gm) * b / 2; | ||
|  |     f = (cosh(sigma) * gamma1 + d * (-a) * gamma2) / c; | ||
|  |     h = p; | ||
|  |     coef = 1; | ||
|  |     sum = coef * f; | ||
|  |     sum1 = coef * h; | ||
|  | 
 | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(p); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(q); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(f); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(sigma); | ||
|  |     BOOST_MATH_INSTRUMENT_CODE(sinh(sigma)); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(gamma1); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(gamma2); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(c); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(d); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(a); | ||
|  | 
 | ||
|  |     // series summation | ||
|  |     tolerance = tools::epsilon<T>(); | ||
|  |     for (k = 1; k < policies::get_max_series_iterations<Policy>(); k++) | ||
|  |     { | ||
|  |         f = (k * f + p + q) / (k*k - v*v); | ||
|  |         p /= k - v; | ||
|  |         q /= k + v; | ||
|  |         h = p - k * f; | ||
|  |         coef *= x * x / (4 * k); | ||
|  |         sum += coef * f; | ||
|  |         sum1 += coef * h; | ||
|  |         if (abs(coef * f) < abs(sum) * tolerance)  | ||
|  |         {  | ||
|  |            break;  | ||
|  |         } | ||
|  |     } | ||
|  |     policies::check_series_iterations<T>("boost::math::bessel_ik<%1%>(%1%,%1%) in temme_ik", k, pol); | ||
|  | 
 | ||
|  |     *K = sum; | ||
|  |     *K1 = 2 * sum1 / x; | ||
|  | 
 | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | // Evaluate continued fraction fv = I_(v+1) / I_v, derived from | ||
|  | // Abramowitz and Stegun, Handbook of Mathematical Functions, 1972, 9.1.73 | ||
|  | template <typename T, typename Policy> | ||
|  | int CF1_ik(T v, T x, T* fv, const Policy& pol) | ||
|  | { | ||
|  |     T C, D, f, a, b, delta, tiny, tolerance; | ||
|  |     unsigned long k; | ||
|  | 
 | ||
|  |     BOOST_MATH_STD_USING | ||
|  | 
 | ||
|  |     // |x| <= |v|, CF1_ik converges rapidly | ||
|  |     // |x| > |v|, CF1_ik needs O(|x|) iterations to converge | ||
|  | 
 | ||
|  |     // modified Lentz's method, see | ||
|  |     // Lentz, Applied Optics, vol 15, 668 (1976) | ||
|  |     tolerance = 2 * tools::epsilon<T>(); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(tolerance); | ||
|  |     tiny = sqrt(tools::min_value<T>()); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(tiny); | ||
|  |     C = f = tiny;                           // b0 = 0, replace with tiny | ||
|  |     D = 0; | ||
|  |     for (k = 1; k < policies::get_max_series_iterations<Policy>(); k++) | ||
|  |     { | ||
|  |         a = 1; | ||
|  |         b = 2 * (v + k) / x; | ||
|  |         C = b + a / C; | ||
|  |         D = b + a * D; | ||
|  |         if (C == 0) { C = tiny; } | ||
|  |         if (D == 0) { D = tiny; } | ||
|  |         D = 1 / D; | ||
|  |         delta = C * D; | ||
|  |         f *= delta; | ||
|  |         BOOST_MATH_INSTRUMENT_VARIABLE(delta-1); | ||
|  |         if (abs(delta - 1) <= tolerance)  | ||
|  |         {  | ||
|  |            break;  | ||
|  |         } | ||
|  |     } | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(k); | ||
|  |     policies::check_series_iterations<T>("boost::math::bessel_ik<%1%>(%1%,%1%) in CF1_ik", k, pol); | ||
|  | 
 | ||
|  |     *fv = f; | ||
|  | 
 | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | // Calculate K(v, x) and K(v+1, x) by evaluating continued fraction | ||
|  | // z1 / z0 = U(v+1.5, 2v+1, 2x) / U(v+0.5, 2v+1, 2x), see | ||
|  | // Thompson and Barnett, Computer Physics Communications, vol 47, 245 (1987) | ||
|  | template <typename T, typename Policy> | ||
|  | int CF2_ik(T v, T x, T* Kv, T* Kv1, const Policy& pol) | ||
|  | { | ||
|  |     BOOST_MATH_STD_USING | ||
|  |     using namespace boost::math::constants; | ||
|  | 
 | ||
|  |     T S, C, Q, D, f, a, b, q, delta, tolerance, current, prev; | ||
|  |     unsigned long k; | ||
|  | 
 | ||
|  |     // |x| >= |v|, CF2_ik converges rapidly | ||
|  |     // |x| -> 0, CF2_ik fails to converge | ||
|  | 
 | ||
|  |     BOOST_ASSERT(abs(x) > 1); | ||
|  | 
 | ||
|  |     // Steed's algorithm, see Thompson and Barnett, | ||
|  |     // Journal of Computational Physics, vol 64, 490 (1986) | ||
|  |     tolerance = tools::epsilon<T>(); | ||
|  |     a = v * v - 0.25f; | ||
|  |     b = 2 * (x + 1);                              // b1 | ||
|  |     D = 1 / b;                                    // D1 = 1 / b1 | ||
|  |     f = delta = D;                                // f1 = delta1 = D1, coincidence | ||
|  |     prev = 0;                                     // q0 | ||
|  |     current = 1;                                  // q1 | ||
|  |     Q = C = -a;                                   // Q1 = C1 because q1 = 1 | ||
|  |     S = 1 + Q * delta;                            // S1 | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(tolerance); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(a); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(b); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(D); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(f); | ||
|  | 
 | ||
|  |     for (k = 2; k < policies::get_max_series_iterations<Policy>(); k++)     // starting from 2 | ||
|  |     { | ||
|  |         // continued fraction f = z1 / z0 | ||
|  |         a -= 2 * (k - 1); | ||
|  |         b += 2; | ||
|  |         D = 1 / (b + a * D); | ||
|  |         delta *= b * D - 1; | ||
|  |         f += delta; | ||
|  | 
 | ||
|  |         // series summation S = 1 + \sum_{n=1}^{\infty} C_n * z_n / z_0 | ||
|  |         q = (prev - (b - 2) * current) / a; | ||
|  |         prev = current; | ||
|  |         current = q;                        // forward recurrence for q | ||
|  |         C *= -a / k; | ||
|  |         Q += C * q; | ||
|  |         S += Q * delta; | ||
|  |         // | ||
|  |         // Under some circumstances q can grow very small and C very | ||
|  |         // large, leading to under/overflow.  This is particularly an | ||
|  |         // issue for types which have many digits precision but a narrow | ||
|  |         // exponent range.  A typical example being a "double double" type. | ||
|  |         // To avoid this situation we can normalise q (and related prev/current) | ||
|  |         // and C.  All other variables remain unchanged in value.  A typical | ||
|  |         // test case occurs when x is close to 2, for example cyl_bessel_k(9.125, 2.125). | ||
|  |         // | ||
|  |         if(q < tools::epsilon<T>()) | ||
|  |         { | ||
|  |            C *= q; | ||
|  |            prev /= q; | ||
|  |            current /= q; | ||
|  |            q = 1; | ||
|  |         } | ||
|  | 
 | ||
|  |         // S converges slower than f | ||
|  |         BOOST_MATH_INSTRUMENT_VARIABLE(Q * delta); | ||
|  |         BOOST_MATH_INSTRUMENT_VARIABLE(abs(S) * tolerance); | ||
|  |         BOOST_MATH_INSTRUMENT_VARIABLE(S); | ||
|  |         if (abs(Q * delta) < abs(S) * tolerance)  | ||
|  |         {  | ||
|  |            break;  | ||
|  |         } | ||
|  |     } | ||
|  |     policies::check_series_iterations<T>("boost::math::bessel_ik<%1%>(%1%,%1%) in CF2_ik", k, pol); | ||
|  | 
 | ||
|  |     if(x >= tools::log_max_value<T>()) | ||
|  |        *Kv = exp(0.5f * log(pi<T>() / (2 * x)) - x - log(S)); | ||
|  |     else | ||
|  |       *Kv = sqrt(pi<T>() / (2 * x)) * exp(-x) / S; | ||
|  |     *Kv1 = *Kv * (0.5f + v + x + (v * v - 0.25f) * f) / x; | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(*Kv); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(*Kv1); | ||
|  | 
 | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | enum{ | ||
|  |    need_i = 1, | ||
|  |    need_k = 2 | ||
|  | }; | ||
|  | 
 | ||
|  | // Compute I(v, x) and K(v, x) simultaneously by Temme's method, see | ||
|  | // Temme, Journal of Computational Physics, vol 19, 324 (1975) | ||
|  | template <typename T, typename Policy> | ||
|  | int bessel_ik(T v, T x, T* I, T* K, int kind, const Policy& pol) | ||
|  | { | ||
|  |     // Kv1 = K_(v+1), fv = I_(v+1) / I_v | ||
|  |     // Ku1 = K_(u+1), fu = I_(u+1) / I_u | ||
|  |     T u, Iv, Kv, Kv1, Ku, Ku1, fv; | ||
|  |     T W, current, prev, next; | ||
|  |     bool reflect = false; | ||
|  |     unsigned n, k; | ||
|  |     int org_kind = kind; | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(v); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(x); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(kind); | ||
|  | 
 | ||
|  |     BOOST_MATH_STD_USING | ||
|  |     using namespace boost::math::tools; | ||
|  |     using namespace boost::math::constants; | ||
|  | 
 | ||
|  |     static const char* function = "boost::math::bessel_ik<%1%>(%1%,%1%)"; | ||
|  | 
 | ||
|  |     if (v < 0) | ||
|  |     { | ||
|  |         reflect = true; | ||
|  |         v = -v;                             // v is non-negative from here | ||
|  |         kind |= need_k; | ||
|  |     } | ||
|  |     n = iround(v, pol); | ||
|  |     u = v - n;                              // -1/2 <= u < 1/2 | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(n); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(u); | ||
|  | 
 | ||
|  |     if (x < 0) | ||
|  |     { | ||
|  |        *I = *K = policies::raise_domain_error<T>(function, | ||
|  |             "Got x = %1% but real argument x must be non-negative, complex number result not supported.", x, pol); | ||
|  |         return 1; | ||
|  |     } | ||
|  |     if (x == 0) | ||
|  |     { | ||
|  |        Iv = (v == 0) ? static_cast<T>(1) : static_cast<T>(0); | ||
|  |        if(kind & need_k) | ||
|  |        { | ||
|  |          Kv = policies::raise_overflow_error<T>(function, 0, pol); | ||
|  |        } | ||
|  |        else | ||
|  |        { | ||
|  |           Kv = std::numeric_limits<T>::quiet_NaN(); // any value will do | ||
|  |        } | ||
|  | 
 | ||
|  |        if(reflect && (kind & need_i)) | ||
|  |        { | ||
|  |            T z = (u + n % 2); | ||
|  |            Iv = boost::math::sin_pi(z, pol) == 0 ?  | ||
|  |                Iv :  | ||
|  |                policies::raise_overflow_error<T>(function, 0, pol);   // reflection formula | ||
|  |        } | ||
|  | 
 | ||
|  |        *I = Iv; | ||
|  |        *K = Kv; | ||
|  |        return 0; | ||
|  |     } | ||
|  | 
 | ||
|  |     // x is positive until reflection | ||
|  |     W = 1 / x;                                 // Wronskian | ||
|  |     if (x <= 2)                                // x in (0, 2] | ||
|  |     { | ||
|  |         temme_ik(u, x, &Ku, &Ku1, pol);             // Temme series | ||
|  |     } | ||
|  |     else                                       // x in (2, \infty) | ||
|  |     { | ||
|  |         CF2_ik(u, x, &Ku, &Ku1, pol);               // continued fraction CF2_ik | ||
|  |     } | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(Ku); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(Ku1); | ||
|  |     prev = Ku; | ||
|  |     current = Ku1; | ||
|  |     T scale = 1; | ||
|  |     T scale_sign = 1; | ||
|  |     for (k = 1; k <= n; k++)                   // forward recurrence for K | ||
|  |     { | ||
|  |         T fact = 2 * (u + k) / x; | ||
|  |         if((tools::max_value<T>() - fabs(prev)) / fact < fabs(current)) | ||
|  |         { | ||
|  |            prev /= current; | ||
|  |            scale /= current; | ||
|  |            scale_sign *= boost::math::sign(current); | ||
|  |            current = 1; | ||
|  |         } | ||
|  |         next = fact * current + prev; | ||
|  |         prev = current; | ||
|  |         current = next; | ||
|  |     } | ||
|  |     Kv = prev; | ||
|  |     Kv1 = current; | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(Kv); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(Kv1); | ||
|  |     if(kind & need_i) | ||
|  |     { | ||
|  |        T lim = (4 * v * v + 10) / (8 * x); | ||
|  |        lim *= lim; | ||
|  |        lim *= lim; | ||
|  |        lim /= 24; | ||
|  |        if((lim < tools::epsilon<T>() * 10) && (x > 100)) | ||
|  |        { | ||
|  |           // x is huge compared to v, CF1 may be very slow | ||
|  |           // to converge so use asymptotic expansion for large | ||
|  |           // x case instead.  Note that the asymptotic expansion | ||
|  |           // isn't very accurate - so it's deliberately very hard  | ||
|  |           // to get here - probably we're going to overflow: | ||
|  |           Iv = asymptotic_bessel_i_large_x(v, x, pol); | ||
|  |        } | ||
|  |        else if((v > 0) && (x / v < 0.25)) | ||
|  |        { | ||
|  |           Iv = bessel_i_small_z_series(v, x, pol); | ||
|  |        } | ||
|  |        else | ||
|  |        { | ||
|  |           CF1_ik(v, x, &fv, pol);                         // continued fraction CF1_ik | ||
|  |           Iv = scale * W / (Kv * fv + Kv1);                  // Wronskian relation | ||
|  |        } | ||
|  |     } | ||
|  |     else | ||
|  |        Iv = std::numeric_limits<T>::quiet_NaN(); // any value will do | ||
|  | 
 | ||
|  |     if (reflect) | ||
|  |     { | ||
|  |         T z = (u + n % 2); | ||
|  |         T fact = (2 / pi<T>()) * (boost::math::sin_pi(z) * Kv); | ||
|  |         if(fact == 0) | ||
|  |            *I = Iv; | ||
|  |         else if(tools::max_value<T>() * scale < fact) | ||
|  |            *I = (org_kind & need_i) ? T(sign(fact) * scale_sign * policies::raise_overflow_error<T>(function, 0, pol)) : T(0); | ||
|  |         else | ||
|  |          *I = Iv + fact / scale;   // reflection formula | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         *I = Iv; | ||
|  |     } | ||
|  |     if(tools::max_value<T>() * scale < Kv) | ||
|  |        *K = (org_kind & need_k) ? T(sign(Kv) * scale_sign * policies::raise_overflow_error<T>(function, 0, pol)) : T(0); | ||
|  |     else | ||
|  |       *K = Kv / scale; | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(*I); | ||
|  |     BOOST_MATH_INSTRUMENT_VARIABLE(*K); | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | }}} // namespaces | ||
|  | 
 | ||
|  | #endif // BOOST_MATH_BESSEL_IK_HPP | ||
|  | 
 |