コード例 #1
0
        /// <summary>
        /// Returns the value of the Airy Ai function at <paramref name="x"/>
        /// </summary>
        /// <param name="x">The function argument</param>
        /// <returns></returns>
        public static double AiryAi(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("AiryAi(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                return(0);
            }

            double absX = Math.Abs(x);

            if (x >= -3 && x <= 2)
            {
                // Use small series. See:
                // http://functions.wolfram.com/Bessel-TypeFunctions/AiryAi/06/01/02/01/0004/
                // http://dlmf.nist.gov/9.4

                double z  = x * x * x / 9;
                double s1 = Constants.AiryAi0 * HypergeometricSeries.Sum0F1(2 / 3.0, z);
                double s2 = x * Constants.AiryAiPrime0 * HypergeometricSeries.Sum0F1(4 / 3.0, z);
                return(s1 + s2);
            }

            const double v    = 1.0 / 3;
            double       zeta = 2 * absX * Math.Sqrt(absX) / 3;

            if (x < 0)
            {
                if (x < -32)
                {
                    return(AiryAsym.AiBiNeg(x).Ai);
                }

                // The following is
                //double j1 = Math2.BesselJ(v, zeta);
                //double j2 = Math2.BesselJ(-v, zeta);
                //double ai = Math.Sqrt(-x) * (j1 + j2) / 3;

                var(J, Y) = _Bessel.JY(v, zeta, true, true);
                double s  = 0.5 * (J - ((double)Y) / Constants.Sqrt3);
                double ai = Math.Sqrt(-x) * s;

                return(ai);
            }
            else
            {
                // Use the K relationship: http://dlmf.nist.gov/9.6

                if (x >= 16)
                {
                    return(AiryAsym.Ai(x));
                }

                double ai = Math2.BesselK(v, zeta) * Math.Sqrt(x / 3) / Math.PI;
                return(ai);
            }
        }
コード例 #2
0
        /// <summary>
        /// Returns the Spherical Bessel function of the first kind: j<sub>n</sub>(x)
        /// </summary>
        /// <param name="n">Order. Requires n &gt;= 0</param>
        /// <param name="x">Argument. Requires x &gt;= 0</param>
        /// <returns></returns>
        public static double SphBesselJ(int n, double x)
        {
            if (!(n >= 0) || !(x >= 0))
            {
                Policies.ReportDomainError("SphBesselJ(n: {0}, x: {1}): Requires n >= 0, x >= 0", n, x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                return(0);
            }

            //
            // Special case, n == 0 resolves down to the sinus cardinal of x:
            //
            if (n == 0)
            {
                return(Math2.Sinc(x));
            }
            if (x < 1)
            {
                // the straightforward evaluation below can underflow too quickly when x is small
                double result = (0.5 * Constants.SqrtPI);
                result *= (Math.Pow(x / 2, n) / Math2.Tgamma(n + 1.5));
                if (result != 0)
                {
                    result *= HypergeometricSeries.Sum0F1(n + 1.5, -x * x / 4);
                }
                return(result);
            }

            // Default case is just a straightforward evaluation of the definition:
            return(Constants.SqrtHalfPI * BesselJ(n + 0.5, x) / Math.Sqrt(x));
        }
コード例 #3
0
        /// <summary>
        /// Returns Iv(x) for small <paramref name="x"/>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double I_SmallArg(double v, double x)
        {
            double prefix;
            if (v <= DoubleLimits.MaxGamma - 1) {
                prefix = Math.Pow(x / 2, v) / Math2.Tgamma(v + 1);
            } else {
                prefix = StirlingGamma.PowDivGamma(x / 2, v) / v;
            }
            if (prefix == 0)
                return prefix;

            double series = HypergeometricSeries.Sum0F1(v + 1, x * x / 4);
            return prefix * series;
        }
コード例 #4
0
        /// <summary>
        /// Series evaluation for Jv(x), for small x
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double J_SmallArg(double v, double x)
        {
            Debug.Assert(v >= 0);

            // See http://functions.wolfram.com/Bessel-TypeFunctions/BesselJ/06/01/04/01/01/0003/
            // Converges rapidly for all x << v.
            // Most of the error occurs calculating the prefix

            double prefix;
            if (v <= DoubleLimits.MaxGamma - 1)
                prefix = Math.Pow(x / 2, v) / Math2.Tgamma(v + 1);
            else
                prefix = StirlingGamma.PowDivGamma(x / 2, v)/v;

            if (prefix == 0)
                return prefix;

            double series = HypergeometricSeries.Sum0F1(v + 1, -x * x / 4);

            return prefix * series;
        }
コード例 #5
0
        //
        // 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 -&gt; 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);
        }
