def test_log1mexp(): vals = np.array([-1, 0, 1e-20, 1e-4, 10, 100, 1e20]) vals_ = vals.copy() # import mpmath # mpmath.mp.dps = 1000 # [float(mpmath.log(1 - mpmath.exp(-x))) for x in vals] expected = np.array([ np.nan, -np.inf, -46.051701859880914, -9.210390371559516, -4.540096037048921e-05, -3.720075976020836e-44, 0.0, ]) actual = at.log1mexp(-vals).eval() npt.assert_allclose(actual, expected) actual_ = log1mexp_numpy(-vals, negative_input=True) npt.assert_allclose(actual_, expected) # Check that input was not changed in place npt.assert_allclose(vals, vals_)
def log1mexp(x, *, negative_input=False): r"""Return log(1 - exp(-x)). This function is numerically more stable than the naive approach. For details, see https://cran.r-project.org/web/packages/Rmpfr/vignettes/log1mexp-note.pdf References ---------- .. [Machler2012] Martin Mächler (2012). "Accurately computing `\log(1-\exp(- \mid a \mid))` Assessed by the Rmpfr package" """ if not negative_input: warnings.warn( "pymc.math.log1mexp will expect a negative input in a future " "version of PyMC.\n To suppress this warning set `negative_input=True`", FutureWarning, stacklevel=2, ) x = -x return at.log1mexp(x)
def logdiffexp(a, b): """log(exp(a) - exp(b))""" return a + at.log1mexp(b - a)
def test_log1mexp_numpy_integer_input(): assert np.isclose(log1mexp_numpy(-2, negative_input=True), at.log1mexp(-2).eval())