Exemple #1
0
        /// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="GammaRatioOp_Laplace"]/message_doc[@name="BAverageConditional(Gamma, Gamma, Gamma, Gamma)"]/*'/>
        public static Gamma BAverageConditional([SkipIfUniform] Gamma ratio, [Proper] Gamma A, [Proper] Gamma B, Gamma q)
        {
            if (ratio.IsPointMass)
            {
                // AAverageConditional computes (1st arg)/(2nd arg)
                return(GammaRatioOp.BAverageConditional(ratio.Point, A));
            }
            if (B.IsPointMass)
            {
                throw new NotImplementedException();
            }
            if (q.IsUniform())
            {
                q = Q(ratio, A, B);
            }
            double x = q.GetMean();

            double[] g = new double[] { x, 1, 0, 0 };
            double   bMean, bVariance;

            GaussianOp_Laplace.LaplaceMoments(q, g, dlogfs(x, ratio, A), out bMean, out bVariance);
            Gamma bMarginal = Gamma.FromMeanAndVariance(bMean, bVariance);
            Gamma result    = new Gamma();

            result.SetToRatio(bMarginal, B, GammaProductOp_Laplace.ForceProper);
            return(result);
        }
        internal void GaussianOpQ_Timing()
        {
            Gaussian X, Mean;
            Gamma    Precision;
            Gamma    q;
            int      n = 1;

            X         = Gaussian.FromNatural(3.9112579392580757, 11.631097473681082);
            Mean      = Gaussian.FromNatural(10.449696977834144, 5.5617978202886995);
            Precision = Gamma.FromShapeAndRate(1.0112702817305146, 0.026480506235719053);
            q         = Gamma.FromMeanAndVariance(1, 1);
            Stopwatch watch = new Stopwatch();

            watch.Start();
            for (int i = 0; i < n; i++)
            {
                GaussianOp_Laplace.Q(X, Mean, Precision, q);
            }
            watch.Stop();
            Console.WriteLine("Q = {0}", watch.ElapsedTicks);
            watch.Restart();
            for (int i = 0; i < n; i++)
            {
                GaussianOp_Laplace.Q_Slow(X, Mean, Precision);
            }
            watch.Stop();
            Console.WriteLine("Q2 = {0}", watch.ElapsedTicks);
        }
Exemple #3
0
        /// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="GammaPowerProductOp_Laplace"]/message_doc[@name="BAverageConditional(GammaPower, GammaPower, GammaPower, Gamma, GammaPower)"]/*'/>
        public static GammaPower BAverageConditional([SkipIfUniform] GammaPower product, [Proper] GammaPower A, [Proper] GammaPower B, Gamma q, GammaPower result)
        {
            if (B.Shape < A.Shape)
            {
                return(AAverageConditional(product, B, A, q, result));
            }
            if (A.IsPointMass)
            {
                return(GammaProductOp.BAverageConditional(product, A.Point, result));
            }
            if (B.IsPointMass)
            {
                return(GammaPower.Uniform(result.Power)); // TODO
            }
            if (product.IsUniform())
            {
                return(product);
            }
            if (q.IsUniform())
            {
                q = Q(product, A, B);
            }
            double bPoint = q.GetMean();

            // derivatives of b
            double[] bDerivatives = new double[] { bPoint, 1, 0, 0 };
            double   bMean, bVariance;

            GaussianOp_Laplace.LaplaceMoments(q, bDerivatives, dlogfs(bPoint, product, A), out bMean, out bVariance);
            GammaPower bMarginal = GammaPower.FromGamma(Gamma.FromMeanAndVariance(bMean, bVariance), result.Power);

            result.SetToRatio(bMarginal, B, GammaProductOp_Laplace.ForceProper);
            return(result);
        }
        private static void GetIQMoments(GammaPower product, GammaPower A, Gamma q, double qPoint, out double iqMean, out double iqVariance)
        {
            double iq  = 1 / qPoint;
            double iq2 = iq * iq;

            double[] iqDerivatives = new double[] { iq, -iq2, 2 * iq2 * iq, -6 * iq2 * iq2 };
            GaussianOp_Laplace.LaplaceMoments(q, iqDerivatives, dlogfs(qPoint, product, A), out iqMean, out iqVariance);
        }
