Example #1
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);
        }
Example #2
0
        /// <summary>
        /// Returns the Digamma function
        /// <para>ψ(x) = d/dx(ln(Γ(x))) = Γ'(x)/Γ(x)</para>
        /// </summary>
        /// <param name="x">Digamma function argument</param>
        public static double Digamma(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Digamma(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x < 0)
                {
                    Policies.ReportDomainError("Digamma(x: {0}): Requires x != -Infinity", x);
                    return(double.NaN);
                }
                return(double.PositiveInfinity);
            }


            if (x <= 0)
            {
                if (IsInteger(x))
                {
                    Policies.ReportPoleError("Digamma(x: {0}) Requires x is not an integer when x <= 0", x);
                    return(double.NaN);
                }


                // use the following equations to reflect
                // ψ(1-x) - ψ(x) = π * cot(π*x)
                // ψ(-x) = 1/x + ψ(x) +  π * cot(π*x)

                if (x < -1)
                {
                    return(Digamma(1 - x) - Math.PI * Math2.CotPI(x));
                }

                // The following is the forward recurrence:
                //      -(1/x + 1/(x+1)) + Digamma(x + 2);
                // with a little less cancellation error near Digamma root: x = -0.50408...
                return(_Digamma.Rational_1_2(x + 2) - (2 * x + 1) / (x * (x + 1)));
            }

            // If we're above the lower-limit for the asymptotic expansion then use it:
            if (x >= 10)
            {
                const double p0 = 0.083333333333333333333333333333333333333333333333333;
                const double p1 = -0.0083333333333333333333333333333333333333333333333333;
                const double p2 = 0.003968253968253968253968253968253968253968253968254;
                const double p3 = -0.0041666666666666666666666666666666666666666666666667;
                const double p4 = 0.0075757575757575757575757575757575757575757575757576;
                const double p5 = -0.021092796092796092796092796092796092796092796092796;
                const double p6 = 0.083333333333333333333333333333333333333333333333333;
                const double p7 = -0.44325980392156862745098039215686274509803921568627;

                double xm1 = x - 1;
                double z   = 1 / (xm1 * xm1);
                double P   = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * (p6 + z * p7))))));
                return(Math.Log(xm1) + 1.0 / (2 * xm1) - z * P);
            }


            double result = 0;

            //
            // If x > 2 reduce to the interval [1,2]:
            //
            while (x > 2)
            {
                x      -= 1;
                result += 1 / x;
            }
            //
            // If x < 1 use recurrence to shift to > 1:
            //
            if (x < 1)
            {
                result = -1 / x;
                x     += 1;
            }
            result += _Digamma.Rational_1_2(x);

            return(result);
        }