예제 #1
0
파일: GammaPower.cs 프로젝트: k1moday/infer
        /// <summary>
        /// Logarithm of the density function.
        /// </summary>
        /// <param name="x">Where to evaluate the density</param>
        /// <param name="shape">Shape parameter</param>
        /// <param name="rate">Rate parameter</param>
        /// <param name="power">Power parameter</param>
        /// <returns>log(GammaPower(x;shape,rate,power))</returns>
        /// <remarks>
        /// The distribution is <c>p(x) = x^(a/c-1)*exp(-b*x^(1/c))*b^a/Gamma(a)/abs(c)</c>.
        /// When a &lt;= 0 or b &lt;= 0 or c = 0 the <c>b^a/Gamma(a)/abs(c)</c> term is dropped.
        /// Thus if shape = 1 and rate = 0 the density is 1.
        /// </remarks>
        public static double GetLogProb(double x, double shape, double rate, double power)
        {
            if (x < 0)
            {
                return(double.NegativeInfinity);
            }
            if (power == 0.0)
            {
                return((x == 1.0) ? 0.0 : Double.NegativeInfinity);
            }
            if (double.IsPositiveInfinity(x)) // Avoid subtracting infinities below
            {
                if (power > 0)
                {
                    if (rate > 0)
                    {
                        return(-x);
                    }
                    else if (rate < 0)
                    {
                        return(x);
                    }
                    // Fall through when rate == 0
                }
                else // This case avoids inf/inf below
                {
                    if (shape > power)
                    {
                        return(-x);
                    }
                    else if (shape < power)
                    {
                        return(x);
                    }
                    // Fall through when shape == power
                }
            }
            double logRate            = Math.Log(rate);
            double logx               = Math.Log(x);
            double logShapeMinusPower = Math.Log(Math.Max(0, shape - power));
            double logMode            = Math.Log(GetMode(shape, rate, power)); // power * (logShapeMinusPower - logRate);

            if (!double.IsNegativeInfinity(logMode))
            {
                // We compute the density in a way that ensures the maximum is at the mode returned by GetMode.
                // mode = ((shape - power)/rate)^power
                // The part of the log-density that depends on x is:
                //   (shape/power-1)*log(x) - rate*x^(1/power)
                // = (shape/power-1)*(log(x) - power/(shape-power)*rate*x^(1/power))
                // = (shape/power-1)*(log(x) - power*(x/mode)^(1/power))
                // = (shape/power-1)*(log(x/mode) - power*(x/mode)^(1/power) + log(mode))
                // = (shape/power-1)*(log(x/mode) - power*(x/mode)^(1/power)) + (shape-power)*log((shape-power)/rate)
                // = (shape-power)*(log(x/mode)/power - exp(log(x/mode)/power)) + (shape-power)*log((shape-power)/rate)
                // = (shape-power)*(log(x/mode)/power + 1 - exp(log(x/mode)/power)) + (shape-power)*(log((shape-power)/rate) - 1)
                // = -(shape-power)*(ExpMinus1RatioMinus1RatioMinusHalf(log(x/mode)/power) + 0.5)*(log(x/mode)/power)^2) + (shape-power)*(log((shape-power)/rate) - 1)
                double xOverModeLn          = logx - logMode;
                double xOverModeLnOverPower = xOverModeLn / power;
                if (Math.Abs(xOverModeLnOverPower) < 10)
                {
                    double result;
                    if (double.IsNegativeInfinity(xOverModeLnOverPower))
                    {
                        result = (shape / power - 1) * logx;
                    }
                    else if (double.IsPositiveInfinity(xOverModeLnOverPower) || double.IsPositiveInfinity(MMath.ExpMinus1RatioMinus1RatioMinusHalf(xOverModeLnOverPower)))
                    {
                        return((power - shape) * double.PositiveInfinity);
                    }
                    else
                    {
                        result = -(shape - power) * (MMath.ExpMinus1RatioMinus1RatioMinusHalf(xOverModeLnOverPower) + 0.5) * xOverModeLnOverPower * xOverModeLnOverPower;
                    }
                    if (IsProper(shape, rate, power))
                    {
                        // Remaining terms are:
                        // shape * Math.Log(rate) - MMath.GammaLn(shape) - Math.Log(Math.Abs(power)) + (shape - power) * (Math.Log((shape - power) / rate) - 1)
                        if (shape > 1e10)
                        {
                            //result += power * (1 + Math.Log(rate)) - Math.Log(Math.Abs(power));
                            // In double precision, we can assume GammaLn(x) = (x-0.5)*log(x) - x for x > 1e10
                            //result += (shape - power) * Math.Log(1 - power / shape) + (0.5 - power) * Math.Log(shape);
                            result += shape * Math.Log(1 - power / shape) + power * (1 + logRate - logShapeMinusPower) + 0.5 * Math.Log(shape) - Math.Log(Math.Abs(power));
                        }
                        else
                        {
                            //result += (shape - power) * Math.Log(shape - power) - shape - MMath.GammaLn(shape);
                            result += (shape - power) * (logShapeMinusPower - logRate - 1);
                            result += shape * logRate - MMath.GammaLn(shape) - Math.Log(Math.Abs(power));
                        }
                    }
                    else
                    {
                        result += (shape - power) * (logShapeMinusPower - logRate - 1);
                    }
                    return(result);
                }
                // Fall through
            }

            {
                double result        = 0;
                double logxOverPower = logx / power;
                if (logx == 0) // avoid inf * 0
                {
                    result -= rate;
                }
                else if (Math.Abs(logxOverPower) < MMath.SqrtUlp1)
                {
                    // The part of the log-density that depends on x is:
                    //   (shape/power-1)*log(x) - rate*x^(1/power)
                    // = (shape/power-1)*log(x) - rate*exp(log(x)/power))
                    //   (when abs(log(x)/power)^2 < ulp(1), exp(log(x)/power) can be approximated by 1 + log(x)/power, because
                    //    the third term of this power series 0.5*log(x)^2/power^2 (as well as all other terms) is less than
                    //    half-ulp of the first)
                    // = (shape/power-1)*log(x) - rate*(1 + log(x)/power)
                    // = ((shape - rate)/power - 1)*log(x) - rate
                    result += ((shape - rate) / power - 1) * logx - rate;
                }
                else
                {
                    if (shape != power && x != 1)
                    {
                        result += (shape / power - 1) * logx;
                    }
                    if (rate != 0)
                    {
                        double xInvPowerRate = -Math.Exp(logxOverPower + logRate);
                        if (double.IsInfinity(xInvPowerRate) && x != 0 && !double.IsPositiveInfinity(x))
                        {
                            // recompute another way to avoid overflow
                            double logRatePower = logRate * power;
                            if (!double.IsInfinity(logRatePower))
                            {
                                xInvPowerRate = -Math.Exp((logx + logRatePower) / power);
                            }
                        }
                        if (double.IsInfinity(xInvPowerRate))
                        {
                            return(xInvPowerRate);
                        }
                        result += xInvPowerRate;
                    }
                }
                if (IsProper(shape, rate, power))
                {
                    double minusLogNormalizer;
                    if (shape > 1e10)
                    {
                        double logShape = Math.Log(shape);
                        // In double precision, we can assume GammaLn(x) = (x-0.5)*log(x) - x for x > 1e10
                        minusLogNormalizer = shape * (logRate - logShape + 1) + 0.5 * logShape - Math.Log(Math.Abs(power));
                        if (double.IsNegativeInfinity(minusLogNormalizer) && double.IsPositiveInfinity(result))
                        {
                            // Recompute a different way to avoid subtracting infinities
                            result = -logx - rate * Math.Exp(logxOverPower) + shape * (logx / power + logRate - logShape + 1) + 0.5 * logShape - Math.Log(Math.Abs(power));
                        }
                        else
                        {
                            result += minusLogNormalizer;
                        }
                    }
                    else
                    {
                        result += shape * logRate - MMath.GammaLn(shape) - Math.Log(Math.Abs(power));
                    }
                }
                return(result);
            }
        }