From 62c6326220d2f68f37cff442f7c87961586e95b0 Mon Sep 17 00:00:00 2001 From: Ricardo Date: Thu, 21 Jan 2021 09:50:42 +0100 Subject: [PATCH 1/3] Evaluate only desired function in log1mexp_numpy --- pymc3/math.py | 7 ++++++- pymc3/tests/test_math.py | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pymc3/math.py b/pymc3/math.py index fc2a55823c..7516c26f13 100644 --- a/pymc3/math.py +++ b/pymc3/math.py @@ -243,7 +243,12 @@ def log1mexp_numpy(x): For details, see https://cran.r-project.org/web/packages/Rmpfr/vignettes/log1mexp-note.pdf """ - return np.where(x < 0.6931471805599453, np.log(-np.expm1(-x)), np.log1p(-np.exp(-x))) + x = np.asarray(x) + mask = x < 0.6931471805599453 + x[mask] = np.log(-np.expm1(-x[mask])) + mask = ~mask + x[mask] = np.log1p(-np.exp(-x[mask])) + return x def flatten_list(tensors): diff --git a/pymc3/tests/test_math.py b/pymc3/tests/test_math.py index 74a782555e..005bf8bdf7 100644 --- a/pymc3/tests/test_math.py +++ b/pymc3/tests/test_math.py @@ -153,6 +153,13 @@ def test_log1mexp(): npt.assert_allclose(actual_, expected) +def test_log1mexp_numpy_no_warning(): + """Assert RuntimeWarning is not raised for very small numbers""" + with pytest.warns(None) as record: + log1mexp_numpy(1e-25) + assert not record + + class TestLogDet(SeededTest): def setup_method(self): super().setup_method() From 695c23e445e9f25480685748c66474c5acda0c4a Mon Sep 17 00:00:00 2001 From: Ricardo Date: Thu, 21 Jan 2021 18:32:53 +0100 Subject: [PATCH 2/3] Do not change input inplace --- pymc3/math.py | 9 +++++---- pymc3/tests/test_math.py | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pymc3/math.py b/pymc3/math.py index 7516c26f13..aff54d13b7 100644 --- a/pymc3/math.py +++ b/pymc3/math.py @@ -244,11 +244,12 @@ def log1mexp_numpy(x): https://cran.r-project.org/web/packages/Rmpfr/vignettes/log1mexp-note.pdf """ x = np.asarray(x) - mask = x < 0.6931471805599453 - x[mask] = np.log(-np.expm1(-x[mask])) + out = np.empty_like(x) + mask = x < 0.6931471805599453 # log(2) + out[mask] = np.log(-np.expm1(-x[mask])) mask = ~mask - x[mask] = np.log1p(-np.exp(-x[mask])) - return x + out[mask] = np.log1p(-np.exp(-x[mask])) + return out def flatten_list(tensors): diff --git a/pymc3/tests/test_math.py b/pymc3/tests/test_math.py index 005bf8bdf7..b31319021f 100644 --- a/pymc3/tests/test_math.py +++ b/pymc3/tests/test_math.py @@ -133,6 +133,7 @@ def test_log1pexp(): 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] @@ -151,6 +152,8 @@ def test_log1mexp(): npt.assert_allclose(actual, expected) actual_ = log1mexp_numpy(vals) npt.assert_allclose(actual_, expected) + # Check that input was not changed in place + npt.assert_allclose(vals, vals_) def test_log1mexp_numpy_no_warning(): From c712fdb022a6b4369de7b94dec9558e98c5c9de7 Mon Sep 17 00:00:00 2001 From: Ricardo Date: Fri, 22 Jan 2021 18:13:36 +0100 Subject: [PATCH 3/3] Add release note --- RELEASE-NOTES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index b6128b2e39..7f4193a4d6 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,14 @@ # Release Notes +## PyMC3 vNext (on deck) + +### Breaking Changes + +### New Features + +### Maintenance +- `math.log1mexp_numpy` no longer raises RuntimeWarning when given very small inputs. These were commonly observed during NUTS sampling (see [#4428](https://github.com/pymc-devs/pymc3/pull/4428)). + ## PyMC3 3.11.0 (21 January 2021) This release breaks some APIs w.r.t. `3.10.0`. It also brings some dreadfully awaited fixes, so be sure to go through the (breaking) changes below.