From 47f9498bf910bd4e8ba12ac98a5ce1155485c3ab Mon Sep 17 00:00:00 2001 From: Ricardo Date: Sun, 20 Dec 2020 12:51:18 +0100 Subject: [PATCH 1/3] Make logsumexp work with inifinite values, matching scipy behavior --- pymc3/math.py | 1 + pymc3/tests/test_math.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/pymc3/math.py b/pymc3/math.py index a8d50f03c6..17f286ceab 100644 --- a/pymc3/math.py +++ b/pymc3/math.py @@ -175,6 +175,7 @@ def tround(*args, **kwargs): def logsumexp(x, axis=None, keepdims=True): # Adapted from https://github.com/Theano/Theano/issues/1563 x_max = tt.max(x, axis=axis, keepdims=True) + x_max = tt.switch(tt.isinf(x_max), 0, x_max) res = tt.log(tt.sum(tt.exp(x - x_max), axis=axis, keepdims=True)) + x_max return res if keepdims else res.squeeze() diff --git a/pymc3/tests/test_math.py b/pymc3/tests/test_math.py index 4c9a3808df..73247a3a7b 100644 --- a/pymc3/tests/test_math.py +++ b/pymc3/tests/test_math.py @@ -15,6 +15,7 @@ import numpy as np import numpy.testing as npt import pytest +from scipy.special import logsumexp as scipy_logsumexp import theano import theano.tensor as tt @@ -31,6 +32,7 @@ log1pexp, logdet, probit, + logsumexp, ) from pymc3.tests.helpers import SeededTest, verify_grad from pymc3.theanof import floatX @@ -207,3 +209,27 @@ def test_expand_packed_triangular(): assert np.all(expand_upper.eval({packed: upper_packed}) == upper) assert np.all(expand_diag_lower.eval({packed: lower_packed}) == floatX(np.diag(vals))) assert np.all(expand_diag_upper.eval({packed: upper_packed}) == floatX(np.diag(vals))) + + +@pytest.mark.parametrize( + "values, axis, keepdims", + [ + (np.array([-4, -2]), None, True), + (np.array([-np.inf, -2]), None, True), + (np.array([-2, np.inf]), None, True), + (np.array([-np.inf, -np.inf]), None, True), + (np.array([np.inf, np.inf]), None, True), + (np.array([-np.inf, np.inf]), None, True), + (np.array([[-np.inf, -np.inf], [-np.inf, -np.inf]]), None, True), + (np.array([[-np.inf, -np.inf], [-np.inf, -np.inf]]), 0, True), + (np.array([[-np.inf, -np.inf], [-np.inf, -np.inf]]), 1, True), + (np.array([[-np.inf, -np.inf], [-np.inf, -np.inf]]), 0, False), + (np.array([[-np.inf, -np.inf], [-np.inf, -np.inf]]), 1, False), + (np.array([[-2, np.inf], [-np.inf, -np.inf]]), 0, True), + ] +) +def test_logsumexp(values, axis, keepdims): + npt.assert_almost_equal( + logsumexp(values, axis=axis, keepdims=keepdims).eval(), + scipy_logsumexp(values, axis=axis, keepdims=keepdims) + ) From 8fdeaf21e5d1d1ac17d96dc3a08f29641407cde0 Mon Sep 17 00:00:00 2001 From: Ricardo Date: Sun, 20 Dec 2020 12:57:03 +0100 Subject: [PATCH 2/3] Run pre-commit --- pymc3/tests/test_math.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pymc3/tests/test_math.py b/pymc3/tests/test_math.py index 73247a3a7b..0f048850b1 100644 --- a/pymc3/tests/test_math.py +++ b/pymc3/tests/test_math.py @@ -15,10 +15,11 @@ import numpy as np import numpy.testing as npt import pytest -from scipy.special import logsumexp as scipy_logsumexp import theano import theano.tensor as tt +from scipy.special import logsumexp as scipy_logsumexp + from pymc3.math import ( LogDet, cartesian, @@ -31,8 +32,8 @@ log1mexp_numpy, log1pexp, logdet, - probit, logsumexp, + probit, ) from pymc3.tests.helpers import SeededTest, verify_grad from pymc3.theanof import floatX @@ -226,10 +227,10 @@ def test_expand_packed_triangular(): (np.array([[-np.inf, -np.inf], [-np.inf, -np.inf]]), 0, False), (np.array([[-np.inf, -np.inf], [-np.inf, -np.inf]]), 1, False), (np.array([[-2, np.inf], [-np.inf, -np.inf]]), 0, True), - ] + ], ) def test_logsumexp(values, axis, keepdims): npt.assert_almost_equal( logsumexp(values, axis=axis, keepdims=keepdims).eval(), - scipy_logsumexp(values, axis=axis, keepdims=keepdims) + scipy_logsumexp(values, axis=axis, keepdims=keepdims), ) From 1caf89b4b60f9880fcbb82fbd670e9016391887d Mon Sep 17 00:00:00 2001 From: Ricardo Date: Sun, 20 Dec 2020 13:03:39 +0100 Subject: [PATCH 3/3] Add note to release_notes --- RELEASE-NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 271af60b57..17cb1d3dff 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -13,7 +13,7 @@ This is the first release to support Python3.9 and to drop Python3.6. - Removed `theanof.set_theano_config` because it illegally touched Theano's privates (see [#4329](https://github.com/pymc-devs/pymc3/pull/4329)). - In `sample_posterior_predictive` the `vars` kwarg was removed in favor of `var_names` (see [#4343](https://github.com/pymc-devs/pymc3/pull/4343)). - The notebook gallery has been moved to https://github.com/pymc-devs/pymc-examples (see [#4348](https://github.com/pymc-devs/pymc3/pull/4348)). - +- `math.logsumexp` now matches `scipy.special.logsumexp` when arrays contain infinite values (see [#4360](https://github.com/pymc-devs/pymc3/pull/4360)). ## PyMC3 3.10.0 (7 December 2020)