Пример #1
0
        // Carlson's degenerate elliptic integral
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Carlson's symmetric form of elliptical integrals
        /// <para>R<sub>C</sub>(x,y) = R<sub>F</sub>(x,y,y) = (1/2) * ∫ dt/((t+y)*Sqrt(t+x)), t={0,∞}</para>
        /// </summary>
        public static double EllintRC(double x, double y)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (y == 0 || double.IsInfinity(y) || double.IsNaN(y)))
            {
                Policies.ReportDomainError("EllintRC(x: {0}, y: {1}): Requires finite x >= 0; finite y != 0", x, y);
                return(double.NaN);
            }

            // Taylor series: Max Value
            const double tolerance = 0.003100392679625389599124425076703727071077135406054400409781;

            Debug.Assert(Math2.AreNearUlps(tolerance, Math.Pow(4 * DoubleLimits.MachineEpsilon, 1.0 / 6), 5), "Incorrect constant");

            // for y < 0, the integral is singular, return Cauchy principal value
            double prefix = 1;

            if (y < 0)
            {
                prefix = Math.Sqrt(x / (x - y));
                x     -= y;
                y      = -y;
            }

            if (x == 0)
            {
                return(prefix * ((Math.PI / 2) / Math.Sqrt(y)));
            }
            if (x == y)
            {
                return(prefix / Math.Sqrt(x));
            }

            int k = 1;

            for (; k < Policies.MaxSeriesIterations; k++)
            {
                double u = (x + y + y) / 3;
                double S = y / u - 1; // 1 - x / u = 2 * S

                if (2 * Math.Abs(S) < tolerance)
                {
                    // Taylor series expansion to the 5th order
                    double value = (1 + S * S * (3.0 / 10 + S * (1.0 / 7 + S * (3.0 / 8 + S * (9.0 / 22))))) / Math.Sqrt(u);
                    return(value * prefix);
                }

                double sx     = Math.Sqrt(x);
                double sy     = Math.Sqrt(y);
                double lambda = 2 * sx * sy + y;
                x = (x + lambda) / 4;
                y = (y + lambda) / 4;
            }

            Policies.ReportDomainError("EllintRC(x: {0}, y: {1}): No convergence after {2} iterations", x, y, k);
            return(double.NaN);
        }
