예제 #1
0
        /// <summary>
        /// Computes <c>GammaUpper(s,x)/(x^(s-1)*exp(-x)) - 1</c> to high accuracy
        /// </summary>
        /// <param name="s"></param>
        /// <param name="x">A real number gt;= 45 and gt; <paramref name="s"/>/0.99</param>
        /// <param name="regularized"></param>
        /// <returns></returns>
        public static double GammaUpperRatio(double s, double x, bool regularized = true)
        {
            if (s >= x * 0.99)
            {
                throw new ArgumentOutOfRangeException(nameof(s), s, "s >= x*0.99");
            }
            if (x < 45)
            {
                throw new ArgumentOutOfRangeException(nameof(x), x, "x < 45");
            }
            double term = (s - 1) / x;
            double sum  = term;

            for (int i = 2; i < 1000; i++)
            {
                term *= (s - i) / x;
                double oldSum = sum;
                sum += term;
                if (MMath.AreEqual(sum, oldSum))
                {
                    return(regularized ? sum / MMath.Gamma(s) : sum);
                }
            }
            throw new Exception($"GammaUpperRatio not converging for s={s:g17}, x={x:g17}, regularized={regularized}");
        }
예제 #2
0
        /// <summary>
        /// Constructs a Gamma distribution with the given mean and mean logarithm.
        /// </summary>
        /// <param name="mean">Desired expected value.</param>
        /// <param name="logMeanMinusMeanLog">Logarithm of desired expected value minus desired expected logarithm.</param>
        /// <returns>A new Gamma distribution.</returns>
        /// <remarks>This function is equivalent to maximum-likelihood estimation of a Gamma distribution
        /// from data given by sufficient statistics.
        /// This function is significantly slower than the other constructors since it
        /// involves nonlinear optimization. The algorithm is a generalized Newton iteration,
        /// described in "Estimating a Gamma distribution" by T. Minka, 2002.
        /// </remarks>
        public static Gamma FromLogMeanMinusMeanLog(double mean, double logMeanMinusMeanLog)
        {
            if (logMeanMinusMeanLog <= 0)
            {
                return(Gamma.PointMass(mean));
            }
            double shape = 0.5 / logMeanMinusMeanLog;

            for (int iter = 0; iter < 100; iter++)
            {
                double g = LogMinusDigamma(shape) - logMeanMinusMeanLog;
                //Trace.WriteLine($"shape = {shape} g = {g}");
                if (MMath.AreEqual(g, 0))
                {
                    break;
                }
                shape /= 1 + g / (1 - shape * MMath.Trigamma(shape));
            }
            if (double.IsNaN(shape))
            {
                throw new InferRuntimeException("shape is nan");
            }
            if (shape > double.MaxValue)
            {
                return(Gamma.PointMass(mean));
            }
            return(Gamma.FromShapeAndRate(shape, shape / mean));
        }
예제 #3
0
파일: GammaPower.cs 프로젝트: k1moday/infer
        /// <summary>
        /// Constructs a GammaPower distribution with the given mean and mean logarithm.
        /// </summary>
        /// <param name="mean">Desired expected value.</param>
        /// <param name="meanLog">Desired expected logarithm.</param>
        /// <param name="power">Desired power.</param>
        /// <returns>A new GammaPower distribution.</returns>
        /// <remarks>This function is equivalent to maximum-likelihood estimation of a Gamma distribution
        /// from data given by sufficient statistics.
        /// This function is significantly slower than the other constructors since it
        /// involves nonlinear optimization. The algorithm is a generalized Newton iteration,
        /// described in "Estimating a Gamma distribution" by T. Minka, 2002.
        /// </remarks>
        public static GammaPower FromMeanAndMeanLog(double mean, double meanLog, double power)
        {
            // Constraints:
            // mean = Gamma(Shape + power)/Gamma(Shape)/Rate^power
            // meanLog = power*(digamma(Shape) - log(Rate))
            // digamma(Shape) =approx log(Shape - 0.5)
            double logMeanOverPower = Math.Log(mean) / power;
            double meanLogOverPower = meanLog / power;
            double shape            = 1;
            double logRate          = 0;

            for (int iter = 0; iter < 1000; iter++)
            {
                double oldLogRate = logRate;
                double oldShape   = shape;
                logRate = MMath.RisingFactorialLnOverN(shape, power) - logMeanOverPower;
                shape   = Math.Exp(meanLogOverPower + logRate) + 0.5;
                //Console.WriteLine($"shape = {shape:g17}, logRate = {logRate:g17}");
                if (MMath.AreEqual(oldLogRate, logRate) && MMath.AreEqual(oldShape, shape))
                {
                    break;
                }
                if (double.IsNaN(shape))
                {
                    throw new Exception("Failed to converge");
                }
            }
            return(FromShapeAndRate(shape, Math.Exp(logRate), power));
        }
