예제 #1
0
        /// <summary>
        /// Returns the sum of a series with each term given by the function f().
        /// </summary>
        /// <param name="f">Delegate returning the next term</param>
        /// <param name="initialValue">initial value</param>
        /// <param name="tolerance">Stops when the relative tolerance is reached</param>
        /// <param name="maxIterations">The maximum number of terms</param>
        /// <returns>
        /// The sum of the series or NaN if the series does not converge within the maximum
        /// number of iterations specified in the policy.
        /// </returns>
        public static double Eval(Func <double> f, double initialValue, double tolerance, uint maxIterations)
        {
            if (f == null)
            {
                Policies.ReportDomainError("Requires f != null");
                return(double.NaN);
            }
            if (!(tolerance >= 0))
            {
                Policies.ReportDomainError("Requires relative tolerance >= 0");
                return(double.NaN);
            }


            double sum = initialValue;

            for (UInt32 i = 0; i < maxIterations; i++)
            {
                double prevSum = sum;
                double delta   = f();
                sum += delta;

                if (Math.Abs(delta) <= Math.Abs(prevSum) * tolerance)
                {
                    return(sum);
                }
            }

            Policies.ReportConvergenceError("Series did not converge in {0} iterations", maxIterations);
            return(double.NaN);
        }
예제 #2
0
        /// <summary>
        /// Optimization of Sum2F1 when a1 == 1. 
        /// <para>Sum2F1_A1 = addend + Σ( (z^k/(b1)_k)/(b2)_k) k={0, ∞}</para>
        /// </summary>
        /// <param name="b1"></param>
        /// <param name="b2"></param>
        /// <param name="z"></param>
        /// <param name="addend">The addend to the series</param>
        /// <returns></returns>
        private static double Sum1F2_A1(double b1, double b2, double z, double addend)
        {

            double term = 1.0;
            double sum = 1.0 + addend;

            int n = 0; 
            for (; n < Policies.MaxSeriesIterations; n++) {

                double prevSum = sum;
                term *= ((z / (b1 + n)) / (b2 + n));
                sum += term;

#if CMP_FLOAT
                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                    return sum;
#else
                if (sum == prevSum)
                    return sum;
#endif

            }

            Policies.ReportConvergenceError("Sum1F2(a1: {0}, b1: {1}, b2: {2}, z: {3}, addend: {4}): No convergence after {0} iterations", 1, b1, b2, z, addend, n);
            return double.NaN;
        }
예제 #3
0
        // Modified Bessel functions of the first and second kind of fractional order


        /// <summary>
        /// Returns (K{v}(x), K{v+1}(x))
        /// <para>|x| ≤ 2, the Temme series converges rapidly</para>
        /// <para>|x| &gt; 2, convergence slows as |x| increases</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        static (double Kv, double Kvp1) K_Temme(double v, double x)
        {
            // see: Temme, Journal of Computational Physics, vol 21, 343 (1976)

            const double tolerance = DoubleLimits.MachineEpsilon;

            Debug.Assert(Math.Abs(x) <= 2);
            Debug.Assert(Math.Abs(v) <= 0.5);

            double gp = Math2.Tgamma1pm1(v);
            double gm = Math2.Tgamma1pm1(-v);

            double a      = Math.Log(x / 2);
            double b      = Math.Exp(v * a);
            double sigma  = -a * v;
            double c      = Math.Abs(v) < DoubleLimits.MachineEpsilon ? 1 : (Math2.SinPI(v) / (v * Math.PI));
            double d      = Math2.Sinhc(sigma);
            double gamma1 = Math.Abs(v) < DoubleLimits.MachineEpsilon ? -Constants.EulerMascheroni : ((0.5 / v) * (gp - gm) * c);
            double gamma2 = (2 + gp + gm) * c / 2;

            // initial values
            double p    = (gp + 1) / (2 * b);
            double q    = (1 + gm) * b / 2;
            double f    = (Math.Cosh(sigma) * gamma1 + d * (-a) * gamma2) / c;
            double h    = p;
            double coef = 1;
            double sum  = coef * f;
            double sum1 = coef * h;

            // series summation
            int k;

            for (k = 1; k < Policies.MaxSeriesIterations; 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 (Math.Abs(coef * f) < Math.Abs(sum) * tolerance)
                {
                    break;
                }
            }


            if (k >= Policies.MaxSeriesIterations)
            {
                Policies.ReportConvergenceError("K_Temme(v:{0}, x:{1}) did not converge after {2} iterations", v, x, Policies.MaxSeriesIterations);
                return(double.NaN, double.NaN);
            }

            return(sum, 2 * sum1 / x);
        }
예제 #4
0
        /// <summary>
        /// Returns I{v}(x) using Hankel’s Expansion for large argument.
        /// <para>If <paramref name="expScale"/> == true, returns e^-x * I{v}(x)</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <param name="expScale"></param>
        /// <returns></returns>
        /// <seealso href="http://dlmf.nist.gov/10.40#E1"/>
        public static double I(double v, double x, bool expScale = false)
        {
            // See: http://dlmf.nist.gov/10.40#i

            double mu = 4 * v * v;
            double ex = 8 * x;

            bool   ok = false;
            double sum = 1, lastSum = sum;
            double sq   = 1;
            double term = 1;
            int    n    = 1;

            for (; n < Policies.MaxSeriesIterations; n++)
            {
                // check to see if the series diverges
                double mult = -((mu - sq * sq) / (n * ex));
                if (Math.Abs(mult) >= 1.0)
                {
                    break;
                }


                lastSum = sum;
                term   *= mult;
                sum    += term;
                sq     += 2;

                if (Math.Abs(term) <= Math.Abs(lastSum) * Policies.SeriesTolerance)
                {
                    ok = true;
                    break;
                }
            }

            if (!ok)
            {
                Policies.ReportConvergenceError("HankelAsym.I(v: {0}, x: {1}): No convergence after {2} iterations", v, x, n);
                return(double.NaN);
            }

            double result;

            if (expScale)
            {
                result = (sum / Math.Sqrt(x)) * Constants.RecipSqrt2PI;
            }
            else
            {
                // Try to avoid overflow:
                double e = Math.Exp(x / 2);
                result = e / Math.Sqrt(x) * Constants.RecipSqrt2PI * sum * e;
            }

            return(result);
        }
