예제 #1
0
        // result is divided by n!! (odd n)
        public static double NormalCdfMomentDy2(int n, double x, double y, double r)
        {
            double omr2 = 1 - r * r;

            if (omr2 == 0)
            {
                double lfact;
                if (n % 2 == 1)
                {
                    lfact = DoubleFactorialLn(n);
                }
                else
                {
                    lfact = DoubleFactorialLn(n - 1);
                }
                return(System.Math.Pow(x - r * y, n) * System.Math.Exp(Gaussian.GetLogProb(y, 0, 1) - lfact));
            }
            else
            {
                double diff = (x - r * y) / System.Math.Sqrt(omr2);
                double lfact;
                if (n % 2 == 1)
                {
                    lfact = DoubleFactorialLn(n - 1);   // n!/n!!
                }
                else
                {
                    lfact = DoubleFactorialLn(n) - System.Math.Log(n + 1);   // n!/(n+1)!!
                }
                return(System.Math.Exp(lfact + Gaussian.GetLogProb(y, 0, 1)
                                       + Gaussian.GetLogProb(diff, 0, 1)
                                       + 0.5 * n * System.Math.Log(omr2)) * MMath.NormalCdfMomentRatio(n, diff));
            }
        }
        // this test shows that a Taylor expansion at zero is not reliable for x less than -0.5
        internal void NormalCdfMomentRatioTaylorZeroTest()
        {
            double x = -1;

            for (int n = 0; n < 20; n++)
            {
                Console.WriteLine("{0} {1} {2}", n, MMath.NormalCdfMomentRatio(n, x), NormalCdfMomentRatioTaylorZero(n, x));
            }
        }
예제 #3
0
        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;
        }
예제 #4
0
        /// <summary>
        /// Computes int_0^Inf x^n N(x;m,v) dx / N(m/sqrt(v);0,1)
        /// </summary>
        /// <param name="nMax"></param>
        /// <param name="m"></param>
        /// <param name="v"></param>
        /// <returns></returns>
        public static double[] NormalCdfMomentRatios(int nMax, double m, double v)
        {
            double[] result = new double[nMax + 1];
            double   sqrtV  = Math.Sqrt(v);

            for (int i = 0; i <= nMax; i++)
            {
                // NormalCdfMomentRatio(0,37.66) = infinity
                result[i] = MMath.NormalCdfMomentRatio(i, m / sqrtV) * Math.Pow(sqrtV, i) * MMath.Gamma(i + 1);
            }
            return(result);
        }
        public void NormalCdfMomentRatioSequenceTest()
        {
            int    n    = 0;
            double x    = -1.1;
            var    iter = MMath.NormalCdfMomentRatioSequence(n, x);

            for (int i = 0; i < 20; i++)
            {
                iter.MoveNext();
                double r1 = MMath.NormalCdfMomentRatio(n + i, x);
                double r2 = iter.Current;
                Console.WriteLine("{0}: {1} {2} {3}", i, r1, r2, Math.Abs(r1 - r2) / Math.Abs(r1));
            }
        }
 public void NormalCdfMomentRatioTest()
 {
     /* Anaconda Python script to generate a true value (must not be indented):
      * from mpmath import *
      * mp.dps = 100; mp.pretty = True
      * x = mpf('-2'); n=300;
      * exp(x*x/4)*pcfu(0.5+n,-x)
      */
     double[,] normcdfMomentRatio_pairs = ReadPairs(Path.Combine(TestUtils.DataFolderPath, "SpecialFunctionsValues", "NormalCdfMomentRatio.csv"));
     CheckFunctionValues("NormalCdfMomentRatio", delegate(double n, double x)
     {
         return(MMath.NormalCdfMomentRatio((int)n, x));
     }, normcdfMomentRatio_pairs);
 }
예제 #7
0
        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));
            }
        }