예제 #4
0
파일: Gamma.cs 프로젝트: 0xCM/arrows
 /// <summary>
 /// Returns the value x such that GetProbLessThan(x) == probability.
 /// </summary>
 /// <param name="probability">A real number in [0,1].</param>
 /// <returns></returns>
 public double GetQuantile(double probability)
 {
     if (probability < 0)
     {
         throw new ArgumentOutOfRangeException("probability < 0");
     }
     if (probability > 1)
     {
         throw new ArgumentOutOfRangeException("probability > 1");
     }
     if (this.IsPointMass)
     {
         return((probability == 1.0) ? MMath.NextDouble(this.Point) : this.Point);
     }
     else if (!IsProper())
     {
         throw new ImproperDistributionException(this);
     }
     else if (MMath.AreEqual(probability, 0))
     {
         return(0);
     }
     else if (MMath.AreEqual(probability, 1))
     {
         return(double.PositiveInfinity);
     }
     else if (Shape == 1)
     {
         // cdf is 1 - exp(-x*rate)
         return(-Math.Log(1 - probability) / Rate);
     }
     else
     {
         // Binary search
         double lowerBound = 0;
         double upperBound = double.MaxValue;
         while (lowerBound < upperBound)
         {
             double average = MMath.Average(lowerBound, upperBound);
             double p       = GetProbLessThan(average);
             if (p == probability)
             {
                 return(average);
             }
             else if (p < probability)
             {
                 lowerBound = MMath.NextDouble(average);
             }
             else
             {
                 upperBound = MMath.PreviousDouble(average);
             }
         }
         return(lowerBound);
     }
 }
예제 #5
0
        // requires x < 0, r <= 0, and x-r*y <= 0 (or equivalently y < -x).
        public static double NormalCdfConFrac3(double x, double y, double r)
        {
            if (x > 0)
            {
                throw new ArgumentException("x >= 0");
            }
            if (r > 0)
            {
                throw new ArgumentException("r > 0");
            }
            if (x - r * y > 0)
            {
                throw new ArgumentException("x - r*y > 0");
            }
            double numer     = NormalCdfDx(x, y, r) + r * NormalCdfMomentDy(0, x, y, r);
            double numerPrev = 0;
            double denom     = x;
            double denomPrev = 1;
            double rprev     = 0;

            for (int i = 1; i < 1000; i++)
            {
                double numerNew = x * numer + i * numerPrev + r * NormalCdfMomentDy(i, x, y, r);
                double denomNew = x * denom + i * denomPrev;
                numerPrev = numer;
                numer     = numerNew;
                denomPrev = denom;
                denom     = denomNew;
                if (i % 2 == 1)
                {
                    //Console.WriteLine("denom/dfact = {0}", denom / dfact);
                    double result = -numer / denom;
                    Console.WriteLine("iter {0}: {1}", i, result);
                    if (double.IsInfinity(result) || double.IsNaN(result))
                    {
                        throw new Exception();
                    }
                    if (MMath.AreEqual(result, rprev))
                    {
                        return(result);
                    }
                    rprev = result;
                }
            }
            throw new Exception(string.Format("NormalCdfConFrac3 not converging for x={0} y={1} r={2}", x, y, r));
        }
