def normal_lccdf(mu, sigma, x): z = (x - mu) / sigma return aet.switch( aet.gt(z, 1.0), aet.log(aet.erfcx(z / aet.sqrt(2.0)) / 2.0) - aet.sqr(z) / 2.0, aet.log1p(-aet.erfc(-z / aet.sqrt(2.0)) / 2.0), )
def normal_lcdf(mu, sigma, x): """Compute the log of the cumulative density function of the normal.""" z = (x - mu) / sigma return aet.switch( aet.lt(z, -1.0), aet.log(aet.erfcx(-z / aet.sqrt(2.0)) / 2.0) - aet.sqr(z) / 2.0, aet.log1p(-aet.erfc(z / aet.sqrt(2.0)) / 2.0), )
def log_diff_normal_cdf(mu, sigma, x, y): """ Compute :math:`\\log(\\Phi(\frac{x - \\mu}{\\sigma}) - \\Phi(\frac{y - \\mu}{\\sigma}))` safely in log space. Parameters ---------- mu: float mean sigma: float std x: float y: float must be strictly less than x. Returns ------- log (\\Phi(x) - \\Phi(y)) """ x = (x - mu) / sigma / aet.sqrt(2.0) y = (y - mu) / sigma / aet.sqrt(2.0) # To stabilize the computation, consider these three regions: # 1) x > y > 0 => Use erf(x) = 1 - e^{-x^2} erfcx(x) and erf(y) =1 - e^{-y^2} erfcx(y) # 2) 0 > x > y => Use erf(x) = e^{-x^2} erfcx(-x) and erf(y) = e^{-y^2} erfcx(-y) # 3) x > 0 > y => Naive formula log( (erf(x) - erf(y)) / 2 ) works fine. return aet.log(0.5) + aet.switch( aet.gt(y, 0), -aet.square(y) + aet.log( aet.erfcx(y) - aet.exp(aet.square(y) - aet.square(x)) * aet.erfcx(x)), aet.switch( aet.lt(x, 0), # 0 > x > y -aet.square(x) + aet.log( aet.erfcx(-x) - aet.exp(aet.square(x) - aet.square(y)) * aet.erfcx(-y)), aet.log(aet.erf(x) - aet.erf(y)), # x >0 > y ), )