예제 #8
0
        /// <summary>
        /// Compute moments of 0.5*exp(-abs(x))*N(x;m,v)
        /// </summary>
        /// <param name="m"></param>
        /// <param name="v"></param>
        /// <param name="logZ"></param>
        /// <param name="mu"></param>
        /// <param name="vu"></param>
        public static void LaplacianTimesGaussianMoments(double m, double v, out double logZ, out double mu, out double vu)
        {
            if (Double.IsPositiveInfinity(v))
            {
                // moments of Laplacian only
                logZ = 0;
                mu   = 0;
                vu   = 2;
                return;
            }
            double invV = 1 / v;
            double invSqrtV = Math.Sqrt(invV);
            double mPlus = (m - v) * invSqrtV;
            double mMinus = (-m - v) * invSqrtV;
            double Z0, Z1, Z2;

            if (mPlus > 30)
            {
                double[] moments = NormalCdfMoments(2, m - v, v);
                Z0   = moments[0];
                Z1   = moments[1];
                Z2   = moments[2];
                logZ = Math.Log(0.5 * Z0) - m + 0.5 * v;
            }
            else if (mMinus > 30)
            {
                double[] moments = NormalCdfMoments(2, -m - v, v);
                Z0   = moments[0];
                Z1   = -moments[1];
                Z2   = moments[2];
                logZ = Math.Log(0.5 * Z0) + m + 0.5 * v;
            }
            else
            {
                Z0   = MMath.NormalCdfRatio(mPlus) + MMath.NormalCdfRatio(mMinus);
                Z1   = Math.Sqrt(v) * (MMath.NormalCdfMomentRatio(1, mPlus) - MMath.NormalCdfMomentRatio(1, mMinus));
                Z2   = 2 * v * (MMath.NormalCdfMomentRatio(2, mPlus) + MMath.NormalCdfMomentRatio(2, mMinus));
                logZ = Math.Log(0.5 * Z0) - MMath.LnSqrt2PI - 0.5 * m * m * invV;
            }
            mu = Z1 / Z0;
            double mu2 = Z2 / Z0;

            vu = mu2 - mu * mu;
        }
예제 #9
0
        public void VarianceGammaTimesGaussianIntegralTest()
        {
            double logZ, mu, vu;

            GaussianFromMeanAndVarianceOp.LaplacianTimesGaussianMoments(-100, 1, out logZ, out mu, out vu);
            Console.WriteLine(logZ);
            //GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianMoments5(1, 1000, 1, out mu, out vu);
            Console.WriteLine(mu);
            Console.WriteLine(vu);
            double[][] vgmoments = GaussianFromMeanAndVarianceOp.NormalVGMomentRatios(10, 1, -6, 1);
            for (int i = 0; i < 10; i++)
            {
                double f = MMath.NormalCdfMomentRatio(i, -6) * MMath.Gamma(i + 1);
                Console.WriteLine("R({0}) = {1}, Zp = {2}", i, f, vgmoments[0][i]);
            }

            // true values computed by tex/factors/matlab/test_variance_gamma2.m
            double scale = 2 * System.Math.Exp(-Gaussian.GetLogProb(2 / System.Math.Sqrt(3), 0, 1));

            Assert.True(MMath.AbsDiff(0.117700554409044, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1, 2, 3) / scale, 1e-20) < 1e-5);
            Assert.True(MMath.AbsDiff(0.112933034747473, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(2, 2, 3) / scale, 1e-20) < 1e-5);
            Assert.True(MMath.AbsDiff(0.117331854901251, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.1, 2, 3) / scale, 1e-20) < 1e-5);
            Assert.True(MMath.AbsDiff(0.115563913123152, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.5, 2, 3) / scale, 1e-20) < 2e-5);

            scale = 2 * System.Math.Exp(-Gaussian.GetLogProb(20 / System.Math.Sqrt(0.3), 0, 1));
            Assert.True(MMath.AbsDiff(1.197359429038085e-009, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1, 20, 0.3) / scale, 1e-20) < 1e-5);
            Assert.True(MMath.AbsDiff(1.239267009054433e-008, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(2, 20, 0.3) / scale, 1e-20) < 1e-5);
            Assert.True(MMath.AbsDiff(1.586340098271600e-009, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.1, 20, 0.3) / scale, 1e-20) < 1e-4);
            Assert.True(MMath.AbsDiff(4.319412089896069e-009, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.5, 20, 0.3) / scale, 1e-20) < 1e-4);

            scale = 2 * System.Math.Exp(-Gaussian.GetLogProb(40 / System.Math.Sqrt(0.3), 0, 1));
            scale = 2 * System.Math.Exp(40 - 0.3 / 2);
            Assert.True(MMath.AbsDiff(2.467941724509690e-018, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1, 40, 0.3) / scale, 1e-20) < 1e-5);
            Assert.True(MMath.AbsDiff(5.022261409377230e-017, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(2, 40, 0.3) / scale, 1e-20) < 1e-5);
            Assert.True(MMath.AbsDiff(3.502361147666615e-018, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.1, 40, 0.3) / scale, 1e-20) < 1e-4);
            Assert.True(MMath.AbsDiff(1.252310352551344e-017, GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.5, 40, 0.3) / scale, 1e-20) < 1e-4);

            Assert.True(GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.138, 33.4, 0.187) > 0);
            Assert.True(GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.138, -33.4, 0.187) > 0);
            Assert.True(GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.138, 58.25, 0.187) > 0);
            Assert.True(GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.138, 50, 0.187) > 0);
            Assert.True(GaussianFromMeanAndVarianceOp.VarianceGammaTimesGaussianIntegral(1.138, 100, 0.187) > 0);
        }