예제 #5
0
        /// <summary>
        /// Returns (Y{v}(x), Y{v+1}(x)) by Temme's method
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        static (double Yv, double Yvp1) Y_Temme(double v, double x)
        {
            //  see Temme, Journal of Computational Physics, vol 21, 343 (1976)

            Debug.Assert(Math.Abs(v) <= 0.5);  // precondition for using this routine

            double gp   = Math2.Tgamma1pm1(v);
            double gm   = Math2.Tgamma1pm1(-v);
            double spv  = Math2.SinPI(v);
            double spv2 = Math2.SinPI(v / 2);
            double xp   = Math.Pow(x / 2, v);

            double a     = Math.Log(x / 2);
            double sigma = -a * v;
            double d     = Math2.Sinhc(sigma);
            double e     = Math.Abs(v) < DoubleLimits.MachineEpsilon ? ((Math.PI * Math.PI / 2) * v) : (2 * spv2 * spv2 / v);

            double g1   = (v == 0) ? -Constants.EulerMascheroni : ((gp - gm) / ((1 + gp) * (1 + gm) * 2 * v));
            double g2   = (2 + gp + gm) / ((1 + gp) * (1 + gm) * 2);
            double vspv = (Math.Abs(v) < DoubleLimits.MachineEpsilon) ? 1 / Math.PI : v / spv;
            double f    = (g1 * Math.Cosh(sigma) - g2 * a * d) * 2 * vspv;

            double p = vspv / (xp * (1 + gm));
            double q = vspv * xp / (1 + gp);

            double g    = f + e * q;
            double h    = p;
            double coef = 1;
            double sum  = coef * g;
            double sum1 = coef * h;

            double v2        = v * v;
            double coef_mult = -x * x / 4;


            int k = 1;

            for (; k < Policies.MaxSeriesIterations; k++)
            {
                f     = (k * f + p + q) / (k * k - v2);
                p    /= k - v;
                q    /= k + v;
                g     = f + e * q;
                h     = p - k * g;
                coef *= coef_mult / k;
                sum  += coef * g;
                sum1 += coef * h;
                if (Math.Abs(coef * g) < Math.Abs(sum) * Policies.SeriesTolerance)
                {
                    return(-sum, -2 * sum1 / x);
                }
            }

            Policies.ReportConvergenceError("Series did not converge after {0} iterations", Policies.MaxSeriesIterations);
            return(double.NaN, double.NaN);
        }
예제 #6
0
        /// <summary>
        /// Returns the sum of a hypergeometric series 3F1. 
        /// <para>Sum3F1 = addend + Σ(( (a1)_n * (a2)_n * (a3)_n)/(b1)_n * (z^n/n!)) n={0,∞}</para>
        /// </summary>
        /// <param name="a1">First numerator</param>
        /// <param name="a2">Second numerator</param>
        /// <param name="a3">Third numerator</param>
        /// <param name="b1">First denominator. Requires b1 > 0</param>
        /// <param name="z">Argument</param>
        /// <param name="addend">The addend to the series</param>
        /// <returns>
        /// The sum of the series, or NaN if the series does not converge
        /// within the policy maximum number of iterations 
        /// </returns>
        public static double Sum3F1(double a1, double a2, double a3, double b1, double z, double addend = 0)
        {
            if ((double.IsNaN(a1) || double.IsInfinity(a1))
            || (double.IsNaN(a2) || double.IsInfinity(a2))
            || (double.IsNaN(a3) || double.IsInfinity(a3))
            || (double.IsNaN(z) || double.IsInfinity(z))
            || (double.IsNaN(b1) || double.IsInfinity(b1))
            || double.IsNaN(addend)) {
                Policies.ReportDomainError("Sum3F1(a1: {0}, a2: {1}, a3: {2}, b1: {3}, z: {4}, addend: {5}): Requires finite arguments", a1, a2, a3, b1, z, addend);
                return double.NaN;
            }
            if (b1 <= 0 && Math2.IsInteger(b1)) {
                Policies.ReportDomainError("Sum3F1(a1: {0}, a2: {1}, a3: {2}, b1: {3}, z: {4}, addend: {5}): Requires b1 is not zero or a negative integer", a1, a2, a3, b1, z, addend);
                return double.NaN;
            }

            double sum = 1.0 + addend;

            if (z == 0)
                return sum;
            if (a1 == b1)
                return Sum2F0(a2, a3, z, addend);
            if (a2 == b1)
                return Sum2F0(a1, a3, z, addend);
            if (a3 == b1)
                return Sum2F0(a1, a2, z, addend);
#if false
            // TODO:
            if (a1 == 1)
                return Sum3F1_A1(a2, a3, b1, z, addend);
            if (a2 == 1)
                return Sum3F1_A1(a1, a3, b1, z, addend);
            if (a3 == 1)
                return Sum3F1_A1(a1, a2, b1, z, addend);
#endif

            double term = 1.0;
            int n = 0; 
            for (; n < Policies.MaxSeriesIterations; n++) {

                double prevSum = sum;
                term *= (z / (n + 1)) * ((a1 + n) / (b1 + n)) * (a2 + n) * (a3 + n);
                sum += term;

#if CMP_FLOAT
                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                    return sum;
#else
                if (sum == prevSum)
                    return sum;
#endif
            }

            Policies.ReportConvergenceError("Sum3F1(a1: {0}, a2: {1}, a3: {2}, b1: {3}, z: {4}, addend: {5}): No convergence after {6} iterations", a1, a2, a3, b1, z, addend, n);
            return double.NaN;
        }
