Example #1
0
        /// <summary>
        /// Returns the Laczos approximation of Γ(z)
        /// </summary>
        public static double Tgamma(double x)
        {
            Debug.Assert(x > 0);

            // check for large x to avoid potential Inf/Inf below
            if (x > DoubleLimits.MaxGamma + 1)
            {
                return(double.PositiveInfinity);
            }

            // use the Lanczos approximation
            double xgh    = x + (Lanczos.G - 0.5);
            double result = Lanczos.Series(x);

            if (x > MaxPowerTermX)
            {
                // we're going to overflow unless this is done with care:
                double hp = Math.Pow(xgh, (x / 2) - 0.25);
                result *= (hp / Math.Exp(xgh));
                result *= hp;
            }
            else
            {
                result *= (Math.Pow(xgh, x - 0.5) / Math.Exp(xgh));
            }

            return(result);
        }
Example #2
0
        /// <summary>
        /// Returns ln(|Γ(1+dz)|) using the Lanczos approzimation
        /// </summary>
        public static double Lgamma1p(double dz)
        {
            double result = dz * Math.Log((dz + (Lanczos.G + 0.5)) / Math.E);

            result += Math2.Log1p(dz / (Lanczos.G + 0.5)) / 2;
            result += Math2.Log1p(Lanczos.Sum_near_1(dz));
            return(result);
        }
Example #3
0
        /// <summary>
        /// Returns Γ(z)/Γ(z+delta) using the Lanczos approximation
        /// </summary>
        public static double TgammaDeltaRatio(double x, double delta)
        {
            Debug.Assert(x > 0 && delta + x > 0);

            // Compute Γ(x)/Γ(x+Δ), which reduces to:
            //
            // ((x+g-0.5)/(x+Δ+g-0.5))^(x-0.5) * (x+Δ+g-0.5)^-Δ * e^Δ * (Sum(x)/Sum(x+Δ))
            // = (1+Δ/(x+g-0.5))^-(x-0.5) * (x+Δ+g-0.5)^-Δ * e^Δ * (Sum(x)/Sum(x+Δ))

            double xgh  = x + (Lanczos.G - 0.5);
            double xdgh = x + delta + (Lanczos.G - 0.5);

            double mu     = delta / xgh;
            double result = Lanczos.Series(x) / Lanczos.Series(x + delta);

            result *= (Math.Abs(mu) < 0.5) ? Math.Exp((0.5 - x) * Math2.Log1p(mu)) : Math.Pow(xgh / xdgh, x - 0.5);
            result *= Math.Pow(Math.E / xdgh, delta);

            return(result);
        }
