Beispiel #1
0
        /// <summary>
        /// Computes the natural logarithm of the cumulative bivariate normal distribution.
        /// </summary>
        /// <param name="x">First upper limit.</param>
        /// <param name="y">Second upper limit.</param>
        /// <param name="r">Correlation coefficient.</param>
        /// <returns><c>ln(phi(x,y,r))</c></returns>
        public static double NormalCdfLn(double x, double y, double r)
        {
            if (Double.IsNegativeInfinity(x) || Double.IsNegativeInfinity(y))
            {
                return(Double.NegativeInfinity);
            }
            else if (Double.IsPositiveInfinity(x))
            {
                return(NormalCdfLn(y));
            }
            else if (Double.IsPositiveInfinity(y))
            {
                return(NormalCdfLn(x));
            }
            else if (r == 0)
            {
                return(NormalCdfLn(x) + NormalCdfLn(y));
            }
            else if (r == 1)
            {
                return(NormalCdfLn(Math.Min(x, y)));
            }
            else if (r == -1)
            {
                if (x > 0)
                {
                    if (y > 0)
                    {
                        // 1-NormalCdf(-x) + 1-NormalCdf(-y)-1 = 1 - NormalCdf(-x) - NormalCdf(-y)
                        return(Log1MinusExp(LogSumExp(NormalCdfLn(-x), NormalCdfLn(-y))));
                    }
                    else
                    {
                        // 1-NormalCdf(-x) + NormalCdf(y) - 1 = NormalCdf(y) - NormalCdf(-x)
                        double nclx = NormalCdfLn(-x);
                        double diff = NormalCdfLn(y) - nclx;
                        if (diff < 0)
                        {
                            return(double.NegativeInfinity);
                        }
                        return(LogExpMinus1(diff) + nclx);
                    }
                }
                else
                {
                    if (y > 0)
                    {
                        // NormalCdf(x) - NormalCdf(-y)
                        if (x < -y)
                        {
                            return(double.NegativeInfinity);
                        }
                        double ncly = NormalCdfLn(-y);
                        double diff = NormalCdfLn(x) - ncly;
                        if (diff < 0)
                        {
                            return(double.NegativeInfinity);
                        }
                        return(LogExpMinus1(diff) + ncly);
                    }
                    else
                    {
                        // x < 0 and y < 0
                        return(double.NegativeInfinity);
                    }
                }
            }
            // at this point, both x and y are finite.
            // swap to ensure |x| > |y|
            if (Math.Abs(y) > Math.Abs(x))
            {
                double t = x;
                x = y;
                y = t;
            }
            double logOffset = double.NegativeInfinity;
            double scale     = 1;

            // ensure x <= 0
            if (x > 0)
            {
                // phi(x,y,r) = phi(inf,y,r) - phi(-x,y,-r)
                logOffset = MMath.NormalCdfLn(y);
                scale     = -1;
                x         = -x;
                r         = -r;
            }
            // ensure r <= 0
            if (r > 0)
            {
                // phi(x,y,r) = phi(x,inf,r) - phi(x,-y,-r)
                double logOffset2 = MMath.NormalCdfLn(x);
                if (scale == 1)
                {
                    logOffset = logOffset2;
                }
                else
                {
                    // the difference here must always be positive since y > -x
                    // offset -= offset2;
                    // logOffset = log(exp(logOffset) - exp(logOffset2))
                    logOffset = MMath.LogDifferenceOfExp(logOffset, logOffset2);
                }
                scale *= -1;
                y      = -y;
                r      = -r;
            }
            double omr2 = (1 - r) * (1 + r); // more accurate than 1-r*r
            double ymrx = (y - r * x) / Math.Sqrt(omr2);
            double exponent;
            double result    = NormalCdf_Helper(x, y, r, omr2, ymrx, out exponent);
            double logResult = exponent + Math.Log(result);

            if (scale == -1)
            {
                return(MMath.LogDifferenceOfExp(logOffset, logResult));
            }
            else
            {
                return(MMath.LogSumExp(logOffset, logResult));
            }
        }