Exemple #5
0
        internal void StudentIsPositiveTest4()
        {
            double shape     = 1;
            Gamma  precPrior = Gamma.FromShapeAndRate(shape, shape);
            // mean=-1 causes improper messages
            double   mean      = -1;
            Gaussian meanPrior = Gaussian.PointMass(mean);
            double   evExpected;
            Gaussian xExpected = StudentIsPositiveExact(mean, precPrior, out evExpected);

            GaussianOp.ForceProper       = false;
            GaussianOp_Laplace.modified  = true;
            GaussianOp_Laplace.modified2 = true;
            Gaussian xF = Gaussian.Uniform();
            Gaussian xB = Gaussian.Uniform();
            Gamma    q  = GaussianOp_Laplace.QInit();
            double   r0 = 0.38;

            r0 = 0.1;
            for (int iter = 0; iter < 20; iter++)
            {
                q = GaussianOp_Laplace.Q(xB, meanPrior, precPrior, q);
                //xF = GaussianOp_Laplace.SampleAverageConditional(xB, meanPrior, precPrior, q);
                xF = Gaussian.FromMeanAndPrecision(mean, r0);
                xB = IsPositiveOp.XAverageConditional(true, xF);
                Console.WriteLine("xF = {0} xB = {1}", xF, xB);
            }
            Console.WriteLine("x = {0} should be {1}", xF * xB, xExpected);

            double[] precs     = EpTests.linspace(1e-3, 5, 100);
            double[] evTrue    = new double[precs.Length];
            double[] evApprox  = new double[precs.Length];
            double[] evApprox2 = new double[precs.Length];
            //r0 = q.GetMean();
            double sum = 0, sum2 = 0;

            for (int i = 0; i < precs.Length; i++)
            {
                double   r   = precs[i];
                Gaussian xFt = Gaussian.FromMeanAndPrecision(mean, r);
                evTrue[i]    = IsPositiveOp.LogAverageFactor(true, xFt) + precPrior.GetLogProb(r);
                evApprox[i]  = IsPositiveOp.LogAverageFactor(true, xF) + precPrior.GetLogProb(r) + xB.GetLogAverageOf(xFt) - xB.GetLogAverageOf(xF);
                evApprox2[i] = IsPositiveOp.LogAverageFactor(true, xF) + precPrior.GetLogProb(r0) + q.GetLogProb(r) - q.GetLogProb(r0);
                sum         += System.Math.Exp(evApprox[i]);
                sum2        += System.Math.Exp(evApprox2[i]);
            }
            Console.WriteLine("r0 = {0}: {1} {2} {3}", r0, sum, sum2, q.GetVariance() + System.Math.Pow(r0 - q.GetMean(), 2));
            //TODO: change path for cross platform using
            using (var writer = new MatlabWriter(@"..\..\..\Tests\student.mat"))
            {
                writer.Write("z", evTrue);
                writer.Write("z2", evApprox);
                writer.Write("z3", evApprox2);
                writer.Write("precs", precs);
            }
        }