Example #4
0
        /// <summary>
        /// Compute the leading power terms in the incomplete Beta:
        /// <para>(x^a)(y^b)/Beta(a,b) when regularized</para>
        /// <para>(x^a)(y^b) otherwise</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="x"></param>
        /// <param name="y">1-x</param>
        /// <param name="normalised"></param>
        /// <param name="multiplier"></param>
        /// <returns></returns>
        /// <remarks>
        /// Almost all of the error in the incomplete beta comes from this
        /// function: particularly when a and b are large. Computing large
        /// powers are *hard* though, and using logarithms just leads to
        /// horrendous cancellation errors.
        /// </remarks>
        public static double PowerTerms(double a, double b, double x, double y, bool normalised, double multiplier)
        {
            if (multiplier == 0)
            {
                return(0);
            }

            if (!normalised)
            {
                return(multiplier * (Math.Pow(x, a) * Math.Pow(y, b)));
            }

            double c = a + b;

            // combine power terms with Lanczos approximation:
            double agh = a + (Lanczos.G - 0.5);
            double bgh = b + (Lanczos.G - 0.5);
            double cgh = c + (Lanczos.G - 0.5);

            // compute:
            // mutiplier * (x*cgh/agh)^a * (y*cgh/bgh)^b * Sqrt((agh*bgh)/(e*cgh)) * (Sum_expG_scaled(c) / (Sum_expG_scaled(a) * Sum_expG_scaled(b)))

            double factor = (Lanczos.SeriesExpGScaled(c) / Lanczos.SeriesExpGScaled(a)) / Lanczos.SeriesExpGScaled(b);

            if (a > b)
            {
                factor *= Math.Sqrt((bgh / Math.E) * (agh / cgh));
            }
            else
            {
                factor *= Math.Sqrt((agh / Math.E) * (bgh / cgh));
            }

            if (double.IsInfinity(factor * multiplier))
            {
#if EXTRA_DEBUG
                Debug.WriteLine("Using Logs in _Beta.PowerTerms: a = {0}; b = {1}; x = {2}; y= {3}; mult = {4}", a, b, x, y, multiplier);
#endif
                // this will probably fail but...
                double logValue;
                if (x <= y)
                {
                    logValue = a * Math.Log(x) + b * Math2.Log1p(-x);
                }
                else
                {
                    logValue = a * Math2.Log1p(-y) + b * Math.Log(y);
                }
                logValue += Math.Log(multiplier);
                logValue -= Math2.LogBeta(a, b);
                return(Math.Exp(logValue));
            }
            else
            {
                factor *= multiplier;
            }

            double result = 1;

            //Debug.WriteLine("IbetaPowerTerms(a: {0}, b: {1}, x: {2}, y: {3}) - Prefix = {4}", a, b, x, y, result);


            // l1 and l2 are the base of the exponents minus one:
            double l1 = (x * b - y * agh) / agh;
            double l2 = (y * a - x * bgh) / bgh;

            // t1 and t2 are the exponent terms
            double t1 = (x * cgh) / agh;
            double t2 = (y * cgh) / bgh;


            // if possible, set the result to partially cancel out with the first term
            bool sameDirection = false;
            if (t1 >= 1 && t2 >= 1)
            {
                sameDirection = true;
                if (factor < 1)
                {
                    result = factor;
                    factor = 1;
                }
            }
            else if (t1 <= 1 && t2 <= 1)
            {
                sameDirection = true;
                if (factor > 1)
                {
                    result = factor;
                    factor = 1;
                }
            }

            if (sameDirection)
            {
                // This first branch handles the simple case where the two power terms
                // both go in the same direction (towards zero or towards infinity).
                // In this case if either term overflows or underflows,
                // then the product of the two must do so also.

                if (Math.Abs(l1) < 0.5)
                {
                    result *= Math.Exp(a * Math2.Log1p(l1));
                }
                else
                {
                    result *= Math.Pow(t1, a);
                }

                if (Math.Abs(l2) < 0.5)
                {
                    result *= Math.Exp(b * Math2.Log1p(l2));
                }
                else
                {
                    result *= Math.Pow(t2, b);
                }

                result *= factor;
            }
            else
            {
                // This second branch handles the case where the two power terms
                // go in opposite directions (towards zero or towards infinity).

                Debug.Assert(result == 1);

                bool useExpT1 = false;
                bool useExpT2 = false;

                double logt1;
                if (Math.Abs(l1) < 0.5)
                {
                    useExpT1 = true;
                    logt1    = a * Math2.Log1p(l1);
                }
                else
                {
                    logt1 = a * Math.Log(t1);
                }

                double logt2;
                if (Math.Abs(l2) < 0.5)
                {
                    useExpT2 = true;
                    logt2    = b * Math2.Log1p(l2);
                }
                else
                {
                    logt2 = b * Math.Log(t2);
                }

                if ((logt1 >= DoubleLimits.MaxLogValue) || (logt1 <= DoubleLimits.MinLogValue) ||
                    (logt2 >= DoubleLimits.MaxLogValue) || (logt2 <= DoubleLimits.MinLogValue)
                    )
                {
                    double logSum = logt1 + logt2;
                    if ((logSum >= DoubleLimits.MaxLogValue) || (logSum <= DoubleLimits.MinLogValue))
                    {
                        result = Math.Exp(logSum + Math.Log(factor));
                    }
                    else
                    {
                        result = factor * Math.Exp(logSum);
                    }
                }
                else
                {
                    // ensure that t1 and result will partially cancel
                    if (t1 >= 1)
                    {
                        if (factor < 1)
                        {
                            result = factor;
                            factor = 1;
                        }
                    }
                    else
                    {
                        if (factor > 1)
                        {
                            result = factor;
                            factor = 1;
                        }
                    }

                    if (useExpT1)
                    {
                        result *= Math.Exp(logt1);
                    }
                    else
                    {
                        result *= Math.Pow(t1, a);
                    }

                    if (useExpT2)
                    {
                        result *= Math.Exp(logt2);
                    }
                    else
                    {
                        result *= Math.Pow(t2, b);
                    }

                    result *= factor;
                }
            }
            return(result);
        }
