Beispiel #1
0
        /// <summary>
        /// Compute (J, Y) using Steeds method
        /// </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_Steed(double v, double x, bool needJ, bool needY)
        {
            Debug.Assert(x > 0 && v >= 0);
            Debug.Assert(needJ || needY);

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

            int    n = (int)Math.Floor(v + 0.5);
            double u = v - n;

            Debug.Assert(u >= -0.5 && u < 0.5); // Ensure u in [-1/2, 1/2)

            // compute J{v+1}(x) / J{v}(x)
            var(fv, s) = J_CF1(v, x);
            var(Jvmn, Jvmnp1, scale) = Recurrence.BackwardJY_B(v, x, n, s, fv * s);

            double ratio = s / Jvmn;       // normalization
            double fu    = Jvmnp1 / Jvmn;

            // prev/current, can also call CF1_jy() to get fu, not much difference in precision
            //double fuCF;
            //int sfu;
            //Bessel.CF1_jy(u,x,fuCF,sfu);
            //fu = fuCF;

            var(p, q) = JY_CF2(u, x);           // continued fraction JY_CF2
            double t     = u / x - fu;          // t = J'/J
            double gamma = (p - t) / q;

            //
            // We can't allow gamma to cancel out to zero competely as it messes up
            // the subsequent logic.  So pretend that one bit didn't cancel out
            // and set to a suitably small value.  The only test case we've been able to
            // find for this, is when v = 8.5 and x = 4*PI.
            //
            if (gamma == 0)
            {
                gamma = u * DoubleLimits.MachineEpsilon / x;
            }

            double W  = (2 / Math.PI) / x;              // Wronskian
            double Ju = Math.Sign(Jvmn) * Math.Sqrt(W / (q + gamma * (p - t)));

            double Jv = Ju * ratio;                    // normalization

            J = Math2.Ldexp(Jv, -scale);

            if (needY)
            {
                double Yu  = gamma * Ju;
                double Yu1 = Yu * (u / x - p - q / gamma);

                var(JYvpn, JYvpnm1, YScale) = Recurrence.ForwardJY_B(u + 1, x, n, Yu1, Yu);
                Y = DoubleX.Ldexp(JYvpnm1, YScale);
            }

            return(J, Y);
        }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
        /// <summary>
        /// Computes Y{v}(x), where v is an integer
        /// </summary>
        /// <param name="v">Integer order</param>
        /// <param name="x">Argument. Requires x &gt; 0</param>
        /// <returns></returns>
        public static DoubleX YN(double v, double x)
        {
            if ((x == 0) && (v == 0))
            {
                return(double.NegativeInfinity);
            }
            if (x <= 0)
            {
                Policies.ReportDomainError("BesselY(v: {0}, x: {1}): Complex number result not supported. Requires x >= 0", v, x);
                return(double.NaN);
            }


            //
            // Reflection comes first:
            //

            double sign = 1;

            if (v < 0)
            {
                // Y_{-n}(z) = (-1)^n Y_n(z)
                if (Math2.IsOdd(v))
                {
                    sign = -1;
                }
                v = -v;
            }

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

            if (v > int.MaxValue)
            {
                Policies.ReportNotImplementedError("BesselY(v: {0}, x: {1}): Large integer values not yet implemented", v, x);
                return(double.NaN);
            }

            int n = (int)v;

            if (n == 0)
            {
                return(Y0(x));
            }

            if (n == 1)
            {
                return(sign * Y1(x));
            }

            if (x < DoubleLimits.RootMachineEpsilon._2)
            {
                DoubleX smallArgValue = YN_SmallArg(n, x);
                return(smallArgValue * sign);
            }

            // if v > MinAsymptoticV, use the asymptotics
            const int MinAsymptoticV = 7; // arbitrary constant to reduce iterations

            if (v > MinAsymptoticV)
            {
                double  Jv;
                DoubleX Yv;
                if (JY_TryAsymptotics(v, x, out Jv, out Yv, false, true))
                {
                    return(sign * Yv);
                }
            }


            // forward recurrence always OK (though unstable)
            double prev    = Y0(x);
            double current = Y1(x);

            var(Yvpn, Yvpnm1, YScale) = Recurrence.ForwardJY_B(1.0, x, n - 1, current, prev);

            return(DoubleX.Ldexp(sign * Yvpn, YScale));
        }
Beispiel #4
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);
        }
Beispiel #5
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);
        }
Beispiel #6
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);
        }