Exemple #6
0
        /// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="GammaRatioOp_Laplace"]/message_doc[@name="AAverageConditional(Gamma, Gamma, Gamma, Gamma)"]/*'/>
        public static Gamma AAverageConditional(Gamma ratio, Gamma A, [SkipIfUniform] Gamma B, Gamma q)
        {
            if (ratio.IsPointMass)
            {
                return(GammaRatioOp.AAverageConditional(ratio.Point, B));
            }
            if (B.IsPointMass)
            {
                return(GammaRatioOp.AAverageConditional(ratio, B.Point));
            }
            if (A.IsPointMass)
            {
                throw new NotImplementedException();
            }

            double aMean, aVariance;
            double x      = q.GetMean();
            double x2     = x * x;
            double p      = 1 / (ratio.Rate + A.Rate * x);
            double p2     = p * p;
            double shape2 = GammaFromShapeAndRateOp_Slow.AddShapesMinus1(ratio.Shape, A.Shape);

            // aMean = shape2/(y_r/b + a_r)
            // aVariance = E[shape2*(shape2+1)/(y_r/b + a_r)^2] - aMean^2 = var(shape2/(y_r/b + a_r)) + E[shape2/(y_r/b + a_r)^2]
            //           = shape2^2*var(1/(y_r/b + a_r)) + shape2*(var(1/(y_r/b + a_r)) + (aMean/shape2)^2)
            double[] g = new double[] { x *p, ratio.Rate *p2, -2 *p2 *p *ratio.Rate *A.Rate, 6 *p2 *p2 *ratio.Rate *A.Rate *A.Rate };
            double   pMean, pVariance;
            GaussianOp_Laplace.LaplaceMoments(q, g, dlogfs(x, ratio, A), out pMean, out pVariance);
            aMean     = shape2 * pMean;
            aVariance = shape2 * shape2 * pVariance + shape2 * (pVariance + pMean * pMean);

            Gamma aMarginal = Gamma.FromMeanAndVariance(aMean, aVariance);
            Gamma result    = new Gamma();
            result.SetToRatio(aMarginal, A, GammaProductOp_Laplace.ForceProper);
            if (double.IsNaN(result.Shape) || double.IsNaN(result.Rate))
            {
                throw new InferRuntimeException("result is nan");
            }
            return(result);
        }
        public void GaussianOpPrecision()
        {
            Gamma    precMsg, precMsg2;
            Gaussian X, Mean;
            Gamma    Precision;

            X         = Gaussian.FromNatural(-1.5098177152950143E-09, 1.061649960537027E-168);
            Mean      = Gaussian.FromNatural(-3.6177299471249587, 0.11664740799025652);
            Precision = Gamma.FromShapeAndRate(306.39423695125572, 1.8326832031565403E+170);
            precMsg   = Gamma.PointMass(0);
            precMsg2  = GaussianOp_Slow.PrecisionAverageConditional(X, Mean, Precision);
            Assert.True(precMsg.MaxDiff(precMsg2) < 1e-4);
            precMsg2 = GaussianOp.PrecisionAverageConditional(X, Mean, Precision);
            Assert.True(precMsg.MaxDiff(precMsg2) < 1e-4);

            X         = Gaussian.FromNatural(-0.55657497231637854, 6.6259783218464713E-141);
            Mean      = Gaussian.FromNatural(-2.9330116542965374, 0.07513822741674292);
            Precision = Gamma.FromShapeAndRate(308.8184220331475, 4.6489382805051884E+142);
            precMsg   = Gamma.FromShapeAndRate(1.5000000000000628, 3.5279086383286634E+279);
            precMsg2  = GaussianOp_Slow.PrecisionAverageConditional(X, Mean, Precision);
            Assert.True(precMsg.MaxDiff(precMsg2) < 1e-4);
            precMsg2 = GaussianOp.PrecisionAverageConditional(X, Mean, Precision);
            Assert.True(precMsg.MaxDiff(precMsg2) < 1e-4);

            X         = Gaussian.FromNatural(0, 1.0705890985886898E-153);
            Mean      = Gaussian.PointMass(0);
            Precision = Gamma.FromShapeAndRate(1.6461630749684018, 1.0021354807958952E+153);
            precMsg   = Gamma.FromShapeAndRate(1.3230815374839406, 5.7102212927459039E+151);
            precMsg2  = GaussianOp_Slow.PrecisionAverageConditional(X, Mean, Precision);
            Assert.True(precMsg.MaxDiff(precMsg2) < 1e-4);
            precMsg2 = GaussianOp.PrecisionAverageConditional(X, Mean, Precision);
            Assert.True(precMsg.MaxDiff(precMsg2) < 1e-4);

            GaussianOp.PrecisionAverageConditional(Gaussian.FromNatural(0, 0.00849303091340374), Gaussian.FromNatural(0.303940178036662, 0.0357912415805232),
                                                   Gamma.FromNatural(0.870172077263786 - 1, 0.241027170904459));
            GaussianOp.PrecisionAverageConditional(Gaussian.FromNatural(0, 0.932143343115292), Gaussian.FromNatural(0.803368837946732, 0.096549750816333),
                                                   Gamma.FromNatural(0.63591693650741 - 1, 0.728459389753854));
            GaussianOp.PrecisionAverageConditional(Gaussian.FromNatural(0, 0.799724777601531), Gaussian.FromNatural(0.351882387116497, 0.0795619408970522),
                                                   Gamma.FromNatural(0.0398852019756498 - 1, 0.260567798400562));
            GaussianOp.PrecisionAverageConditional(Gaussian.FromNatural(0, 0.826197353576402), Gaussian.FromNatural(0.655970732055591, 0.125333868956814),
                                                   Gamma.FromNatural(0.202543332801453 - 1, 0.147645744563847));

            precMsg = GaussianOp.PrecisionAverageConditional(new Gaussian(-6.235e+207, 1.947e+209), Gaussian.PointMass(11), Gamma.PointMass(7));
            Assert.True(!double.IsNaN(precMsg.Rate));

            Gaussian X0         = Gaussian.FromMeanAndVariance(3, 0.5);
            Gaussian Mean0      = Gaussian.FromMeanAndVariance(7, 1.0 / 3);
            Gamma    Precision0 = Gamma.FromShapeAndScale(3, 3);

            precMsg = GaussianOp_Slow.PrecisionAverageConditional(Gaussian.FromNatural(0.010158033515400506, 0.0041117304509528533),
                                                                  Gaussian.FromNatural(33.157651455559929, 13.955304749880149),
                                                                  Gamma.FromShapeAndRate(7.1611372018172794, 1.8190207317123008));

            precMsg = GaussianOp_Slow.PrecisionAverageConditional(Gaussian.FromNatural(-0.020177353724675218, 0.0080005002339157711),
                                                                  Gaussian.FromNatural(-12.303440746896294, 4.6439574387849714),
                                                                  Gamma.FromShapeAndRate(5.6778922774773992, 1.0667129560350435));

            precMsg = GaussianOp_Slow.PrecisionAverageConditional(Gaussian.PointMass(248), Gaussian.FromNatural(0.099086933095776319, 0.00032349393599347853),
                                                                  Gamma.FromShapeAndRate(0.001, 0.001));
            precMsg2 = GaussianOp.PrecisionAverageConditional_slow(Gaussian.PointMass(248), Gaussian.FromNatural(0.099086933095776319, 0.00032349393599347853),
                                                                   Gamma.FromShapeAndRate(0.001, 0.001));
            Assert.True(precMsg.MaxDiff(precMsg2) < 0.3);

            precMsg =
                GaussianOp_Slow.PrecisionAverageConditional(Gaussian.FromNatural(-0.21769764449791806, 0.0000024898838689952023),
                                                            Gaussian.FromNatural(0, 0.5), Gamma.FromShapeAndRate(5, 5));
            precMsg2 =
                GaussianOp.PrecisionAverageConditional_slow(Gaussian.FromNatural(-0.21769764449791806, 0.0000024898838689952023),
                                                            Gaussian.FromNatural(0, 0.5), Gamma.FromShapeAndRate(5, 5));
            //Console.WriteLine("{0} should be {1}", precMsg2, precMsg);
            Assert.True(precMsg.MaxDiff(precMsg2) < 1e-4);
            Gamma precMsg3 =
                GaussianOp_Laplace.PrecisionAverageConditional_slow(Gaussian.FromNatural(-0.21769764449791806, 0.0000024898838689952023),
                                                                    Gaussian.FromNatural(0, 0.5), Gamma.FromShapeAndRate(5, 5));

            precMsg2 =
                GaussianOp.PrecisionAverageConditional(Gaussian.FromNatural(-0.21769764449791806, 0.0000024898838689952023),
                                                       Gaussian.FromNatural(0, 0.5), Gamma.FromShapeAndRate(5, 5));
            //Console.WriteLine("{0} should be {1}", precMsg2, precMsg);
            Assert.True(precMsg.MaxDiff(precMsg2) < 1e-4);

            Assert.True(GaussianOp.PrecisionAverageConditional_slow(Gaussian.FromNatural(-2.3874057896477092, 0.0070584383295080044),
                                                                    Gaussian.FromNatural(1.3999879871144227, 0.547354438587195), Gamma.FromShapeAndRate(3, 1))
                        .MaxDiff(Gamma.FromShapeAndRate(1.421, 55546)) < 10);

            // Unknown precision
            if (GaussianOp.ForceProper)
            {
                Assert.True(GaussianOp.PrecisionAverageConditional_slow(X0, Mean0, Precision0).MaxDiff(Gamma.FromShapeAndRate(1, 0.3632)) < 1e-4);
            }
            else
            {
                Assert.True(GaussianOp.PrecisionAverageConditional_slow(X0, Mean0, Precision0).MaxDiff(Gamma.FromShapeAndRate(-0.96304, -0.092572)) < 1e-4);
            }
            if (GaussianOp.ForceProper)
            {
                Assert.True(GaussianOp.PrecisionAverageConditional_slow(Gaussian.PointMass(3.0), Mean0, Precision0).MaxDiff(Gamma.FromShapeAndRate(1, 4.13824)) < 1e-4);
            }
            else
            {
                Assert.True(GaussianOp.PrecisionAverageConditional_slow(Gaussian.PointMass(3.0), Mean0, Precision0).MaxDiff(Gamma.FromShapeAndRate(-0.24693, 2.2797)) < 1e-4);
            }
            Assert.True(GaussianOp.PrecisionAverageConditional(3.0, 7.0).MaxDiff(Gamma.FromShapeAndRate(1.5, 8.0)) < 1e-4);
            Assert.True(GaussianOp.PrecisionAverageConditional_slow(new Gaussian(), Gaussian.PointMass(7.0), Precision0).MaxDiff(Gamma.FromShapeAndRate(1.0, 0.0)) < 1e-4);
        }
 private static void GetQMoments(GammaPower product, GammaPower A, Gamma q, double qPoint, out double qMean, out double qVariance)
 {
     double[] qDerivatives = new double[] { qPoint, 1, 0, 0 };
     GaussianOp_Laplace.LaplaceMoments(q, qDerivatives, dlogfs(qPoint, product, A), out qMean, out qVariance);
 }
        /// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="GammaPowerProductOp_Laplace"]/message_doc[@name="AAverageConditional(GammaPower, GammaPower, GammaPower, Gamma, GammaPower)"]/*'/>
        public static GammaPower AAverageConditional([SkipIfUniform] GammaPower product, GammaPower A, [SkipIfUniform] GammaPower B, Gamma q, GammaPower result)
        {
            if (B.Shape < A.Shape)
            {
                return(BAverageConditional(product, B, A, q, result));
            }
            if (B.IsPointMass)
            {
                return(GammaProductOp.AAverageConditional(product, B.Point, result));
            }
            if (A.IsPointMass)
            {
                return(GammaPower.Uniform(A.Power)); // TODO
            }
            if (product.IsUniform())
            {
                return(product);
            }
            double     qPoint = q.GetMean();
            GammaPower aMarginal;

            if (product.IsPointMass)
            {
                // Z = int Ga(y/q; s, r)/q Ga(q; q_s, q_r) dq
                // E[a] = E[product/q]
                // E[a^2] = E[product^2/q^2]
                // aVariance = E[a^2] - aMean^2
                double productPoint = product.Point;
                if (productPoint == 0)
                {
                    aMarginal = GammaPower.PointMass(0, result.Power);
                }
                else
                {
                    double iqMean, iqVariance;
                    GetIQMoments(product, A, q, qPoint, out iqMean, out iqVariance);
                    double aMean     = productPoint * iqMean;
                    double aVariance = productPoint * productPoint * iqVariance;
                    aMarginal = GammaPower.FromGamma(Gamma.FromMeanAndVariance(aMean, aVariance), result.Power);
                }
            }
            else
            {
                if (double.IsPositiveInfinity(product.Rate))
                {
                    return(GammaPower.PointMass(0, result.Power));
                }
                if (A.Power != product.Power)
                {
                    throw new NotSupportedException($"A.Power ({A.Power}) != product.Power ({product.Power})");
                }
                if (B.Power != product.Power)
                {
                    throw new NotSupportedException($"B.Power ({B.Power}) != product.Power ({product.Power})");
                }
                double r      = product.Rate;
                double g      = 1 / (qPoint * r + A.Rate);
                double g2     = g * g;
                double shape2 = GammaFromShapeAndRateOp_Slow.AddShapesMinus1(product.Shape, A.Shape) + (1 - A.Power);
                // From above:
                // a^(y_s-pa + a_s-1) exp(-(y_r b + a_r)*a)
                if (shape2 > 2)
                {
                    // Compute the moments of a^(-1/a.Power)
                    // Here q = b^(1/b.Power)
                    // E[a^(-1/a.Power)] = E[(q r + a_r)/(shape2-1)]
                    // var(a^(-1/a.Power)) = E[(q r + a_r)^2/(shape2-1)/(shape2-2)] - E[a^(-1/a.Power)]^2
                    //          = (var(q r + a_r) + E[(q r + a_r)]^2)/(shape2-1)/(shape2-2) - E[(q r + a_r)]^2/(shape2-1)^2
                    //          = var(q r + a_r)/(shape2-1)/(shape2-2) + E[(q r + a_r)/(shape2-1)]^2/(shape2-2)
                    // TODO: share this computation with BAverageConditional
                    double qMean, qVariance;
                    GetQMoments(product, A, q, qPoint, out qMean, out qVariance);
                    double iaMean = (qMean * r + A.Rate) / (shape2 - 1);
                    //double iaVariance = (qVariance * r2 / (shape2 - 1) + iaMean * iaMean) / (shape2 - 2);
                    // shape = mean^2/variance + 2
                    //double iaVarianceOverMeanSquared = (qVariance / (shape2 - 1) * r / iaMean * r / iaMean + 1) / (shape2 - 2);
                    double iaVarianceOverMeanSquared = (qVariance * (shape2 - 1) / (qMean + A.Rate / r) / (qMean + A.Rate / r) + 1) / (shape2 - 2);
                    //GammaPower iaMarginal = GammaPower.FromMeanAndVariance(iaMean, iaVariance, -1);
                    GammaPower iaMarginal = InverseGammaFromMeanAndVarianceOverMeanSquared(iaMean, iaVarianceOverMeanSquared);
                    if (iaMarginal.IsUniform())
                    {
                        if (result.Power > 0)
                        {
                            return(GammaPower.PointMass(0, result.Power));
                        }
                        else
                        {
                            return(GammaPower.Uniform(result.Power));
                        }
                    }
                    else
                    {
                        aMarginal = GammaPower.FromShapeAndRate(iaMarginal.Shape, iaMarginal.Rate, result.Power);
                    }
                    bool check = false;
                    if (check)
                    {
                        // Importance sampling
                        MeanVarianceAccumulator mvaB    = new MeanVarianceAccumulator();
                        MeanVarianceAccumulator mvaInvA = new MeanVarianceAccumulator();
                        Gamma bPrior = Gamma.FromShapeAndRate(B.Shape, B.Rate);
                        q = bPrior;
                        double shift = (product.Shape - product.Power) * Math.Log(qPoint) - shape2 * Math.Log(A.Rate + qPoint * r) + bPrior.GetLogProb(qPoint) - q.GetLogProb(qPoint);
                        for (int i = 0; i < 1000000; i++)
                        {
                            double bSample = q.Sample();
                            // logf = (y_s-y_p)*log(b) - (s+y_s-pa)*log(r + b*y_r)
                            double logf   = (product.Shape - product.Power) * Math.Log(bSample) - shape2 * Math.Log(A.Rate + bSample * r) + bPrior.GetLogProb(bSample) - q.GetLogProb(bSample);
                            double weight = Math.Exp(logf - shift);
                            mvaB.Add(bSample, weight);
                            double invA = (bSample * r + A.Rate) / (shape2 - 1);
                            mvaInvA.Add(invA, weight);
                        }
                        Trace.WriteLine($"b = {mvaB}, {qMean}, {qVariance}");
                        Trace.WriteLine($"invA = {mvaInvA} {mvaInvA.Variance * (shape2 - 1) / (shape2 - 2) + mvaInvA.Mean * mvaInvA.Mean / (shape2 - 2)}, {iaMean}, {iaVarianceOverMeanSquared * iaMean * iaMean}");
                        Trace.WriteLine($"aMarginal = {aMarginal}");
                    }
                }
                else
                {
                    // Compute the moments of a^(1/a.Power)
                    // aMean = shape2/(b y_r + a_r)
                    // aVariance = E[shape2*(shape2+1)/(b y_r + a_r)^2] - aMean^2 = var(shape2/(b y_r + a_r)) + E[shape2/(b y_r + a_r)^2]
                    //           = shape2^2*var(1/(b y_r + a_r)) + shape2*(var(1/(b y_r + a_r)) + (aMean/shape2)^2)
                    double   r2 = r * r;
                    double[] gDerivatives = new double[] { g, -r * g2, 2 * g2 * g * r2, -6 * g2 * g2 * r2 * r };
                    double   gMean, gVariance;
                    GaussianOp_Laplace.LaplaceMoments(q, gDerivatives, dlogfs(qPoint, product, A), out gMean, out gVariance);
                    double aMean     = shape2 * gMean;
                    double aVariance = shape2 * shape2 * gVariance + shape2 * (gVariance + gMean * gMean);
                    aMarginal = GammaPower.FromGamma(Gamma.FromMeanAndVariance(aMean, aVariance), result.Power);
                }
            }
            result.SetToRatio(aMarginal, A, GammaProductOp_Laplace.ForceProper);
            if (double.IsNaN(result.Shape) || double.IsNaN(result.Rate))
            {
                throw new InferRuntimeException("result is nan");
            }
            return(result);
        }
        /// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="GammaPowerProductOp_Laplace"]/message_doc[@name="ProductAverageConditional(GammaPower, GammaPower, GammaPower, Gamma, GammaPower)"]/*'/>
        public static GammaPower ProductAverageConditional(GammaPower product, [Proper] GammaPower A, [SkipIfUniform] GammaPower B, Gamma q, GammaPower result)
        {
            if (B.Shape < A.Shape)
            {
                return(ProductAverageConditional(product, B, A, q, result));
            }
            if (B.IsPointMass)
            {
                return(GammaProductOp.ProductAverageConditional(A, B.Point));
            }
            if (B.IsUniform())
            {
                return(GammaPower.Uniform(result.Power));
            }
            if (A.IsPointMass)
            {
                return(GammaProductOp.ProductAverageConditional(A.Point, B));
            }
            if (product.IsPointMass)
            {
                return(GammaPower.Uniform(result.Power)); // TODO
            }
            if (A.Power != product.Power)
            {
                throw new NotSupportedException($"A.Power ({A.Power}) != product.Power ({product.Power})");
            }
            if (B.Power != product.Power)
            {
                throw new NotSupportedException($"B.Power ({B.Power}) != product.Power ({product.Power})");
            }
            if (A.Rate == 0)
            {
                if (B.Rate == 0)
                {
                    return(GammaPower.FromShapeAndRate(Math.Min(A.Shape, B.Shape), 0, result.Power));
                }
                else
                {
                    return(A);
                }
            }
            if (B.Rate == 0)
            {
                return(B);
            }

            double     qPoint = q.GetMean();
            double     r      = product.Rate;
            double     shape2 = GammaFromShapeAndRateOp_Slow.AddShapesMinus1(product.Shape, A.Shape) + (1 - A.Power);
            GammaPower productMarginal;
            // threshold ensures 6/qPoint^4 does not overflow
            double threshold = Math.Sqrt(Math.Sqrt(6 / double.MaxValue));

            if (shape2 > 2 && result.Power < 0 && qPoint > threshold)
            {
                // Compute the moments of product^(-1/product.Power)
                // Here q = b^(1/b.Power)
                // E[a^(-1/a.Power) b^(-1/b.Power)] = E[(q r + a_r)/(shape2-1)/q]
                // var(a^(-1/a.Power) b^(-1/b.Power)) = E[(q r + a_r)^2/(shape2-1)/(shape2-2)/q^2] - E[a^(-1/a.Power) b^(-1/b.Power)]^2
                //          = (var((q r + a_r)/q) + E[(q r + a_r)/q]^2)/(shape2-1)/(shape2-2) - E[(q r + a_r)/q]^2/(shape2-1)^2
                //          = var((q r + a_r)/q)/(shape2-1)/(shape2-2) + E[(q r + a_r)/(shape2-1)/q]^2/(shape2-2)
                double iqMean, iqVariance;
                GetIQMoments(product, A, q, qPoint, out iqMean, out iqVariance);
                double ipMean     = (r + A.Rate * iqMean) / (shape2 - 1);
                double ipVariance = (iqVariance * A.Rate * A.Rate / (shape2 - 1) + ipMean * ipMean) / (shape2 - 2);
                // TODO: use ipVarianceOverMeanSquared
                GammaPower ipMarginal = GammaPower.FromMeanAndVariance(ipMean, ipVariance, -1);
                if (ipMarginal.IsUniform())
                {
                    return(GammaPower.Uniform(result.Power));
                }
                else
                {
                    productMarginal = GammaPower.FromShapeAndRate(ipMarginal.Shape, ipMarginal.Rate, result.Power);
                }
                bool check = false;
                if (check)
                {
                    // Importance sampling
                    MeanVarianceAccumulator mvaInvQ       = new MeanVarianceAccumulator();
                    MeanVarianceAccumulator mvaInvProduct = new MeanVarianceAccumulator();
                    Gamma  qPrior = Gamma.FromShapeAndRate(B.Shape, B.Rate);
                    double shift  = (product.Shape - product.Power) * Math.Log(qPoint) - shape2 * Math.Log(A.Rate + qPoint * r) + qPrior.GetLogProb(qPoint) - q.GetLogProb(qPoint);
                    for (int i = 0; i < 1000000; i++)
                    {
                        double qSample = q.Sample();
                        // logf = (y_s-y_p)*log(b) - (s+y_s-pa)*log(r + b*y_r)
                        double logf   = (product.Shape - product.Power) * Math.Log(qSample) - shape2 * Math.Log(A.Rate + qSample * r) + qPrior.GetLogProb(qSample) - q.GetLogProb(qSample);
                        double weight = Math.Exp(logf - shift);
                        mvaInvQ.Add(1 / qSample, weight);
                        double invProduct = (r + A.Rate / qSample) / (shape2 - 1);
                        mvaInvProduct.Add(invProduct, weight);
                    }
                    Trace.WriteLine($"invQ = {mvaInvQ}, {iqMean}, {iqVariance}");
                    Trace.WriteLine($"invProduct = {mvaInvProduct}");
                    Trace.WriteLine($"invA = {mvaInvProduct.Variance * (shape2 - 1) / (shape2 - 2) + mvaInvProduct.Mean * mvaInvProduct.Mean / (shape2 - 2)}, {ipMean}, {ipVariance}");
                    Trace.WriteLine($"productMarginal = {productMarginal}");
                }
            }
            else
            {
                // Compute the moments of y = product^(1/product.Power)
                // yMean = E[shape2*b/(b y_r + a_r)]
                // yVariance = E[shape2*(shape2+1)*b^2/(b y_r + a_r)^2] - yMean^2
                //           = var(shape2*b/(b y_r + a_r)) + E[shape2*b^2/(b y_r + a_r)^2]
                //           = shape2^2*var(b/(b y_r + a_r)) + shape2*(var(b/(b y_r + a_r)) + (yMean/shape2)^2)
                // Let g = b/(b y_r + a_r)
                double   denom        = qPoint * r + A.Rate;
                double   denom2       = denom * denom;
                double   rOverDenom   = r / denom;
                double[] gDerivatives = (denom == 0)
                    ? new double[] { 0, 0, 0, 0 }
                    : new double[] { qPoint / denom, A.Rate / denom2, -2 * A.Rate / denom2 * rOverDenom, 6 * A.Rate / denom2 * rOverDenom * rOverDenom };
                double gMean, gVariance;
                GaussianOp_Laplace.LaplaceMoments(q, gDerivatives, dlogfs(qPoint, product, A), out gMean, out gVariance);
                double yMean     = shape2 * gMean;
                double yVariance = shape2 * shape2 * gVariance + shape2 * (gVariance + gMean * gMean);
                productMarginal = GammaPower.FromGamma(Gamma.FromMeanAndVariance(yMean, yVariance), result.Power);
            }

            result.SetToRatio(productMarginal, product, GammaProductOp_Laplace.ForceProper);
            if (double.IsNaN(result.Shape) || double.IsNaN(result.Rate))
            {
                throw new InferRuntimeException("result is nan");
            }
            return(result);
        }