예제 #10
0
        private static IEnumerator <double> NormalCdfMomentRatioSequence(double x)
        {
            if (x > -1)
            {
                double rPrev = MMath.NormalCdfRatio(x);
                yield return(rPrev);

                double r = x * rPrev + 1;
                yield return(r);

                for (int i = 1; ; i++)
                {
                    double rNew = (x * r + rPrev) / (i + 1);
                    rPrev = r;
                    r     = rNew;
                    yield return(r);
                }
            }
            else
            {
                int tableSize = 10;
                // rtable[tableStart-i] = R_i
                double[] rtable     = new double[tableSize];
                int      tableStart = -1;
                for (int i = 0; ; i++)
                {
                    if (i > tableStart)
                    {
                        // build the table
                        tableStart = i + tableSize - 1;
                        rtable[0]  = MMath.NormalCdfMomentRatio(tableStart, x);
                        rtable[1]  = MMath.NormalCdfMomentRatio(tableStart - 1, x);
                        for (int j = 2; j < tableSize; j++)
                        {
                            int n = tableStart - j + 1;
                            rtable[j] = (n + 1) * rtable[j - 2] - x * rtable[j - 1];
                        }
                    }
                    yield return(rtable[tableStart - i]);
                }
            }
        }
 // returns int_0^Inf x^n VG(x;a) N(x;m,v) dx / (0.5*N(m;0,1))
 public static double NormalVGMomentRatio(int n, int a, double m, double v)
 {
     if (a < 1)
     {
         throw new ArgumentException("a < 1", "a");
     }
     if (a == 1)
     {
         double sqrtV = Math.Sqrt(v);
         return(MMath.Gamma(n + 1) * MMath.NormalCdfMomentRatio(n, m / sqrtV) * Math.Pow(sqrtV, n));
     }
     else if (a == 2)
     {
         double sqrtV = Math.Sqrt(v);
         return(0.5 * NormalVGMomentRatio(n, 1, m, v) + 0.5 * MMath.Gamma(n + 2) * MMath.NormalCdfMomentRatio(n + 1, m / sqrtV) * Math.Pow(sqrtV, n + 1));
     }
     else                 // a > 2
     {
         return(0.25 / ((a - 2) * (a - 1)) * NormalVGMomentRatio(n + 2, a - 2, m, v) + (a - 1.5) / (a - 1) * NormalVGMomentRatio(n, a - 1, m, v));
     }
 }
