/// <summary> /// Compute K{v}(x) and K{v+1}(x). Approximately O(v). /// <para>Multiply K{v}(x) and K{v+1}(x) by 2^binaryScale to get the true result</para> /// </summary> /// <param name="v"></param> /// <param name="x"></param> /// <returns></returns> static (double Kv, double Kvp1, int BScale) K_CF(double v, double x) { int binaryScale = 0; // Ku = K{u}, Ku1 = K{u+1} double Ku, Kup1; int n = (int)Math.Floor(v + 0.5); double u = v - n; // -1/2 <= u < 1/2 // for x in (0, 2], use Temme series // otherwise use scaled continued fraction K_CF2 // to prevent Kv from underflowing too quickly for large x bool expScale = false; if (x <= 2) { (Ku, Kup1) = K_Temme(u, x); } else { expScale = true; (Ku, Kup1) = K_CF2(u, x, expScale); } var(Kvp1, Kv, bScale) = Recurrence.ForwardK_B(u + 1, x, n, Kup1, Ku); if (expScale) { var sf = DoubleX.Ldexp(DoubleX.Exp(-x), bScale); Kv = Kv * sf.Mantissa; Kvp1 = Kvp1 * sf.Mantissa; binaryScale = sf.Exponent; } else { binaryScale = bScale; } return(Kv, Kvp1, binaryScale); }
// // Series form for BesselY as z -> 0, // see: http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/01/0003/ // This series is only useful when the second term is small compared to the first // otherwise we get catestrophic cancellation errors. // // Approximating tgamma(v) by v^v, and assuming |tgamma(-z)| < eps we end up requiring: // eps/2 * v^v(x/2)^-v > (x/2)^v or Math.Log(eps/2) > v Math.Log((x/2)^2/v) // /// <summary> /// Series form for Y{v}(x) as z -> 0 and v is not an integer /// <para>Y{v}(z) = (-((z/2)^v Cos(πv) Γ(-v))/π)) * 0F1(; 1+v; -z^2/4) - ((z/2)^-v Γ(v))/π) * 0F1(; 1-v; -z^2/4)</para> /// </summary> /// <param name="v"></param> /// <param name="x"></param> /// <returns></returns> /// <seealso href="http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/26/01/02/0001/"/> public static DoubleX Y_SmallArgSeries(double v, double x) { Debug.Assert(v >= 0 && x >= 0); Debug.Assert( !Math2.IsInteger(v), "v cannot be an integer"); DoubleX powDivGamma; double lnp = v * Math.Log(x / 2); if (v > DoubleLimits.MaxGamma || lnp < DoubleLimits.MinLogValue || lnp > DoubleLimits.MaxLogValue) { powDivGamma = DoubleX.Exp(lnp - Math2.Lgamma(v)); } else { DoubleX p = Math.Pow(x / 2, v); DoubleX gam = Math2.Tgamma(v); powDivGamma = p / gam; } // Series 1: -((z/2)^-v Γ(v))/π) * 0F1(; 1-v; -z^2/4) double s1 = HypergeometricSeries.Sum0F1(1 - v, -x * x / 4); DoubleX result1 = -(s1/Math.PI)/ powDivGamma; // Series2: -((z/2)^v Cos(πv) Γ(-v))/π)) * 0F1(; 1+v; -z^2/4) // using the reflection formula: Γ(-v) = -π/(Γ(v) * v * Sin(π * v)) // prefix = (z/2)^v * Cos(π * v)/(Γ(v) * v * Sin(π * v)) // prefix = (z/2)^v * Cot(π * v) / (Γ(v) * v) DoubleX result2 = DoubleX.Zero; double cot = Math2.CotPI(v); if (cot != 0) { double s2 = HypergeometricSeries.Sum0F1(v + 1, -x * x / 4); result2 = powDivGamma * cot * s2 / v; // Do this all in DoubleX } return (result1 - result2); }
/// <summary> /// Returns K{n}(x) for integer order /// </summary> /// <param name="n"></param> /// <param name="x"></param> /// <returns></returns> public static double KN(int n, double x) { if (x < 0) { Policies.ReportDomainError("BesselK(v: {0}, x: {1}): Requires x >= 0 for real result", n, x); return(double.NaN); } if (x == 0) { return(double.PositiveInfinity); } // even function // K{-n}(z) = K{n}(z) if (n < 0) { n = -n; } if (n == 0) { return(K0(x)); } if (n == 1) { return(K1(x)); } double v = n; // Hankel is fast, and reasonably accurate, saving us from many recurrences. if (x >= HankelAsym.IKMinX(v)) { return(HankelAsym.K(v, x)); } // the uniform expansion is here as a last resort // to limit the number of recurrences, but it is less accurate. if (UniformAsym.IsIKAvailable(v, x)) { return(UniformAsym.K(v, x)); } // Since K{v}(x) has a (e^-x)/sqrt(x) multiplier // using recurrence can underflow too quickly for large x, // so, use a scaled version double result; if (x > 1) { double prev = K0(x, true); double current = K1(x, true); // for large v and x this number can get very large // maximum observed K(1000,10) = 2^6211 var(Kv, _, binaryScale) = Recurrence.ForwardK_B(1, x, n - 1, current, prev); // Compute: value * 2^(binaryScale) * e^-x if (x < -DoubleX.MinLogValue) { DoubleX exs = DoubleX.Ldexp(DoubleX.Exp(-x), binaryScale); result = Math2.Ldexp(Kv * exs.Mantissa, exs.Exponent); } else { result = Math.Exp(-x + Math.Log(Kv) + binaryScale * Constants.Ln2); } } else { double prev = K0(x); double current = K1(x); result = Recurrence.ForwardK(1, x, n - 1, current, prev).Kvpn; } return(result); }