Example #5
0
        /// <summary>
        /// Compute: multiplier * x^a/Beta(a,b) while trying to avoid overflows/underflows
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="multiplier"></param>
        /// <returns></returns>
        static double SeriesRegularizedPrefix(double a, double b, double x, double y, double multiplier)
        {
            double c = a + b;

            // incomplete beta power term, combined with the Lanczos approximation:
            double agh = a + (Lanczos.G - 0.5);
            double bgh = b + (Lanczos.G - 0.5);
            double cgh = c + (Lanczos.G - 0.5);


            // compute:
            // mutiplier * (x*cgh/agh)^a * (cgh/bgh)^b * Sqrt((agh*bgh)/(e*cgh)) * (SumGScaled(c)/(SumGScaled(a)*SumGScaled(b))

            double factor = (Lanczos.SeriesExpGScaled(c) / Lanczos.SeriesExpGScaled(a)) / Lanczos.SeriesExpGScaled(b);

            if (a > b)
            {
                factor *= Math.Sqrt((bgh / Math.E) * (agh / cgh));
            }
            else
            {
                factor *= Math.Sqrt((agh / Math.E) * (bgh / cgh));
            }
            factor *= multiplier;

            double result = 1;


            // l1 and l2 are the base of the exponents minus one:
            double l1 = (x * b - y * agh) / agh;
            double l2 = a / bgh;

            // t1 and t2 are the exponent terms
            double t1 = (x * cgh) / agh;
            double t2 = cgh / bgh;


            // if possible, set the result to partially cancel out with the first term
            Debug.Assert(t2 >= 1);

            if (t1 >= 1)
            {
                // This first branch handles the simple case where the two power terms
                // both go in the same direction (towards zero or towards infinity).
                // In this case if either term overflows or underflows,
                // then the product of the two must do so also.

                if (factor < 1)
                {
                    result = factor;
                    factor = 1;
                }

                if (Math.Abs(l1) < 0.5)
                {
                    result *= Math.Exp(a * Math2.Log1p(l1));
                }
                else
                {
                    result *= Math.Pow(t1, a);
                }

                if (Math.Abs(l2) < 0.5)
                {
                    result *= Math.Exp(b * Math2.Log1p(l2));
                }
                else
                {
                    result *= Math.Pow(t2, b);
                }

                result *= factor;
            }
            else
            {
                // This second branch handles the case where the two power terms
                // go in opposite directions (towards zero or towards infinity).

                Debug.Assert(result == 1);
                Debug.Assert(t1 < 1);

                bool useExpT1 = false;
                bool useExpT2 = false;

                double logt1;
                if (Math.Abs(l1) < 0.5)
                {
                    useExpT1 = true;
                    logt1    = a * Math2.Log1p(l1);
                }
                else
                {
                    logt1 = a * Math.Log(t1);
                }

                double logt2;
                if (Math.Abs(l2) < 0.5)
                {
                    useExpT2 = true;
                    logt2    = b * Math2.Log1p(l2);
                }
                else
                {
                    logt2 = b * Math.Log(t2);
                }

                if ((logt1 >= DoubleLimits.MaxLogValue) || (logt1 <= DoubleLimits.MinLogValue) ||
                    (logt2 >= DoubleLimits.MaxLogValue) || (logt2 <= DoubleLimits.MinLogValue)
                    )
                {
                    double logSum = logt1 + logt2;
                    if ((logSum >= DoubleLimits.MaxLogValue) || (logSum <= DoubleLimits.MinLogValue))
                    {
                        result = Math.Exp(logSum + Math.Log(factor));
                    }
                    else
                    {
                        result = factor * Math.Exp(logSum);
                    }
                }
                else
                {
                    // Set result so that t1 and result will partially cancel
                    if (factor > 1)
                    {
                        result = factor;
                        factor = 1;
                    }

                    if (useExpT1)
                    {
                        result *= Math.Exp(logt1);
                    }
                    else
                    {
                        result *= Math.Pow(t1, a);
                    }

                    if (useExpT2)
                    {
                        result *= Math.Exp(logt2);
                    }
                    else
                    {
                        result *= Math.Pow(t2, b);
                    }

                    result *= factor;
                }
            }

            return(result);
        }
