/// <summary>
 /// Evidence message for EP.
 /// </summary>
 /// <param name="cases">Incoming message from 'cases'.</param>
 /// <param name="b">Incoming message from 'b'.</param>
 public static double LogEvidenceRatio(IList <Bernoulli> cases, Bernoulli b)
 {
     // result = log (p(data|b=true) p(b=true) + p(data|b=false) p(b=false))
     //          log (p(data|b=true) p(b=true) + p(data|b=false) (1-p(b=true))
     //          log ((p(data|b=true) - p(data|b=false)) p(b=true) + p(data|b=false))
     //          log ((p(data|b=true)/p(data|b=false) - 1) p(b=true) + 1) + log p(data|b=false)
     // where cases[0].LogOdds = log p(data|b=true)
     //       cases[1].LogOdds = log p(data|b=false)
     if (b.IsPointMass)
     {
         return(b.Point ? cases[0].LogOdds : cases[1].LogOdds);
     }
     //else return MMath.LogSumExp(cases[0].LogOdds + b.GetLogProbTrue(), cases[1].LogOdds + b.GetLogProbFalse());
     else
     {
         // the common case is when cases[0].LogOdds == cases[1].LogOdds.  we must not introduce rounding error in that case.
         if (cases[0].LogOdds >= cases[1].LogOdds)
         {
             if (Double.IsNegativeInfinity(cases[1].LogOdds))
             {
                 return(cases[0].LogOdds + b.GetLogProbTrue());
             }
             else
             {
                 return(cases[1].LogOdds + MMath.Log1Plus(b.GetProbTrue() * MMath.ExpMinus1(cases[0].LogOdds - cases[1].LogOdds)));
             }
         }
         else
         {
             if (Double.IsNegativeInfinity(cases[0].LogOdds))
             {
                 return(cases[1].LogOdds + b.GetLogProbFalse());
             }
             else
             {
                 return(cases[0].LogOdds + MMath.Log1Plus(b.GetProbFalse() * MMath.ExpMinus1(cases[1].LogOdds - cases[0].LogOdds)));
             }
         }
     }
 }
        /// <summary>
        /// Find a Beta distribution with given integral and mean times a Beta weight function.
        /// </summary>
        /// <param name="mean">The desired value of the mean</param>
        /// <param name="logZ">The desired value of the integral</param>
        /// <param name="a">trueCount-1 of the weight function</param>
        /// <param name="b">falseCount-1 of the weight function</param>
        /// <returns></returns>
        private static Beta BetaFromMeanAndIntegral(double mean, double logZ, double a, double b)
        {
            // The constraints are:
            // 1. int_p to_p(p) p^a (1-p)^b dp = exp(logZ)
            // 2. int_p to_p(p) p p^a (1-p)^b dp = mean*exp(logZ)
            // Let to_p(p) = Beta(p; af, bf)
            // The LHS of (1) is gamma(af+bf)/gamma(af+bf+a+b) gamma(af+a)/gamma(af) gamma(bf+b)/gamma(bf)
            // The LHS of (2) is gamma(af+bf)/gamma(af+bf+a+b+1) gamma(af+a+1)/gamma(af) gamma(bf+b)/gamma(bf)
            // The ratio of (2)/(1) is gamma(af+a+1)/gamma(af+a) gamma(af+bf+a+b)/gamma(af+bf+a+b+1) = (af+a)/(af+bf+a+b) = mean
            // Solving for bf gives bf = (af+a)/mean - (af+a+b).
            // To solve for af, we apply a generalized Newton algorithm to solve equation (1) with bf substituted.
            // af0 is the smallest value of af that ensures (af >= 0, bf >= 0).
            if (mean <= 0)
            {
                throw new ArgumentException("mean <= 0");
            }
            if (mean >= 1)
            {
                throw new ArgumentException("mean >= 1");
            }
            if (double.IsNaN(mean))
            {
                throw new ArgumentException("mean is NaN");
            }
            // bf = (af+bx)*(1-m)/m
            double bx = -(mean * (a + b) - a) / (1 - mean);
            // af0 is the lower bound for af
            // we need both af>0 and bf>0
            double af0     = Math.Max(0, -bx);
            double x       = Math.Max(0, bx);
            double af      = af0 + 1;      // initial guess for af
            double invMean = 1 / mean;
            double bf      = (af + a) * invMean - (af + a + b);

            for (int iter = 0; iter < 20; iter++)
            {
                double old_af = af;
                double f      = (MMath.GammaLn(af + bf) - MMath.GammaLn(af + bf + a + b)) + (MMath.GammaLn(af + a) - MMath.GammaLn(af)) + (MMath.GammaLn(bf + b) - MMath.GammaLn(bf));
                double g      = (MMath.Digamma(af + bf) - MMath.Digamma(af + bf + a + b)) * invMean + (MMath.Digamma(af + a) - MMath.Digamma(af)) + (MMath.Digamma(bf + b) - MMath.Digamma(bf)) * (invMean - 1);
                // fit a fcn of the form: s*log((af-af0)/(af+x)) + c
                // whose deriv is s/(af-af0) - s/(af+x)
                double s            = g / (1 / (af - af0) - 1 / (af + x));
                double c            = f - s * Math.Log((af - af0) / (af + x));
                bool   isIncreasing = (x > -af0);
                if ((!isIncreasing && c >= logZ) || (isIncreasing && c <= logZ))
                {
                    // the approximation doesn't fit; use Gauss-Newton instead
                    af += (logZ - f) / g;
                }
                else
                {
                    // now solve s*log((af-af0)/(af+x))+c = logz
                    // af-af0 = exp((logz-c)/s) (af+x)
                    af = af0 + (x + af0) / MMath.ExpMinus1((c - logZ) / s);
                    if (af == af0)
                    {
                        throw new ArgumentException("logZ is out of range");
                    }
                }
                if (double.IsNaN(af))
                {
                    throw new ApplicationException("af is nan");
                }
                bf = (af + a) / mean - (af + a + b);
                if (Math.Abs(af - old_af) < 1e-8)
                {
                    break;
                }
            }
            if (false)
            {
                // check that integrals are correct
                double f = (MMath.GammaLn(af + bf) - MMath.GammaLn(af + bf + a + b)) + (MMath.GammaLn(af + a) - MMath.GammaLn(af)) + (MMath.GammaLn(bf + b) - MMath.GammaLn(bf));
                if (Math.Abs(f - logZ) > 1e-6)
                {
                    throw new ApplicationException("wrong f");
                }
                double f2 = (MMath.GammaLn(af + bf) - MMath.GammaLn(af + bf + a + b + 1)) + (MMath.GammaLn(af + a + 1) - MMath.GammaLn(af)) + (MMath.GammaLn(bf + b) - MMath.GammaLn(bf));
                if (Math.Abs(f2 - (Math.Log(mean) + logZ)) > 1e-6)
                {
                    throw new ApplicationException("wrong f2");
                }
            }
            return(new Beta(af, bf));
        }