예제 #7
0
        /// <summary>
        /// Implements the Exponential Series for integer n
        /// <para>Sum = ((-z)^(n-1)/(n-1)!) * (ψ(n)-Log(z)) - Σ( (k==n-1) ? 0 : ((-1)^k * z^k)/((k-n+1) * k!)) k={0,∞}</para>
        /// </summary>
        /// <param name="n"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        /// <see href="http://functions.wolfram.com/GammaBetaErf/ExpIntegralE/06/01/04/01/02/0005/"/>
        public static double En_Series(int n, double z)
        {
            Debug.Assert(n > 0);

            const double DefaultTolerance = 2 * DoubleLimits.MachineEpsilon;

            // the sign of the summation is negative
            // so negate these
            double sum  = (n > 1) ? -1.0 / (1.0 - n) : 0;
            double term = -1.0;

            int k = 1;

            for (; k < n - 1; k++)
            {
                term *= -z / k;
                if (term == 0)
                {
                    break;
                }
                sum += term / (k - n + 1);
            }

            sum += Math.Pow(-z, n - 1) * (Math2.Digamma(n) - Math.Log(z)) / Math2.Factorial(n - 1);

            if (term == 0)
            {
                return(sum);
            }

            // skip n-1 term
            term *= -z / k;

            for (int i = 0; i < Policies.MaxSeriesIterations; i++)
            {
                double prevSum = sum;

                k++;
                term *= -z / k;
                double delta = term / (k - n + 1);
                sum += delta;

                if (Math.Abs(delta) <= Math.Abs(prevSum) * DefaultTolerance)
                {
                    return(sum);
                }
            }


            Policies.ReportConvergenceError("Series did not converge in {0} iterations", Policies.MaxSeriesIterations);
            return(double.NaN);
        }
예제 #8
0
        /// <summary>
        /// Evaluate fv = J{v+1}(x)/J{v}(x) using continued fractions
        /// <para>|x| &lt;= |v|, function converges rapidly</para>
        /// <para>|x| &gt; |v|, function needs O(|x|) iterations to converge</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        static (double JRatio, int sign) J_CF1(double v, double x)
        {
            //  See Abramowitz and Stegun, Handbook of Mathematical Functions, 1972, 9.1.73

            // note that the interations required to converge are approximately order(x)
            const int    MaxIterations = 100000;
            const double tolerance     = 2 * DoubleLimits.MachineEpsilon;
            double       tiny          = Math.Sqrt(DoubleLimits.MinNormalValue);

            // modified Lentz's method, see
            // Lentz, Applied Optics, vol 15, 668 (1976)

            int    s = 1; // sign of denominator
            double f;
            double C = f = tiny;
            double D = 0;

            int k;

            for (k = 1; k < MaxIterations; k++)
            {
                double a = -1;
                double 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;
                double delta = C * D;
                f *= delta;
                if (D < 0)
                {
                    s = -s;
                }
                if (Math.Abs(delta - 1) < tolerance)
                {
                    return(-f, s);
                }
            }

            Policies.ReportConvergenceError("Series did not converge after {0} iterations", Policies.MaxSeriesIterations);
            return(double.NaN, 0);
        }
예제 #9
0
            static double Amplitude(double v, double x)
            {
                // see: http://dlmf.nist.gov/10.18.iii


                double k    = 1;
                double mu   = 4 * v * v;
                double txq  = 4 * x * x;
                double sq   = 1;
                double term = 1;


                // The following is the generalized form of the following
                // as per DLMF:
                // double sum = 1;
                // sum += (mu - 1) / (2 * txq);
                // sum += 3 * (mu - 1) * (mu - 9) / (txq * txq * 8);
                // sum += 15 * (mu - 1) * (mu - 9) * (mu - 25) / (txq * txq * txq * 8 * 6);



                double sum = 1, lastSum = sum;

                for (int n = 0; n < Policies.MaxSeriesIterations; n++)
                {
                    // check to see when the series starts to diverge
                    double mult = (k / (k + 1.0)) * ((mu - sq * sq) / txq);
                    if (Math.Abs(mult) >= 1.0)
                    {
                        Policies.ReportConvergenceError("Series diverges after {0} iterations", k);
                        return(double.NaN);
                    }


                    lastSum = sum;
                    term   *= mult;
                    sum    += term;
                    k      += 2;
                    sq     += 2;

                    if (Math.Abs(term) <= Math.Abs(lastSum) * Policies.SeriesTolerance)
                    {
                        return(Constants.RecipSqrtHalfPI * Math.Sqrt(sum / x));
                    }
                }

                Policies.ReportConvergenceError("Series did not converge after {0} iterations", Policies.MaxSeriesIterations);
                return(double.NaN);
            }
