Ejemplo n.º 1
0
        /// <summary>
        /// Simultaneously compute I{v}(x) and K{v}(x)
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <param name="needI"></param>
        /// <param name="needK"></param>
        /// <returns>A tuple of (I, K). If needI == false or needK == false, I = NaN or K = NaN  respectively</returns>
        public static (double I, double K) IK(double v, double x, bool needI, bool needK)
        {
            Debug.Assert(needI || needK, "NeedI and NeedK cannot both be false");

            // set initial values for parameters
            double I = double.NaN;
            double K = double.NaN;

            // check v first so that there are no integer throws later
            if (Math.Abs(v) > int.MaxValue)
            {
                Policies.ReportNotImplementedError("BesselIK(v: {0}, x: {1}): Requires |v| <= {2}", v, x, int.MaxValue);
                return(I, K);
            }


            if (v < 0)
            {
                v = -v;

                // for integer orders only, we can use the following identities:
                //  I{-n}(x) = I{n}(v)
                //  K{-n}(x) = K{n}(v)
                // for any v, use reflection rule:
                //  I{-v}(x) = I{v}(x) + (2/PI) * sin(pi*v)*K{v}(x)
                //  K{-v}(x) = K{v}(x)

                if (needI && !Math2.IsInteger(v))
                {
                    var(IPos, KPos) = IK(v, x, true, true);

                    I = IPos + (2 / Math.PI) * Math2.SinPI(v) * KPos;
                    if (needK)
                    {
                        K = KPos;
                    }
                    return(I, K);
                }
            }

            if (x < 0)
            {
                Policies.ReportDomainError("BesselIK(v: {0}, x: {1}): Complex result not supported. Requires x >= 0", v, x);
                return(I, K);
            }

            // both x and v are non-negative from here
            Debug.Assert(x >= 0 && v >= 0);

            if (x == 0)
            {
                if (needI)
                {
                    I = (v == 0) ? 1.0 : 0.0;
                }
                if (needK)
                {
                    K = double.PositiveInfinity;
                }
                return(I, K);
            }

            if (needI && (x < 2 || 3 * (v + 1) > x * x))
            {
                I     = I_SmallArg(v, x);
                needI = false;
                if (!needK)
                {
                    return(I, K);
                }
            }

            // Hankel is fast, and reasonably accurate.
            // at x == 32, it will converge in about 15 iterations
            if (x >= HankelAsym.IKMinX(v))
            {
                if (needK)
                {
                    K = HankelAsym.K(v, x);
                }
                if (needI)
                {
                    I = HankelAsym.I(v, x);
                }
                return(I, K);
            }

            // 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))
            {
                if (needK)
                {
                    K = UniformAsym.K(v, x);
                }
                if (needI)
                {
                    I = UniformAsym.I(v, x);
                }
                return(I, K);
            }


            // K{v}(x) and K{v+1}(x), binary scaled
            var(Kv, Kv1, binaryScale) = K_CF(v, x);
            if (needK)
            {
                K = Math2.Ldexp(Kv, binaryScale);
            }
            if (needI)
            {
                // use the Wronskian relationship

                // Since CF1 is O(x) for x > v, try to weed out obvious overflows
                // Note: I{v+1}(x)/I{v}(x) is in [0, 1].
                // I{0}(713. ...) == MaxDouble
                const double I0MaxX = 713;
                if (x > I0MaxX && x > v)
                {
                    I = Math2.Ldexp(1.0 / (x * (Kv + Kv1)), -binaryScale);
                    if (double.IsInfinity(I))
                    {
                        return(I, K);
                    }
                }

                double W  = 1 / x;
                double fv = I_CF1(v, x);
                I = Math2.Ldexp(W / (Kv * fv + Kv1), -binaryScale);
            }


            return(I, K);
        }
