示例#1
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);
        }
示例#2
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);
        }