コード例 #6
0
        /// <summary>
        /// Returns the value of first derivative to the Airy Bi function at <paramref name="x"/>
        /// </summary>
        /// <param name="x">AiryBiPrime function argument</param>
        /// <returns></returns>
        public static double AiryBiPrime(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("AiryBiPrime(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x < 0)
                {
                    return(0);
                }
                return(double.PositiveInfinity);
            }

            double absX = Math.Abs(x);

            if (x >= -3 && x <= 3)
            {
                // Use small series. See:
                // http://functions.wolfram.com/Bessel-TypeFunctions/AiryBiPrime/26/01/01/
                // http://dlmf.nist.gov/9.4

                double z  = x * x * x / 9;
                double s1 = x * x * (Constants.AiryBi0 / 2) * HypergeometricSeries.Sum0F1(5 / 3.0, z);
                double s2 = Constants.AiryBiPrime0 * HypergeometricSeries.Sum0F1(1 / 3.0, z);

                return(s1 + s2);
            }


            const double v    = 2.0 / 3;
            double       zeta = 2 * absX * Math.Sqrt(absX) / 3;

            if (x < 0)
            {
                if (x < -32)
                {
                    return(AiryAsym.AiBiPrimeNeg(x).Bi);
                }

                // The following is
                //double j1 = Math2.BesselJ(v, zeta);
                //double j2 = Math2.BesselJ(-v, zeta);
                //double bip = -x * (j1 + j2) / Constants.Sqrt3;
                //return bip;

                var(J, Y) = _Bessel.JY(v, zeta, true, true);
                double s   = 0.5 * (J / Constants.Sqrt3 - ((double)Y));
                double bip = -x * s;
                return(bip);
            }
            else
            {
                if (x >= 16)
                {
                    return(AiryAsym.BiPrime(x));
                }

                // The following is
                //double j1 = Math2.BesselI(v, zeta);
                //double j2 = Math2.BesselI(-v, zeta);
                //double bip = (x / Constants.Sqrt3) * (j1 + j2);

                var(I, K) = _Bessel.IK(v, zeta, true, true);
                double s   = (2 / Constants.Sqrt3 * I + K / Math.PI);
                var    bip = x * s;
                return(bip);
            }
        }
コード例 #7
0
        /// <summary>
        /// Returns the value of the first derivative to the Airy Ai function at <paramref name="x"/>
        /// </summary>
        /// <param name="x">The function argument</param>
        /// <returns></returns>
        public static double AiryAiPrime(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("AiryAiPrime(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x > 0)
                {
                    return(0);
                }

                Policies.ReportDomainError("AiryAiPrime(x: {0}): Requires x != -Infinity", x);
                return(double.NaN);
            }

            double absX = Math.Abs(x);

            if (x >= -3 && x <= 2)
            {
                // Use small series. See:
                // http://functions.wolfram.com/Bessel-TypeFunctions/AiryAiPrime/26/01/01/
                // http://dlmf.nist.gov/9.4

                double z  = x * x * x / 9;
                double s1 = x * x * (Constants.AiryAi0 / 2) * HypergeometricSeries.Sum0F1(5 / 3.0, z);
                double s2 = Constants.AiryAiPrime0 * HypergeometricSeries.Sum0F1(1 / 3.0, z);

                return(s1 + s2);
            }

            const double v    = 2.0 / 3;
            double       zeta = 2 * absX * Math.Sqrt(absX) / 3;

            if (x < 0)
            {
                if (x < -32)
                {
                    return(AiryAsym.AiBiPrimeNeg(x).Ai);
                }

                // The following is
                //double j1 = Math2.BesselJ(v, zeta);
                //double j2 = Math2.BesselJ(-v, zeta);
                //double aip = -x * (j1 - j2) / 3;

                var(J, Y) = _Bessel.JY(v, zeta, true, true);
                double s   = 0.5 * (J + ((double)Y) / Constants.Sqrt3);
                double aip = -x * s;
                return(aip);
            }
            else
            {
                if (x >= 16)
                {
                    return(AiryAsym.AiPrime(x));
                }

                double aip = -Math2.BesselK(v, zeta) * x / (Constants.Sqrt3 * Math.PI);
                return(aip);
            }
        }