Ejemplo n.º 2
0
        /// <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);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Try to use asymptotics to generate the result
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <param name="J"></param>
        /// <param name="Y"></param>
        /// <param name="needJ"></param>
        /// <param name="needY"></param>
        /// <returns></returns>
        public static bool JY_TryAsymptotics(double v, double x, out double J, out DoubleX Y, bool needJ, bool needY)
        {
            J = double.NaN;
            Y = DoubleX.NaN;

            // Try asymptotics directly:

            if (x > v)
            {
                // x > v*v
                if (x >= HankelAsym.JYMinX(v))
                {
                    var result = HankelAsym.JY(v, x);
                    if (needJ)
                    {
                        J = result.J;
                    }
                    if (needY)
                    {
                        Y = result.Y;
                    }
                    return(true);
                }

                // Try Asymptotic Phase for x > 47v
                if (x >= MagnitudePhase.MinX(v))
                {
                    var result = MagnitudePhase.BesselJY(v, x);
                    if (needJ)
                    {
                        J = result.J;
                    }
                    if (needY)
                    {
                        Y = result.Y;
                    }
                    return(true);
                }
            }

            if (UniformAsym.IsJYPrecise(v, x))
            {
                var(Jv, Yv) = UniformAsym.JY(v, x);
                if (needJ)
                {
                    J = Jv;
                }
                if (needY)
                {
                    Y = Yv;
                }
                return(true);
            }

            // Try asymptotics with recurrence
            if (x > v && x >= HankelAsym.JYMinX(v - Math.Floor(v) + 1))
            {
                var(Jv, Yv) = JY_AsymRecurrence(v, x, needJ, needY);
                if (needJ)
                {
                    J = Jv;
                }
                if (needY)
                {
                    Y = Yv;
                }
                return(true);
            }

            return(false);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Compute J{v}(x) and Y{v}(x)
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <param name="needJ"></param>
        /// <param name="needY"></param>
        /// <returns></returns>
        public static (double J, DoubleX Y) JY(double v, double x, bool needJ, bool needY)
        {
            Debug.Assert(needJ || needY);

            // uses  Steed's method
            // see Barnett et al, Computer Physics Communications, vol 8, 377 (1974)

            // set [out] parameters
            double  J = double.NaN;
            DoubleX Y = DoubleX.NaN;

            // check v first so that there are no integer throws later
            if (Math.Abs(v) > int.MaxValue)
            {
                Policies.ReportNotImplementedError("BesselJY(v: {0}): Large |v| > int.MaxValue not yet implemented", v);
                return(J, Y);
            }


            if (v < 0)
            {
                v = -v;

                if (Math2.IsInteger(v))
                {
                    // for integer orders only, we can use the following identities:
                    //      J{-n}(x) = (-1)^n * J{n}(x)
                    //      Y{-n}(x) = (-1)^n * Y{n}(x)

                    if (Math2.IsOdd(v))
                    {
                        var(JPos, YPos) = JY(v, x, needJ, needY);

                        if (needJ)
                        {
                            J = -JPos;
                        }
                        if (needY)
                        {
                            Y = -YPos;
                        }

                        return(J, Y);
                    }
                }
                if (v - Math.Floor(v) == 0.5)
                {
                    Debug.Assert(v >= 0);

                    // use reflection rule:
                    // for integer m >= 0
                    // J{-(m+1/2)}(x) = (-1)^(m+1) * Y{m+1/2}(x)
                    // Y{-(m+1/2)}(x) = (-1)^m * J{m+1/2}(x)

                    // call the general bessel functions with needJ and needY reversed
                    var(JPos, YPos) = JY(v, x, needY, needJ);

                    double m     = v - 0.5;
                    bool   isOdd = Math2.IsOdd(m);

                    if (needJ)
                    {
                        double y = (double)YPos;
                        J = isOdd ? y : -y;
                    }
                    if (needY)
                    {
                        Y = isOdd ? -JPos : JPos;
                    }

                    return(J, Y);
                }
                else
                {
                    // use reflection rule:
                    // J{-v}(x) = cos(pi*v)*J{v}(x) - sin(pi*v)*Y{v}(x)
                    // Y{-v}(x) = sin(pi*v)*J{v}(x) + cos(pi*v)*Y{v}(x)

                    var(JPos, YPos) = JY(v, x, true, true);

                    double cp = Math2.CosPI(v);
                    double sp = Math2.SinPI(v);

                    J = cp * JPos - (double)(sp * YPos);
                    Y = sp * JPos + cp * YPos;

                    return(J, Y);
                }
            }

            // both x and v are positive from here
            Debug.Assert(x >= 0 && v >= 0);

            if (x == 0)
            {
                // For v > 0
                if (needJ)
                {
                    J = 0;
                }
                if (needY)
                {
                    Y = DoubleX.NegativeInfinity;
                }
                return(J, Y);
            }

            int    n = (int)Math.Floor(v + 0.5);
            double u = v - n;                              // -1/2 <= u < 1/2

            // is it an integer?
            if (u == 0)
            {
                if (v == 0)
                {
                    if (needJ)
                    {
                        J = J0(x);
                    }
                    if (needY)
                    {
                        Y = Y0(x);
                    }
                    return(J, Y);
                }

                if (v == 1)
                {
                    if (needJ)
                    {
                        J = J1(x);
                    }
                    if (needY)
                    {
                        Y = Y1(x);
                    }
                    return(J, Y);
                }

                // for integer order only
                if (needY && x < DoubleLimits.RootMachineEpsilon._2)
                {
                    Y = YN_SmallArg(n, x);
                    if (!needJ)
                    {
                        return(J, Y);
                    }
                    needY = !needY;
                }
            }


            if (needJ && ((x < 5) || (v > x * x / 4)))
            {
                // always use the J series if we can
                J = J_SmallArg(v, x);
                if (!needY)
                {
                    return(J, Y);
                }
                needJ = !needJ;
            }

            if (needY && x <= 2)
            {
                // J should have already been solved above
                Debug.Assert(!needJ);

                // Evaluate using series representations.
                // Much quicker than Y_Temme below.
                // This is particularly important for x << v as in this
                // area Y_Temme may be slow to converge, if it converges at all.

                // for non-integer order only
                if (u != 0)
                {
                    if ((x < 1) && (Math.Log(DoubleLimits.MachineEpsilon / 2) > v * Math.Log((x / 2) * (x / 2) / v)))
                    {
                        Y = Y_SmallArgSeries(v, x);
                        return(J, Y);
                    }
                }

                // Use Temme to find Yu where |u| <= 1/2, then use forward recurrence for Yv

                var(Yu, Yu1) = Y_Temme(u, x);
                var(Yvpn, Yvpnm1, YScale) = Recurrence.ForwardJY_B(u + 1, x, n, Yu1, Yu);
                Y = DoubleX.Ldexp(Yvpnm1, YScale);
                return(J, Y);
            }

            Debug.Assert(x > 2 && v >= 0);

            // Try asymptotics directly:

            if (x > v)
            {
                // x > v*v
                if (x >= HankelAsym.JYMinX(v))
                {
                    var result = HankelAsym.JY(v, x);
                    if (needJ)
                    {
                        J = result.J;
                    }
                    if (needY)
                    {
                        Y = result.Y;
                    }
                    return(J, Y);
                }

                // Try Asymptotic Phase for x > 47v
                if (x >= MagnitudePhase.MinX(v))
                {
                    var result = MagnitudePhase.BesselJY(v, x);
                    if (needJ)
                    {
                        J = result.J;
                    }
                    if (needY)
                    {
                        Y = result.Y;
                    }
                    return(J, Y);
                }
            }

            // fast and accurate within a limited range of v ~= x
            if (UniformAsym.IsJYPrecise(v, x))
            {
                var(Jv, Yv) = UniformAsym.JY(v, x);
                if (needJ)
                {
                    J = Jv;
                }
                if (needY)
                {
                    Y = Yv;
                }
                return(J, Y);
            }

            // Try asymptotics with recurrence:
            if (x > v && x >= HankelAsym.JYMinX(v - Math.Floor(v) + 1))
            {
                var(Jv, Yv) = JY_AsymRecurrence(v, x, needJ, needY);
                if (needJ)
                {
                    J = Jv;
                }
                if (needY)
                {
                    Y = Yv;
                }
                return(J, Y);
            }

            // Use Steed's Method
            var(SteedJv, SteedYv) = JY_Steed(v, x, needJ, needY);
            if (needJ)
            {
                J = SteedJv;
            }
            if (needY)
            {
                Y = SteedYv;
            }
            return(J, Y);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Computes J{v}(x), Y{v}(x) using a combination of asymptotic approximation and recurrence
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <param name="needJ"></param>
        /// <param name="needY"></param>
        /// <returns></returns>
        static (double J, DoubleX Y) JY_AsymRecurrence(double v, double x, bool needJ, bool needY)
        {
            Debug.Assert(v >= 0 && x >= 1, "Requires positive values for v,x");
            Debug.Assert(v < int.MaxValue, "v too large: v = " + v);

            var J = double.NaN;
            var Y = DoubleX.NaN;

            int    nPos = (int)Math.Floor(v);
            double uPos = v - nPos;

            // Using Hankel, find:
            // J{v-Floor(v)}(x) and J{v-Floor(v)+1}(x)
            // Y{v-Floor(v)}(x) and Y{v-Floor(v)+1}(x)
            // then use recurrence to find J{v}(x), Y{v}(x)

            double u0, u1;;
            int    n;

            if (x >= 9)
            {
                // set the start of the recurrence near sqrt(x)
                double maxV = Math.Floor(Math.Sqrt(x));
                u1 = (maxV - 1) + uPos;
                u0 = u1 - 1;
                n  = (int)Math.Floor(v - u1 + 0.5);
                Debug.Assert(n >= 0);
            }
            else
            {
                u0 = uPos;
                u1 = uPos + 1;
                n  = nPos - 1;
            }

            Debug.Assert(x >= HankelAsym.JYMinX(u1), "x is too small for HankelAsym");

            var(Ju, Yu)     = HankelAsym.JY(u0, x);
            var(Jup1, Yup1) = HankelAsym.JY(u1, x);

            if (needJ)
            {
                if (v < x)
                {
                    J = Recurrence.ForwardJY(u1, x, n, Jup1, Ju).JYvpn;
                }
                else
                {
                    // Use fv = J{v+1}(x) / J{v}(x)
                    // and backward recurrence to find (J{v-n+1}/J{v-n})
                    var(fv, s) = J_CF1(v, x);
                    var(Jvmn, Jvmnp1, scale) = Recurrence.BackwardJY_B(v, x, n, s, fv * s);

                    var Jv = Math2.Ldexp(Jup1 / Jvmn, -scale);

                    J = s * Jv;      // normalization
                }
            }

            if (needY)
            {
                var(Yv, Yvm1, YScale) = Recurrence.ForwardJY_B(u1, x, n, Yup1, Yu);
                Y = DoubleX.Ldexp(Yv, YScale);
            }

            return(J, Y);
        }