/// <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 <= 0 or b <= 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); } }