From 09215c21415103c6e9437c2a1663809c00ac3a97 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 6 May 2023 13:07:32 +0100 Subject: [PATCH 1/2] Add ICDF function to laplace distribution Adds ICDF (quantile) function for the laplace distribution. Source https://en.wikipedia.org/wiki/Laplace_distribution Issue #6612 --- pymc/distributions/continuous.py | 13 +++++++++++++ tests/distributions/test_continuous.py | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/pymc/distributions/continuous.py b/pymc/distributions/continuous.py index 91d2193504..def2ccb87a 100644 --- a/pymc/distributions/continuous.py +++ b/pymc/distributions/continuous.py @@ -1480,6 +1480,19 @@ def logcdf(value, mu, b): msg="b > 0", ) + def icdf(value, mu, b): + res = pt.switch( + pt.le(value, 0.5), + mu + b * np.log(2 * value), + mu - b * np.log(2 - 2 * value) + ) + res = check_icdf_value(res, value) + return check_icdf_parameters( + res, + b > 0, + msg="b > 0" + ) + class AsymmetricLaplaceRV(RandomVariable): name = "asymmetriclaplace" diff --git a/tests/distributions/test_continuous.py b/tests/distributions/test_continuous.py index 1f673eb285..64d581a88a 100644 --- a/tests/distributions/test_continuous.py +++ b/tests/distributions/test_continuous.py @@ -457,6 +457,11 @@ def test_laplace(self): {"mu": R, "b": Rplus}, lambda value, mu, b: st.laplace.logcdf(value, mu, b), ) + check_icdf( + pm.Laplace, + {"mu": R, "b": Rplus}, + lambda q, mu, b: st.laplace.ppf(q, mu, b) + ) def test_laplace_asymmetric(self): check_logp( From 9abe6fb9beb03e11667ccb0b975eaeddb288da8d Mon Sep 17 00:00:00 2001 From: James Date: Sat, 6 May 2023 13:19:40 +0100 Subject: [PATCH 2/2] Add ICDF function to pareto distribution Adds ICDF (quantile) function for the Pareto distribution. Source https://en.wikipedia.org/wiki/Pareto_distribution Issue #6612 --- pymc/distributions/continuous.py | 20 ++++++++++++-------- tests/distributions/test_continuous.py | 11 ++++++----- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/pymc/distributions/continuous.py b/pymc/distributions/continuous.py index def2ccb87a..a4371a940e 100644 --- a/pymc/distributions/continuous.py +++ b/pymc/distributions/continuous.py @@ -1482,16 +1482,10 @@ def logcdf(value, mu, b): def icdf(value, mu, b): res = pt.switch( - pt.le(value, 0.5), - mu + b * np.log(2 * value), - mu - b * np.log(2 - 2 * value) + pt.le(value, 0.5), mu + b * np.log(2 * value), mu - b * np.log(2 - 2 * value) ) res = check_icdf_value(res, value) - return check_icdf_parameters( - res, - b > 0, - msg="b > 0" - ) + return check_icdf_parameters(res, b > 0, msg="b > 0") class AsymmetricLaplaceRV(RandomVariable): @@ -1943,6 +1937,16 @@ def logcdf(value, alpha, m): msg="alpha > 0, m > 0", ) + def icdf(value, alpha, m): + res = m * pt.pow(1 - value, -1 / alpha) + res = check_icdf_value(res, value) + return check_icdf_parameters( + res, + alpha > 0, + m > 0, + msg="alpha > 0, m > 0", + ) + @_default_transform.register(Pareto) def pareto_default_transform(op, rv): diff --git a/tests/distributions/test_continuous.py b/tests/distributions/test_continuous.py index 64d581a88a..77ba827bfb 100644 --- a/tests/distributions/test_continuous.py +++ b/tests/distributions/test_continuous.py @@ -457,11 +457,7 @@ def test_laplace(self): {"mu": R, "b": Rplus}, lambda value, mu, b: st.laplace.logcdf(value, mu, b), ) - check_icdf( - pm.Laplace, - {"mu": R, "b": Rplus}, - lambda q, mu, b: st.laplace.ppf(q, mu, b) - ) + check_icdf(pm.Laplace, {"mu": R, "b": Rplus}, lambda q, mu, b: st.laplace.ppf(q, mu, b)) def test_laplace_asymmetric(self): check_logp( @@ -638,6 +634,11 @@ def test_pareto(self): {"alpha": Rplusbig, "m": Rplusbig}, lambda value, alpha, m: st.pareto.logcdf(value, alpha, scale=m), ) + check_icdf( + pm.Pareto, + {"alpha": Rplusbig, "m": Rplusbig}, + lambda q, alpha, m: st.pareto.ppf(q, alpha, scale=m), + ) @pytest.mark.skipif( condition=(pytensor.config.floatX == "float32"),