Skip to content

Commit 07f09ad

Browse files
committed
Add multiple value test for logcdf.
Add more informative comment for Gamma and InverseGamma hack. Update Release note.
1 parent 9289599 commit 07f09ad

File tree

3 files changed

+56
-17
lines changed

3 files changed

+56
-17
lines changed

RELEASE-NOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ It also brings some dreadfully awaited fixes, so be sure to go through the chang
2929
- Fixed mathematical formulation in `MvStudentT` random method. (see [#4359](https://github.com/pymc-devs/pymc3/pull/4359))
3030
- Fix issue in `logp` method of `HyperGeometric`. It now returns `-inf` for invalid parameters (see [4367](https://github.com/pymc-devs/pymc3/pull/4367))
3131
- Fixed `MatrixNormal` random method to work with parameters as random variables. (see [#4368](https://github.com/pymc-devs/pymc3/pull/4368))
32+
- Fix issue in `logcdf` methods of `Uniform`, `HalfNormal`, `Gamma` and `InverseGamma`. These functions now return correct values when evaluated with invalid parameters or values (see [4393](https://github.com/pymc-devs/pymc3/pull/4393))
3233

3334
## PyMC3 3.10.0 (7 December 2020)
3435

pymc3/distributions/continuous.py

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def logcdf(self, value):
290290
upper = self.upper
291291

292292
return tt.switch(
293-
tt.or_(tt.lt(value, lower), tt.lt(upper, lower)),
293+
tt.lt(value, lower) | tt.lt(upper, lower),
294294
-np.inf,
295295
tt.switch(
296296
tt.lt(value, upper),
@@ -1307,13 +1307,26 @@ def logcdf(self, value):
13071307
-------
13081308
TensorVariable
13091309
"""
1310-
value = floatX(tt.as_tensor(value))
1311-
a = floatX(tt.as_tensor(self.alpha))
1312-
b = floatX(tt.as_tensor(self.beta))
1313-
return tt.switch(
1314-
tt.le(value, 0),
1315-
-np.inf,
1316-
tt.switch(tt.ge(value, 1), 0, tt.log(incomplete_beta(a, b, value))),
1310+
# incomplete_beta function can only handle scalar values (see #4342)
1311+
if np.ndim(value):
1312+
raise TypeError(
1313+
"Beta.logcdf expects a scalar value but received a {}-dimensional object.".format(
1314+
np.ndim(value)
1315+
)
1316+
)
1317+
1318+
a = self.alpha
1319+
b = self.beta
1320+
1321+
return bound(
1322+
tt.switch(
1323+
tt.lt(value, 1),
1324+
tt.log(incomplete_beta(a, b, value)),
1325+
0
1326+
),
1327+
0 <= value,
1328+
0 < a,
1329+
0 < b,
13171330
)
13181331

13191332
def _distr_parameters_for_repr(self):
@@ -1965,13 +1978,28 @@ def logcdf(self, value):
19651978
-------
19661979
TensorVariable
19671980
"""
1981+
# incomplete_beta function can only handle scalar values (see #4342)
1982+
if np.ndim(value):
1983+
raise TypeError(
1984+
"StudentT.logcdf expects a scalar value but received a {}-dimensional object.".format(
1985+
np.ndim(value)
1986+
)
1987+
)
1988+
19681989
nu = self.nu
19691990
mu = self.mu
19701991
sigma = self.sigma
1992+
lam = self.lam
19711993
t = (value - mu) / sigma
19721994
sqrt_t2_nu = tt.sqrt(t ** 2 + nu)
19731995
x = (t + sqrt_t2_nu) / (2.0 * sqrt_t2_nu)
1974-
return tt.log(incomplete_beta(nu / 2.0, nu / 2.0, x))
1996+
1997+
return bound(
1998+
tt.log(incomplete_beta(nu / 2.0, nu / 2.0, x)),
1999+
0 < nu,
2000+
0 < sigma,
2001+
0 < lam,
2002+
)
19752003

19762004

19772005
class Pareto(Continuous):
@@ -2481,16 +2509,16 @@ def logcdf(self, value):
24812509
"""
24822510
alpha = self.alpha
24832511
beta = self.beta
2484-
# To avoid issue with #4340
2512+
# To avoid gammainc C-assertion when given invalid values (#4340)
24852513
safe_alpha = tt.switch(tt.lt(alpha, 0), 0, alpha)
24862514
safe_beta = tt.switch(tt.lt(beta, 0), 0, beta)
24872515
safe_value = tt.switch(tt.lt(value, 0), 0, value)
24882516

24892517
return bound(
24902518
tt.log(tt.gammainc(safe_alpha, safe_beta * safe_value)),
2491-
value >= 0,
2492-
alpha > 0,
2493-
beta > 0,
2519+
0 <= value,
2520+
0 < alpha,
2521+
0 < beta,
24942522
)
24952523

24962524
def _distr_parameters_for_repr(self):
@@ -2655,16 +2683,16 @@ def logcdf(self, value):
26552683
"""
26562684
alpha = self.alpha
26572685
beta = self.beta
2658-
# To avoid issue with #4340
2686+
# To avoid gammaincc C-assertion when given invalid values (#4340)
26592687
safe_alpha = tt.switch(tt.lt(alpha, 0), 0, alpha)
26602688
safe_beta = tt.switch(tt.lt(beta, 0), 0, beta)
26612689
safe_value = tt.switch(tt.lt(value, 0), 0, value)
26622690

26632691
return bound(
26642692
tt.log(tt.gammaincc(safe_alpha, safe_beta / safe_value)),
2665-
value >= 0,
2666-
alpha > 0,
2667-
beta > 0,
2693+
0 <= value,
2694+
0 < alpha,
2695+
0 < beta,
26682696
)
26692697

26702698

@@ -3532,6 +3560,7 @@ def logcdf(self, value):
35323560
l = self.lower
35333561
u = self.upper
35343562
c = self.c
3563+
35353564
return tt.switch(
35363565
tt.le(value, l),
35373566
-np.inf,

pymc3/tests/test_distributions.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,15 @@ def check_selfconsistency_discrete_logcdf(
615615
err_msg=str(above_domain),
616616
)
617617

618+
# TODO: Test that logcdf wih invalid parameters is always evaluated to -inf
619+
620+
# Test that method works with multiple values or raises informative TypeError
621+
try:
622+
dist.logcdf(np.array([value, value])).tag.test_value
623+
except TypeError as err:
624+
if not str(err).endswith(".logcdf expects a scalar value but received a 1-dimensional object."):
625+
raise
626+
618627
def check_int_to_1(self, model, value, domain, paramdomains):
619628
pdf = model.fastfn(exp(model.logpt))
620629
for pt in product(paramdomains, n_samples=10):

0 commit comments

Comments
 (0)