diff --git a/pymc3/distributions/continuous.py b/pymc3/distributions/continuous.py index 6b05961191..43ae2f29b4 100644 --- a/pymc3/distributions/continuous.py +++ b/pymc3/distributions/continuous.py @@ -571,15 +571,17 @@ def __init__(self, mu=0, sigma=None, tau=None, lower=None, upper=None, tau, sigma = get_tau_sigma(tau=tau, sigma=sigma) self.sigma = self.sd = tt.as_tensor_variable(sigma) self.tau = tt.as_tensor_variable(tau) - self.lower = tt.as_tensor_variable(floatX(lower)) if lower is not None else lower - self.upper = tt.as_tensor_variable(floatX(upper)) if upper is not None else upper + self.lower_check = tt.as_tensor_variable(floatX(lower)) if lower is not None else lower + self.upper_check = tt.as_tensor_variable(floatX(upper)) if upper is not None else upper + self.lower = tt.as_tensor_variable(floatX(lower)) if lower is not None else tt.as_tensor_variable(floatX(-np.inf)) + self.upper = tt.as_tensor_variable(floatX(upper)) if upper is not None else tt.as_tensor_variable(floatX(np.inf)) self.mu = tt.as_tensor_variable(floatX(mu)) - if self.lower is None and self.upper is None: + if self.lower_check is None and self.upper_check is None: self._defaultval = mu - elif self.lower is None and self.upper is not None: + elif self.lower_check is None and self.upper_check is not None: self._defaultval = self.upper - 1. - elif self.lower is not None and self.upper is None: + elif self.lower_check is not None and self.upper_check is None: self._defaultval = self.lower + 1. else: self._defaultval = (self.lower + self.upper) / 2 @@ -639,19 +641,19 @@ def logp(self, value): logp = Normal.dist(mu=mu, sigma=sigma).logp(value) - norm bounds = [sigma > 0] - if self.lower is not None: + if self.lower_check is not None: bounds.append(value >= self.lower) - if self.upper is not None: + if self.upper_check is not None: bounds.append(value <= self.upper) return bound(logp, *bounds) def _normalization(self): mu, sigma = self.mu, self.sigma - if self.lower is None and self.upper is None: + if self.lower_check is None and self.upper_check is None: return 0. - if self.lower is not None and self.upper is not None: + if self.lower_check is not None and self.upper_check is not None: lcdf_a = normal_lcdf(mu, sigma, self.lower) lcdf_b = normal_lcdf(mu, sigma, self.upper) lsf_a = normal_lccdf(mu, sigma, self.lower) @@ -663,7 +665,7 @@ def _normalization(self): logdiffexp(lcdf_b, lcdf_a), ) - if self.lower is not None: + if self.lower_check is not None: return normal_lccdf(mu, sigma, self.lower) else: return normal_lcdf(mu, sigma, self.upper) diff --git a/pymc3/tests/test_distributions_random.py b/pymc3/tests/test_distributions_random.py index 6fa49ca843..55ce66bce9 100644 --- a/pymc3/tests/test_distributions_random.py +++ b/pymc3/tests/test_distributions_random.py @@ -259,6 +259,14 @@ class TestTruncatedNormal(BaseTestCases.BaseTestCase): distribution = pm.TruncatedNormal params = {'mu': 0., 'tau': 1., 'lower':-0.5, 'upper':0.5} +class TestTruncatedNormalLower(BaseTestCases.BaseTestCase): + distribution = pm.TruncatedNormal + params = {'mu': 0., 'tau': 1., 'lower':-0.5} + +class TestTruncatedNormalUpper(BaseTestCases.BaseTestCase): + distribution = pm.TruncatedNormal + params = {'mu': 0., 'tau': 1., 'upper':0.5} + class TestSkewNormal(BaseTestCases.BaseTestCase): distribution = pm.SkewNormal params = {'mu': 0., 'sigma': 1., 'alpha': 5.} @@ -475,6 +483,18 @@ def ref_rand(size, mu, sigma, lower, upper): pymc3_random(pm.TruncatedNormal, {'mu': R, 'sigma': Rplusbig, 'lower':-Rplusbig, 'upper':Rplusbig}, ref_rand=ref_rand) + def test_truncated_normal_lower(self): + def ref_rand(size, mu, sigma, lower): + return st.truncnorm.rvs((lower-mu)/sigma, np.inf, size=size, loc=mu, scale=sigma) + pymc3_random(pm.TruncatedNormal, {'mu': R, 'sigma': Rplusbig, 'lower':-Rplusbig}, + ref_rand=ref_rand) + + def test_truncated_normal_upper(self): + def ref_rand(size, mu, sigma, upper): + return st.truncnorm.rvs(-np.inf, (upper-mu)/sigma, size=size, loc=mu, scale=sigma) + pymc3_random(pm.TruncatedNormal, {'mu': R, 'sigma': Rplusbig, 'upper':Rplusbig}, + ref_rand=ref_rand) + def test_skew_normal(self): def ref_rand(size, alpha, mu, sigma): return st.skewnorm.rvs(size=size, a=alpha, loc=mu, scale=sigma)