예제 #3
0
        /// <summary>
        /// Sample from a Gaussian(0,1) truncated at the given upper and lower bounds
        /// </summary>
        /// <param name="lowerBound">Can be -Infinity.</param>
        /// <param name="upperBound">Must be &gt;= <paramref name="lowerBound"/>.  Can be Infinity.</param>
        /// <returns>A real number &gt;= <paramref name="lowerBound"/> and &lt; <paramref name="upperBound"/></returns>
        public static double NormalBetween(double lowerBound, double upperBound)
        {
            if (double.IsNaN(lowerBound))
            {
                throw new ArgumentException("lowerBound is NaN");
            }
            if (double.IsNaN(upperBound))
            {
                throw new ArgumentException("upperBound is NaN");
            }
            double delta = upperBound - lowerBound;

            if (delta == 0)
            {
                return(lowerBound);
            }
            if (delta < 0)
            {
                throw new ArgumentException("upperBound (" + upperBound + ") < lowerBound (" + lowerBound + ")");
            }
            // Switch between the following 3 options:
            // 1. Gaussian rejection, with acceptance rate Z = NormalCdf(upperBound) - NormalCdf(lowerBound)
            // 2. Uniform rejection, with acceptance rate sqrt(2*pi)*Z/delta if the interval contains 0
            // 3. Truncated exponential rejection, with acceptance rate
            //    = sqrt(2*pi)*Z*lambda*exp(-lambda^2/2)/(exp(-lambda*lowerBound)-exp(-lambda*upperBound))
            //    = sqrt(2*pi)*Z*lowerBound*exp(lowerBound^2/2)/(1-exp(-lowerBound*(upperBound-lowerBound)))
            // (3) has the highest acceptance rate under the following conditions:
            //     lowerBound > 0.5 or (lowerBound > 0 and delta < 2.5)
            // (2) has the highest acceptance rate if the interval contains 0 and delta < sqrt(2*pi)
            // (1) has the highest acceptance rate otherwise
            if (lowerBound > 0.5 || (lowerBound > 0 && delta < 2.5))
            {
                // Rejection sampler using truncated exponential proposal
                double lambda = lowerBound;
                double s      = MMath.ExpMinus1(-lambda * delta);
                double c      = 2 * lambda * lambda;
                while (true)
                {
                    double x = -MMath.Log1Plus(s * Rand.Double());
                    double u = -System.Math.Log(Rand.Double());
                    if (c * u > x * x)
                    {
                        return(x / lambda + lowerBound);
                    }
                }
                throw new InferRuntimeException("failed to sample");
            }
            else if (upperBound < -0.5 || (upperBound < 0 && delta < 2.5))
            {
                return(-NormalBetween(-upperBound, -lowerBound));
            }
            else if (lowerBound <= 0 && upperBound >= 0 && delta < MMath.Sqrt2PI)
            {
                // Uniform rejection
                while (true)
                {
                    double x = Rand.Double() * delta + lowerBound;
                    double u = -System.Math.Log(Rand.Double());
                    if (2 * u > x * x)
                    {
                        return(x);
                    }
                }
            }
            else
            {
                // Gaussian rejection
                while (true)
                {
                    double x = Rand.Normal();
                    if (x >= lowerBound && x < upperBound)
                    {
                        return(x);
                    }
                }
            }
        }