예제 #6
0
        public static Gaussian XAverageConditional_Helper([SkipIfUniform] Bernoulli isPositive, [SkipIfUniform, Proper] Gaussian x, bool forceProper)
        {
            if (x.IsPointMass)
            {
                if (isPositive.IsPointMass && (isPositive.Point != (x.Point > 0)))
                {
                    return(Gaussian.PointMass(0));
                }
                else
                {
                    return(Gaussian.Uniform());
                }
            }
            double tau  = x.MeanTimesPrecision;
            double prec = x.Precision;

            if (prec == 0.0)
            {
                if (isPositive.IsPointMass)
                {
                    if ((isPositive.Point && tau < 0) ||
                        (!isPositive.Point && tau > 0))
                    {
                        // posterior is proportional to I(x>0) exp(tau*x)
                        //double mp = -1 / tau;
                        //double vp = mp * mp;
                        return(Gaussian.FromNatural(-tau, tau * tau) / x);
                    }
                }
                return(Gaussian.Uniform());
            }
            else if (prec < 0)
            {
                throw new ImproperMessageException(x);
            }
            double sqrtPrec = Math.Sqrt(prec);
            // m/sqrt(v) = (m/v)/sqrt(1/v)
            double z = tau / sqrtPrec;
            // epsilon = p(b=F)
            // eq (51) in EP quickref
            double alpha;

            if (isPositive.IsPointMass)
            {
                if ((isPositive.Point && z < -10) || (!isPositive.Point && z > 10))
                {
                    if (z > 10)
                    {
                        z = -z;
                    }
                    //double Y = MMath.NormalCdfRatio(z);
                    // dY = z*Y + 1
                    // d2Y = z*dY + Y
                    // posterior mean = m + sqrt(v)/Y = sqrt(v)*(z + 1/Y) = sqrt(v)*dY/Y
                    //                = sqrt(v)*(d2Y/Y - 1)/z = (d2Y/Y - 1)/tau
                    //                =approx sqrt(v)*(-1/z) = -1/tau
                    // posterior variance = v - v*dY/Y^2 =approx v/z^2
                    // posterior E[x^2] = v - v*dY/Y^2 + v*dY^2/Y^2 = v - v*dY/Y^2*(1 - dY) = v + v*z*dY/Y = v*d2Y/Y
                    // d3Y = z*d2Y + 2*dY
                    //     = z*(z*dY + Y) + 2*dY
                    //     = z^2*dY + z*Y + 2*dY
                    //     = z^2*dY + 3*dY - 1
                    double d3Y = 6 * MMath.NormalCdfMomentRatio(3, z);
                    //double dY = MMath.NormalCdfMomentRatio(1, z);
                    double dY = (d3Y + 1) / (z * z + 3);
                    if (MMath.AreEqual(dY, 0))
                    {
                        double tau2 = tau * tau;
                        if (tau2 > double.MaxValue)
                        {
                            return(Gaussian.PointMass(-1 / tau));
                        }
                        else
                        {
                            return(Gaussian.FromNatural(-2 * tau, tau2));
                        }
                    }
                    // Y = (dY-1)/z
                    // alpha = sqrtPrec*z/(dY-1) = tau/(dY-1)
                    // alpha+tau = tau*(1 + 1/(dY-1)) = tau*dY/(dY-1) = alpha*dY
                    // beta = alpha * (alpha + tau)
                    //      = alpha * tau * dY / (dY - 1)
                    //      = prec * z^2 * dY/(dY-1)^2
                    // prec/beta = (dY-1)^2/(dY*z^2)
                    // prec/beta - 1 = ((dY-1)^2 - dY*z^2)/(dY*z^2)
                    // weight = beta/(prec - beta)
                    //        = dY*z^2/((dY-1)^2 - dY*z^2)
                    //        = dY*z^2/(dY^2 -2*dY + 1 - dY*z^2)
                    //        = dY*z^2/(dY^2 + 1 + z*Y - d3Y)
                    //        = dY*z^2/(dY^2 + dY - d3Y)
                    //        = z^2/(dY + 1 - d3Y/dY)
                    double d3YidY  = d3Y / dY;
                    double denom   = dY - d3YidY + 1;
                    double msgPrec = tau * tau / denom;
                    // weight * (tau + alpha) + alpha
                    // = z^2/(dY + 1 - d3Y/dY) * alpha*dY + alpha
                    // = alpha*(z^2*dY/(dY + 1 - d3Y/dY) + 1)
                    // = alpha*(z^2*dY + dY + 1 - d3Y/dY)/(dY + 1 - d3Y/dY)
                    // = alpha*(z^2*dY + dY + 1 - d3Y/dY)/(dY + 1 - d3Y/dY)
                    // = alpha*(d3Y - 2*dY + 2 - d3Y/dY)/(dY + 1 - d3Y/dY)
                    // z^2*dY = d3Y - 3*dY + 1
                    double numer            = (d3Y - 2 * dY - d3YidY + 2) / (dY - 1);
                    double msgMeanTimesPrec = tau * numer / denom;
                    if (msgPrec > double.MaxValue)
                    {
                        // In this case, the message should be the posterior.
                        // posterior mean = (msgMeanTimesPrec + tau)*denom/(tau*tau)
                        // = (numer + denom)/tau
                        return(Gaussian.PointMass((numer + denom) / tau));
                    }
                    else
                    {
                        return(Gaussian.FromNatural(msgMeanTimesPrec, msgPrec));
                    }
                }
                else if (isPositive.Point)
                {
                    alpha = sqrtPrec / MMath.NormalCdfRatio(z);
                }
                else
                {
                    alpha = -sqrtPrec / MMath.NormalCdfRatio(-z);
                }
            }
            else
            {
                //double v = MMath.LogSumExp(isPositive.LogProbTrue + MMath.NormalCdfLn(z), isPositive.LogProbFalse + MMath.NormalCdfLn(-z));
                double v = LogAverageFactor(isPositive, x);
                alpha = sqrtPrec * Math.Exp(-z * z * 0.5 - MMath.LnSqrt2PI - v) * (2 * isPositive.GetProbTrue() - 1);
            }
            // eq (52) in EP quickref (where tau = mnoti/Vnoti)
            double beta;

            if (alpha == 0)
            {
                beta = 0;  // avoid 0 * infinity
            }
            else
            {
                beta = alpha * (alpha + tau);
            }
            double weight = beta / (prec - beta);

            if (forceProper && weight < 0)
            {
                weight = 0;
            }
            Gaussian result = new Gaussian();

            if (weight == 0)
            {
                // avoid 0 * infinity
                result.MeanTimesPrecision = alpha;
            }
            else
            {
                // eq (31) in EP quickref; same as inv(inv(beta)-inv(prec))
                result.Precision = prec * weight;
                // eq (30) in EP quickref times above and simplified
                result.MeanTimesPrecision = weight * (tau + alpha) + alpha;
            }
            if (double.IsNaN(result.Precision) || double.IsNaN(result.MeanTimesPrecision))
            {
                throw new InferRuntimeException($"result is NaN.  isPositive={isPositive}, x={x}, forceProper={forceProper}");
            }
            return(result);
        }