예제 #10
0
        /// <summary>
        /// Returns the sum of a hypergeometric series 1F2. 
        /// <para>Sum2F1 = addend + Σ( ((a1)_n)/((b1)_n * (b2)_n) * (z^n/n!)) n={0,∞}</para>
        /// </summary>
        /// <param name="a1">First numerator</param>
        /// <param name="b1">First denominator. Requires b1 &gt; 0</param>
        /// <param name="b2">Second denominator. Requires b2 &gt; 0</param>
        /// <param name="z">Argument</param>
        /// <param name="addend">The addend to the series</param>
        /// <returns>
        /// The sum of the series, or NaN if the series does not converge
        /// within the policy maximum number of iterations 
        /// </returns>
        public static double Sum1F2(double a1, double b1, double b2, double z, double addend = 0)
        {
            if ((double.IsNaN(a1) || double.IsInfinity(a1))
            || (double.IsNaN(z) || double.IsInfinity(z))
            || (double.IsNaN(b1) || double.IsInfinity(b1))
            || (double.IsNaN(b2) || double.IsInfinity(b2))
            || double.IsNaN(addend)) {
                Policies.ReportDomainError("Sum1F2(a1: {0}, b1: {1}, b2: {2}, z: {3}, addend: {4}): Requires finite arguments", a1, b1, b2, z, addend);
                return double.NaN;
            }
            if ((b1 <= 0 && Math2.IsInteger(b1))
                || (b2 <= 0 && Math2.IsInteger(b2))) {
                Policies.ReportDomainError("Sum1F2(a1: {0}, b1: {1}, b2: {2}, z: {3}, addend: {4}): Requires b1, b2 are not zero or negative integers", a1, b1, b2, z, addend);
                return double.NaN;
            }

            double sum = 1.0 + addend;

            if (z == 0)
                return sum;
            if (a1 == b1)
                return Sum0F1(b2, z, addend);
            if (a1 == b2)
                return Sum0F1(b1, z, addend);
            if (a1 == 1)
                return Sum1F2_A1(b1, b2, z, addend);

            double term = 1.0;
            int n = 0; 
            for (; n < Policies.MaxSeriesIterations; n++) {

                double prevSum = sum;
                term *= (z / (n + 1)) * (((a1 + n) / (b1 + n)) / (b2 + n));
                sum += term;

#if CMP_FLOAT
                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                    return sum;
#else
                if (sum == prevSum)
                    return sum;
#endif

            }

            Policies.ReportConvergenceError("Sum1F2(a1: {0}, b1: {1}, b2: {2}, z: {3}, addend: {4}): No convergence after {0} iterations", a1, b1, b2, z, addend, n);
            return double.NaN;
        }
예제 #11
0
        /// <summary>
        /// Returns the sum of a hypergeometric series 1F1. 
        /// <para>Sum1F0 = addend + Σ( (a1)_n/n! * z^n) n={0,∞}</para>
        /// </summary>
        /// <param name="a1">First numerator</param>
        /// <param name="z">Argument</param>
        /// <param name="addend">The addend to the series</param>
        /// <returns>
        /// The sum of the series, or NaN if the series does not converge
        /// within the policy maximum number of iterations 
        /// </returns>
        public static double Sum1F0(double a1, double z, double addend = 0)
        {
            if ((double.IsNaN(a1) || double.IsInfinity(a1))
            || (double.IsNaN(z) || double.IsInfinity(z))
            || double.IsNaN(addend)) {
                Policies.ReportDomainError("Sum1F0(a1: {0}, z: {1}, addend: {2}): Requires finite arguments", a1, z, addend);
                return double.NaN;
            }

            double sum = 1.0 + addend;


            if (z == 0)
                return sum;
            if (a1 == 1) {
                if (Math.Abs(z) < 1)
                    return sum - z / (1 - z); // 1-1/(1-z)
                if (z >= 1) {
                    // Divergent Series
                    return double.PositiveInfinity;
                }

                Policies.ReportDomainError("Sum1F0(a1: {0}, z: {1}, addend: {2}): Divergent Series", a1, z, addend);
                return double.NaN;
            }


            double term = 1.0;
            int n = 0;
            for (; n < Policies.MaxSeriesIterations; n++) {

                double prevSum = sum;
                term *= ((a1 + n) / (n + 1)) * z;
                sum += term;

#if CMP_FLOAT
                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                    return sum;
#else
                if (sum == prevSum)
                    return sum;
#endif

            }

            Policies.ReportConvergenceError("Sum1F0(a1: {0}, z: {1}, addend: {2}): No convergence after {3} iterations", a1, z, n);
            return double.NaN;
        }
예제 #12
0
        /// <summary>
        /// Evaluate I{v+1}(x) / I{v}(x) using continued fractions
        /// <para>|x| ≤ |v|, CF1_ik converges rapidly</para>
        /// <para>|x| &gt; |v|, CF1_ik needs O(|x|) iterations to converge</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        static double I_CF1(double v, double x)
        {
            const double tolerance = 2 * DoubleLimits.MachineEpsilon;

            double f;
            int    k;

            // See Abramowitz and Stegun, Handbook of Mathematical Functions, 1972, 9.1.73

            // modified Lentz's method, see
            // Lentz, Applied Optics, vol 15, 668 (1976)
            double tiny = Math.Sqrt(DoubleLimits.MinNormalValue);

            double C = f = tiny;
            double D = 0;

            for (k = 1; k < Policies.MaxSeriesIterations; k++)
            {
                double a = 1;
                double 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;
                double delta = C * D;
                f *= delta;
                if (Math.Abs(delta - 1) <= tolerance)
                {
                    break;
                }
            }

            if (k >= Policies.MaxSeriesIterations)
            {
                Policies.ReportConvergenceError("CF1(v:{0}, x:{1}) did not converge after {2} iterations", v, x, Policies.MaxSeriesIterations);
                return(double.NaN);
            }

            return(f);
        }
예제 #13
0
        /// <summary>
        /// Series representation for γ(a,x)-1/a for small a
        /// <para>LowerSmallASeriesSum = Σ((-x)^k/((a+k)*k!)) k={1, inf}</para>
        /// <para>γ(a,x) = z^a * LowerSmallASeriesSum(a, x, 1/a)</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="z"></param>
        /// <param name="initValue"></param>
        /// <returns></returns>
        /// <seealso href="http://functions.wolfram.com/GammaBetaErf/Gamma2/06/01/04/01/01/0003/"/>
        public static double LowerSmallASeries(double a, double z, double initValue)
        {
            double term = 1.0;
            double sum  = initValue;

            for (int n = 1; n < Policies.MaxSeriesIterations; n++)
            {
                double prevSum = sum;
                term *= (-z / n);
                sum  += term / (a + n);

                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                {
                    return(sum);
                }
            }

            Policies.ReportConvergenceError("Series did not converge after {0} iterations", Policies.MaxSeriesIterations);
            return(double.NaN);
        }