Пример #2
0
        // Carlson's elliptic integral of the second kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Carlson's symmetric form of elliptical integrals
        /// <para>R<sub>D</sub>(x,y,z) = R<sub>J</sub>(x,y,z,z) = (3/2) * ∫ dt/((t+z)*Sqrt((t+x)(t+y)(t+z))), t={0,∞}</para>
        /// </summary>
        /// <param name="x">Argument. Requires x >= 0</param>
        /// <param name="y">Argument. Requires y >= 0</param>
        /// <param name="z">Argument. Requires z > 0</param>
        public static double EllintRD(double x, double y, double z)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (!(y >= 0) || double.IsInfinity(y)) ||
                (!(z > 0) || double.IsInfinity(z)) ||
                (x + y == 0))
            {
                Policies.ReportDomainError("EllintRD(x: {0}, y: {1}, z: {2}) requires: finite x,y >= 0; finite z > 0; at most one argument == 0", x, y, z);
                return(double.NaN);
            }

            // Special cases from http://dlmf.nist.gov/19.20#iv

            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }
            if (x == 0)
            {
                if (y == 0)
                {
                    return(double.PositiveInfinity); // RD(0, 0, z)
                }
                if (y == z)
                {
                    return(3 * (Math.PI / 4) / (y * Math.Sqrt(y))); // RD(0, y, y)
                }
                //
                // Special handling for common case, from
                // Numerical Computation of Real or Complex Elliptic Integrals, eq.47
                //
                double xn      = Math.Sqrt(y);
                double yn      = Math.Sqrt(z);
                double x0      = xn;
                double y0      = yn;
                double sum     = 0;
                double sum_pow = 0.25;

                double c = xn - yn;
                while (Math.Abs(c) >= 2.7 * DoubleLimits.RootMachineEpsilon._2 * xn)
                {
                    double t = Math.Sqrt(xn * yn);
                    xn = (xn + yn) / 2;
                    yn = t;

                    c        = xn - yn;
                    sum_pow *= 2;
                    sum     += sum_pow * c * c;
                }
                double RF = Math.PI / (xn + yn);
                //
                // This following calculation suffers from serious cancellation when y ~ z
                // unless we combine terms.  We have:
                //
                // ( ((x0 + y0)/2)^2 - z ) / (z(y-z))
                //
                // Substituting y = x0^2 and z = y0^2 and simplifying we get the following:
                //
                double pt = (x0 + 3 * y0) / (4 * z * (x0 + y0));
                //
                // Since we've moved the demoninator from eq.47 inside the expression, we
                // need to also scale "sum" by the same value:
                //
                pt -= sum / (z * (y - z));
                return(pt * RF * 3);
            }

            if (x == y && x == z)
            {
                return(1 / (x * Math.Sqrt(x)));
            }


            // Taylor series upper limit
            const double tolerance = 0.002049052858245406612358440409548690638254492642397575328974;

            Debug.Assert(Math2.AreNearUlps(Math.Pow(DoubleLimits.MachineEpsilon / 3, 1.0 / 6.0), tolerance, 5), "Incorrect constant");

            // duplication
            double sigma  = 0;
            double factor = 1;
            int    k      = 1;

            for (; k < Policies.MaxSeriesIterations; k++)
            {
                double u = (x + y + z + z + z) / 5;
                double X = (u - x);
                double Y = (u - y);
                double Z = (u - z);

                // Termination condition:
                double utol = u * tolerance;
                if (Math.Abs(X) < utol && Math.Abs(Y) < utol && Math.Abs(Z) < utol)
                {
                    X /= u;
                    Y /= u;
                    Z /= u;

                    // Taylor series expansion to the 5th order
                    double EA    = X * Y;
                    double EB    = Z * Z;
                    double EC    = EA - EB;
                    double ED    = EA - 6 * EB;
                    double EE    = ED + EC + EC;
                    double S1    = ED * (ED * (9.0 / 88) - Z * EE * (9.0 / 52) - 3.0 / 14);
                    double S2    = Z * (EE / 6 + Z * (-EC * (9.0 / 22) + Z * EA * (3.0 / 26)));
                    double value = 3 * sigma + factor * (1 + S1 + S2) / (u * Math.Sqrt(u));

                    return(value);
                }

                double sx     = Math.Sqrt(x);
                double sy     = Math.Sqrt(y);
                double sz     = Math.Sqrt(z);
                double lambda = sy * (sx + sz) + sz * sx;
                sigma  += factor / (sz * (z + lambda));
                factor /= 4;
                x       = (x + lambda) / 4;
                y       = (y + lambda) / 4;
                z       = (z + lambda) / 4;
            }

            Policies.ReportDomainError("EllintRD(x: {0}, y: {1}, z: {2}) No convergence after {3} iterations", x, y, z, k);
            return(double.NaN);
        }
