/// <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)); } }