예제 #12
0
        // Returns NormalCdf divided by N(x;0,1) N((y-rx)/sqrt(1-r^2);0,1), multiplied by scale
        // This version works best for small r^2
        // We need x <= 0 and (y - r*x) <= 0
        private static double NormalCdfRatioConFrac3b(double x, double y, double r, double scale)
        {
            if (scale == 0)
            {
                return(scale);
            }
            //if (r * (y - r * x) < 0)
            //    throw new ArgumentException("r*(y - r*x) < 0");
            if (x - r * y > 0)
            {
                throw new ArgumentException("x - r*y > 0");
            }
            if (x > 0)
            {
                throw new ArgumentException("x > 0");
            }
            double omr2     = 1 - r * r;
            double sqrtomr2 = System.Math.Sqrt(omr2);
            double rxmy     = r * x - y;
            double ymrx     = -rxmy / sqrtomr2;

            if (ymrx > 0)
            {
                throw new ArgumentException("ymrx > 0");
            }
            double offset  = MMath.NormalCdfRatio(x) * MMath.NormalCdfRatio(ymrx) * scale;
            double omsomr2 = MMath.OneMinusSqrtOneMinus(r * r);
            double delta   = (r * y - x * omsomr2) / sqrtomr2;
            double diff    = (x - r * y) / sqrtomr2;
            double Rdiff   = MMath.NormalCdfRatio(diff);
            //var RdiffIter = MMath.NormalCdfMomentRatioSequence(0, diff);
            //RdiffIter.MoveNext();
            //double Rdiff = RdiffIter.Current;
            double scale2 = scale * omr2;
            double numer;

            if (System.Math.Abs(delta) > 0.5)
            {
                // for r =approx 0 this becomes inaccurate due to cancellation
                numer = scale2 * (MMath.NormalCdfRatio(x) / sqrtomr2 - Rdiff);
            }
            else
            {
                numer = scale2 * (MMath.NormalCdfRatioDiff(diff, delta) + omsomr2 * Rdiff) / sqrtomr2;
            }
            double numerPrev = 0;
            double denom     = rxmy;
            double denomPrev = 1;
            double rOld      = 0;
            double result    = 0;
            double cEven     = scale2;
            double cIncr     = r * sqrtomr2;
            double cOdd      = cEven * cIncr;

            cIncr *= cIncr;
            for (int i = 1; i < 10000; i++)
            {
                double numerNew, denomNew;
                //RdiffIter.MoveNext();
                double c = MMath.NormalCdfMomentRatio(i, diff);
                //double c = RdiffIter.Current;
                if (i % 2 == 1)
                {
                    if (i > 1)
                    {
                        cOdd *= (i - 1) * cIncr;
                    }
                    c       *= cOdd;
                    numerNew = rxmy * numer + omr2 * numerPrev - c;
                    denomNew = rxmy * denom + omr2 * denomPrev;
                }
                else
                {
                    cEven   *= i * cIncr;
                    c       *= cEven;
                    numerNew = (rxmy * numer + omr2 * i * numerPrev - c) / (i + 1);
                    denomNew = (rxmy * denom + omr2 * i * denomPrev) / (i + 1);
                }
                numerPrev = numer;
                numer     = numerNew;
                denomPrev = denom;
                denom     = denomNew;
                if (i % 2 == 1)
                {
                    result = -numer / denom;
                    Console.WriteLine($"iter {i.ToString().PadLeft(3)}: {result.ToString("r").PadRight(24)} {numer.ToString("r").PadRight(24)} {denom.ToString("r").PadRight(24)} {c}");
                    if (double.IsInfinity(result) || double.IsNaN(result))
                    {
                        throw new Exception(string.Format("NormalCdfRatioConFrac3 not converging for x={0} y={1} r={2} scale={3}", x, y, r, scale));
                    }
                    //if (result == rOld)
                    //    return result + offset;
                    rOld = result;
                }
            }
            throw new Exception(string.Format("NormalCdfRatioConFrac3 not converging for x={0} y={1} r={2} scale={3}", x, y, r, scale));
        }
예제 #13
0
        private double NormalCdfIntegralBasic2(double x, double y, double r)
        {
            double omr2     = 1 - r * r;
            double sqrtomr2 = System.Math.Sqrt(omr2);
            double ymrx     = (y - r * x) / sqrtomr2;
            double xmry     = (x - r * y) / sqrtomr2;

            double func(double t)
            {
                return((y - r * x + r * t) * System.Math.Exp(Gaussian.GetLogProb(t, x, 1) + MMath.NormalCdfLn(ymrx + r * t / sqrtomr2)));
            }

            func(0);
            double func2(double t)
            {
                double ymrxt = ymrx + r * t / sqrtomr2;

                return(sqrtomr2 * System.Math.Exp(Gaussian.GetLogProb(t, x, 1) + Gaussian.GetLogProb(ymrxt, 0, 1)) * (MMath.NormalCdfMomentRatio(1, ymrxt) - 1));
            }

            func2(0);
            //return -MMath.NormalCdf(x, y, r) * (y / r - x) + Integrate(func2) / r;
            double func3(double t)
            {
                double xmryt = xmry + r * t / sqrtomr2;

                return(sqrtomr2 * System.Math.Exp(Gaussian.GetLogProb(t, y, 1) + Gaussian.GetLogProb(xmryt, 0, 1)) * MMath.NormalCdfMomentRatio(1, xmryt));
            }

            //double Z = MMath.NormalCdf(x, y, r, out double exponent);
            double Z3 = Integrate(func3);

            //return System.Math.Exp(exponent)*(-Z * (y / r - x) - omr2 / r * MMath.NormalCdfRatio(xmry)) + Z3/r;
            return(Z3);
        }
예제 #14
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);
        }