Пример #3
0
        /// <summary>
        /// Carlson's symmetric form of elliptical integrals
        /// <para>R<sub>J</sub>(x,y,z,p) = (3/2) * ∫ dt/((t+p)*Sqrt((t+x)(t+y)(t+z))), t={0,∞}</para>
        /// </summary>
        /// <param name="x">Argument. Requires finite x >= 0</param>
        /// <param name="y">Argument. Requires finite y >= 0</param>
        /// <param name="z">Argument. Requires finite z >= 0</param>
        /// <param name="p">Argument. Requires finite p != 0</param>
        public static double EllintRJ(double x, double y, double z, double p)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (!(y >= 0) || double.IsInfinity(y)) ||
                (!(z >= 0) || double.IsInfinity(z)) ||
                (p == 0 || double.IsInfinity(p) || double.IsNaN(p)) ||
                (x + y == 0 || y + z == 0 || z + x == 0))
            {
                Policies.ReportDomainError("EllintRJ(x: {0}, y: {1}, z: {2}, p: {3}) requires: finite x,y,z >= 0; finite p != 0; at most one of x,y,z == 0", x, y, z, p);
                return(double.NaN);
            }

            // See Carlson, Numerische Mathematik, vol 33, 1 (1979)

            double value;

            // error scales as the 6th power of tolerance
            const double tolerance = 0.002049052858245406612358440409548690638254492642397575328974;

            Debug.Assert(Math2.AreNearUlps(Math.Pow(DoubleLimits.MachineEpsilon / 3, 1.0 / 6.0), tolerance, 5), "Incorrect constant");

            // reorder x,y,z such that x <= y <= z
            // so that 0 shows up in x first
            // and so that the p<0 case below doesn't suffer cancellation errors
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }
            if (y > z)
            {
                Utility.Swap(ref y, ref z);
            }
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }

            // for p < 0, the integral is singular, return Cauchy principal value
            if (p < 0)
            {
                // We must ensure that (z - y) * (y - x) is positive.
                Debug.Assert(x <= y && y <= z, "x, y, z must be in ascending order");

                double q   = -p;
                double pmy = (z - y) * (y - x) / (y + q); // p - y

                Debug.Assert(pmy >= 0);

                double pAdj = pmy + y;
                value  = pmy * Math2.EllintRJ(x, y, z, pAdj);
                value -= 3 * Math2.EllintRF(x, y, z);
                value += 3 * Math.Sqrt((x * y * z) / (x * z + pAdj * q)) * Math2.EllintRC(x * z + pAdj * q, pAdj * q);
                value /= (y + q);
                return(value);
            }

            if (x == 0)
            {
                if (y == 0) //RJ(0, 0, z, p)
                {
                    return(double.PositiveInfinity);
                }
                if (y == z) //RJ(0, y, y, p)
                {
                    return(3 * (Math.PI / 2) / (y * Math.Sqrt(p) + p * Math.Sqrt(y)));
                }
            }

            if (p == z)
            {
                if (z == y)
                {
                    if (x == y)
                    {
                        return(1 / (x * Math.Sqrt(x))); // RJ(x, x, x, x)
                    }
                    return(EllintRD(x, y, y));          // RJ(x, y, y, y)
                }
                return(EllintRD(x, y, z));              // RJ(x, y, z, z)
            }

            if (x == y && y == z)
            {
                return(EllintRD(p, p, x)); // RJ(x, x, x, p)
            }
            // duplication
            double sigma  = 0;
            double factor = 1;
            int    k      = 1;

            for (; k < Policies.MaxSeriesIterations; k++)
            {
                double u = (x + y + z + p + p) / 5;
                double X = (u - x);
                double Y = (u - y);
                double Z = (u - z);
                double P = (u - p);

                // Termination condition:
                double utol = u * tolerance;
                if (Math.Abs(X) < utol && Math.Abs(Y) < utol && Math.Abs(Z) < utol && Math.Abs(P) < utol)
                {
                    X /= u;
                    Y /= u;
                    Z /= u;
                    P /= u;

                    // Taylor series expansion to the 5th order
                    double EA = X * Y + Y * Z + Z * X;
                    double EB = X * Y * Z;
                    double EC = P * P;
                    double E2 = EA - 3 * EC;
                    double E3 = EB + 2 * P * (EA - EC);
                    double S1 = 1 + E2 * (E2 * (9.0 / 88) - E3 * (9.0 / 52) - 3.0 / 14);
                    double S2 = EB * (1.0 / 6 + P * (-6.0 / 22 + P * (3.0 / 26)));
                    double S3 = P * ((EA - EC) / 3 - P * EA * (3.0 / 22));
                    value = 3 * sigma + factor * (S1 + S2 + S3) / (u * Math.Sqrt(u));

                    return(value);
                }

                double sx = Math.Sqrt(x);
                double sy = Math.Sqrt(y);
                double sz = Math.Sqrt(z);

                double lambda = sy * (sx + sz) + sz * sx;
                double alpha  = p * (sx + sy + sz) + sx * sy * sz;
                alpha *= alpha;
                double beta = p * (p + lambda) * (p + lambda);
                sigma  += factor * Math2.EllintRC(alpha, beta);
                factor /= 4;
                x       = (x + lambda) / 4;
                y       = (y + lambda) / 4;
                z       = (z + lambda) / 4;
                p       = (p + lambda) / 4;
            }

            Policies.ReportDomainError("EllintRJ(x: {0}, y: {1}, z: {2}, p: {3}) No convergence after {4} iterations", x, y, z, p, k);
            return(double.NaN);
        }
