/// <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> /// 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 >= <paramref name="lowerBound"/>. Can be Infinity.</param> /// <returns>A real number >= <paramref name="lowerBound"/> and < <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); } } } }