From bdd9a65ab00ee879fa43da019462c9c2180523bd Mon Sep 17 00:00:00 2001 From: amyoshino Date: Fri, 4 Aug 2023 19:03:20 -0400 Subject: [PATCH 1/2] Add icdf functions for Beta, Gamma, Chisquared and Students distributions removing unnecessary parameterization test on studentT test removing unnecessary test on chisquared test --- pymc/distributions/continuous.py | 37 +++++++++++++++++++++++++- tests/distributions/test_continuous.py | 21 +++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/pymc/distributions/continuous.py b/pymc/distributions/continuous.py index aa722787da..c099a9d6a5 100644 --- a/pymc/distributions/continuous.py +++ b/pymc/distributions/continuous.py @@ -33,7 +33,7 @@ from pytensor.raise_op import Assert from pytensor.tensor import gammaln from pytensor.tensor.extra_ops import broadcast_shape -from pytensor.tensor.math import tanh +from pytensor.tensor.math import betaincinv, gammaincinv, tanh from pytensor.tensor.random.basic import ( BetaRV, _gamma, @@ -1227,6 +1227,16 @@ def logcdf(value, alpha, beta): msg="alpha > 0, beta > 0", ) + def icdf(value, alpha, beta): + res = betaincinv(alpha, beta, value) + res = check_icdf_value(res, value) + return check_icdf_parameters( + res, + alpha > 0, + beta > 0, + msg="alpha > 0, beta > 0", + ) + class KumaraswamyRV(RandomVariable): name = "kumaraswamy" @@ -1872,6 +1882,21 @@ def logcdf(value, nu, mu, sigma): msg="nu > 0, sigma > 0", ) + def icdf(value, nu, mu, sigma): + res = pt.switch( + pt.lt(value, 0.5), + -pt.sqrt(nu) * pt.sqrt((1.0 / betaincinv(nu * 0.5, 0.5, 2.0 * value)) - 1.0), + pt.sqrt(nu) * pt.sqrt((1.0 / betaincinv(nu * 0.5, 0.5, 2.0 * (1 - value))) - 1.0), + ) + res = mu + res * sigma + res = check_icdf_value(res, value) + return check_icdf_parameters( + res, + nu > 0, + sigma > 0, + msg="nu > 0, sigma > 0", + ) + class Pareto(BoundedContinuous): r""" @@ -2272,6 +2297,16 @@ def logcdf(value, alpha, scale): ) return check_parameters(res, 0 < alpha, 0 < beta, msg="alpha > 0, beta > 0") + def icdf(value, alpha, scale): + res = scale * gammaincinv(alpha, value) + res = check_icdf_value(res, value) + return check_icdf_parameters( + res, + alpha > 0, + scale > 0, + msg="alpha > 0, beta > 0", + ) + class InverseGamma(PositiveContinuous): r""" diff --git a/tests/distributions/test_continuous.py b/tests/distributions/test_continuous.py index c8119d1482..31809720a5 100644 --- a/tests/distributions/test_continuous.py +++ b/tests/distributions/test_continuous.py @@ -411,6 +411,13 @@ def test_beta_logcdf(self): lambda value, alpha, beta: st.beta.logcdf(value, alpha, beta), ) + def test_beta_icdf(self): + check_icdf( + pm.Beta, + {"alpha": Rplus, "beta": Rplus}, + lambda q, alpha, beta: st.beta.ppf(q, alpha, beta), + ) + def test_kumaraswamy(self): # Scipy does not have a built-in Kumaraswamy def scipy_log_pdf(value, a, b): @@ -557,6 +564,13 @@ def test_studentt_logcdf(self): lambda value, nu, mu, sigma: st.t.logcdf(value, nu, mu, sigma), ) + def test_studentt_icdf(self): + check_icdf( + pm.StudentT, + {"nu": Rplusbig, "mu": R, "sigma": Rplusbig}, + lambda q, nu, mu, sigma: st.t.ppf(q, nu, mu, sigma), + ) + def test_cauchy(self): check_logp( pm.Cauchy, @@ -623,6 +637,13 @@ def test_gamma_logcdf(self): lambda value, alpha, beta: st.gamma.logcdf(value, alpha, scale=1.0 / beta), ) + def test_gamma_icdf(self): + check_icdf( + pm.Gamma, + {"alpha": Rplusbig, "beta": Rplusbig}, + lambda q, alpha, beta: st.gamma.ppf(q, alpha, scale=1.0 / beta), + ) + def test_inverse_gamma_logp(self): check_logp( pm.InverseGamma, From 00c1f34fcb106422081b4e21821779dcb82da6fe Mon Sep 17 00:00:00 2001 From: amyoshino Date: Mon, 8 Jan 2024 23:21:10 -0300 Subject: [PATCH 2/2] fixing check_parameters occurences in icdf functions, replacing with check_icdf_parameters --- pymc/distributions/continuous.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pymc/distributions/continuous.py b/pymc/distributions/continuous.py index c099a9d6a5..b83260f671 100644 --- a/pymc/distributions/continuous.py +++ b/pymc/distributions/continuous.py @@ -2088,7 +2088,7 @@ def logcdf(value, alpha, beta): def icdf(value, alpha, beta): res = alpha + beta * pt.tan(np.pi * (value - 0.5)) res = check_icdf_value(res, value) - return check_parameters( + return check_icdf_parameters( res, beta > 0, msg="beta > 0", @@ -2172,7 +2172,7 @@ def logcdf(value, loc, beta): def icdf(value, loc, beta): res = loc + beta * pt.tan(np.pi * (value) / 2.0) res = check_icdf_value(res, value) - return check_parameters( + return check_icdf_parameters( res, beta > 0, msg="beta > 0", @@ -2591,7 +2591,7 @@ def logp(value, alpha, beta): def icdf(value, alpha, beta): res = beta * (-pt.log(1 - value)) ** (1 / alpha) res = check_icdf_value(res, value) - return check_parameters( + return check_icdf_parameters( res, alpha > 0, beta > 0, @@ -3151,7 +3151,7 @@ def icdf(value, lower, c, upper): upper - np.sqrt((upper - lower) * (upper - c) * (1 - value)), ) res = check_icdf_value(res, value) - return check_parameters( + return check_icdf_parameters( res, lower <= c, c <= upper, @@ -3254,7 +3254,7 @@ def logcdf(value, mu, beta): def icdf(value, mu, beta): res = mu - beta * pt.log(-pt.log(value)) res = check_icdf_value(res, value) - return check_parameters( + return check_icdf_parameters( res, beta > 0, msg="beta > 0", @@ -3472,7 +3472,7 @@ def logcdf(value, mu, s): def icdf(value, mu, s): res = mu + s * pt.log(value / (1 - value)) res = check_icdf_value(res, value) - return check_parameters( + return check_icdf_parameters( res, s > 0, msg="s > 0", @@ -3822,7 +3822,7 @@ def logcdf(value, mu, sigma): def icdf(value, mu, sigma): res = sigma * -pt.log(2.0 * pt.erfcinv(value) ** 2) + mu res = check_icdf_value(res, value) - return check_parameters( + return check_icdf_parameters( res, sigma > 0, msg="sigma > 0",