Пример #4
0
        // Carlson's elliptic integral of the first kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)

        /// <summary>
        /// Carlson's symmetric form of elliptical integrals
        /// <para>R<sub>F</sub>(x,y,z) = (1/2) * ∫ dt/((t+x)(t+y)(t+z)), t={0,∞}</para>
        /// </summary>
        /// <param name="x">Argument. Requires x >= 0</param>
        /// <param name="y">Argument. Requires y >= 0</param>
        /// <param name="z">Argument. Requires z >= 0</param>
        public static double EllintRF(double x, double y, double z)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (!(y >= 0) || double.IsInfinity(y)) ||
                (!(z >= 0) || double.IsInfinity(z)) ||
                (x + y == 0 || y + z == 0 || z + x == 0))
            {
                Policies.ReportDomainError("EllintRF(x: {0}, y: {1}, z: {2}) requires: finite x, y, z >= 0; at most one of x,y,z == 0", x, y, z);
                return(double.NaN);
            }

            double xOrig = x;
            double yOrig = y;
            double zOrig = z;

            // reorder x,y,z such that x <= y <= z
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }
            if (y > z)
            {
                Utility.Swap(ref y, ref z);
            }
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }

            // Special cases from http://dlmf.nist.gov/19.20#i
            if (x == 0)
            {
                //Debug.Assert(y != 0 || z != 0, "Expecting only one zero");
                if (y == z)
                {
                    return((Math.PI / 2) / Math.Sqrt(y));
                }

                // http://dlmf.nist.gov/19.22#E8
                return((Math.PI / 2) / Agm(Math.Sqrt(y), Math.Sqrt(z)));
            }

            if (x == y)
            {
                if (x == z)
                {
                    return(1 / Math.Sqrt(x)); // EllintRF(x, x, x);
                }
                return(EllintRC(z, x));       // EllintRF(x, x, z);
            }

            Debug.Assert(x != z, "Parameters are not ordered properly");

            if (y == z)
            {
                return(EllintRC(x, y)); // EllintRF(x, y, y)
            }
            // Carlson scales error as the 6th power of tolerance,
            // but this seems not to work for types larger than
            // 80-bit reals, this heuristic seems to work OK:
            const double tolerance = 0.003100392679625389599124425076703727071077135406054400409781;

            Debug.Assert(Math2.AreNearUlps(tolerance, Math.Pow(4 * DoubleLimits.MachineEpsilon, 1.0 / 6), 5), "Incorrect constant");

            // duplication
            int k = 1;

            for (; k < Policies.MaxSeriesIterations; k++)
            {
                double u = (x + y + z) / 3;
                double X = (u - x);
                double Y = (u - y);
                double Z = (u - z);

                // Termination condition:
                double utol = u * tolerance;
                if (Math.Abs(X) < utol && Math.Abs(Y) < utol && Math.Abs(Z) < utol)
                {
                    // Taylor series expansion to the 5th order
                    X /= u;
                    Y /= u;
                    Z /= u;

                    double E2    = X * Y - Z * Z;
                    double E3    = X * Y * Z;
                    double value = (1 + E2 * (E2 / 24 - E3 * (3.0 / 44) - 0.1) + E3 / 14) / Math.Sqrt(u);

                    return(value);
                }

                double sx     = Math.Sqrt(x);
                double sy     = Math.Sqrt(y);
                double sz     = Math.Sqrt(z);
                double lambda = sy * (sx + sz) + sz * sx;
                x = (x + lambda) / 4;
                y = (y + lambda) / 4;
                z = (z + lambda) / 4;
            }


            Policies.ReportDomainError("EllintRF(x: {0}, y: {1}, z: {2}) No convergence after {3} iterations", xOrig, yOrig, zOrig, k);
            return(double.NaN);
        }