diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index c578820dc9..9f8b5f82e0 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -13,6 +13,7 @@ jobs: floatx: [float32, float64] test-subset: - | + --ignore=pymc3/tests/test_backports.py --ignore=pymc3/tests/test_dist_math.py --ignore=pymc3/tests/test_distribution_defaults.py --ignore=pymc3/tests/test_distributions.py @@ -42,6 +43,7 @@ jobs: pymc3/tests/test_shared.py pymc3/tests/test_smc.py - | + pymc3/tests/test_backports.py pymc3/tests/test_examples.py pymc3/tests/test_mixture.py pymc3/tests/test_ode.py diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 7f67343eea..98b349cc03 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,9 @@ # Release Notes +## PyMC 3.11.5 (TBD) +### Backports ++ The `pm.logp(rv, x)` syntax is now available and recommended to make your model code `v4`-ready. Note that this backport is just an alias and much less capable than what's available with `pymc >=4` (see [#5083](https://github.com/pymc-devs/pymc/pulls/5083)). + ## PyMC3 3.11.4 (20 August 2021) ### New Features diff --git a/docs/source/developer_guide.rst b/docs/source/developer_guide.rst index 64463cd5b4..5c2c89e663 100644 --- a/docs/source/developer_guide.rst +++ b/docs/source/developer_guide.rst @@ -158,7 +158,7 @@ explicit about the conversion. For example: with pm.Model() as model: z = pm.Normal('z', mu=0., sigma=5.) # ==> pymc3.model.FreeRV, or theano.tensor with logp x = pm.Normal('x', mu=z, sigma=1., observed=5.) # ==> pymc3.model.ObservedRV, also has logp properties - x.logp({'z': 2.5}) # ==> -4.0439386 + pm.logp(x, {'z': 2.5}) # ==> -4.0439386 model.logp({'z': 2.5}) # ==> -6.6973152 **TFP** diff --git a/pymc3/__init__.py b/pymc3/__init__.py index 684efd5c71..131bd5b394 100644 --- a/pymc3/__init__.py +++ b/pymc3/__init__.py @@ -82,6 +82,7 @@ def _hotfix_theano_printing(): from pymc3 import gp, ode, sampling from pymc3.backends import load_trace, save_trace from pymc3.backends.tracetab import * +from pymc3.backports import logp from pymc3.blocking import * from pymc3.data import * from pymc3.distributions import * diff --git a/pymc3/backports.py b/pymc3/backports.py new file mode 100644 index 0000000000..cc56f5b332 --- /dev/null +++ b/pymc3/backports.py @@ -0,0 +1,43 @@ +# Copyright 2021 The PyMC Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Union + +import numpy as np + +from theano.tensor import TensorVariable + +from pymc3.distributions.distribution import Distribution +from pymc3.model import Factor + + +def logp( + rv: Union[Factor, Distribution], value: Union[TensorVariable, np.ndarray] +) -> Union[TensorVariable, np.ndarray]: + """ + Calculate log-probability of a distribution at specified value. + + This function is a limited functionality backported version of PyMC >=4.0 like capabilities. + + Parameters + ---------- + value : numeric + Value(s) for which log-probability is calculated. If the log-probabilities for multiple + values are desired the values must be provided in a numpy array or theano tensor + + Returns + ------- + logp : TensorVariable or np.ndarray + """ + return rv.logp(value) diff --git a/pymc3/tests/test_backports.py b/pymc3/tests/test_backports.py new file mode 100644 index 0000000000..172f2418ff --- /dev/null +++ b/pymc3/tests/test_backports.py @@ -0,0 +1,32 @@ +# Copyright 2021 The PyMC Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +import pymc3 as pm + + +class TestLogpSyntax: + def test_equivalence(self): + with pm.Model(): + rv = pm.Normal("n") + input = {"n": 2} + np.testing.assert_array_equal(rv.logp(input), pm.logp(rv, input)) + + def test_equivalence_dist(self): + rv = pm.Normal.dist() + assert rv.logp(2).eval() == pm.logp(rv, 2).eval() + np.testing.assert_array_equal( + rv.logp(np.arange(3)).eval(), pm.logp(rv, np.arange(3)).eval() + )