예제 #14
0
        /// <summary>
        /// Returns the sum of a hypergeometric series 2F0. 
        /// <para>Sum2F0 = addend + Σ( (a1)_n/n! * (a2)_n * z^n) n={0,∞}</para>
        /// </summary>
        /// <param name="a1">First numerator</param>
        /// <param name="a2">Second numerator</param>
        /// <param name="z">Argument</param>
        /// <param name="addend">The addend to the series</param>
        /// <returns>
        /// The sum of the series, or NaN if the series does not converge
        /// within the policy maximum number of iterations 
        /// </returns>
        public static double Sum2F0(double a1, double a2, double z, double addend = 0)
        {
            if ((double.IsNaN(a1) || double.IsInfinity(a1))
            || (double.IsNaN(a2) || double.IsInfinity(a2))
            || (double.IsNaN(z) || double.IsInfinity(z))
            || double.IsNaN(addend)) {
                Policies.ReportDomainError("Sum2F0(a1: {0}, a2: {1}, z: {2}, addend: {3}): Requires arguments", a1, a2, z, addend);
                return double.NaN;
            }

            double sum = 1.0 + addend;

            if (z == 0)
                return sum;
            if (a1 == 1)
                return Sum2F0_A1(a2, z, addend);
            if (a2 == 1)
                return Sum2F0_A1(a1, z, addend);

            double term = 1.0;

            int n = 0; 
            for (; n < Policies.MaxSeriesIterations; n++) {

                double prevSum = sum;
                term *= z * ((a1 + n) / (n + 1)) * (a2 + n);
                sum += term;

#if CMP_FLOAT
                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                    return sum;
#else
                if (sum == prevSum)
                    return sum;
#endif

            }

            Policies.ReportConvergenceError("Sum2F0(a1: {0}, a2: {1}, z: {2}, addend: {3}): No converge after {4} iterations", a1, a2, z, addend, n);
            return double.NaN;
        }
예제 #15
0
        // not used but kept for future reference

        /// <summary>
        /// Ei(x) = γ + log(x) + Σ (x^k/(k*k!)) k={1,∞}
        /// </summary>
        /// <param name="z"></param>
        /// <returns></returns>
        public static double Ei_Series(double z)
        {
            const double DefaultTolerance = 2 * DoubleLimits.MachineEpsilon;

            double term = 1.0;
            double sum  = Math.Log(z) + Constants.EulerMascheroni;

            for (int n = 1; n < Policies.MaxSeriesIterations; n++)
            {
                double prevSum = sum;
                term *= (z / n);
                sum  += term / n;

                if (Math.Abs(term) <= Math.Abs(prevSum) * DefaultTolerance)
                {
                    return(sum);
                }
            }

            Policies.ReportConvergenceError("Series did not converge after {0} iterations", Policies.MaxSeriesIterations);
            return(double.NaN);
        }
예제 #16
0
        /// <summary>
        /// Series approximation to the incomplete beta:
        /// <para>Σ( (1-b)_{k} * x^k/( k! * (a+k) ), k={0, Inf}</para>
        /// <para>Bx(a, b) = z^a/a * SeriesSum</para>
        /// <para>Ix(a, b) = z^a/(a*B(a,b)) * SeriesSum</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        static double SeriesSum(double a, double b, double x)
        {
            const double DefaultTolerance = 2 * DoubleLimits.MachineEpsilon;

            double sum  = 1 / a;
            double term = 1.0;

            for (int n = 1; n < Policies.MaxSeriesIterations; n++)
            {
                double prevSum = sum;
                term *= (1.0 - b / n) * x;
                double delta = term / (a + n);
                sum += delta;

                if (Math.Abs(delta) <= Math.Abs(prevSum) * DefaultTolerance)
                {
                    return(sum);
                }
            }

            Policies.ReportConvergenceError("Series did not converge after {0} iterations", Policies.MaxSeriesIterations);
            return(double.NaN);
        }
예제 #17
0
        /// <summary>
        /// Calculate (K{v}(x), K{v+1}(x)) by evaluating continued fraction
        /// z1 / z0 = U(v+1.5, 2v+1, 2x) / U(v+0.5, 2v+1, 2x)
        /// <para>|x| &gt;= |v|, CF2_ik converges rapidly</para>
        /// <para>|x| -&gt; 0, CF2_ik fails to converge</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <param name="expScale">if true, exponentially scale the results</param>
        /// <returns>
        /// If <paramref name="expScale"/> is false K{v}(x) and K{v+1}(x);
        /// otherwise e^x * K{v}(x) and e^x * K{v+1}(x)
        /// </returns>
        static (double Kv, double Kvp1) K_CF2(double v, double x, bool expScale)
        {
            // See Thompson and Barnett, Computer Physics Communications, vol 47, 245 (1987)

            const double tolerance = DoubleLimits.MachineEpsilon;
            double       C, f, q, delta;
            int          k;

            Debug.Assert(Math.Abs(x) > 1);

            // Steed's algorithm, see Thompson and Barnett,
            // Journal of Computational Physics, vol 64, 490 (1986)
            double a = v * v - 0.25;
            double b = 2 * (x + 1);
            double D = 1 / b;

            f = delta = D;                                // f1 = delta1 = D1, coincidence
            double prev    = 0;
            double current = 1;
            double Q = C = -a;
            double S = 1 + Q * delta;

            // starting from 2
            for (k = 2; k < Policies.MaxSeriesIterations; k++)
            {
                // continued fraction f = z1 / z0
                a     -= 2 * (k - 1);
                b     += 2;
                D      = 1 / (b + a * D);
                delta *= b * D - 1;
                f     += delta;

                q       = (prev - (b - 2) * current) / a;
                prev    = current;
                current = q;                        // forward recurrence for q
                C      *= -a / k;
                Q      += C * q;
                S      += Q * delta;

                // S converges slower than f

                if (Math.Abs(Q * delta) < Math.Abs(S) * tolerance)
                {
                    break;
                }
            }

            if (k >= Policies.MaxSeriesIterations)
            {
                Policies.ReportConvergenceError("K_CF2(v:{0}, x:{1}) did not converge after {2} iterations", v, x, Policies.MaxSeriesIterations);
                return(double.NaN, double.NaN);
            }

            double Kv, Kv1;

            if (expScale)
            {
                Kv = (Constants.SqrtHalfPI / Math.Sqrt(x)) / S;
            }
            else
            {
                Kv = (Constants.SqrtHalfPI / Math.Sqrt(x)) * Math.Exp(-x) / S;
            }

            Kv1 = Kv * (0.5 + v + x + (v * v - 0.25) * f) / x;

            return(Kv, Kv1);
        }