예제 #15
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((new Gaussian(mp, vp)) / x);
                    }
                }
                if (x.IsUniform())
                {
                    return(Gaussian.Uniform());
                }
                throw new ImproperMessageException(x);
            }
            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)
                {
                    if (z < -1e10)
                    {
                        double mp = -1 / (z * sqrtPrec);
                        double vp = 1 / (z * z * prec);
                        return((new Gaussian(mp, vp)) / x);
                    }
                    else if (z < -100)
                    {
                        double Y = MMath.NormalCdfRatio(z);
                        // double dY = MMath.NormalCdfMomentRatio(1, z);
                        // dY = z*Y + 1
                        // d2Y = z*dY + Y
                        // posterior mean = m + sqrt(v)/Y = sqrt(v)*(z + 1/Y) = sqrt(v)*dY/Y =approx sqrt(v)*(-1/z)
                        // 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
                        double d2Y = 2 * MMath.NormalCdfMomentRatio(2, z);
                        // m2TimesPrec = z*dY/Y + 1
                        double m2TimesPrec = d2Y / Y;
                        Assert.IsTrue(tau != 0);
                        double mp = (m2TimesPrec - 1) / tau;
                        double vp = m2TimesPrec / prec - mp * mp;
                        return((new Gaussian(mp, vp)) / x);
                    }
                    alpha = sqrtPrec / MMath.NormalCdfRatio(z);
                }
                else
                {
                    if (z > 1e10)
                    {
                        double mp = 1 / (z * sqrtPrec);
                        double vp = 1 / (z * z * prec);
                        return((new Gaussian(mp, vp)) / x);
                    }
                    else if (z > 100)
                    {
                        double Y = MMath.NormalCdfRatio(-z);
                        // dY = -(d2Y/prec - Y)/(-z)*sqrtPrec
                        // dY/Y/prec = -(d2Y/Y/prec/prec - 1/prec)/(-z)*sqrtPrec
                        // dY/Y/prec = -(d2Y/Y/prec - 1)/(-tau)
                        //double dY = -MMath.NormalCdfMomentRatio(1,-z)*sqrtPrec;
                        double d2Y         = 2 * MMath.NormalCdfMomentRatio(2, -z);
                        double m2TimesPrec = d2Y / Y;
                        Assert.IsTrue(tau != 0);
                        double mp = (m2TimesPrec - 1) / tau;
                        double vp = m2TimesPrec / prec - mp * mp;
                        return((new Gaussian(mp, vp)) / x);
                    }
                    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   = alpha * (alpha + tau);
            double weight = beta / (prec - beta);

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

            // 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 ApplicationException("result is nan");
            }
            return(result);
        }
예제 #16
0
        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))));
            }
        }
예제 #17
0
        // returns phi_n(m,v)/n!
        public static double NormalCdfMoment(int n, double m, double v)
        {
            double InvSqrtV = Math.Sqrt(1 / v);

            return(MMath.NormalCdfMomentRatio(n, m * InvSqrtV) * MMath.InvSqrt2PI * Math.Exp(-0.5 * m * m / v) * Math.Pow(v, 0.5 * n));
        }