Example #6
0
        /// <summary>
        /// Returns Log(Beta(a,b))
        /// </summary>
        /// <param name="a">Requires finite a > 0</param>
        /// <param name="b">Requires finite b > 0</param>
        public static double LogBeta(double a, double b)
        {
            double c = a + b;

            if ((!(a > 0) || double.IsInfinity(a))
                || (!(b > 0) || double.IsInfinity(b))) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite a,b > 0", a, b);
                return double.NaN;
            }
            if (double.IsInfinity(c)) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite c == a+b: c = {2}", a, b, c);
                return double.NaN;
            }

            // Special cases:
            if ((c == a) && (b < DoubleLimits.MachineEpsilon))
                return Math2.Lgamma(b);
            if ((c == b) && (a < DoubleLimits.MachineEpsilon))
                return Math2.Lgamma(a);
            if (b == 1)
                return -Math.Log(a);
            if (a == 1)
                return -Math.Log(b);

            // B(a,b) == B(b, a)
            if (a < b)
                Utility.Swap(ref a, ref b);

            // from this point a >= b

            if (a < 1) {
                // When x < TinyX, Γ(x) ~ 1/x 
                const double SmallX = DoubleLimits.MachineEpsilon / Constants.EulerMascheroni;
                if (a < SmallX) {
                    if (c < SmallX) {
                        // In this range, Beta(a, b) ~= 1/a + 1/b 
                        // so, we won't have an argument overflow as long as 
                        // the min(a, b) >= 2*DoubleLimits.MinNormalValue

                        if (b < 2 * DoubleLimits.MinNormalValue)
                            return Math.Log(c / a) - Math.Log(b);

                        return Math.Log((c / a) / b);
                    }
                    Debug.Assert(c <= GammaSmall.UpperLimit);
                    return -Math.Log((GammaSmall.Tgamma(c) * a * b));
                }
                if (b < SmallX)
                    return Math.Log(Tgamma(a) / (b * Tgamma(c)));

                return Math.Log((Tgamma(a) / Tgamma(c)) * Tgamma(b));

            } else if (a >= StirlingGamma.LowerLimit) {

                if ( b >= StirlingGamma.LowerLimit )
                    return StirlingGamma.LogBeta(a, b);

                return StirlingGamma.LgammaDelta(a, b) + Lgamma(b);
            }

            return Lanczos.LogBeta(a, b);
        }
Example #7
0
        /// <summary>
        /// Returns the Beta function
        /// <para>B(a,b) = Γ(a)*Γ(b)/Γ(a+b)</para>
        /// </summary>
        /// <param name="a">Requires finite a > 0</param>
        /// <param name="b">Requires finite b > 0</param>
        public static double Beta(double a, double b)
        {
            double c = a + b;

            if ((!(a > 0) || double.IsInfinity(a))
                || (!(b > 0) || double.IsInfinity(b))) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite a,b > 0", a, b);
                return double.NaN;
            }
            if (double.IsInfinity(c)) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite c == a+b: c = {2}", a, b, c);
                return double.NaN;
            }


            // Special cases:
            if ((c == a) && (b < DoubleLimits.MachineEpsilon))
                return Math2.Tgamma(b);
            if ((c == b) && (a < DoubleLimits.MachineEpsilon))
                return Math2.Tgamma(a);
            if (b == 1)
                return 1 / a;
            if (a == 1)
                return 1 / b;

            // B(a,b) == B(b, a)
            if (a < b)
                Utility.Swap(ref a, ref b);

            // from this point a >= b

            if (a < 1) {
                // When x < TinyX, Γ(x) ~ 1/x 
                const double SmallX = DoubleLimits.MachineEpsilon / Constants.EulerMascheroni;
                if ( a < SmallX ) {
                    if (c < SmallX)
                        return (c / a) / b;
                    Debug.Assert(c <= GammaSmall.UpperLimit);
                    return 1/(GammaSmall.Tgamma(c) * a * b);
                }
                if ( b < SmallX ) 
                    return Tgamma(a) / (b * Tgamma(c));
                return Tgamma(a) * ( Tgamma(b)/Tgamma(c) );
            }

            // Our Stirling series is more accurate than Lanczos
            if (a >= StirlingGamma.LowerLimit ) {
                if (b >= StirlingGamma.LowerLimit)
                    return StirlingGamma.Beta(a, b);

                // for large a, Γ(a)/Γ(a + b) ~ a^-b, so don't underflow too soon
                if (b < 1 || b * Math.Log(a) <= -DoubleLimits.MinLogValue)
                    return Tgamma(b) * StirlingGamma.TgammaDeltaRatio(a, b);

                // fall through for very large a small 1 < b < LowerLimit

            }


            return Lanczos.Beta(a, b);
        }