예제 #4
0
#pragma warning disable 162
#endif

        /// <summary>
        /// Find a Beta distribution with given integral and mean times a Beta weight function.
        /// </summary>
        /// <param name="mean">The desired value of the mean</param>
        /// <param name="logZ">The desired value of the integral</param>
        /// <param name="a">trueCount-1 of the weight function</param>
        /// <param name="b">falseCount-1 of the weight function</param>
        /// <returns></returns>
        private static Beta BetaFromMeanAndIntegral(double mean, double logZ, double a, double b)
        {
            // The constraints are:
            // 1. int_p to_p(p) p^a (1-p)^b dp = exp(logZ)
            // 2. int_p to_p(p) p p^a (1-p)^b dp = mean*exp(logZ)
            // Let to_p(p) = Beta(p; af, bf)
            // The LHS of (1) is gamma(af+bf)/gamma(af+bf+a+b) gamma(af+a)/gamma(af) gamma(bf+b)/gamma(bf)
            // The LHS of (2) is gamma(af+bf)/gamma(af+bf+a+b+1) gamma(af+a+1)/gamma(af) gamma(bf+b)/gamma(bf)
            // The ratio of (2)/(1) is gamma(af+a+1)/gamma(af+a) gamma(af+bf+a+b)/gamma(af+bf+a+b+1) = (af+a)/(af+bf+a+b) = mean
            // Solving for bf gives bf = (af+a)/mean - (af+a+b).
            // To solve for af, we apply a generalized Newton algorithm to solve equation (1) with bf substituted.
            // af0 is the smallest value of af that ensures (af >= 0, bf >= 0).
            if (mean <= 0)
            {
                throw new ArgumentException("mean <= 0");
            }
            if (mean >= 1)
            {
                throw new ArgumentException("mean >= 1");
            }
            if (double.IsNaN(mean))
            {
                throw new ArgumentException("mean is NaN");
            }
            // If exp(logZ) exceeds the largest possible value of (1), then we return a point mass.
            // gammaln(x) =approx (x-0.5)*log(x) - x + 0.5*log(2pi)
            // (af+x)*log(af+x) =approx (af+x)*log(af) + x + 0.5*x*x/af
            // For large af, logZ = (af+bf-0.5)*log(af+bf) - (af+bf+a+b-0.5)*log(af+bf+a+b) +
            //                      (af+a-0.5)*log(af+a) - (af-0.5)*log(af) +
            //                      (bf+b-0.5)*log(bf+b) - (bf-0.5)*log(bf)
            // =approx (af+bf-0.5)*log(af+bf) - ((af+bf+a+b-0.5)*log(af+bf) + (a+b) + 0.5*(a+b)*(a+b)/(af+bf) -0.5*(a+b)/(af+bf)) +
            //   ((af+a-0.5)*log(af) + a + 0.5*a*a/af - 0.5*a/af) - (af-0.5)*log(af) +
            //   ((bf+b-0.5)*log(bf) + b + 0.5*b*b/bf - 0.5*b/bf) - (bf-0.5)*log(bf)
            // = -(a+b)*log(af+bf) - 0.5*(a+b)*(a+b-1)/(af+bf) + a*log(af) + 0.5*a*(a-1)/af + b*log(bf) + 0.5*b*(b-1)/bf
            // =approx (a+b)*log(m) + b*log((1-m)/m) + 0.5*(a+b)*(a+b-1)*m/af - 0.5*a*(a+1)/af - 0.5*b*(b+1)*m/(1-m)/af
            // =approx (a+b)*log(mean) + b*log((1-mean)/mean)
            double maxLogZ = (a + b) * Math.Log(mean) + b * Math.Log((1 - mean) / mean);
            // slope determines whether maxLogZ is the maximum or minimum possible value of logZ
            double slope = (a + b) * (a + b - 1) * mean - a * (a + 1) - b * (b + 1) * mean / (1 - mean);

            if ((slope <= 0 && logZ >= maxLogZ) || (slope > 0 && logZ <= maxLogZ))
            {
                // optimal af is infinite
                return(Beta.PointMass(mean));
            }
            // bf = (af+bx)*(1-m)/m
            double bx = -(mean * (a + b) - a) / (1 - mean);
            // af0 is the lower bound for af
            // we need both af>0 and bf>0
            double af0      = Math.Max(0, -bx);
            double x        = Math.Max(0, bx);
            double af       = af0 + 1; // initial guess for af
            double invMean  = 1 / mean;
            double bf       = (af + a) * invMean - (af + a + b);
            int    numIters = 20;

            for (int iter = 0; iter < numIters; iter++)
            {
                double old_af = af;
                double f      = (MMath.GammaLn(af + bf) - MMath.GammaLn(af + bf + a + b)) + (MMath.GammaLn(af + a) - MMath.GammaLn(af)) +
                                (MMath.GammaLn(bf + b) - MMath.GammaLn(bf));
                double g = (MMath.Digamma(af + bf) - MMath.Digamma(af + bf + a + b)) * invMean + (MMath.Digamma(af + a) - MMath.Digamma(af)) +
                           (MMath.Digamma(bf + b) - MMath.Digamma(bf)) * (invMean - 1);
                // fit a fcn of the form: s*log((af-af0)/(af+x)) + c
                // whose deriv is s/(af-af0) - s/(af+x)
                double s            = g / (1 / (af - af0) - 1 / (af + x));
                double c            = f - s * Math.Log((af - af0) / (af + x));
                bool   isIncreasing = (x > -af0);
                if ((!isIncreasing && c >= logZ) || (isIncreasing && c <= logZ))
                {
                    // the approximation doesn't fit; use Gauss-Newton instead
                    af += (logZ - f) / g;
                }
                else
                {
                    // now solve s*log((af-af0)/(af+x))+c = logz
                    // af-af0 = exp((logz-c)/s) (af+x)
                    af = af0 + (x + af0) / MMath.ExpMinus1((c - logZ) / s);
                    //if (af == af0)
                    //    throw new ArgumentException("logZ is out of range");
                }
                if (double.IsNaN(af))
                {
                    throw new InferRuntimeException("af is nan");
                }
                bf = (af + a) / mean - (af + a + b);
                if (Math.Abs(af - old_af) < 1e-8 || af == af0)
                {
                    break;
                }
                //if (iter == numIters-1)
                //    throw new Exception("not converging");
            }
            if (false)
            {
                // check that integrals are correct
                double f = (MMath.GammaLn(af + bf) - MMath.GammaLn(af + bf + a + b)) + (MMath.GammaLn(af + a) - MMath.GammaLn(af)) +
                           (MMath.GammaLn(bf + b) - MMath.GammaLn(bf));
                if (Math.Abs(f - logZ) > 1e-6)
                {
                    throw new InferRuntimeException("wrong f");
                }
                double f2 = (MMath.GammaLn(af + bf) - MMath.GammaLn(af + bf + a + b + 1)) + (MMath.GammaLn(af + a + 1) - MMath.GammaLn(af)) +
                            (MMath.GammaLn(bf + b) - MMath.GammaLn(bf));
                if (Math.Abs(f2 - (Math.Log(mean) + logZ)) > 1e-6)
                {
                    throw new InferRuntimeException("wrong f2");
                }
            }
            return(new Beta(af, bf));
        }