예제 #18
0
        /// <summary>
        /// Returns the sum of a hypergeometric series PFQ, which has a variable number of numerators and denominators. 
        /// <para> PFQ = addend + Σ(( Prod(a[j]_n)/Prod(b[j])_n * (z^n/n!)) n={0,∞}</para>
        /// </summary>
        /// <param name="a">The array of numerators</param>
        /// <param name="b">The array of denominators. (b != 0 or b != negative integer)</param>
        /// <param name="z">Argument</param>
        /// <param name="addend">The addend to the series</param>
        /// <returns>
        /// The sum of the series, or NaN if the series does not converge
        /// within the policy maximum number of iterations 
        /// </returns>
        public static double SumPFQ(double[] a, double[] b, double z, double addend = 0)
        {
            if ( (a == null || b == null)
                || (double.IsNaN(z) || double.IsInfinity(z))
                || double.IsNaN(addend)) {
                string aStr = (a == null) ? "null" : a.ToString();
                string bStr = (b == null) ? "null" : b.ToString();
                Policies.ReportDomainError("SumPFQ(a: {0}; b: {1}, z: {2}, addend: {3}): Requires finite non-null arguments", aStr, bStr, z, addend);
                return double.NaN;
            }

            if (a.Length == 0 && b.Length == 0)
                return Sum0F0(z, addend);

            // check for infinities or non-positive integer b

            bool hasBadParameter = false;
            for (int i = 0; i < a.Length; i++) {
                if (double.IsInfinity(a[i])) {
                    hasBadParameter = true;
                    break;
                }
            }

            if (!hasBadParameter) {
                for (int i = 0; i < b.Length; i++) {
                    if (double.IsInfinity(b[i]) || (b[i] <= 0 && Math2.IsInteger(b[i]))) {
                        hasBadParameter = true;
                        break;
                    }


                }
            }

            if (hasBadParameter) {
                Policies.ReportDomainError("SumPFQ(a: {0}; b: {1}, z: {2}, addend: {3}): Requires finite arguments and b not zero or a negative integer", a, b, z, addend);
                return double.NaN;
            }

            double sum = 1.0 + addend;

            if (z == 0)
                return sum;

            double term = 1.0;
            int n = 0; 
            for (; n < Policies.MaxSeriesIterations; n++) {


                double term_part = (z / (n + 1));

                // use a/b where we can for max(a.Length, b.Length)

                int j = 0;
                for (; j < a.Length && j < b.Length; j++) {
                    term_part *= (a[j] + n) / (b[j] + n);
                }

                // otherwise multiply or divide the rest of the factors 
                // either a or b

                for (; j < a.Length; j++) {
                    term_part *= (a[j] + n);
                }

                for (; j < b.Length; j++) {
                    term_part /= (b[j] + n);
                }

                term *= term_part;

                double prevSum = sum;
                sum += term;

#if CMP_FLOAT
                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                    return sum;
#else
                if (sum == prevSum)
                    return sum;
#endif

            }

            Policies.ReportConvergenceError("No convergence after {0} iterations", n);
            return double.NaN;

        }
예제 #19
0
        /// <summary>
        /// p+iq = (J'{v}+iY'{v})/(J{v}+iY{v})
        /// <para>|x| >= |v|, function converges rapidly</para>
        /// <para>|x| -> 0, function fails to converge</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        static (double P, double Q) JY_CF2(double v, double x)
        {
            //
            // This algorithm was originally written by Xiaogang Zhang
            // using complex numbers to perform the complex arithmetic.
            // However, that turns out to 10x or more slower than using
            // all real-valued arithmetic, so it's been rewritten using
            // real values only.
            //

            Debug.Assert(Math.Abs(x) > 1);

            // modified Lentz's method, complex numbers involved, see
            // Lentz, Applied Optics, vol 15, 668 (1976)
            const double tolerance = 2 * DoubleLimits.MachineEpsilon;
            const double tiny = DoubleLimits.MinNormalValue;
            double       Cr = -0.5 / x, fr = Cr;
            double       Ci = 1, fi = Ci;

            double v2 = v * v;
            double a  = (0.25 - v2) / x;
            double br = 2 * x;
            double bi = 2;
            double temp = Cr * Cr + 1;

            Ci = bi + a * Cr / temp;
            Cr = br + a / temp;
            double Dr = br;
            double Di = bi;

            if (Math.Abs(Cr) + Math.Abs(Ci) < tiny)
            {
                Cr = tiny;
            }
            if (Math.Abs(Dr) + Math.Abs(Di) < tiny)
            {
                Dr = tiny;
            }
            temp = Dr * Dr + Di * Di;
            Dr   = Dr / temp;
            Di   = -Di / temp;
            double delta_r = Cr * Dr - Ci * Di;
            double delta_i = Ci * Dr + Cr * Di;

            temp = fr;
            fr   = temp * delta_r - fi * delta_i;
            fi   = temp * delta_i + fi * delta_r;
            int k;

            for (k = 2; k < Policies.MaxSeriesIterations; k++)
            {
                a    = k - 0.5;
                a   *= a;
                a   -= v2;
                bi  += 2;
                temp = Cr * Cr + Ci * Ci;
                Cr   = br + a * Cr / temp;
                Ci   = bi - a * Ci / temp;
                Dr   = br + a * Dr;
                Di   = bi + a * Di;
                if (Math.Abs(Cr) + Math.Abs(Ci) < tiny)
                {
                    Cr = tiny;
                }
                if (Math.Abs(Dr) + Math.Abs(Di) < tiny)
                {
                    Dr = tiny;
                }
                temp    = Dr * Dr + Di * Di;
                Dr      = Dr / temp;
                Di      = -Di / temp;
                delta_r = Cr * Dr - Ci * Di;
                delta_i = Ci * Dr + Cr * Di;
                temp    = fr;
                fr      = temp * delta_r - fi * delta_i;
                fi      = temp * delta_i + fi * delta_r;
                if (Math.Abs(delta_r - 1) + Math.Abs(delta_i) < tolerance)
                {
                    break;
                }
            }

            if (k >= Policies.MaxSeriesIterations)
            {
                Policies.ReportConvergenceError("JY_CF2(v:{0}, x:{1}) did not converge after {2} iterations", v, x, Policies.MaxSeriesIterations);
                return(double.NaN, double.NaN);
            }

            return(fr, fi);
        }
