diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 4cbb2a6c613..8f415b0932d 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -16,6 +16,7 @@ It also brings some dreadfully awaited fixes, so be sure to go through the chang - Removed `theanof.set_theano_config` because it illegally changed Theano's internal state (see [#4329](https://github.com/pymc-devs/pymc3/pull/4329)). ### New Features +- Option to `disable_bounds_check=True` when instantiating `pymc3.Model()` for faster sampling for models that cannot violate boundary constraints (which are most of them; see [#4377](https://github.com/pymc-devs/pymc3/pull/4377)). - `OrderedProbit` distribution added (see [#4232](https://github.com/pymc-devs/pymc3/pull/4232)). - `plot_posterior_predictive_glm` now works with `arviz.InferenceData` as well (see [#4234](https://github.com/pymc-devs/pymc3/pull/4234)) diff --git a/pymc3/distributions/dist_math.py b/pymc3/distributions/dist_math.py index e3020007356..dc67f89652a 100644 --- a/pymc3/distributions/dist_math.py +++ b/pymc3/distributions/dist_math.py @@ -33,6 +33,7 @@ from pymc3.distributions.shape_utils import to_tuple from pymc3.distributions.special import gammaln +from pymc3.model import modelcontext from pymc3.theanof import floatX f = floatX @@ -67,6 +68,15 @@ def bound(logp, *conditions, **kwargs): ------- logp with elements set to -inf where any condition is False """ + + # If called inside a model context, see if bounds check is disabled + try: + model = modelcontext(kwargs.get("model")) + if model.disable_bounds_check: + return logp + except TypeError: # No model found + pass + broadcast_conditions = kwargs.get("broadcast_conditions", True) if broadcast_conditions: diff --git a/pymc3/model.py b/pymc3/model.py index 14d72442780..6a6e159b6ca 100644 --- a/pymc3/model.py +++ b/pymc3/model.py @@ -809,6 +809,11 @@ class Model(Factor, WithMemoization, metaclass=ContextMeta): temporarily in the model context. See the documentation of theano for a complete list. Set config key ``compute_test_value`` to `raise` if it is None. + disable_bounds_check: bool + Disable checks that ensure that input parameters to distributions + are in a valid range. If your model is built in a way where you + know your parameters can only take on valid values you can disable + this for increased speed. Examples -------- @@ -895,11 +900,14 @@ def __new__(cls, *args, **kwargs): instance._theano_config = theano_config return instance - def __init__(self, name="", model=None, theano_config=None, coords=None): + def __init__( + self, name="", model=None, theano_config=None, coords=None, disable_bounds_check=False + ): self.name = name self.coords = {} self.RV_dims = {} self.add_coords(coords) + self.disable_bounds_check = disable_bounds_check if self.parent is not None: self.named_vars = treedict(parent=self.parent.named_vars) diff --git a/pymc3/tests/test_dist_math.py b/pymc3/tests/test_dist_math.py index 9aa9dbf16c8..359cc8d656c 100644 --- a/pymc3/tests/test_dist_math.py +++ b/pymc3/tests/test_dist_math.py @@ -60,6 +60,13 @@ def test_bound(): assert np.prod(bound(logp, cond).eval()) == -np.inf +def test_bound_disabled(): + with pm.Model(disable_bounds_check=True): + logp = tt.ones(3) + cond = np.array([1, 0, 1]) + assert np.all(bound(logp, cond).eval() == logp.eval()) + + def test_alltrue_scalar(): assert alltrue_scalar([]).eval() assert alltrue_scalar([True]).eval()