diff --git a/pymc3/distributions/bound.py b/pymc3/distributions/bound.py index 11fc9ddafe..ae0ac67354 100644 --- a/pymc3/distributions/bound.py +++ b/pymc3/distributions/bound.py @@ -16,7 +16,6 @@ import numpy as np import theano.tensor as tt -import theano from pymc3.distributions.distribution import ( Distribution, @@ -28,6 +27,8 @@ from pymc3.distributions import transforms from pymc3.distributions.dist_math import bound +from pymc3.theanof import floatX + __all__ = ["Bound"] @@ -148,6 +149,25 @@ def random(self, point=None, size=None): not_broadcast_kwargs={"point": point}, ) + def _distr_parameters_for_repr(self): + return ["lower", "upper"] + + def _distr_name_for_repr(self): + return "Bound" + + def _str_repr(self, **kwargs): + distr_repr = self._wrapped._str_repr(**{**kwargs, "dist": self._wrapped}) + if "formatting" in kwargs and kwargs["formatting"] == "latex": + distr_repr = distr_repr[distr_repr.index(r" \sim") + 6 :] + else: + distr_repr = distr_repr[distr_repr.index(" ~") + 3 :] + self_repr = super()._str_repr(**kwargs) + + if "formatting" in kwargs and kwargs["formatting"] == "latex": + return self_repr + " -- " + distr_repr + else: + return self_repr + "-" + distr_repr + class _DiscreteBounded(_Bounded, Discrete): def __init__(self, distribution, lower, upper, transform="infer", *args, **kwargs): @@ -187,12 +207,10 @@ class _ContinuousBounded(_Bounded, Continuous): """ def __init__(self, distribution, lower, upper, transform="infer", *args, **kwargs): - dtype = kwargs.get("dtype", theano.config.floatX) - if lower is not None: - lower = tt.as_tensor_variable(lower).astype(dtype) + lower = tt.as_tensor_variable(floatX(lower)) if upper is not None: - upper = tt.as_tensor_variable(upper).astype(dtype) + upper = tt.as_tensor_variable(floatX(upper)) if transform == "infer": if lower is None and upper is None: diff --git a/pymc3/tests/test_distributions.py b/pymc3/tests/test_distributions.py index 0ee4e00dd5..20349d104f 100644 --- a/pymc3/tests/test_distributions.py +++ b/pymc3/tests/test_distributions.py @@ -1779,9 +1779,12 @@ def setup_class(self): # Expected value of outcome mu = Deterministic("mu", floatX(alpha + tt.dot(X, b))) + # add a bounded variable as well + bound_var = Bound(Normal, lower=1.0)("bound_var", mu=0, sigma=10) + # Likelihood (sampling distribution) of observations Y_obs = Normal("Y_obs", mu=mu, sigma=sigma, observed=Y) - self.distributions = [alpha, sigma, mu, b, Z, Y_obs] + self.distributions = [alpha, sigma, mu, b, Z, Y_obs, bound_var] self.expected_latex = ( r"$\text{alpha} \sim \text{Normal}(\mathit{mu}=0.0,~\mathit{sigma}=10.0)$", r"$\text{sigma} \sim \text{HalfNormal}(\mathit{sigma}=1.0)$", @@ -1789,6 +1792,7 @@ def setup_class(self): r"$\text{beta} \sim \text{Normal}(\mathit{mu}=0.0,~\mathit{sigma}=10.0)$", r"$\text{Z} \sim \text{MvNormal}(\mathit{mu}=array,~\mathit{chol_cov}=array)$", r"$\text{Y_obs} \sim \text{Normal}(\mathit{mu}=\text{mu},~\mathit{sigma}=f(\text{sigma}))$", + r"$\text{bound_var} \sim \text{Bound}(\mathit{lower}=1.0,~\mathit{upper}=\text{None})$ -- \text{Normal}(\mathit{mu}=0.0,~\mathit{sigma}=10.0)$", ) self.expected_str = ( r"alpha ~ Normal(mu=0.0, sigma=10.0)", @@ -1797,6 +1801,7 @@ def setup_class(self): r"beta ~ Normal(mu=0.0, sigma=10.0)", r"Z ~ MvNormal(mu=array, chol_cov=array)", r"Y_obs ~ Normal(mu=mu, sigma=f(sigma))", + r"bound_var ~ Bound(lower=1.0, upper=None)-Normal(mu=0.0, sigma=10.0)", ) def test__repr_latex_(self): diff --git a/pymc3/util.py b/pymc3/util.py index 95c530825a..ec624ab344 100644 --- a/pymc3/util.py +++ b/pymc3/util.py @@ -128,8 +128,8 @@ def get_default_varnames(var_iterator, include_transformed): def get_repr_for_variable(variable, formatting="plain"): """Build a human-readable string representation for a variable.""" - name = variable.name - if name is None: + name = variable.name if variable is not None else None + if name is None and variable is not None: if hasattr(variable, "get_parents"): try: names = [