예제 #20
0
        /// <summary>
        /// Returns Log(1 + x) - x with improved accuracy for |x| ≤ 0.5
        /// </summary>
        /// <param name="x">The function argument. Requires x ≥ -1</param>
        public static double Log1pmx(double x)
        {
            if (!(x >= -1))
            {
                Policies.ReportDomainError("Log1pmx(x: {0}): Requires x >= -1", x);
                return(double.NaN);
            }
            if (x == -1)
            {
                return(double.NegativeInfinity);
            }
            if (double.IsInfinity(x))
            {
                return(double.PositiveInfinity);
            }

            // if x is not in [-0.5, 0.625] use the function
            if (x < -0.5 || x > 0.625)
            {
                return(Math.Log(1 + x) - x);
            }

            // Log1p works fine
            if (x < -7.0 / 16)
            {
                return(Math2.Log1p(x) - x);
            }

            double absX = Math.Abs(x);

            if (absX < (3.0 / 4) * DoubleLimits.MachineEpsilon)
            {
                return(-x * x / 2);
            }

            // Based on the techniques used in DiDonato & Morris
            // Significant Digit Computation of the Incomplete Beta function
            // Function: Rlog1

            // The following taylor series converges very slowly:
            // log(1+x)-x = -x^2 * (1/2 - x/3 + x^2/4 - x^3/5 + x^4/6 - x^5/7 ... )
            //
            // So, use a combination of preset points and argument reduction:
            // Step #1: Create preset intervals
            // let a = preset constant near x
            // log((1+x)/(1+a)) = log(1+(x-a)/(1+a)) = log(1+u) where u = (x-a)/(1+a); x=a+u+au
            // log(1+x) = log(1+u) + log(1+a)
            // log(1+x)-x = log(1+u)  + log(1+a) - (a+u+a*u)
            // log(1+x)-x = (log(1+u)-u)  + (log(1+a)-a) - a*u
            //
            // Step #2: Argument reduction
            // Using argument reduction: http://dlmf.nist.gov/4.6#E6
            // log(1+u) = 2 * (y + y^3/3 + y^5/5 + y^7/7 ...), where y = u/(2+u)
            // Thus, u maps to u = 2*y/(1-y)
            //
            // log(1 + u) - u
            // = 2 * y * (1 + y^2/3 + y^4/5 + y^6/7 ...) - 2y/(1-y)
            // = 2 * y * (1 - 1/(1-y) + y^2/3 + y^4/5 + y^6/7 ...)
            // = 2 * y * y * (-1/(1-y) + y/3 + y^3/5 + y^5/7 ...)
            // = -2 * y * y * (1 + u/2 - y*(1/3 + y^2/5 + y^4/7 ...))
            //
            // Step#3: Together
            // log(1+x)-x = (log(1+a)-a) - a*u - 2 * y * y * (1 + u/2 - y*(1/3 + y^2/5 + y^4/7 ...))
            // where
            // a = an arbitrary point near x
            // u = (x-a)/(1+a)
            // y = u/(2+u)



#if !USE_GENERAL
            // Approach for double precision:
            // With some preset points,
            // Set u, y for a = 0;

            double result = 0;
            double u      = x;
            double y      = x / (x + 2);

            // Set maximum limit for y = +/-0.125 = 1/8 => x = [-2/9, 2/7] = [ -0.2222..., 0.2857... ]
            const double ymax = 0.125;
            if (y < -ymax)
            {
                // Try to set the magnitude of "a" as low as possible to avoid cancellation errors
                // but ensure that |(x-a)/(1+a)| < ymax for all x

                // a, Log[1 + a] - a
                const double a       = -9.0 / 32;
                const double log1pma = -0.0489916868705768562794077754807;
                const double minX    = (a - ymax * (2 + a)) / (1 + ymax); // ~= -0.44

                Debug.Assert(x > minX, "Expecting x > minX, a: " + a + " ymax: " + ymax + " minX: " + minX + " x: " + x);

                // adjust u, y for a;
                u      = (x - a) / (1 + a);
                y      = u / (u + 2);
                result = log1pma - a * u;
            }
            else if (y > ymax)
            {
                // Try to set the magnitude of "a" as low as possible to avoid cancellation errors
                // but ensure that |(x-a)/(1+a)| < ymax for all x

                // a, Log[1 + a] - a
                const double a       = 9.0 / 32;
                const double log1pma = -0.0334138360954187432193972342535;
                const double maxX    = (a + ymax * (2 + a)) / (1 - ymax); // ~= 0.645

                Debug.Assert(x < maxX, "Expecting x < maxX, a: " + a + " ymax: " + ymax + " maxX: " + maxX + " x: " + x);

                // adjust u, y for a;
                u      = (x - a) / (1 + a);
                y      = u / (u + 2);
                result = log1pma - a * u;
            }


            Debug.Assert(Math.Abs(y) <= ymax, "Y too large for the series. x: " + x + " y: " + y);


            double y2 = y * y;

            // Set upper limit to y = +/- 0.125
            const double c0 = 1.0 / 3;
            const double c1 = 1.0 / 5;
            const double c2 = 1.0 / 7;
            const double c3 = 1.0 / 9;
            const double c4 = 1.0 / 11;
            const double c5 = 1.0 / 13;
            const double c6 = 1.0 / 15;
            const double c7 = 1.0 / 17;

            double series = (c0 + y2 * (c1 + y2 * (c2 + y2 * (c3 + y2 * (c4 + y2 * (c5 + y2 * (c6 + y2 * c7)))))));
            result += -2 * y2 * (1 + u / 2 - y * series);
            return(result);
#else
            // The generalized approach

            double y  = x / (x + 2);
            double y2 = y * y;

            double sum  = 1.0 / 3;
            double mult = y2;
            double k    = 5;
            for (int n = 1; n < Policies.MaxSeriesIterations; n++)
            {
                double prevSum = sum;
                sum += mult / k;

                if (prevSum == sum)
                {
                    return(-2 * y2 * (1 + x / 2 - y * sum));
                }

                mult *= y2;
                k    += 2;
            }

            Policies.ReportConvergenceError("Series did not converge after {0} iterations", Policies.MaxSeriesIterations);
            return(double.NaN);
#endif
        }
