From 2465a0fb43a1d83048025e292d9eddaaad5c9950 Mon Sep 17 00:00:00 2001 From: Purna Chandra Mansingh Date: Mon, 28 Mar 2022 15:57:39 +0530 Subject: [PATCH 01/13] keep the original dims in the log_jac_det --- pymc/distributions/transforms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymc/distributions/transforms.py b/pymc/distributions/transforms.py index ee142b46fe..355dfd76db 100644 --- a/pymc/distributions/transforms.py +++ b/pymc/distributions/transforms.py @@ -87,7 +87,7 @@ def forward(self, value, *inputs): return y def log_jac_det(self, value, *inputs): - return at.sum(value[..., 1:], axis=-1) + return at.sum(value[..., 1:], axis=-1, keepdims=True) class SumTo1(RVTransform): @@ -107,7 +107,7 @@ def forward(self, value, *inputs): def log_jac_det(self, value, *inputs): y = at.zeros(value.shape) - return at.sum(y, axis=-1) + return at.sum(y, axis=-1, keepdims=True) class CholeskyCovPacked(RVTransform): From c9a04708e766ac0c3012bf84d659eca880cd7810 Mon Sep 17 00:00:00 2001 From: Purna Chandra Mansingh Date: Mon, 28 Mar 2022 17:22:59 +0530 Subject: [PATCH 02/13] added tests to test_transforms.py --- pymc/tests/distributions/test_transform.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pymc/tests/distributions/test_transform.py b/pymc/tests/distributions/test_transform.py index 850b285f4d..fa04eb86d1 100644 --- a/pymc/tests/distributions/test_transform.py +++ b/pymc/tests/distributions/test_transform.py @@ -598,3 +598,17 @@ def test_discrete_trafo(): with pytest.raises(ValueError) as err: pm.Binomial("a", n=5, p=0.5, transform="log") err.match("Transformations for discrete distributions") +def test_transforms_ordered(): + COORDS = {"question": np.arange(10), "thresholds": np.arange(4)} + + with pm.Model(coords=COORDS) as model: + kappa = pm.Normal( + "kappa", + mu=[-3, -1, 1, 2], + sigma=1, + dims=["question", "thresholds"], + transform=pm.distributions.transforms.ordered, + ) + + log_prob = model.point_logps() + np.testing.assert_allclose(list(log_prob.values()), np.array([18.69])) From 0862134275797643926da4a873311d7cb9f9fa99 Mon Sep 17 00:00:00 2001 From: Purna Chandra Mansingh Date: Mon, 28 Mar 2022 18:19:20 +0530 Subject: [PATCH 03/13] added tests for sum_to_1 transforms --- pymc/tests/distributions/test_transform.py | 26 ++++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/pymc/tests/distributions/test_transform.py b/pymc/tests/distributions/test_transform.py index fa04eb86d1..ae3414e31e 100644 --- a/pymc/tests/distributions/test_transform.py +++ b/pymc/tests/distributions/test_transform.py @@ -599,16 +599,28 @@ def test_discrete_trafo(): pm.Binomial("a", n=5, p=0.5, transform="log") err.match("Transformations for discrete distributions") def test_transforms_ordered(): - COORDS = {"question": np.arange(10), "thresholds": np.arange(4)} - - with pm.Model(coords=COORDS) as model: - kappa = pm.Normal( - "kappa", + with pm.Model() as model: + pm.Normal( + "x", mu=[-3, -1, 1, 2], sigma=1, - dims=["question", "thresholds"], + size=(10, 4), transform=pm.distributions.transforms.ordered, ) - + log_prob = model.point_logps() np.testing.assert_allclose(list(log_prob.values()), np.array([18.69])) + + +def test_transforms_sumto1(): + with pm.Model() as model: + pm.Normal( + "x", + mu=[-3, -1, 1, 2], + sigma=1, + size=(10, 4), + transform=pm.distributions.transforms.sum_to_1, + ) + + log_prob = model.point_logps() + np.testing.assert_allclose(list(log_prob.values()), np.array([-56.76])) From 7fb4c37c344365500bdf9e8ee482206dca4446c6 Mon Sep 17 00:00:00 2001 From: Purna Chandra Mansingh Date: Mon, 28 Mar 2022 18:20:05 +0530 Subject: [PATCH 04/13] fix pre-commit --- pymc/tests/distributions/test_transform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymc/tests/distributions/test_transform.py b/pymc/tests/distributions/test_transform.py index ae3414e31e..efba74de89 100644 --- a/pymc/tests/distributions/test_transform.py +++ b/pymc/tests/distributions/test_transform.py @@ -607,7 +607,7 @@ def test_transforms_ordered(): size=(10, 4), transform=pm.distributions.transforms.ordered, ) - + log_prob = model.point_logps() np.testing.assert_allclose(list(log_prob.values()), np.array([18.69])) @@ -621,6 +621,6 @@ def test_transforms_sumto1(): size=(10, 4), transform=pm.distributions.transforms.sum_to_1, ) - + log_prob = model.point_logps() np.testing.assert_allclose(list(log_prob.values()), np.array([-56.76])) From 2d2bab46e2b0bfd3a194506210202216eca4ff40 Mon Sep 17 00:00:00 2001 From: "Tim Maier (Ubuntu Desktop)" Date: Wed, 26 Oct 2022 17:09:44 +0200 Subject: [PATCH 05/13] ordered transform class with n_dims_supp --- pymc/distributions/transforms.py | 8 +++++++- pymc/tests/distributions/test_transform.py | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pymc/distributions/transforms.py b/pymc/distributions/transforms.py index 355dfd76db..f9a15f6cc9 100644 --- a/pymc/distributions/transforms.py +++ b/pymc/distributions/transforms.py @@ -74,6 +74,9 @@ def log_jac_det(self, value, *inputs): class Ordered(RVTransform): name = "ordered" + def __init__(self, ndim_supp=0): + self.ndim_supp = ndim_supp + def backward(self, value, *inputs): x = at.zeros(value.shape) x = at.inc_subtensor(x[..., 0], value[..., 0]) @@ -87,7 +90,10 @@ def forward(self, value, *inputs): return y def log_jac_det(self, value, *inputs): - return at.sum(value[..., 1:], axis=-1, keepdims=True) + if self.ndim_supp == 0: + return at.sum(value[..., 1:], axis=-1, keepdims=True) + else: + return at.sum(value[..., 1:], axis=-1) class SumTo1(RVTransform): diff --git a/pymc/tests/distributions/test_transform.py b/pymc/tests/distributions/test_transform.py index efba74de89..b6aec0e384 100644 --- a/pymc/tests/distributions/test_transform.py +++ b/pymc/tests/distributions/test_transform.py @@ -598,6 +598,8 @@ def test_discrete_trafo(): with pytest.raises(ValueError) as err: pm.Binomial("a", n=5, p=0.5, transform="log") err.match("Transformations for discrete distributions") + + def test_transforms_ordered(): with pm.Model() as model: pm.Normal( From 0372528dc45bf61b452c677bbc35861fe97e7427 Mon Sep 17 00:00:00 2001 From: "Tim Maier (Ubuntu Desktop)" Date: Fri, 28 Oct 2022 11:54:30 +0200 Subject: [PATCH 06/13] univariate_ordered and multivariate_ordered --- pymc/distributions/transforms.py | 13 ++++++++++--- pymc/tests/distributions/test_transform.py | 8 ++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pymc/distributions/transforms.py b/pymc/distributions/transforms.py index f9a15f6cc9..f6131a0182 100644 --- a/pymc/distributions/transforms.py +++ b/pymc/distributions/transforms.py @@ -336,10 +336,17 @@ def extend_axis_rev(array, axis): Instantiation of :class:`pymc.distributions.transforms.LogExpM1` for use in the ``transform`` argument of a random variable.""" -ordered = Ordered() -ordered.__doc__ = """ +univariate_ordered = Ordered(ndim_supp=0) +univariate_ordered.__doc__ = """ Instantiation of :class:`pymc.distributions.transforms.Ordered` -for use in the ``transform`` argument of a random variable.""" +for use in the ``transform`` argument of a univariate random variable.""" + +multivariate_ordered = Ordered(ndim_supp=1) +multivariate_ordered.__doc__ = """ +Instantiation of :class:`pymc.distributions.transforms.Ordered` +for use in the ``transform`` argument of a multivariate random variable.""" + +ordered = univariate_ordered log = LogTransform() log.__doc__ = """ diff --git a/pymc/tests/distributions/test_transform.py b/pymc/tests/distributions/test_transform.py index b6aec0e384..99a58c45c0 100644 --- a/pymc/tests/distributions/test_transform.py +++ b/pymc/tests/distributions/test_transform.py @@ -327,7 +327,7 @@ def check_vectortransform_elementwise_logp(self, model): jacob_det = transform.log_jac_det(test_array_transf, *x.owner.inputs) # Original distribution is univariate if x.owner.op.ndim_supp == 0: - assert model.logp(x, sum=False)[0].ndim == x.ndim == (jacob_det.ndim + 1) + assert model.logp(x, sum=False)[0].ndim == x.ndim == jacob_det.ndim # Original distribution is multivariate else: assert model.logp(x, sum=False)[0].ndim == (x.ndim - 1) == jacob_det.ndim @@ -569,7 +569,11 @@ def test_uniform_other(self, lower, upper, size, transform): def test_mvnormal_ordered(self, mu, cov, size, shape): initval = np.sort(np.random.randn(*shape)) model = self.build_model( - pm.MvNormal, {"mu": mu, "cov": cov}, size=size, initval=initval, transform=tr.ordered + pm.MvNormal, + {"mu": mu, "cov": cov}, + size=size, + initval=initval, + transform=tr.multivariate_ordered, ) self.check_vectortransform_elementwise_logp(model) From 7ef9b2e9ab91f1c66ff89dcdeed43cd4f49be013 Mon Sep 17 00:00:00 2001 From: "Tim Maier (Ubuntu Desktop)" Date: Sun, 30 Oct 2022 10:28:57 +0100 Subject: [PATCH 07/13] ndim_supp cases in sumto1 transform --- pymc/distributions/transforms.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pymc/distributions/transforms.py b/pymc/distributions/transforms.py index f6131a0182..6a308469b4 100644 --- a/pymc/distributions/transforms.py +++ b/pymc/distributions/transforms.py @@ -104,6 +104,9 @@ class SumTo1(RVTransform): name = "sumto1" + def __init__(self, ndim_supp=0): + self.ndim_supp = ndim_supp + def backward(self, value, *inputs): remaining = 1 - at.sum(value[..., :], axis=-1, keepdims=True) return at.concatenate([value[..., :], remaining], axis=-1) @@ -113,7 +116,10 @@ def forward(self, value, *inputs): def log_jac_det(self, value, *inputs): y = at.zeros(value.shape) - return at.sum(y, axis=-1, keepdims=True) + if self.ndim_supp == 0: + return at.sum(y, axis=-1, keepdims=True) + else: + return at.sum(y, axis=-1) class CholeskyCovPacked(RVTransform): From 9d279db3ac2516e189e225b8d0159190fd8bd400 Mon Sep 17 00:00:00 2001 From: "Tim Maier (Ubuntu Desktop)" Date: Mon, 31 Oct 2022 12:24:41 +0100 Subject: [PATCH 08/13] keep transforms.ordered for backward compatibility --- pymc/distributions/transforms.py | 18 ++++++++++++++++-- pymc/tests/distributions/test_transform.py | 10 +++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/pymc/distributions/transforms.py b/pymc/distributions/transforms.py index 6a308469b4..cf000f4c79 100644 --- a/pymc/distributions/transforms.py +++ b/pymc/distributions/transforms.py @@ -352,14 +352,28 @@ def extend_axis_rev(array, axis): Instantiation of :class:`pymc.distributions.transforms.Ordered` for use in the ``transform`` argument of a multivariate random variable.""" -ordered = univariate_ordered +ordered = Ordered(ndim_supp=1) +ordered.__doc__ = """ +Instantiation of :class:`pymc.distributions.transforms.Ordered` +for use in the ``transform`` argument. """ + log = LogTransform() log.__doc__ = """ Instantiation of :class:`aeppl.transforms.LogTransform` for use in the ``transform`` argument of a random variable.""" -sum_to_1 = SumTo1() +univariate_sum_to_1 = SumTo1(ndim_supp=0) +univariate_sum_to_1.__doc__ = """ +Instantiation of :class:`pymc.distributions.transforms.SumTo1` +for use in the ``transform`` argument of a univariate random variable.""" + +multivariate_sum_to_1 = SumTo1(ndim_supp=1) +multivariate_sum_to_1.__doc__ = """ +Instantiation of :class:`pymc.distributions.transforms.SumTo1` +for use in the ``transform`` argument of a multivariate random variable.""" + +sum_to_1 = SumTo1(ndim_supp=1) sum_to_1.__doc__ = """ Instantiation of :class:`pymc.distributions.transforms.SumTo1` for use in the ``transform`` argument of a random variable.""" diff --git a/pymc/tests/distributions/test_transform.py b/pymc/tests/distributions/test_transform.py index 99a58c45c0..54c9e695d5 100644 --- a/pymc/tests/distributions/test_transform.py +++ b/pymc/tests/distributions/test_transform.py @@ -327,7 +327,7 @@ def check_vectortransform_elementwise_logp(self, model): jacob_det = transform.log_jac_det(test_array_transf, *x.owner.inputs) # Original distribution is univariate if x.owner.op.ndim_supp == 0: - assert model.logp(x, sum=False)[0].ndim == x.ndim == jacob_det.ndim + assert model.logp(x, sum=False)[0].ndim == x.ndim == (jacob_det.ndim + 1) # Original distribution is multivariate else: assert model.logp(x, sum=False)[0].ndim == (x.ndim - 1) == jacob_det.ndim @@ -573,7 +573,7 @@ def test_mvnormal_ordered(self, mu, cov, size, shape): {"mu": mu, "cov": cov}, size=size, initval=initval, - transform=tr.multivariate_ordered, + transform=tr.ordered, ) self.check_vectortransform_elementwise_logp(model) @@ -607,11 +607,11 @@ def test_discrete_trafo(): def test_transforms_ordered(): with pm.Model() as model: pm.Normal( - "x", + "x_univariate", mu=[-3, -1, 1, 2], sigma=1, size=(10, 4), - transform=pm.distributions.transforms.ordered, + transform=tr.univariate_ordered, ) log_prob = model.point_logps() @@ -625,7 +625,7 @@ def test_transforms_sumto1(): mu=[-3, -1, 1, 2], sigma=1, size=(10, 4), - transform=pm.distributions.transforms.sum_to_1, + transform=tr.univariate_sum_to_1, ) log_prob = model.point_logps() From 7515c73201590eea77ca3fd55ee449cdb7b3e95a Mon Sep 17 00:00:00 2001 From: "Tim Maier (Ubuntu Desktop)" Date: Sat, 19 Nov 2022 21:32:02 +0100 Subject: [PATCH 09/13] pre-commit run --- pymc/distributions/transforms.py | 20 ++++----- pymc/tests/distributions/test_transform.py | 49 ++++++++++++++-------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/pymc/distributions/transforms.py b/pymc/distributions/transforms.py index cf000f4c79..6fd727358b 100644 --- a/pymc/distributions/transforms.py +++ b/pymc/distributions/transforms.py @@ -75,6 +75,11 @@ class Ordered(RVTransform): name = "ordered" def __init__(self, ndim_supp=0): + if ndim_supp > 1: + raise ValueError( + f"For Ordered transformation number of core dimensions" + f"(ndim_supp) must not exceed 1 but is {ndim_supp}" + ) self.ndim_supp = ndim_supp def backward(self, value, *inputs): @@ -105,6 +110,11 @@ class SumTo1(RVTransform): name = "sumto1" def __init__(self, ndim_supp=0): + if ndim_supp > 1: + raise ValueError( + f"For SumTo1 transformation number of core dimensions" + f"(ndim_supp) must not exceed 1 but is {ndim_supp}" + ) self.ndim_supp = ndim_supp def backward(self, value, *inputs): @@ -352,11 +362,6 @@ def extend_axis_rev(array, axis): Instantiation of :class:`pymc.distributions.transforms.Ordered` for use in the ``transform`` argument of a multivariate random variable.""" -ordered = Ordered(ndim_supp=1) -ordered.__doc__ = """ -Instantiation of :class:`pymc.distributions.transforms.Ordered` -for use in the ``transform`` argument. """ - log = LogTransform() log.__doc__ = """ @@ -373,11 +378,6 @@ def extend_axis_rev(array, axis): Instantiation of :class:`pymc.distributions.transforms.SumTo1` for use in the ``transform`` argument of a multivariate random variable.""" -sum_to_1 = SumTo1(ndim_supp=1) -sum_to_1.__doc__ = """ -Instantiation of :class:`pymc.distributions.transforms.SumTo1` -for use in the ``transform`` argument of a random variable.""" - circular = CircularTransform() circular.__doc__ = """ Instantiation of :class:`aeppl.transforms.CircularTransform` diff --git a/pymc/tests/distributions/test_transform.py b/pymc/tests/distributions/test_transform.py index 54c9e695d5..59db3f398b 100644 --- a/pymc/tests/distributions/test_transform.py +++ b/pymc/tests/distributions/test_transform.py @@ -13,6 +13,8 @@ # limitations under the License. +from typing import Union + import aesara import aesara.tensor as at import numpy as np @@ -139,10 +141,12 @@ def test_simplex_accuracy(): def test_sum_to_1(): - check_vector_transform(tr.sum_to_1, Simplex(2)) - check_vector_transform(tr.sum_to_1, Simplex(4)) + check_vector_transform(tr.univariate_sum_to_1, Simplex(2)) + check_vector_transform(tr.univariate_sum_to_1, Simplex(4)) - check_jacobian_det(tr.sum_to_1, Vector(Unit, 2), at.dvector, np.array([0, 0]), lambda x: x[:-1]) + check_jacobian_det( + tr.univariate_sum_to_1, Vector(Unit, 2), at.dvector, np.array([0, 0]), lambda x: x[:-1] + ) def test_log(): @@ -241,28 +245,30 @@ def test_circular(): def test_ordered(): - check_vector_transform(tr.ordered, SortedVector(6)) + check_vector_transform(tr.univariate_ordered, SortedVector(6)) - check_jacobian_det(tr.ordered, Vector(R, 2), at.dvector, np.array([0, 0]), elemwise=False) + check_jacobian_det( + tr.univariate_ordered, Vector(R, 2), at.dvector, np.array([0, 0]), elemwise=False + ) - vals = get_values(tr.ordered, Vector(R, 3), at.dvector, np.zeros(3)) + vals = get_values(tr.univariate_ordered, Vector(R, 3), at.dvector, np.zeros(3)) close_to_logical(np.diff(vals) >= 0, True, tol) def test_chain_values(): - chain_tranf = tr.Chain([tr.logodds, tr.ordered]) + chain_tranf = tr.Chain([tr.logodds, tr.univariate_ordered]) vals = get_values(chain_tranf, Vector(R, 5), at.dvector, np.zeros(5)) close_to_logical(np.diff(vals) >= 0, True, tol) def test_chain_vector_transform(): - chain_tranf = tr.Chain([tr.logodds, tr.ordered]) + chain_tranf = tr.Chain([tr.logodds, tr.univariate_ordered]) check_vector_transform(chain_tranf, UnitSortedVector(3)) @pytest.mark.xfail(reason="Fails due to precision issue. Values just close to expected.") def test_chain_jacob_det(): - chain_tranf = tr.Chain([tr.logodds, tr.ordered]) + chain_tranf = tr.Chain([tr.logodds, tr.univariate_ordered]) check_jacobian_det(chain_tranf, Vector(R, 4), at.dvector, np.zeros(4), elemwise=False) @@ -327,7 +333,14 @@ def check_vectortransform_elementwise_logp(self, model): jacob_det = transform.log_jac_det(test_array_transf, *x.owner.inputs) # Original distribution is univariate if x.owner.op.ndim_supp == 0: - assert model.logp(x, sum=False)[0].ndim == x.ndim == (jacob_det.ndim + 1) + tr_steps = getattr(transform, "transform_list", [transform]) + transform_keeps_dim = any( + [isinstance(ts, Union[tr.SumTo1, tr.Ordered]) for ts in tr_steps] + ) + if transform_keeps_dim: + assert model.logp(x, sum=False)[0].ndim == x.ndim == jacob_det.ndim + else: + assert model.logp(x, sum=False)[0].ndim == x.ndim == (jacob_det.ndim + 1) # Original distribution is multivariate else: assert model.logp(x, sum=False)[0].ndim == (x.ndim - 1) == jacob_det.ndim @@ -449,7 +462,7 @@ def test_normal_ordered(self): {"mu": 0.0, "sigma": 1.0}, size=3, initval=np.asarray([-1.0, 1.0, 4.0]), - transform=tr.ordered, + transform=tr.univariate_ordered, ) self.check_vectortransform_elementwise_logp(model) @@ -467,7 +480,7 @@ def test_half_normal_ordered(self, sigma, size): {"sigma": sigma}, size=size, initval=initval, - transform=tr.Chain([tr.log, tr.ordered]), + transform=tr.Chain([tr.log, tr.univariate_ordered]), ) self.check_vectortransform_elementwise_logp(model) @@ -479,7 +492,7 @@ def test_exponential_ordered(self, lam, size): {"lam": lam}, size=size, initval=initval, - transform=tr.Chain([tr.log, tr.ordered]), + transform=tr.Chain([tr.log, tr.univariate_ordered]), ) self.check_vectortransform_elementwise_logp(model) @@ -501,7 +514,7 @@ def test_beta_ordered(self, a, b, size): {"alpha": a, "beta": b}, size=size, initval=initval, - transform=tr.Chain([tr.logodds, tr.ordered]), + transform=tr.Chain([tr.logodds, tr.univariate_ordered]), ) self.check_vectortransform_elementwise_logp(model) @@ -524,7 +537,7 @@ def transform_params(*inputs): {"lower": lower, "upper": upper}, size=size, initval=initval, - transform=tr.Chain([interval, tr.ordered]), + transform=tr.Chain([interval, tr.univariate_ordered]), ) self.check_vectortransform_elementwise_logp(model) @@ -536,7 +549,7 @@ def test_vonmises_ordered(self, mu, kappa, size): {"mu": mu, "kappa": kappa}, size=size, initval=initval, - transform=tr.Chain([tr.circular, tr.ordered]), + transform=tr.Chain([tr.circular, tr.univariate_ordered]), ) self.check_vectortransform_elementwise_logp(model) @@ -545,7 +558,7 @@ def test_vonmises_ordered(self, mu, kappa, size): [ (0.0, 1.0, (2,), tr.simplex), (0.5, 5.5, (2, 3), tr.simplex), - (np.zeros(3), np.ones(3), (4, 3), tr.Chain([tr.sum_to_1, tr.logodds])), + (np.zeros(3), np.ones(3), (4, 3), tr.Chain([tr.univariate_sum_to_1, tr.logodds])), ], ) def test_uniform_other(self, lower, upper, size, transform): @@ -573,7 +586,7 @@ def test_mvnormal_ordered(self, mu, cov, size, shape): {"mu": mu, "cov": cov}, size=size, initval=initval, - transform=tr.ordered, + transform=tr.multivariate_ordered, ) self.check_vectortransform_elementwise_logp(model) From 744247457ae025641415273d7f494c1e17b23a75 Mon Sep 17 00:00:00 2001 From: "Tim Maier (Ubuntu Desktop)" Date: Sat, 19 Nov 2022 22:50:02 +0100 Subject: [PATCH 10/13] updated `__all__` variable 1. updated variable `__all__` to contain `univariate_ordered`,`multivariate_ordered` and analogous `sum_to_1` instantiations. --- pymc/distributions/transforms.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pymc/distributions/transforms.py b/pymc/distributions/transforms.py index 6fd727358b..394a5d0dd6 100644 --- a/pymc/distributions/transforms.py +++ b/pymc/distributions/transforms.py @@ -37,9 +37,11 @@ "logodds", "Interval", "log_exp_m1", - "ordered", + "univariate_ordered", + "multivariate_ordered", "log", - "sum_to_1", + "univariate_sum_to_1", + "multivariate_sum_to_1", "circular", "CholeskyCovPacked", "Chain", From bc8166e245744f0e0c58c73814edc27a8cb636e7 Mon Sep 17 00:00:00 2001 From: "Tim Maier (Ubuntu Desktop)" Date: Sun, 20 Nov 2022 18:51:17 +0100 Subject: [PATCH 11/13] Tests for uni- and multivariate `SumTo1, Ordered` 1. Old `transforms.ordered` and `transforms.sum_to_1` instantiations were added again for backwards compatibility. 2. Tests for multivariate and univariate usage of `SumTo1` and `Ordered` transformations were added. --- pymc/distributions/transforms.py | 13 ++++ pymc/tests/distributions/test_transform.py | 90 +++++++++++++++++++--- 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/pymc/distributions/transforms.py b/pymc/distributions/transforms.py index 394a5d0dd6..9852d0f3f2 100644 --- a/pymc/distributions/transforms.py +++ b/pymc/distributions/transforms.py @@ -37,9 +37,11 @@ "logodds", "Interval", "log_exp_m1", + "ordered", "univariate_ordered", "multivariate_ordered", "log", + "sum_to_1", "univariate_sum_to_1", "multivariate_sum_to_1", "circular", @@ -364,6 +366,11 @@ def extend_axis_rev(array, axis): Instantiation of :class:`pymc.distributions.transforms.Ordered` for use in the ``transform`` argument of a multivariate random variable.""" +# backwards compatibility +ordered = Ordered(ndim_supp=1) +ordered.__doc__ = """ +Instantiation of :class:`pymc.distributions.transforms.Ordered` +for use in the ``transform`` argument. """ log = LogTransform() log.__doc__ = """ @@ -380,6 +387,12 @@ def extend_axis_rev(array, axis): Instantiation of :class:`pymc.distributions.transforms.SumTo1` for use in the ``transform`` argument of a multivariate random variable.""" +# backwards compatibility +sum_to_1 = SumTo1(ndim_supp=1) +sum_to_1.__doc__ = """ +Instantiation of :class:`pymc.distributions.transforms.SumTo1` +for use in the ``transform`` argument of a random variable.""" + circular = CircularTransform() circular.__doc__ = """ Instantiation of :class:`aeppl.transforms.CircularTransform` diff --git a/pymc/tests/distributions/test_transform.py b/pymc/tests/distributions/test_transform.py index 59db3f398b..eff0223184 100644 --- a/pymc/tests/distributions/test_transform.py +++ b/pymc/tests/distributions/test_transform.py @@ -147,6 +147,9 @@ def test_sum_to_1(): check_jacobian_det( tr.univariate_sum_to_1, Vector(Unit, 2), at.dvector, np.array([0, 0]), lambda x: x[:-1] ) + check_jacobian_det( + tr.multivariate_sum_to_1, Vector(Unit, 2), at.dvector, np.array([0, 0]), lambda x: x[:-1] + ) def test_log(): @@ -250,6 +253,9 @@ def test_ordered(): check_jacobian_det( tr.univariate_ordered, Vector(R, 2), at.dvector, np.array([0, 0]), elemwise=False ) + check_jacobian_det( + tr.multivariate_ordered, Vector(R, 2), at.dvector, np.array([0, 0]), elemwise=False + ) vals = get_values(tr.univariate_ordered, Vector(R, 3), at.dvector, np.zeros(3)) close_to_logical(np.diff(vals) >= 0, True, tol) @@ -617,29 +623,93 @@ def test_discrete_trafo(): err.match("Transformations for discrete distributions") -def test_transforms_ordered(): +def test_2d_univariate_ordered(): with pm.Model() as model: - pm.Normal( - "x_univariate", + x_1d = pm.Normal( + "x_1d", + mu=[-3, -1, 1, 2], + sigma=1, + size=(4,), + transform=tr.univariate_ordered, + ) + x_2d = pm.Normal( + "x_2d", mu=[-3, -1, 1, 2], sigma=1, size=(10, 4), transform=tr.univariate_ordered, ) - log_prob = model.point_logps() - np.testing.assert_allclose(list(log_prob.values()), np.array([18.69])) + log_p = model.compile_logp(sum=False)( + {"x_1d_ordered__": np.zeros((4,)), "x_2d_ordered__": np.zeros((10, 4))} + ) + np.testing.assert_allclose(np.tile(log_p[0], (10, 1)), log_p[1]) + + +def test_2d_multivariate_ordered(): + with pm.Model() as model: + x_1d = pm.MvNormal( + "x_1d", + mu=[-1, 1], + cov=np.eye(2), + initval=[-1, 1], + transform=tr.multivariate_ordered, + ) + x_2d = pm.MvNormal( + "x_2d", + mu=[-1, 1], + cov=np.eye(2), + size=2, + initval=[[-1, 1], [-1, 1]], + transform=tr.multivariate_ordered, + ) + + log_p = model.compile_logp(sum=False)( + {"x_1d_ordered__": np.zeros((2,)), "x_2d_ordered__": np.zeros((2, 2))} + ) + np.testing.assert_allclose(log_p[0], log_p[1]) -def test_transforms_sumto1(): +def test_2d_univariate_sum_to_1(): with pm.Model() as model: - pm.Normal( - "x", + x_1d = pm.Normal( + "x_1d", + mu=[-3, -1, 1, 2], + sigma=1, + size=(4,), + transform=tr.univariate_sum_to_1, + ) + x_2d = pm.Normal( + "x_2d", mu=[-3, -1, 1, 2], sigma=1, size=(10, 4), transform=tr.univariate_sum_to_1, ) - log_prob = model.point_logps() - np.testing.assert_allclose(list(log_prob.values()), np.array([-56.76])) + log_p = model.compile_logp(sum=False)( + {"x_1d_sumto1__": np.zeros(3), "x_2d_sumto1__": np.zeros((10, 3))} + ) + np.testing.assert_allclose(np.tile(log_p[0], (10, 1)), log_p[1]) + + +def test_2d_multivariate_sum_to_1(): + with pm.Model() as model: + x_1d = pm.MvNormal( + "x_1d", + mu=[-1, 1], + cov=np.eye(2), + transform=tr.multivariate_sum_to_1, + ) + x_2d = pm.MvNormal( + "x_2d", + mu=[-1, 1], + cov=np.eye(2), + size=2, + transform=tr.multivariate_sum_to_1, + ) + + log_p = model.compile_logp(sum=False)( + {"x_1d_sumto1__": np.zeros(1), "x_2d_sumto1__": np.zeros((2, 1))} + ) + np.testing.assert_allclose(log_p[0], log_p[1]) From 8ad2c03eedfade45e3544ab18fbcff7ab479f9cd Mon Sep 17 00:00:00 2001 From: Tim Maier Date: Sun, 20 Nov 2022 22:10:31 +0100 Subject: [PATCH 12/13] test `Ordered` and `SumTo1` raise `ValueError` --- pymc/tests/distributions/test_transform.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pymc/tests/distributions/test_transform.py b/pymc/tests/distributions/test_transform.py index eff0223184..5bed2a9a48 100644 --- a/pymc/tests/distributions/test_transform.py +++ b/pymc/tests/distributions/test_transform.py @@ -144,6 +144,9 @@ def test_sum_to_1(): check_vector_transform(tr.univariate_sum_to_1, Simplex(2)) check_vector_transform(tr.univariate_sum_to_1, Simplex(4)) + with pytest.raises(ValueError, match=r"\(ndim_supp\) must not exceed 1"): + tr.SumTo1(2) + check_jacobian_det( tr.univariate_sum_to_1, Vector(Unit, 2), at.dvector, np.array([0, 0]), lambda x: x[:-1] ) @@ -250,6 +253,9 @@ def test_circular(): def test_ordered(): check_vector_transform(tr.univariate_ordered, SortedVector(6)) + with pytest.raises(ValueError, match=r"\(ndim_supp\) must not exceed 1"): + tr.Ordered(2) + check_jacobian_det( tr.univariate_ordered, Vector(R, 2), at.dvector, np.array([0, 0]), elemwise=False ) From 9daae3536efa065be3ab3d57dfb421d58cd3da36 Mon Sep 17 00:00:00 2001 From: "Tim Maier (Ubuntu Desktop)" Date: Mon, 21 Nov 2022 10:02:11 +0100 Subject: [PATCH 13/13] improved docs for `SumTo1, Ordered` instantiations --- pymc/distributions/transforms.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pymc/distributions/transforms.py b/pymc/distributions/transforms.py index 9852d0f3f2..2f3d7cb626 100644 --- a/pymc/distributions/transforms.py +++ b/pymc/distributions/transforms.py @@ -370,7 +370,9 @@ def extend_axis_rev(array, axis): ordered = Ordered(ndim_supp=1) ordered.__doc__ = """ Instantiation of :class:`pymc.distributions.transforms.Ordered` -for use in the ``transform`` argument. """ +for use in the ``transform`` argument of a random variable. +This instantiation is for backwards compatibility only. +Please use `univariate_ordererd` or `multivariate_ordered` instead.""" log = LogTransform() log.__doc__ = """ @@ -391,7 +393,9 @@ def extend_axis_rev(array, axis): sum_to_1 = SumTo1(ndim_supp=1) sum_to_1.__doc__ = """ Instantiation of :class:`pymc.distributions.transforms.SumTo1` -for use in the ``transform`` argument of a random variable.""" +for use in the ``transform`` argument of a random variable. +This instantiation is for backwards compatibility only. +Please use `univariate_sum_to_1` or `multivariate_sum_to_1` instead.""" circular = CircularTransform() circular.__doc__ = """