Example #8
0
        /// <summary>
        /// Returns the ratio of gamma functions
        /// <para>TgammaDeltaRatio(x,delta) = Γ(x)/Γ(x+delta)</para>
        /// </summary>
        /// <param name="x">The argument for the gamma function in the numerator. Requires x &gt; 0</param>
        /// <param name="delta">The offset for the denominator. Requires x + delta &gt; 0</param>
        public static double TgammaDeltaRatio(double x, double delta)
        {
            double xpd = x + delta;
            if (double.IsNaN(x) || double.IsNaN(delta)) {
                Policies.ReportDomainError("TgammaDeltaRatio(x: {0}, delta: {1}): Requires x > 0; x + delta > 0", x, delta);
                return double.NaN;
            }

            // Γ(x)/Γ(x) == 1
            // this needs to be here in case x = 0
            if (delta == 0)
                return 1;

            if (!(x > 0)) {
                Policies.ReportDomainError("TgammaDeltaRatio(x: {0}, delta: {1}): Requires x > 0; negative x not implemented", x, delta);
                return double.NaN;
            }
            if (!(xpd > 0)) {
                Policies.ReportDomainError("TgammaDeltaRatio(x: {0}, delta: {1}): Requires x+delta > 0", x, delta);
                return double.NaN;
            }
            if (double.IsInfinity(x)) {
                if (double.IsInfinity(delta)) {
                    Policies.ReportDomainError("TgammaDeltaRatio(x: {0}, delta: {1}): Infinity/Infinity", x, delta);
                    return double.NaN;
                }
                return double.PositiveInfinity;
            }

            if (double.IsInfinity(delta))
                return 0.0;

            if (IsInteger(delta)) {
                if (IsInteger(x)) {
                    // Both x and delta are integers, see if we can just use table lookup
                    // of the factorials to get the result:
                    if ((x <= Math2.FactorialTable.Length) && (x + delta <= Math2.FactorialTable.Length))
                        return Math2.FactorialTable[(int)x - 1] / Math2.FactorialTable[(int)(x + delta) - 1];
                }


                // if delta is a small integer, we can use a finite product
                if (Math.Abs(delta) < 20) {
                    if (delta == 0)
                        return 1;
                    if (delta < 0) {
                        x -= 1;
                        double result = x;
                        while ( ++delta < 0 ) {
                            x -= 1;
                            result *= x;
                        }
                        return result;
                    } else {
                        double result = x;
                        while ( --delta > 0 ) {
                            x += 1;
                            result *= x;
                        }
                        return 1/result;
                    }
                }
            }

            // our Tgamma ratio already handles cases where 
            // one or both arguments are small 

            const double LowerLimit = DoubleLimits.MachineEpsilon / Constants.EulerMascheroni; // 1.73eps
            if (x < LowerLimit || xpd < LowerLimit)
                return Math2.TgammaRatio(x, xpd);

            if ((x < 1 && delta > Math2.FactorialTable.Length) || (xpd < 1 && x > Math2.FactorialTable.Length))
                return Math2.TgammaRatio(x, xpd);

            // Use the Lanczos approximation to compute the delta ratio 
            return Lanczos.TgammaDeltaRatio(x, delta);
        }