예제 #21
0
        /// <summary>
        /// Returns the Arithmetic-Geometric Mean of a and b
        /// </summary>
        /// <param name="a">First Agm argument</param>
        /// <param name="b">Second Agm argument</param>
        /// <returns></returns>
        public static double Agm(double a, double b)
        {
            if (double.IsNaN(a) || double.IsNaN(b))
            {
                Policies.ReportDomainError("Agm(a: {0}, b: {1}): NaNs not allowed", a, b);
                return(double.NaN);
            }

            // Agm(x, 0) = Agm(0, x) = 0
            if (a == 0 || b == 0)
            {
                return(0);
            }

            // We don't handle complex results
            if (Math.Sign(a) != Math.Sign(b))
            {
                Policies.ReportDomainError("Agm(a: {0}, b: {1}): Requires a, b have the same sign", a, b);
                return(double.NaN);
            }

            // Agm(± a, ± ∞) = ± ∞
            if (double.IsInfinity(a))
            {
                return(a);
            }
            if (double.IsInfinity(b))
            {
                return(b);
            }

            if (a == b)
            {
                return(a); //or b
            }
            // save a, b for error messages
            double aOriginal = a;
            double bOriginal = b;

            // Agm(-a, -b) = -Agm(a, b)
            double mult = 1;

            if (a < 0)
            {
                a    = -a;
                b    = -b;
                mult = -1;
            }

            // Set a > b: Agm(a, b) == Agm(b, a)
            if (a < b)
            {
                Utility.Swap(ref a, ref b);
            }


            // If b/a < eps^(1/4), we can transform Agm(a, b) to a * Agm(1, b/a)
            // and use the Taylor series Agm(1, y) at y = 0
            // See: http://functions.wolfram.com/EllipticFunctions/ArithmeticGeometricMean/06/01/01/

            double y = b / a;

            if (y <= DoubleLimits.RootMachineEpsilon._4)
            {
                // Compute log((b/a)/4) being careful not to underflow on b/a
                double logyDiv4;
                if (y < 4 * DoubleLimits.MinNormalValue)
                {
                    logyDiv4  = (y < DoubleLimits.MinNormalValue) ? Math.Log(b) - Math.Log(a) : Math.Log(y);
                    logyDiv4 -= 2 * Constants.Ln2;
                }
                else
                {
                    logyDiv4 = Math.Log(y / 4);
                }

                double r      = (Math.PI / 8) * (-4.0 + (1 + 1 / logyDiv4) * y * y) / logyDiv4;
                double result = mult * a * r;
                return(result);
            }

            // scale by a to guard against overflows/underflows in a*b
            mult *= a;
            b     = y;
            a     = 1;

            // Use a series of arithmetic and geometric means.
            int n = 0;

            for (; n < Policies.MaxSeriesIterations; n++)
            {
#if !USE_GENERAL
                // If |b/a-1| < eps^(1/9), we can transform Agm(a, b) to a*Agm(1, b/a)
                // and use the Taylor series Agm(1, z) at z = 1:
                // Agm(1, z) = 1 + (z-1)/2 - (z-1)^2/16 + ....
                // http://functions.wolfram.com/EllipticFunctions/ArithmeticGeometricMean/06/01/02/0001/

                double z = (b - a);
                if (Math.Abs(z) < a * DoubleLimits.RootMachineEpsilon._9)
                {
                    const double c0 = 1;
                    const double c1 = 1.0 / 2;
                    const double c2 = -1.0 / 16;
                    const double c3 = 1.0 / 32;
                    const double c4 = -21.0 / 1024;
                    const double c5 = 31.0 / 2048;
                    const double c6 = -195.0 / 16384;
                    const double c7 = 319.0 / 32768;
                    const double c8 = -34325.0 / 4194304;

                    z /= a; // z=(b-a)/a = b/a-1
                    double r      = c0 + z * (c1 + z * (c2 + z * (c3 + z * (c4 + z * (c5 + z * (c6 + z * (c7 + z * c8)))))));
                    double result = mult * a * r;
                    return(result);
                }

                // take a step

                double nextA = 0.5 * (a + b);
                b = Math.Sqrt(a * b);
                a = nextA;
#else
                // the double specialized series above is a little faster,
                // so this more general approach is disabled

                // tol = Sqrt(8 * eps)
                const double tol   = 2 * Constants.Sqrt2 * DoubleLimits.RootMachineEpsilon._2;
                double       nextA = 0.5 * (a + b);
                if (Math.Abs(b - a) < a * tol)
                {
                    return(mult * nextA);
                }

                // take a step

                b = Math.Sqrt(a * b);
                a = nextA;
#endif
            }

            Policies.ReportConvergenceError("Agm(a: {0}, b: {1}): Did not converge after {2} iterations", aOriginal, bOriginal, n);
            return(double.NaN);
        }