예제 #18
0
        /// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="MaxGaussianOp"]/message_doc[@name="AAverageConditional(Gaussian, Gaussian, Gaussian)"]/*'/>
        public static Gaussian AAverageConditional([SkipIfUniform] Gaussian max, [Proper] Gaussian a, [Proper] Gaussian b)
        {
            if (max.IsUniform())
            {
                return(Gaussian.Uniform());
            }
            if (!b.IsProper())
            {
                throw new ImproperMessageException(b);
            }

            double logw1, alpha1, vx1, mx1;
            double logw2, alpha2, vx2, mx2;
            double logz;

            ComputeStats(max, a, b, out logz, out logw1, out alpha1, out vx1, out mx1,
                         out logw2, out alpha2, out vx2, out mx2);
            double w1 = Math.Exp(logw1 - logz);
            double w2 = Math.Exp(logw2 - logz);
            bool   checkDerivatives = false;

            if (a.IsPointMass)
            {
                // f(a) = int p(x = max(a,b)) p(b) db
                // f(a) = p(x = a) p(a >= b) + p(x = b) p(a < b | x = b)
                // f(a) = N(a; mx, vx) phi((a - m2)/sqrt(v2)) + N(m2; mx, vx + v2) phi((mx2 - a)/sqrt(vx2))
                // f'(a) = (mx-a)/vx N(a; mx, vx) phi((a - m2)/sqrt(v2)) + N(a; mx, vx) N(a; m2, v2) - N(m2; mx, vx + v2) N(a; mx2, vx2)
                //       = (mx-a)/vx N(a; mx, vx) phi((a - m2)/sqrt(v2))
                // f''(a) = -1/vx N(a; mx, vx) phi((a - m2)/sqrt(v2)) + (mx-a)^2/vx^2 N(a; mx, vx) phi((a - m2)/sqrt(v2)) + (mx-a)/vx N(a; mx, vx) N(a; m2, v2)
                // ddlogf = f''(a)/f(a) - (f'(a)/f(a))^2 = (f''(a) f(a) - f'(a)^2)/f(a)^2
                double aPoint = a.Point;
                if (max.IsPointMass)
                {
                    return(max);
                }
                double z     = max.MeanTimesPrecision - aPoint * max.Precision;
                double alpha = z * w1;
                double beta  = (z * alpha1 - max.Precision + z * z) * w1 - alpha * alpha;
                if (b.IsPointMass && b.Point != aPoint)
                {
                    beta -= max.Precision * w2 * alpha2 * (b.Point - aPoint);
                }
                if (checkDerivatives)
                {
                    double m1 = a.GetMean();
                    double delta = m1 * 1e-6;
                    double logzd, logw1d, alpha1d, vx1d, mx1d, logw2d, alpha2d, vx2d, mx2d;
                    ComputeStats(max, Gaussian.FromMeanAndPrecision(m1 + delta, a.Precision), b, out logzd, out logw1d, out alpha1d, out vx1d, out mx1d, out logw2d, out alpha2d, out vx2d, out mx2d);
                    double logzd2;
                    ComputeStats(max, Gaussian.FromMeanAndPrecision(m1 - delta, a.Precision), b, out logzd2, out logw1d, out alpha1d, out vx1d, out mx1d, out logw2d, out alpha2d, out vx2d, out mx2d);
                    double alphaCheck = (logzd - logzd2) / (2 * delta);
                    double alphaError = Math.Abs(alpha - alphaCheck);
                    double betaCheck  = (logzd + logzd2 - 2 * logz) / (delta * delta);
                    double betaError  = Math.Abs(beta - betaCheck);
                    Console.WriteLine($"alpha={alpha} check={alphaCheck} error={alphaError} beta={beta} check={betaCheck} error={betaError}");
                }
                return(Gaussian.FromDerivatives(aPoint, alpha, beta, ForceProper));
            }
            bool useMessage = (w1 > 0) || (logw2 > -100);

            if (useMessage)
            {
                // vx1 = 1/(1/vx + 1/v1) = vx*v1/(vx + v1)
                double z, alpha, beta;
                if (max.IsPointMass)
                {
                    if (w2 == 0)
                    {
                        return(max);
                    }
                    z     = max.Point * a.Precision - a.MeanTimesPrecision;
                    alpha = z * w1 - w2 * alpha2;
                    beta  = (z * z - a.Precision) * w1 - z * alpha2 * w2 - alpha * alpha;
                }
                else
                {
                    //z = vx1 * (max.MeanTimesPrecision * a.Precision - a.MeanTimesPrecision * max.Precision);
                    z     = (max.MeanTimesPrecision - a.GetMean() * max.Precision) / (max.Precision / a.Precision + 1);
                    alpha = z * w1 - vx1 * max.Precision * w2 * alpha2;
                    if (w2 == 0)
                    {
                        //beta = (z * alpha1 - max.Precision) / (max.Precision / a.Precision + 1);
                        //double resultPrecision = a.Precision * beta / (-a.Precision - beta);
                        //resultPrecision = beta / (-1 - beta/a.Precision);
                        //resultPrecision = 1 / (-1/beta - 1 / a.Precision);
                        //resultPrecision = a.Precision / (-a.Precision / beta - 1);
                        //resultPrecision = a.Precision / (-(a.Precision + max.Precision) / (z * alpha1 - max.Precision) - 1);
                        //resultPrecision = -a.Precision / ((a.Precision + z*alpha1) / (z * alpha1 - max.Precision));
                        double zalpha1         = z * alpha1;
                        double denom           = (1 + zalpha1 / a.Precision);
                        double resultPrecision = (max.Precision - zalpha1) / denom;
                        //double weight = (max.Precision - z * alpha1) / (a.Precision + z * alpha1);
                        //double weightPlus1 = (max.Precision + a.Precision) / (a.Precision + z * alpha1);
                        //double resultMeanTimesPrecision = weight * (a.MeanTimesPrecision + alpha) + alpha;
                        //resultMeanTimesPrecision = weight * a.MeanTimesPrecision + weightPlus1 * alpha;
                        //double resultMeanTimesPrecision = (a.Precision * max.MeanTimesPrecision - z * alpha1 * a.MeanTimesPrecision) / (a.Precision + z * alpha1);
                        double resultMeanTimesPrecision = (max.MeanTimesPrecision - zalpha1 * a.GetMean()) / denom;
                        return(Gaussian.FromNatural(resultMeanTimesPrecision, resultPrecision));
                    }
                    else
                    {
                        beta = ((z * alpha1 - max.Precision) * vx1 * a.Precision + z * z) * w1
                               - max.Precision * vx1 * w2 * alpha2 * (mx2 * a.Precision - a.MeanTimesPrecision) / (vx2 * a.Precision + 1) - alpha * alpha;
                    }
                }
                //Console.WriteLine($"z={z} w1={w1:r} w2={w2:r} logw2={logw2} alpha1={alpha1} alpha2={alpha2} alpha={alpha:r} beta={beta:r}");
                if (checkDerivatives)
                {
                    double m1 = a.GetMean();
                    double delta = m1 * 1e-6;
                    double logzd, logw1d, alpha1d, vx1d, mx1d, logw2d, alpha2d, vx2d, mx2d;
                    ComputeStats(max, Gaussian.FromMeanAndPrecision(m1 + delta, a.Precision), b, out logzd, out logw1d, out alpha1d, out vx1d, out mx1d, out logw2d, out alpha2d, out vx2d, out mx2d);
                    double logzd2;
                    ComputeStats(max, Gaussian.FromMeanAndPrecision(m1 - delta, a.Precision), b, out logzd2, out logw1d, out alpha1d, out vx1d, out mx1d, out logw2d, out alpha2d, out vx2d, out mx2d);
                    double alphaCheck = (logzd - logzd2) / (2 * delta);
                    double alphaError = Math.Abs(alpha - alphaCheck);
                    double betaCheck  = (logzd + logzd2 - 2 * logz) / (delta * delta);
                    double betaError  = Math.Abs(beta - betaCheck);
                    Console.WriteLine($"alpha={alpha} check={alphaCheck} error={alphaError} beta={beta} check={betaCheck} error={betaError}");
                }
                return(GaussianOp.GaussianFromAlphaBeta(a, alpha, -beta, ForceProper));
            }
            else
            {
                double m1, v1, m2, v2;
                a.GetMeanAndVariance(out m1, out v1);
                b.GetMeanAndVariance(out m2, out v2);
                // the posterior is a mixture model with weights exp(logw1-logz), exp(logw2-logz) and distributions
                // N(a; mx1, vx1) phi((a - m2)/sqrt(v2)) / phi((mx1 - m2)/sqrt(vx1 + v2))
                // N(a; m1, v1) phi((mx2 - a)/sqrt(vx2)) / phi((mx2 - m1)/sqrt(vx2 + v1))
                // the moments of the posterior are computed via the moments of these two components.
                if (vx1 == 0)
                {
                    alpha1 = 0;
                }
                if (vx2 == 0)
                {
                    alpha2 = 0;
                }
                double mc1 = mx1;
                if (alpha1 != 0) // avoid 0*infinity
                {
                    mc1 += alpha1 * vx1;
                }
                alpha2 = -alpha2;
                double mc2 = m1;
                if (alpha2 != 0) // avoid 0*infinity
                {
                    mc2 += alpha2 * v1;
                }
                // z2 = (mx2 - m1) / Math.Sqrt(vx2 + v1)
                // logw2 = MMath.NormalCdfLn(z2);
                // alpha2 = -Math.Exp(Gaussian.GetLogProb(mx2, m1, vx2 + v1) - logw2);
                //        = -1/sqrt(vx2+v1)/NormalCdfRatio(z2)
                // m1 + alpha2*v1 = sqrt(vx2+v1)*(m1/sqrt(vx2+v1) - v1/(vx2+v1)/NormalCdfRatio(z2))
                //                = sqrt(vx2+v1)*(mx2/sqrt(vx2+v1) - z2 - v1/(vx2+v1)/NormalCdfRatio(z2))
                //                = sqrt(vx2+v1)*(mx2/sqrt(vx2+v1) - dY/Y)  if vx2=0
                double       z2 = 0, Y = 0, dY = 0;
                const double z2small = 0;
                if (vx2 == 0)
                {
                    z2 = (mx2 - m1) / Math.Sqrt(vx2 + v1);
                    if (z2 < z2small)
                    {
                        Y   = MMath.NormalCdfRatio(z2);
                        dY  = MMath.NormalCdfMomentRatio(1, z2);
                        mc2 = mx2 - Math.Sqrt(v1) * dY / Y;
                    }
                }
                double m = w1 * mc1 + w2 * mc2;
                double beta1;
                if (alpha1 == 0)
                {
                    beta1 = 0;  // avoid 0*infinity
                }
                else
                {
                    double r1 = (mx1 - m2) / (vx1 + v2);
                    beta1 = alpha1 * (alpha1 + r1);
                }
                double beta2;
                if (alpha2 == 0)
                {
                    beta2 = 0;  // avoid 0*infinity
                }
                else
                {
                    double r2 = (mx2 - m1) / (vx2 + v1);
                    beta2 = alpha2 * (alpha2 - r2);
                }
                double vc1 = vx1 * (1 - vx1 * beta1);
                double vc2;
                if (vx2 == 0 && z2 < z2small)
                {
                    // beta2 = alpha2 * (alpha2 - z2/sqrt(v1))
                    // vc2 = v1 - v1^2 * alpha2 * (alpha2 - z2/sqrt(v1))
                    //     = v1 - v1*dY/Y^2
                    //     =approx v1/z2^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
                    //vc2 = v1 * (1 - dY / (Y * Y));
                    double d2Y  = 2 * MMath.NormalCdfMomentRatio(2, z2);
                    double dYiY = dY / Y;
                    vc2 = v1 * (d2Y / Y - dYiY * dYiY);
                }
                else if (beta2 == 0)
                {
                    vc2 = v1;
                }
                else
                {
                    vc2 = v1 * (1 - v1 * beta2);
                }
                double   diff   = mc1 - mc2;
                double   v      = w1 * vc1 + w2 * vc2 + w1 * w2 * diff * diff;
                Gaussian result = new Gaussian(m, v);
                //Console.WriteLine($"z2={z2} m={m} v={v} vc2={vc2} diff={diff}");
                result.SetToRatio(result, a, ForceProper);
                if (Double.IsNaN(result.Precision) || Double.IsNaN(result.MeanTimesPrecision))
                {
                    throw new InferRuntimeException($"result is NaN.  max={max}, a={a}, b={b}");
                }
                return(result);
            }
        }
        public static Gaussian XAverageConditional_Helper([SkipIfUniform] Bernoulli isPositive, [SkipIfUniform, Proper] Gaussian x, bool forceProper)
        {
            Gaussian result = new Gaussian();

            if (x.IsPointMass)
            {
                result.SetToUniform();
                return(result);
            }
            double prec = x.Precision;

            if (prec == 0.0)
            {
                result.SetToUniform();
                return(result);
            }
            else if (prec < 0)
            {
                throw new ImproperMessageException(x);
            }
            double sqrtPrec = Math.Sqrt(prec);
            double tau      = x.MeanTimesPrecision;
            // 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)
                {
                    if (z < -100)
                    {
                        double Y           = MMath.NormalCdfRatio(z);
                        double d2Y         = 2 * MMath.NormalCdfMomentRatio(2, z);
                        double m2TimesPrec = d2Y / Y;
                        Assert.IsTrue(tau != 0);
                        double mp = (m2TimesPrec - 1) / tau;
                        double vp = m2TimesPrec / prec - mp * mp;
                        return((new Gaussian(mp, vp)) / x);
                    }
                    alpha = sqrtPrec / MMath.NormalCdfRatio(z);
                }
                else
                {
                    if (z > 100)
                    {
                        double Y = MMath.NormalCdfRatio(-z);
                        // dY = -(d2Y/prec - Y)/(-z)*sqrtPrec
                        // dY/Y/prec = -(d2Y/Y/prec/prec - 1/prec)/(-z)*sqrtPrec
                        // dY/Y/prec = -(d2Y/Y/prec - 1)/(-tau)
                        //double dY = -MMath.NormalCdfMomentRatio(1,-z)*sqrtPrec;
                        double d2Y         = 2 * MMath.NormalCdfMomentRatio(2, -z);
                        double m2TimesPrec = d2Y / Y;
                        Assert.IsTrue(tau != 0);
                        double mp = (m2TimesPrec - 1) / tau;
                        double vp = m2TimesPrec / prec - mp * mp;
                        return((new Gaussian(mp, vp)) / x);
                    }
                    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   = alpha * (alpha + tau);
            double weight = beta / (prec - beta);

            if (forceProper && weight < 0)
            {
                weight = 0;
            }
            // 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 ApplicationException("result is nan");
            }
            return(result);
        }