/// <summary>
        /// Creates a new Gaussian which is the product of two other Gaussians
        /// </summary>
        /// <param name="a">First Gaussian</param>
        /// <param name="b">Second Gaussian</param>
        /// <returns>Result</returns>
        public static Gaussian operator*(Gaussian a, Gaussian b)
        {
            Gaussian result = new Gaussian();

            result.SetToProduct(a, b);
            return(result);
        }
Example #2
0
 /// <summary>
 /// Set this distribution equal to the product of a and b
 /// </summary>
 /// <param name="a"></param>
 /// <param name="b"></param>
 public void SetToProduct(TruncatedGaussian a, TruncatedGaussian b)
 {
     LowerBound = Math.Max(a.LowerBound, b.LowerBound);
     UpperBound = Math.Min(a.UpperBound, b.UpperBound);
     if (LowerBound > UpperBound)
     {
         throw new AllZeroException();
     }
     Gaussian.SetToProduct(a.Gaussian, b.Gaussian);
 }
Example #3
0
        /// <summary>
        /// Set this distribution equal to the approximate product of a and b
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <remarks>
        /// Since WrappedGaussians are not closed under multiplication, the result is approximate.
        /// </remarks>
        public void SetToProduct(WrappedGaussian a, WrappedGaussian b)
        {
            if (a.Period < b.Period)
            {
                SetToProduct(b, a);
                return;
            }
            // a.Period >= b.Period
            if (a.IsUniform())
            {
                SetTo(b);
                return;
            }
            if (b.IsUniform())
            {
                SetTo(a);
                return;
            }
            if (a.IsPointMass)
            {
                if (b.IsPointMass && !a.Point.Equals(b.Point))
                {
                    throw new AllZeroException();
                }
                Point = a.Point;
                return;
            }
            if (b.IsPointMass)
            {
                Point = b.Point;
                return;
            }
            // (a,b) are not uniform or point mass
            double ratio    = a.Period / b.Period;
            int    intRatio = (int)Math.Round(ratio);

            if (Math.Abs(ratio - intRatio) > a.Period * 1e-4)
            {
                throw new ArgumentException("a.Period (" + a.Period + ") is not a multiple of b.Period (" + b.Period + ")");
            }
            this.Period = a.Period;
            // a.Period = k*b.Period, k >= 1
            // because one period is a multiple of the other, we only need to sum over one set of shifts.
            // otherwise, we would need to sum over two sets of shifts.
            double ma, va, mb, vb;

            a.Gaussian.GetMeanAndVariance(out ma, out va);
            b.Gaussian.GetMeanAndVariance(out mb, out vb);
            double diff = (ma - mb) / b.Period;

#if true
            // approximate using only the one best shift
            int      k        = (int)Math.Round(diff);
            Gaussian bShifted = new Gaussian(mb + k * b.Period, vb);
            Gaussian.SetToProduct(a.Gaussian, bShifted);
#else
            // we will sum over shifts from kMin to kMax, numbering intRatio in total
            int kMin, kMax;
            if (intRatio % 2 == 1)
            {
                // odd number of shifts
                int kMid      = (int)Math.Round(diff);
                int halfRatio = intRatio / 2;
                kMin = kMid - halfRatio;
                kMax = kMid + halfRatio;
            }
            else
            {
                // even number of shifts
                int kMid      = (int)Math.Floor(diff);
                int halfRatio = intRatio / 2;
                kMin = kMid - halfRatio + 1;
                kMax = kMid + halfRatio;
            }
            if (kMax - kMin != intRatio - 1)
            {
                throw new ApplicationException("kMax - kMin != intRatio-1");
            }
            // exclude shifts that are too far away
            double sa         = Math.Sqrt(va + vb);
            double lowerBound = ma - 5 * sa;
            double upperBound = ma + 5 * sa;
            // find the largest k such that (mb + k*Lb <= lowerBound)
            double kLower = Math.Floor((lowerBound - mb) / b.Period);
            if (kLower > kMin)
            {
                kMin = (int)kLower;
            }
            // find the smallest k such that (mb + k*Lb >= upperBound)
            double kUpper = Math.Ceiling((upperBound - mb) / b.Period);
            if (kUpper < kMax)
            {
                kMax = (int)kUpper;
            }
            if (kMax - kMin > 100)
            {
                throw new ApplicationException("kMax - kMin = " + (kMax - kMin));
            }
            double totalWeight = Double.NegativeInfinity;
            for (int k = kMin; k <= kMax; k++)
            {
                Gaussian bShifted = new Gaussian(mb + k * b.Period, vb);
                Gaussian product  = a.Gaussian * bShifted;
                double   weight   = a.Gaussian.GetLogAverageOf(bShifted);
                if (double.IsNegativeInfinity(totalWeight))
                {
                    Gaussian.SetTo(product);
                    totalWeight = weight;
                }
                else
                {
                    Gaussian.SetToSum(1.0, Gaussian, Math.Exp(weight - totalWeight), product);
                    totalWeight = MMath.LogSumExp(totalWeight, weight);
                }
            }
#endif
            if (double.IsNaN(Gaussian.MeanTimesPrecision))
            {
                throw new ApplicationException("result is nan");
            }
            Normalize();
        }