/// <summary> /// Gets log normalizer /// </summary> /// <returns></returns> public double GetLogNormalizer() { if (IsProper() && !IsPointMass) { return(MMath.GammaLn(Shape) - Shape * Math.Log(Rate)); } else { return(0.0); } }
/// <summary> /// Gets log normalizer /// </summary> /// <returns></returns> public double GetLogNormalizer() { if (IsProper()) { return(MMath.GammaLn(Shape) - Shape * Math.Log(Rate) + Math.Log(Math.Abs(Power))); } else { return(0.0); } }
/// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="WishartFromShapeAndRateOp"]/message_doc[@name="LogAverageFactor(PositiveDefiniteMatrix, double, Wishart)"]/*'/> public static double LogAverageFactor(PositiveDefiniteMatrix sample, double shape, Wishart rate) { // f(X,a,B) = |X|^(a-c) |B|^a/Gamma_d(a) exp(-tr(BX)) = |X|^(-2c) Gamma_d(a+c)/Gamma_d(a) W(B; a+c, X) // p(B) = |B|^(a'-c) |B'|^(a')/Gamma_d(a') exp(-tr(BB')) // int_B f p(B) dB = |X|^(a-c) Gamma_d(a+a')/Gamma_d(a)/Gamma_d(a') |B'|^(a') |B'+X|^(-(a+a')) int dimension = sample.Rows; double c = 0.5 * (dimension + 1); Wishart to_rate = new Wishart(dimension); to_rate = RateAverageConditional(sample, shape, to_rate); return(rate.GetLogAverageOf(to_rate) - 2 * c * sample.LogDeterminant() + MMath.GammaLn(shape + c, dimension) - MMath.GammaLn(shape, dimension)); }
/// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="MultinomialOp"]/message_doc[@name="LogAverageFactor(IList{int}, int, Dirichlet)"]/*'/> public static double LogAverageFactor(IList <int> sample, int trialCount, Dirichlet p) { double result = MMath.GammaLn(trialCount + 1); for (int i = 0; i < sample.Count; i++) { result += MMath.GammaLn(sample[i] + p.PseudoCount[i]) + MMath.GammaLn(p.PseudoCount[i]) - MMath.GammaLn(sample[i] + 1); } result += MMath.GammaLn(p.TotalCount) - MMath.GammaLn(p.TotalCount + trialCount); return(result); }
public double GetLogProb(int value) { if (value < 0 || value > TrialCount) { return(double.NegativeInfinity); } if (IsPointMass) { return((value == Point) ? 1.0 : 0.0); } return(MMath.GammaLn(TrialCount + 1) - A * MMath.GammaLn(value + 1) - B * MMath.GammaLn(TrialCount - value + 1) + value * LogOdds + TrialCount * MMath.LogisticLn(-LogOdds)); }
/// <summary> /// Logarithm of the Gamma density function. /// </summary> /// <param name="x">Where to evaluate the density</param> /// <param name="shape">Shape parameter</param> /// <param name="rate">Rate parameter</param> /// <returns>log(Gamma(x;shape,rate))</returns> /// <remarks> /// The distribution is <c>p(x) = x^(a-1)*exp(-x*b)*b^a/Gamma(a)</c>. /// When a <= 0 or b <= 0 the <c>b^a/Gamma(a)</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) { if (x < 0) { return(double.NegativeInfinity); } if (x > double.MaxValue) // Avoid subtracting infinities below { if (rate > 0) { return(-x); } else if (rate < 0) { return(x); } // fall through when rate == 0 } if (shape > 1e10 && IsProper(shape, rate)) { // In double precision, we can assume GammaLn(x) = (x-0.5)*log(x) - x for x > 1e10 // Also log(1-1/x) = -1/x - 0.5/x^2 for x > 1e10 // We compute the density in a way that ensures the maximum is at the mode returned by GetMode. double mode = (shape - 1) / rate; // cannot be zero double xOverMode = x / mode; if (xOverMode > double.MaxValue) { return(double.NegativeInfinity); } else { return((shape - 1) * (Math.Log(xOverMode) + (1 - xOverMode)) + (0.5 + 0.5 / shape) / shape + Math.Log(rate) - 0.5 * Math.Log(shape)); } } double result = 0; if (shape != 1) { result += (shape - 1) * Math.Log(x); } if (rate != 0 && x != 0) { result -= x * rate; } if (IsProper(shape, rate)) { result += shape * Math.Log(rate) - MMath.GammaLn(shape); } return(result); }
/// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="BinomialOp"]/message_doc[@name="LogAverageFactor(int, Beta, int)"]/*'/> public static double LogAverageFactor(int sample, Beta p, int trialCount) { if (trialCount < sample) { return(double.NegativeInfinity); } double a = p.TrueCount; double b = p.FalseCount; return(MMath.ChooseLn(trialCount, sample) + MMath.GammaLn(a + sample) - MMath.GammaLn(a) + MMath.GammaLn(b + trialCount - sample) - MMath.GammaLn(b) + MMath.GammaLn(a + b) - MMath.GammaLn(a + b + trialCount)); }
/// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="WishartFromShapeAndRateOp_Laplace2"]/message_doc[@name="LogAverageFactor(Wishart, double, Wishart, Wishart)"]/*'/> public static double LogAverageFactor(Wishart sample, double shape, Wishart rate, [Fresh] Wishart to_sample) { // int_R f(Y,R) p(R) dR = |Y|^(a-c) |Y+B_r|^-(a+a_r) Gamma_d(a+a_r)/Gamma_d(a)/Gamma_d(a_r) |B_r|^a_r int dim = sample.Dimension; double c = 0.5 * (dim + 1); double shape2 = shape + rate.Shape; Wishart samplePost = sample * to_sample; PositiveDefiniteMatrix y = samplePost.GetMean(); PositiveDefiniteMatrix yPlusBr = y + rate.Rate; double result = (shape - c) * y.LogDeterminant() - shape2 * yPlusBr.LogDeterminant() + sample.GetLogProb(y) - samplePost.GetLogProb(y); result += MMath.GammaLn(shape2, dim) - MMath.GammaLn(shape, dim) - MMath.GammaLn(rate.Shape, dim); result += rate.Shape * rate.Rate.LogDeterminant(); return(result); }
/// <summary> /// Computes (sum_{x=0..infinity} log(x!) Rate^x / x!^Precision) / (sum_{x=0..infinity} Rate^x / x!^Precision ) /// </summary> /// <returns></returns> public double GetMeanLogFactorial() { if (IsPointMass) { return(MMath.GammaLn(Point + 1)); } else if (!IsProper()) { throw new ImproperDistributionException(this); } else { return(GetSumLogFactorial(Rate, Precision) / Math.Exp(GetLogNormalizer(Rate, Precision))); } }
public static double NormalCdfMomentDyRatio(int n, double x, double y, double r) { double omr2 = 1 - r * r; if (omr2 == 0) { throw new ArgumentException(); } else { double diff = (x - r * y) / System.Math.Sqrt(omr2); //return Math.Exp(MMath.GammaLn(n + 1) + 0.5 * n * Math.Log(omr2)) * MMath.NormalCdfMomentRatio(n, diff); return(System.Math.Exp(MMath.GammaLn(n + 1) + 0.5 * n * System.Math.Log(omr2) + System.Math.Log(MMath.NormalCdfMomentRatio(n, diff)))); } }
/// <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 (power == 0.0) { return((x == 1.0) ? 0.0 : Double.NegativeInfinity); } double invPower = 1.0 / power; double result = (shape * invPower - 1) * Math.Log(x) - Math.Pow(x, invPower) * rate; if (IsProper(shape, rate)) { result += shape * Math.Log(rate) - MMath.GammaLn(shape) - Math.Log(Math.Abs(power)); } return(result); }
public static double NormalCdfMomentDy(int n, double x, double y, double r) { double omr2 = 1 - r * r; if (omr2 == 0) { return(System.Math.Pow(x - r * y, n) * System.Math.Exp(Gaussian.GetLogProb(y, 0, 1))); } else { double diff = (x - r * y) / System.Math.Sqrt(omr2); return(System.Math.Exp(MMath.GammaLn(n + 1) + Gaussian.GetLogProb(y, 0, 1) + Gaussian.GetLogProb(diff, 0, 1) + 0.5 * n * System.Math.Log(omr2)) * MMath.NormalCdfMomentRatio(n, diff)); } }
// returns (E[x],var(x)) where p(x) =propto VG(x;a) N(x;m,v). public static void VarianceGammaTimesGaussianMoments3(double a, double m, double v, out double mu, out double vu) { // compute weights // termMoments[i,j] is the ith moment of the jth term Matrix termMoments = new Matrix(nWeights, nWeights); DenseVector exactMoments = DenseVector.Constant(termMoments.Rows, 1.0); for (int i = 0; i < exactMoments.Count; i++) { // ii is half of the exponent int ii = i; double logMoment = MMath.GammaLn(ii + a) - MMath.GammaLn(a) - MMath.GammaLn(ii + 1); for (int j = 0; j < termMoments.Cols; j++) { // jj is the term shape int jj = j + 1; termMoments[i, j] = Math.Exp(MMath.GammaLn(ii + jj) - MMath.GammaLn(ii + 1) - MMath.GammaLn(jj) - logMoment); } } //Console.WriteLine("exactMoments = {0}, termMoments = ", exactMoments); //Console.WriteLine(termMoments); (new LuDecomposition(termMoments)).Solve(exactMoments); DenseVector weights = exactMoments; Console.WriteLine("weights = {0}", weights); double Z0Plus = 0, Z1Plus = 0, Z2Plus = 0; double Z0Minus = 0, Z1Minus = 0, Z2Minus = 0; for (int j = 0; j < weights.Count; j++) { int jj = j + 1; Z0Plus += weights[j] * NormalVGMomentRatio(0, jj, m - v, v); Z1Plus += weights[j] * NormalVGMomentRatio(1, jj, m - v, v); Z2Plus += weights[j] * NormalVGMomentRatio(2, jj, m - v, v); Z0Minus += weights[j] * NormalVGMomentRatio(0, jj, -m - v, v); Z1Minus += weights[j] * NormalVGMomentRatio(1, jj, -m - v, v); Z2Minus += weights[j] * NormalVGMomentRatio(2, jj, -m - v, v); } double Z0 = Z0Plus + Z0Minus; double Z1 = Z1Plus - Z1Minus; double Z2 = Z2Plus + Z2Minus; mu = Z1 / Z0; vu = Z2 / Z0 - mu * mu; //Console.WriteLine("mu = {0}, vu = {1}", mu, vu); }
/// <summary> /// EP message to 'trialCount' /// </summary> /// <param name="sample">Constant value for 'sample'.</param> /// <param name="p">Incoming message from 'p'.</param> /// <param name="result">Modified to contain the outgoing message</param> /// <returns><paramref name="result"/></returns> /// <remarks><para> /// The outgoing message is a distribution matching the moments of 'trialCount' as the random arguments are varied. /// The formula is <c>proj[p(trialCount) sum_(p) p(p) factor(sample,trialCount,p)]/p(trialCount)</c>. /// </para></remarks> public static Discrete TrialCountAverageConditional(int sample, Beta p, Discrete result) { if (p.IsPointMass) { return(TrialCountAverageConditional(sample, p.Point, result)); } Vector probs = result.GetWorkspace(); double a = p.TrueCount; double b = p.FalseCount; // p(n) = nchoosek(n,k) p^k (1-p)^(n-k) for (int n = 0; n < result.Dimension; n++) { probs[n] = Math.Exp(MMath.ChooseLn(n, sample) + MMath.GammaLn(b + n - sample) - MMath.GammaLn(a + b + n)); } result.SetProbs(probs); return(result); }
#pragma warning disable 162 #endif /// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="MultinomialOp"]/message_doc[@name="AverageLogFactor(IList{int}, int, Dirichlet, Vector)"]/*'/> public static double AverageLogFactor(IList <int> sample, int trialCount, Dirichlet p, [Fresh] Vector MeanLog) { double result = MMath.GammaLn(trialCount + 1); if (true) { result += sample.Inner(MeanLog); result -= sample.ListSum(x => MMath.GammaLn(x + 1)); } else { for (int i = 0; i < sample.Count; i++) { result += sample[i] * MeanLog[i] - MMath.GammaLn(sample[i] + 1); } } return(result); }
internal static void GammaSpeedTest() { Stopwatch watch = new Stopwatch(); MMath.GammaLn(0.1); MMath.GammaLn(3); MMath.GammaLn(20); for (int i = 0; i < 100; i++) { double x = (i + 1) * 0.1; watch.Restart(); for (int trial = 0; trial < 1000; trial++) { MMath.GammaLn(x); } watch.Stop(); Console.WriteLine($"{x} {watch.ElapsedTicks}"); } }
/// <summary> /// Gets the normalizer for a Wishart density function specified by shape and rate matrix /// </summary> /// <param name="shape">Shape parameter</param> /// <param name="rate">rate matrix</param> /// <returns></returns> public static double GetLogNormalizer(double shape, PositiveDefiniteMatrix rate) { int dimension = rate.Rows; // inline call to IsProper() if (!(shape > 0.5 * (dimension - 1))) { return(0.0); } LowerTriangularMatrix rateChol = new LowerTriangularMatrix(dimension, dimension); bool isPosDef = rateChol.SetToCholesky(rate); if (!isPosDef) { return(0.0); } // LogDeterminant(rate) = 2*L.TraceLn() return(MMath.GammaLn(shape, dimension) - shape * 2 * rateChol.TraceLn()); }
/// <summary> /// Computes <c>log(n!!)</c> where n!! = n(n-2)(n-4)...2 (if n even) or n(n-2)...1 (if n odd) /// </summary> /// <param name="n">An integer >= 0</param> /// <returns></returns> public static double DoubleFactorialLn(int n) { if (n < 0) { throw new ArgumentException("n < 0"); } else if (n == 0) { return(0); } else if (n % 2 == 0) { int h = n / 2; return(h * System.Math.Log(2) + MMath.GammaLn(h + 1)); } else { int h = (n + 1) / 2; return(h * System.Math.Log(2) + MMath.GammaLn(h + 0.5) - MMath.GammaLn(0.5)); } }
/// <summary> /// Computes E[x^power] /// </summary> /// <returns></returns> public double GetMeanPower(double power) { if (power == 0.0) { return(1.0); } else if (IsPointMass) { return(Math.Pow(Point, power)); } //else if (Rate == 0.0) return (power > 0) ? Double.PositiveInfinity : 0.0; else if (!IsProper()) { throw new ImproperDistributionException(this); } else if (this.Gamma.Shape <= -power && LowerBound == 0) { throw new ArgumentException("Cannot compute E[x^" + power + "] for " + this + " (shape <= " + (-power) + ")"); } else { double Z = GetNormalizer(); double shapePlusPower = this.Gamma.Shape + power; double Z1; bool regularized = shapePlusPower >= 1; if (regularized) { Z1 = Math.Exp(MMath.GammaLn(shapePlusPower) - MMath.GammaLn(this.Gamma.Shape)) * (MMath.GammaLower(shapePlusPower, this.Gamma.Rate * UpperBound) - MMath.GammaLower(shapePlusPower, this.Gamma.Rate * LowerBound)); } else { Z1 = Math.Exp(-MMath.GammaLn(this.Gamma.Shape)) * (MMath.GammaUpper(shapePlusPower, this.Gamma.Rate * LowerBound, regularized) - MMath.GammaUpper(shapePlusPower, this.Gamma.Rate * UpperBound, regularized)); } return(Math.Pow(this.Gamma.Rate, -power) * Z1 / Z); } }
/// <summary> /// Computes E[x^power] /// </summary> /// <returns></returns> public double GetMeanPower(double power) { if (power == 0.0) { return(1.0); } else if (IsPointMass) { return(Math.Pow(Point, power)); } //else if (Rate == 0.0) return (power > 0) ? Double.PositiveInfinity : 0.0; else if (!IsProper()) { throw new ImproperDistributionException(this); } else if (Shape <= -power) { throw new ArgumentException("Cannot compute E[x^" + power + "] for " + this + " (shape <= " + (-power) + ")"); } else { return(Math.Exp(MMath.GammaLn(Shape + power) - MMath.GammaLn(Shape) - power * Math.Log(Rate))); } }
/// <summary> /// Evaluates the log of of the density function of this COM-Poisson at the given value /// </summary> /// <param name="value">The value at which to calculate the density</param> /// <returns>log p(x=value)</returns> /// <remarks> /// The formula for the distribution is <c>p(x) = rate^x exp(-rate) / x!</c> /// when nu=1. In the general case, it is <c>p(x) =propto rate^x / x!^nu</c>. /// If the distribution is improper, the normalizer is omitted. /// </remarks> public double GetLogProb(int value) { if (IsPointMass) { return((value == Point) ? 0.0 : Double.NegativeInfinity); } else if (value < 0) { return(Double.NegativeInfinity); } else if (IsUniform()) { return(0.0); } else if (Precision == 1) { // we know that Rate > 0 return(value * Math.Log(Rate) - MMath.GammaLn(value + 1) - Rate); } else { return(value * Math.Log(Rate) - Precision * MMath.GammaLn(value + 1) - GetLogNormalizer(Rate, Precision)); } }
/// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="GammaPowerProductOp_Laplace"]/message_doc[@name="LogAverageFactor(GammaPower, GammaPower, GammaPower, Gamma)"]/*'/> public static double LogAverageFactor(GammaPower product, GammaPower A, GammaPower B, Gamma q) { if (B.Shape < A.Shape) { return(LogAverageFactor(product, B, A, q)); } if (B.IsPointMass) { return(GammaProductOp.LogAverageFactor(product, A, B.Point)); } if (A.IsPointMass) { return(GammaProductOp.LogAverageFactor(product, A.Point, B)); } double qPoint = q.GetMean(); double logf; if (product.IsPointMass) { // Ga(y/q; s, r)/q if (qPoint == 0) { if (product.Point == 0) { logf = A.GetLogProb(0); } else { logf = double.NegativeInfinity; } } else { logf = A.GetLogProb(product.Point / qPoint) - Math.Log(qPoint); } } else { // int Ga^y_p(a^pa b^pb; y_s, y_r) Ga(a; s, r) da = q^(y_s-y_p) / (r + q y_r)^(y_s + s-pa) Gamma(y_s+s-pa) double shape = product.Shape - product.Power; double shape2 = GammaFromShapeAndRateOp_Slow.AddShapesMinus1(product.Shape, A.Shape) + (1 - A.Power); if (IsProper(product) && product.Shape > A.Shape) { // same as below but product.GetLogNormalizer() is inlined and combined with other terms double AShapeMinusPower = A.Shape - A.Power; logf = shape * Math.Log(qPoint) - Gamma.FromShapeAndRate(A.Shape, A.Rate).GetLogNormalizer() - product.Shape * Math.Log(A.Rate / product.Rate + qPoint) - Math.Log(Math.Abs(product.Power)); if (AShapeMinusPower != 0) { logf += AShapeMinusPower * (MMath.RisingFactorialLnOverN(product.Shape, AShapeMinusPower) - Math.Log(A.Rate + qPoint * product.Rate)); } } else { logf = shape * Math.Log(qPoint) - shape2 * Math.Log(A.Rate + qPoint * product.Rate) + MMath.GammaLn(shape2) - Gamma.FromShapeAndRate(A.Shape, A.Rate).GetLogNormalizer() - product.GetLogNormalizer(); // normalizer is -MMath.GammaLn(Shape) + Shape * Math.Log(Rate) - Math.Log(Math.Abs(Power)) } } double logz = logf + Gamma.FromShapeAndRate(B.Shape, B.Rate).GetLogProb(qPoint) - q.GetLogProb(qPoint); return(logz); }
public static int Poisson(double mean) { // TODO: There are more efficient samplers if (mean < 0) { throw new ArgumentException("mean < 0"); } else if (mean == 0.0) { return(0); } else if (mean < 10) { double L = System.Math.Exp(-mean); double p = 1.0; int k = 0; do { k++; p *= Rand.Double(); } while (p > L); return(k - 1); } else { // mean >= 10 // Devroye ch10.3, with corrections // Reference: "Non-Uniform Random Variate Generation" by Luc Devroye (1986) double mu = System.Math.Floor(mean); double muLogFact = MMath.GammaLn(mu + 1); double logMeanMu = System.Math.Log(mean / mu); double delta = System.Math.Max(6, System.Math.Min(mu, System.Math.Sqrt(2 * mu * System.Math.Log(128 * mu / System.Math.PI)))); double c1 = System.Math.Sqrt(System.Math.PI * mu / 2); double c2 = c1 + System.Math.Sqrt(System.Math.PI * (mu + delta / 2) / 2) * System.Math.Exp(1 / (2 * mu + delta)); double c3 = c2 + 2; double c4 = c3 + System.Math.Exp(1.0 / 78); double c = c4 + 2 / delta * (2 * mu + delta) * System.Math.Exp(-delta / (2 * mu + delta) * (1 + delta / 2)); while (true) { double u = Rand.Double() * c; double x, w; if (u <= c1) { double n = Rand.Normal(); double y = -System.Math.Abs(n) * System.Math.Sqrt(mu) - 1; x = System.Math.Floor(y); if (x < -mu) { continue; } w = -n * n / 2; } else if (u <= c2) { double n = Rand.Normal(); double y = 1 + System.Math.Abs(n) * System.Math.Sqrt(mu + delta / 2); x = System.Math.Ceiling(y); if (x > delta) { continue; } w = (2 - y) * y / (2 * mu + delta); } else if (u <= c3) { x = 0; w = 0; } else if (u <= c4) { x = 1; w = 0; } else { double v = -System.Math.Log(Rand.Double()); double y = delta + v * 2 / delta * (2 * mu + delta); x = System.Math.Ceiling(y); w = -delta / (2 * mu + delta) * (1 + y / 2); } double e = -System.Math.Log(Rand.Double()); w -= e + x * logMeanMu; double qx = x * System.Math.Log(mu) - MMath.GammaLn(mu + x + 1) + muLogFact; if (w <= qx) { return((int)System.Math.Round(x + mu)); } } } }
public static void VarianceGammaTimesGaussianMoments2(double a, double m, double v, out double mu, out double vu) { // compute weights Matrix laplacianMoments = new Matrix(nWeights, nWeights); DenseVector exactMoments = DenseVector.Constant(laplacianMoments.Rows, 1.0); // a=10: 7-1 // a=15: 8-1 // a=20: 10-1 // a=21: 10-1 // a=30: 12-1 // get best results if the lead term has flat moment ratio int jMax = Math.Max(laplacianMoments.Cols, (int)Math.Round(a - 10)) - 1; jMax = laplacianMoments.Cols - 1; for (int i = 0; i < exactMoments.Count; i++) { //int ii = jMax-i; int ii = i; double logMoment = MMath.GammaLn(ii + a) - MMath.GammaLn(a) - MMath.GammaLn(ii + 1); for (int j = 0; j < laplacianMoments.Cols; j++) { int jj = jMax - j; laplacianMoments[i, j] = Math.Exp(MMath.GammaLn(2 * ii + jj + 1) - MMath.GammaLn(2 * ii + 1) - MMath.GammaLn(jj + 1) - logMoment); } } //Console.WriteLine("exactMoments = {0}, laplacianMoments = ", exactMoments); //Console.WriteLine(laplacianMoments); (new LuDecomposition(laplacianMoments)).Solve(exactMoments); DenseVector weights = exactMoments; Console.WriteLine("weights = {0}", weights); double Z0Plus = 0, Z1Plus = 0, Z2Plus = 0; double Z0Minus = 0, Z1Minus = 0, Z2Minus = 0; double sqrtV = Math.Sqrt(v); double InvSqrtV = 1 / sqrtV; double mPlus = (m - v) * InvSqrtV; double mMinus = (-m - v) * InvSqrtV; for (int j = 0; j < weights.Count; j++) { int jj = jMax - j; Z0Plus += weights[j] * MMath.NormalCdfMomentRatio(0 + jj, mPlus) * Math.Pow(sqrtV, 0 + jj); Z1Plus += weights[j] * MMath.NormalCdfMomentRatio(1 + jj, mPlus) * (1 + jj) * Math.Pow(sqrtV, 1 + jj); Z2Plus += weights[j] * MMath.NormalCdfMomentRatio(2 + jj, mPlus) * (1 + jj) * (2 + jj) * Math.Pow(sqrtV, 2 + jj); Z0Minus += weights[j] * MMath.NormalCdfMomentRatio(0 + jj, mMinus) * Math.Pow(sqrtV, 0 + jj); Z1Minus += weights[j] * MMath.NormalCdfMomentRatio(1 + jj, mMinus) * (1 + jj) * Math.Pow(sqrtV, 1 + jj); Z2Minus += weights[j] * MMath.NormalCdfMomentRatio(2 + jj, mMinus) * (1 + jj) * (2 + jj) * Math.Pow(sqrtV, 2 + jj); } double Z0 = Z0Plus + Z0Minus; double Z1 = Z1Plus - Z1Minus; double Z2 = Z2Plus + Z2Minus; mu = Z1 / Z0; vu = Z2 / Z0 - mu * mu; }
/// <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)); }
#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)); }
/// <summary> /// Gets the log of the normalizer for the truncated Gamma density function /// </summary> /// <returns></returns> public double GetLogNormalizer() { if (IsProper() && !IsPointMass) { if (this.Gamma.Shape < 1 && (double)(this.Gamma.Rate * LowerBound) > 0) { // When Shape < 1, Gamma(Shape) > 1 so use the unregularized version to avoid underflow. return(Math.Log(GammaProbBetween(this.Gamma.Shape, this.Gamma.Rate, LowerBound, UpperBound, false)) - MMath.GammaLn(this.Gamma.Shape)); } else { return(Math.Log(GammaProbBetween(this.Gamma.Shape, this.Gamma.Rate, LowerBound, UpperBound))); } } else { return(0.0); } }
/// <summary> /// EP message to 'trialCount' /// </summary> /// <param name="sample">Incoming message from 'sample'.</param> /// <param name="p">Incoming message from 'p'.</param> /// <param name="result">Modified to contain the outgoing message</param> /// <returns><paramref name="result"/></returns> /// <remarks><para> /// The outgoing message is a distribution matching the moments of 'trialCount' as the random arguments are varied. /// The formula is <c>proj[p(trialCount) sum_(sample,p) p(sample,p) factor(sample,trialCount,p)]/p(trialCount)</c>. /// </para></remarks> public static Discrete TrialCountAverageConditional(Discrete sample, Beta p, Discrete result) { if (p.IsPointMass) { return(TrialCountAverageConditional(sample, p.Point, result)); } if (sample.IsPointMass) { return(TrialCountAverageConditional(sample.Point, p, result)); } // n must range from 0 to sampleMax if (result.Dimension < sample.Dimension) { throw new ArgumentException("result.Dimension (" + result.Dimension + ") < sample.Dimension (" + sample.Dimension + ")"); } Vector probs = result.GetWorkspace(); double a = p.TrueCount; double b = p.FalseCount; // p(n) = sum_(k<=n) p(k) nchoosek(n,k) p^k (1-p)^(n-k) for (int n = 0; n < result.Dimension; n++) { double s = 0.0; for (int k = 0; k <= n; k++) { s += sample[k] * Math.Exp(MMath.ChooseLn(n, k) + MMath.GammaLn(a + k) + MMath.GammaLn(b + n - k)); } probs[n] = Math.Exp(-MMath.GammaLn(a + b + n)) * s; } result.SetProbs(probs); return(result); }
//-- VMP ------------------------------------------------------------------------------------------- /// <summary>Evidence message for VMP.</summary> /// <param name="sample">Constant value for <c>sample</c>.</param> /// <param name="mean">Incoming message from <c>mean</c>. Must be a proper distribution. If uniform, the result will be uniform.</param> /// <returns>Average of the factor's log-value across the given argument distributions.</returns> /// <remarks> /// <para>The formula for the result is <c>sum_(mean) p(mean) log(factor(sample,mean))</c>. Adding up these values across all factors and variables gives the log-evidence estimate for VMP.</para> /// </remarks> /// <exception cref="ImproperMessageException"> /// <paramref name="mean" /> is not a proper distribution.</exception> public static double AverageLogFactor(int sample, [Proper] Gamma mean) { return(sample * mean.GetMeanLog() - MMath.GammaLn(sample + 1) - mean.GetMean()); }
/// <summary> /// Computes E[x^power] /// </summary> /// <returns></returns> public double GetMeanPower(double power) { if (power == 0.0) { return(1.0); } else if (power == 1.0) { return(GetMean()); } else if (IsPointMass) { return(Math.Pow(Point, power)); } //else if (Rate == 0.0) return (power > 0) ? Double.PositiveInfinity : 0.0; else if (!IsProper()) { throw new ImproperDistributionException(this); } else if (this.Gamma.Shape <= -power && LowerBound == 0) { throw new ArgumentException("Cannot compute E[x^" + power + "] for " + this + " (shape <= " + (-power) + ")"); } else if (power != 1) { // Large powers lead to overflow power = Math.Min(Math.Max(power, -1e300), 1e300); double logZ = GetLogNormalizer(); if (logZ < double.MinValue) { return(Math.Pow(GetMode(), power)); } double shapePlusPower = this.Gamma.Shape + power; double logZ1; bool regularized = shapePlusPower >= 1; if (regularized) { // This formula cannot be used when shapePlusPower <= 0 logZ1 = (power * MMath.RisingFactorialLnOverN(this.Gamma.Shape, power)) + Math.Log(GammaProbBetween(shapePlusPower, this.Gamma.Rate, LowerBound, UpperBound, regularized)); } else { logZ1 = -MMath.GammaLn(this.Gamma.Shape) + Math.Log(GammaProbBetween(shapePlusPower, this.Gamma.Rate, LowerBound, UpperBound, regularized)); } return(Math.Exp(-power * Math.Log(this.Gamma.Rate) + logZ1 - logZ)); } else { double Z = GetNormalizer(); if (Z == 0.0) { return(Math.Pow(GetMode(), power)); } double shapePlusPower = this.Gamma.Shape + power; double Z1; double gammaLnShapePlusPower = MMath.GammaLn(shapePlusPower); double gammaLnShape = MMath.GammaLn(this.Gamma.Shape); bool regularized = true; // (gammaLnShapePlusPower - gammaLnShape <= 700); if (regularized) { // If shapePlusPower is large and Gamma.Rate * UpperBound is small, then this can lead to Inf * 0 Z1 = Math.Exp(power * MMath.RisingFactorialLnOverN(this.Gamma.Shape, power)) * GammaProbBetween(shapePlusPower, this.Gamma.Rate, LowerBound, UpperBound, regularized); } else { Z1 = Math.Exp(-gammaLnShape) * GammaProbBetween(shapePlusPower, this.Gamma.Rate, LowerBound, UpperBound, regularized); } return(Z1 / (Math.Pow(this.Gamma.Rate, power) * Z)); } }