From 8b6b15a8a1f1fe7bb0e0f91e82f9e3aaf7e8319c Mon Sep 17 00:00:00 2001 From: Chandan Kumar Roy <chandanroy559@outlook.com> Date: Tue, 29 Dec 2020 18:13:11 +0530 Subject: [PATCH 1/7] asymmetric laplace distribution added --- pymc3/distributions/__init__.py | 2 ++ pymc3/distributions/continuous.py | 53 +++++++++++++++++++++++++++++++ pymc3/tests/test_distributions.py | 17 ++++++++-- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/pymc3/distributions/__init__.py b/pymc3/distributions/__init__.py index ade7c9ef00..afbdf76b04 100644 --- a/pymc3/distributions/__init__.py +++ b/pymc3/distributions/__init__.py @@ -16,6 +16,7 @@ from pymc3.distributions.bart import BART from pymc3.distributions.bound import Bound from pymc3.distributions.continuous import ( + AsymmetricLaplace, Beta, Cauchy, ChiSquared, @@ -160,6 +161,7 @@ "LKJCorr", "AR1", "AR", + "AsymmetricLaplace", "GaussianRandomWalk", "MvGaussianRandomWalk", "MvStudentTRandomWalk", diff --git a/pymc3/distributions/continuous.py b/pymc3/distributions/continuous.py index 25a97f7688..402cfe9ffa 100644 --- a/pymc3/distributions/continuous.py +++ b/pymc3/distributions/continuous.py @@ -80,6 +80,7 @@ "Interpolated", "Rice", "Moyal", + "AsymmetricLaplace", ] @@ -1660,6 +1661,58 @@ def logcdf(self, value): tt.switch(tt.gt(y, 1), tt.log1p(-0.5 * tt.exp(-y)), tt.log(1 - 0.5 * tt.exp(-y))), ) +class AsymmetricLaplace(Continuous): + """ + Assymetric Laplace Distribution + See also: https://en.wikipedia.org/wiki/Asymmetric_Laplace_distribution + """ + + def __init__(self, scale, symmetry, testval=0.0, *args, **kwargs): + """ + Constructor + :param scale: scale parameter + :param symmetry: asymmetry parameter. Reduces to a normal laplace distribution with value 1 + """ + self.scale = tt.as_tensor_variable(scale) + self.symmetry = tt.as_tensor_variable(symmetry) + + super().__init__(*args, **kwargs, testval=testval) + + def random(self, point=None, size=None): + """ + Draw random samples from this distribution, using the inverse CDF method. + :param point: not used + :param size: size of sample to draw + :return: Samples + """ + if point is not None: + raise NotImplementedError('Random not implemented with point specified') + + if size is not None: + u = np.random.uniform(size=size) + x = - tt.log((1 - u) * (1 + self.symmetry ** 2)) / (self.symmetry * self.scale) * ( + u > ((self.symmetry ** 2) / (1 + self.symmetry ** 2))) + self.symmetry * tt.log( + u * (1 + self.symmetry ** 2) / (self.symmetry ** 2)) / self.scale * ( + u < ((self.symmetry ** 2) / (1 + self.symmetry ** 2))) + + return x + + u = np.random.uniform() + if u > (self.symmetry ** 2) / (1 + self.symmetry ** 2): + x = - tt.log((1 - u) * (1 + self.symmetry ** 2)) / (self.symmetry * self.scale) + else: + x = self.symmetry * tt.log(u * (1 + self.symmetry ** 2) / (self.symmetry ** 2)) / self.scale + + return x + + def logp(self, value): + """ + Compute logp. + :param value: evaluation point + :return: log probability at evaluation point + """ + return tt.log(self.scale / (self.symmetry + (self.symmetry ** -1))) + ( + -value * self.scale * tt.sgn(value) * (self.symmetry ** tt.sgn(value))) class Lognormal(PositiveContinuous): r""" diff --git a/pymc3/tests/test_distributions.py b/pymc3/tests/test_distributions.py index a2d00a2d60..41338f894c 100644 --- a/pymc3/tests/test_distributions.py +++ b/pymc3/tests/test_distributions.py @@ -33,6 +33,7 @@ from pymc3.blocking import DictToVarBijection from pymc3.distributions import ( AR1, + AsymmetricLaplace, Bernoulli, Beta, BetaBinomial, @@ -206,6 +207,7 @@ def product(domains, n_samples=-1): Bool = Domain([0, 0, 1, 1], "int64") +Scale = Domain([0.5,1,1.5]) def build_model(distfam, valuedomain, vardomains, extra_args=None): if extra_args is None: @@ -218,7 +220,12 @@ def build_model(distfam, valuedomain, vardomains, extra_args=None): distfam("value", shape=valuedomain.shape, transform=None, **vals) return m - +def laplace_asymmetric_logpdf(value, symmetry,scale = None): + kapinv = 1/symmetry + lPx = value * np.where(value >= 0, -symmetry, kapinv) + lPx -=np.log((symmetry+kapinv)) + return lPx + def integrate_nd(f, domain, shape, dtype): if shape == () or shape == (1,): if dtype in continuous_types: @@ -986,7 +993,13 @@ def test_laplace(self): {"mu": R, "b": Rplus}, lambda value, mu, b: sp.laplace.logcdf(value, mu, b), ) - + def test_laplace_asymmetric(self): + self.pymc3_matches_scipy( + AsymmetricLaplace, + R, + {"scale":Scale,"symmetry":Rplus}, + laplace_asymmetric_logpdf, + ) def test_lognormal(self): self.pymc3_matches_scipy( Lognormal, From b9c335e0c6b8fd0672e469cdcd1a2964376b3c9a Mon Sep 17 00:00:00 2001 From: Chandan Kumar Roy <chandanroy559@outlook.com> Date: Thu, 31 Dec 2020 23:50:31 +0530 Subject: [PATCH 2/7] unit test for random function added --- pymc3/distributions/continuous.py | 92 +++++++++++++++--------- pymc3/tests/test_distributions.py | 19 ++--- pymc3/tests/test_distributions_random.py | 20 ++++++ 3 files changed, 90 insertions(+), 41 deletions(-) diff --git a/pymc3/distributions/continuous.py b/pymc3/distributions/continuous.py index 402cfe9ffa..f808fcd3f0 100644 --- a/pymc3/distributions/continuous.py +++ b/pymc3/distributions/continuous.py @@ -1661,58 +1661,84 @@ def logcdf(self, value): tt.switch(tt.gt(y, 1), tt.log1p(-0.5 * tt.exp(-y)), tt.log(1 - 0.5 * tt.exp(-y))), ) + class AsymmetricLaplace(Continuous): - """ - Assymetric Laplace Distribution + r""" + Asymmetric-Laplace log-likelihood. + + The pdf of this distribution is + + ..math:: + {f(x|\\b,\kappa) = + \left({\frac{\\b}{\kappa + 1/\kappa}}\right)\,e^{-(x)\\b\,s\kappa ^{s}}} + where s = sgn(x) + + See also: https://en.wikipedia.org/wiki/Asymmetric_Laplace_distribution """ - def __init__(self, scale, symmetry, testval=0.0, *args, **kwargs): - """ - Constructor - :param scale: scale parameter - :param symmetry: asymmetry parameter. Reduces to a normal laplace distribution with value 1 - """ - self.scale = tt.as_tensor_variable(scale) - self.symmetry = tt.as_tensor_variable(symmetry) + def __init__(self, b, kappa, testval=0.0, *args, **kwargs): + self.b = tt.as_tensor_variable(b) + self.kappa = tt.as_tensor_variable(kappa) super().__init__(*args, **kwargs, testval=testval) - def random(self, point=None, size=None): - """ - Draw random samples from this distribution, using the inverse CDF method. - :param point: not used - :param size: size of sample to draw - :return: Samples - """ - if point is not None: - raise NotImplementedError('Random not implemented with point specified') - + def _random(self, b, kappa, size=None): if size is not None: u = np.random.uniform(size=size) - x = - tt.log((1 - u) * (1 + self.symmetry ** 2)) / (self.symmetry * self.scale) * ( - u > ((self.symmetry ** 2) / (1 + self.symmetry ** 2))) + self.symmetry * tt.log( - u * (1 + self.symmetry ** 2) / (self.symmetry ** 2)) / self.scale * ( - u < ((self.symmetry ** 2) / (1 + self.symmetry ** 2))) - + x = -np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) * ( + u > ((kappa ** 2) / (1 + kappa ** 2)) + ) + kappa * np.log(u * (1 + kappa ** 2) / (kappa ** 2)) / b * ( + u < ((kappa ** 2) / (1 + kappa ** 2)) + ) return x u = np.random.uniform() - if u > (self.symmetry ** 2) / (1 + self.symmetry ** 2): - x = - tt.log((1 - u) * (1 + self.symmetry ** 2)) / (self.symmetry * self.scale) + if u > (kappa ** 2) / (1 + kappa ** 2): + x = -np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) else: - x = self.symmetry * tt.log(u * (1 + self.symmetry ** 2) / (self.symmetry ** 2)) / self.scale + x = kappa * np.log(u * (1 + kappa ** 2) / (kappa ** 2)) / b return x + def random(self, point=None, size=None): + """ + Draw random samples from this distribution, using the inverse CDF method. + + Parameters + ---------- + point: dict, optional + Dict of variable values on which random values are to be + conditioned (uses default point if not specified). + size:int, optional + Desired size of random sample (returns one sample if not + specified). + + Returns + ------- + array + """ + b, kappa = draw_values([self.b, self.kappa], point=point, size=size) + return generate_samples(self._random, b, kappa, dist_shape=self.shape, size=size) + def logp(self, value): """ - Compute logp. - :param value: evaluation point - :return: log probability at evaluation point + Calculate log-probability of Asymmetric-Laplace distribution at specified value. + + Parameters + ---------- + value: numeric + Value(s) for which log-probability is calculated. If the log probabilities for multiple + values are desired the values must be provided in a numpy array or theano tensor + + Returns + ------- + TensorVariable """ - return tt.log(self.scale / (self.symmetry + (self.symmetry ** -1))) + ( - -value * self.scale * tt.sgn(value) * (self.symmetry ** tt.sgn(value))) + return tt.log(self.b / (self.kappa + (self.kappa ** -1))) + ( + -value * self.b * tt.sgn(value) * (self.kappa ** tt.sgn(value)) + ) + class Lognormal(PositiveContinuous): r""" diff --git a/pymc3/tests/test_distributions.py b/pymc3/tests/test_distributions.py index 41338f894c..948041373e 100644 --- a/pymc3/tests/test_distributions.py +++ b/pymc3/tests/test_distributions.py @@ -207,7 +207,6 @@ def product(domains, n_samples=-1): Bool = Domain([0, 0, 1, 1], "int64") -Scale = Domain([0.5,1,1.5]) def build_model(distfam, valuedomain, vardomains, extra_args=None): if extra_args is None: @@ -220,12 +219,14 @@ def build_model(distfam, valuedomain, vardomains, extra_args=None): distfam("value", shape=valuedomain.shape, transform=None, **vals) return m -def laplace_asymmetric_logpdf(value, symmetry,scale = None): - kapinv = 1/symmetry - lPx = value * np.where(value >= 0, -symmetry, kapinv) - lPx -=np.log((symmetry+kapinv)) + +def laplace_asymmetric_logpdf(value, kappa, b=None): + kapinv = 1 / kappa + lPx = value * np.where(value >= 0, -kappa, kapinv) + lPx -= np.log(kappa + kapinv) return lPx - + + def integrate_nd(f, domain, shape, dtype): if shape == () or shape == (1,): if dtype in continuous_types: @@ -993,13 +994,15 @@ def test_laplace(self): {"mu": R, "b": Rplus}, lambda value, mu, b: sp.laplace.logcdf(value, mu, b), ) + def test_laplace_asymmetric(self): self.pymc3_matches_scipy( AsymmetricLaplace, R, - {"scale":Scale,"symmetry":Rplus}, + {"b": Domain([0, 1, inf]), "kappa": Rplus}, laplace_asymmetric_logpdf, - ) + ) + def test_lognormal(self): self.pymc3_matches_scipy( Lognormal, diff --git a/pymc3/tests/test_distributions_random.py b/pymc3/tests/test_distributions_random.py index 5e735b6aed..eb359091aa 100644 --- a/pymc3/tests/test_distributions_random.py +++ b/pymc3/tests/test_distributions_random.py @@ -375,6 +375,11 @@ class TestLaplace(BaseTestCases.BaseTestCase): params = {"mu": 1.0, "b": 1.0} +class TestAsymmetricLaplace(BaseTestCases.BaseTestCase): + distribution = pm.AsymmetricLaplace + params = {"kappa": 1.0, "b": 1.0} + + class TestLognormal(BaseTestCases.BaseTestCase): distribution = pm.Lognormal params = {"mu": 1.0, "tau": 1.0} @@ -626,6 +631,21 @@ def ref_rand(size, mu, b): pymc3_random(pm.Laplace, {"mu": R, "b": Rplus}, ref_rand=ref_rand) + def test_laplace_asymmetric(self): + def ref_rand(size, kappa, b): + u = np.random.uniform(size=size) + x = -np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) * ( + u > ((kappa ** 2) / (1 + kappa ** 2)) + ) + kappa * np.log(u * (1 + kappa ** 2) / (kappa ** 2)) / b * ( + u < ((kappa ** 2) / (1 + kappa ** 2)) + ) + + return x + + pymc3_random( + pm.AsymmetricLaplace, {"b": Domain([0, 1, np.inf]), "kappa": Rplus}, ref_rand=ref_rand + ) + def test_lognormal(self): def ref_rand(size, mu, tau): return np.exp(mu + (tau ** -0.5) * st.norm.rvs(loc=0.0, scale=1.0, size=size)) From a6cc0ba824614a1eaf3340d813c4d847684e167a Mon Sep 17 00:00:00 2001 From: Chandan Kumar Roy <chandanroy559@outlook.com> Date: Sat, 2 Jan 2021 18:54:23 +0530 Subject: [PATCH 3/7] continuous.py updated --- pymc3/distributions/continuous.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pymc3/distributions/continuous.py b/pymc3/distributions/continuous.py index f808fcd3f0..1c68287cb6 100644 --- a/pymc3/distributions/continuous.py +++ b/pymc3/distributions/continuous.py @@ -1673,6 +1673,12 @@ class AsymmetricLaplace(Continuous): \left({\frac{\\b}{\kappa + 1/\kappa}}\right)\,e^{-(x)\\b\,s\kappa ^{s}}} where s = sgn(x) + Parameters + ---------- + b: + Scale parameter (b > 0) + kappa: + Symmetry parameter (kappa > 0) See also: https://en.wikipedia.org/wiki/Asymmetric_Laplace_distribution """ @@ -1735,8 +1741,11 @@ def logp(self, value): ------- TensorVariable """ - return tt.log(self.b / (self.kappa + (self.kappa ** -1))) + ( - -value * self.b * tt.sgn(value) * (self.kappa ** tt.sgn(value)) + return bound( + tt.log(self.b / (self.kappa + (self.kappa ** -1))) + + (-value * self.b * tt.sgn(value) * (self.kappa ** tt.sgn(value))), + 0 < self.b, + 0 < self.kappa, ) From 8b65c66173d3c79caed11e18e55e608616917efb Mon Sep 17 00:00:00 2001 From: Chandan Kumar Roy <chandanroy559@outlook.com> Date: Tue, 5 Jan 2021 01:18:00 +0530 Subject: [PATCH 4/7] suggested changes added --- pymc3/distributions/continuous.py | 60 ++++++++++++++---------- pymc3/tests/test_distributions.py | 8 ++-- pymc3/tests/test_distributions_random.py | 4 +- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/pymc3/distributions/continuous.py b/pymc3/distributions/continuous.py index 1c68287cb6..88cff6779f 100644 --- a/pymc3/distributions/continuous.py +++ b/pymc3/distributions/continuous.py @@ -1668,44 +1668,53 @@ class AsymmetricLaplace(Continuous): The pdf of this distribution is - ..math:: + .. math:: {f(x|\\b,\kappa) = \left({\frac{\\b}{\kappa + 1/\kappa}}\right)\,e^{-(x)\\b\,s\kappa ^{s}}} - where s = sgn(x) + + where + + .. math:: + + s = sgn(x) + + ======== ======================== + Support :math:`x \in \mathbb{R}` + Mean :math:`\mu-\frac{\\\kappa-1/\kappa}b` + Variance :math:`\frac{1+\kappa^{4}}{b^2\kappa^2 }` + ======== ======================== Parameters ---------- - b: + b: float Scale parameter (b > 0) - kappa: + kappa: float Symmetry parameter (kappa > 0) + mu: float + Location parameter - See also: https://en.wikipedia.org/wiki/Asymmetric_Laplace_distribution + See Also: + -------- + `Reference <https://en.wikipedia.org/wiki/Asymmetric_Laplace_distribution>`_ """ - def __init__(self, b, kappa, testval=0.0, *args, **kwargs): - self.b = tt.as_tensor_variable(b) - self.kappa = tt.as_tensor_variable(kappa) + def __init__(self, b, kappa, mu=0, *args, **kwargs): + self.b = tt.as_tensor_variable(floatX(b)) + self.kappa = tt.as_tensor_variable(floatX(kappa)) + self.mu = mu = tt.as_tensor_variable(floatX(mu)) - super().__init__(*args, **kwargs, testval=testval) + self.mean = self.mu - (self.kappa - 1 / self.kappa) / b + self.variance = (1 + self.kappa ** 4) / (self.kappa ** 2 * self.b ** 2) - def _random(self, b, kappa, size=None): - if size is not None: - u = np.random.uniform(size=size) - x = -np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) * ( - u > ((kappa ** 2) / (1 + kappa ** 2)) - ) + kappa * np.log(u * (1 + kappa ** 2) / (kappa ** 2)) / b * ( - u < ((kappa ** 2) / (1 + kappa ** 2)) - ) - return x - - u = np.random.uniform() - if u > (kappa ** 2) / (1 + kappa ** 2): - x = -np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) - else: - x = kappa * np.log(u * (1 + kappa ** 2) / (kappa ** 2)) / b + super().__init__(*args, **kwargs) - return x + def _random(self, b, kappa, size=None): + u = np.random.uniform(size=size) + switch = kappa ** 2 / (1 + kappa ** 2) + non_positive_x = kappa * np.log(u * (1 / switch)) / b + positive_x = -np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) + draws = non_positive_x * (u <= switch) + positive_x * (u > switch) + return draws def random(self, point=None, size=None): """ @@ -1741,6 +1750,7 @@ def logp(self, value): ------- TensorVariable """ + value = value - self.mu return bound( tt.log(self.b / (self.kappa + (self.kappa ** -1))) + (-value * self.b * tt.sgn(value) * (self.kappa ** tt.sgn(value))), diff --git a/pymc3/tests/test_distributions.py b/pymc3/tests/test_distributions.py index 948041373e..7f7e1195e9 100644 --- a/pymc3/tests/test_distributions.py +++ b/pymc3/tests/test_distributions.py @@ -220,10 +220,10 @@ def build_model(distfam, valuedomain, vardomains, extra_args=None): return m -def laplace_asymmetric_logpdf(value, kappa, b=None): +def laplace_asymmetric_logpdf(value, kappa, b): kapinv = 1 / kappa - lPx = value * np.where(value >= 0, -kappa, kapinv) - lPx -= np.log(kappa + kapinv) + lPx = value * b * np.where(value >= 0, -kappa, kapinv) + lPx += np.log(b / (kappa + kapinv)) return lPx @@ -999,7 +999,7 @@ def test_laplace_asymmetric(self): self.pymc3_matches_scipy( AsymmetricLaplace, R, - {"b": Domain([0, 1, inf]), "kappa": Rplus}, + {"b": Rplus, "kappa": Rplus}, laplace_asymmetric_logpdf, ) diff --git a/pymc3/tests/test_distributions_random.py b/pymc3/tests/test_distributions_random.py index eb359091aa..19b7901f8e 100644 --- a/pymc3/tests/test_distributions_random.py +++ b/pymc3/tests/test_distributions_random.py @@ -642,9 +642,7 @@ def ref_rand(size, kappa, b): return x - pymc3_random( - pm.AsymmetricLaplace, {"b": Domain([0, 1, np.inf]), "kappa": Rplus}, ref_rand=ref_rand - ) + pymc3_random(pm.AsymmetricLaplace, {"b": Rplus, "kappa": Rplus}, ref_rand=ref_rand) def test_lognormal(self): def ref_rand(size, mu, tau): From d3331418f4ac89cf57ac35beb0be688d5ae5098d Mon Sep 17 00:00:00 2001 From: Chandan Kumar Roy <chandanroy559@outlook.com> Date: Tue, 5 Jan 2021 18:53:25 +0530 Subject: [PATCH 5/7] updated --- pymc3/distributions/continuous.py | 19 +++++++++++-------- pymc3/tests/test_distributions.py | 5 +++-- pymc3/tests/test_distributions_random.py | 18 ++++++++---------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/pymc3/distributions/continuous.py b/pymc3/distributions/continuous.py index 88cff6779f..c60898ab16 100644 --- a/pymc3/distributions/continuous.py +++ b/pymc3/distributions/continuous.py @@ -1669,14 +1669,14 @@ class AsymmetricLaplace(Continuous): The pdf of this distribution is .. math:: - {f(x|\\b,\kappa) = - \left({\frac{\\b}{\kappa + 1/\kappa}}\right)\,e^{-(x)\\b\,s\kappa ^{s}}} + {f(x|\\b,\kappa,\mu) = + \left({\frac{\\b}{\kappa + 1/\kappa}}\right)\,e^{-(x-\mu)\\b\,s\kappa ^{s}}} where .. math:: - s = sgn(x) + s = sgn(x-\mu) ======== ======================== Support :math:`x \in \mathbb{R}` @@ -1706,13 +1706,16 @@ def __init__(self, b, kappa, mu=0, *args, **kwargs): self.mean = self.mu - (self.kappa - 1 / self.kappa) / b self.variance = (1 + self.kappa ** 4) / (self.kappa ** 2 * self.b ** 2) + assert_negative_support(kappa, "kappa", "AsymmetricLaplace") + assert_negative_support(b, "b", "AsymmetricLaplace") + super().__init__(*args, **kwargs) - def _random(self, b, kappa, size=None): + def _random(self, b, kappa, mu, size=None): u = np.random.uniform(size=size) switch = kappa ** 2 / (1 + kappa ** 2) - non_positive_x = kappa * np.log(u * (1 / switch)) / b - positive_x = -np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) + non_positive_x = mu + kappa * np.log(u * (1 / switch)) / b + positive_x = mu - np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) draws = non_positive_x * (u <= switch) + positive_x * (u > switch) return draws @@ -1733,8 +1736,8 @@ def random(self, point=None, size=None): ------- array """ - b, kappa = draw_values([self.b, self.kappa], point=point, size=size) - return generate_samples(self._random, b, kappa, dist_shape=self.shape, size=size) + b, kappa, mu = draw_values([self.b, self.kappa, self.mu], point=point, size=size) + return generate_samples(self._random, b, kappa, mu, dist_shape=self.shape, size=size) def logp(self, value): """ diff --git a/pymc3/tests/test_distributions.py b/pymc3/tests/test_distributions.py index 7f7e1195e9..c2548423f6 100644 --- a/pymc3/tests/test_distributions.py +++ b/pymc3/tests/test_distributions.py @@ -220,8 +220,9 @@ def build_model(distfam, valuedomain, vardomains, extra_args=None): return m -def laplace_asymmetric_logpdf(value, kappa, b): +def laplace_asymmetric_logpdf(value, kappa, b, mu): kapinv = 1 / kappa + value = value - mu lPx = value * b * np.where(value >= 0, -kappa, kapinv) lPx += np.log(b / (kappa + kapinv)) return lPx @@ -999,7 +1000,7 @@ def test_laplace_asymmetric(self): self.pymc3_matches_scipy( AsymmetricLaplace, R, - {"b": Rplus, "kappa": Rplus}, + {"b": Rplus, "kappa": Rplus, "mu": R}, laplace_asymmetric_logpdf, ) diff --git a/pymc3/tests/test_distributions_random.py b/pymc3/tests/test_distributions_random.py index 19b7901f8e..6cc4fe8042 100644 --- a/pymc3/tests/test_distributions_random.py +++ b/pymc3/tests/test_distributions_random.py @@ -377,7 +377,7 @@ class TestLaplace(BaseTestCases.BaseTestCase): class TestAsymmetricLaplace(BaseTestCases.BaseTestCase): distribution = pm.AsymmetricLaplace - params = {"kappa": 1.0, "b": 1.0} + params = {"kappa": 1.0, "b": 1.0, "mu": 0.0} class TestLognormal(BaseTestCases.BaseTestCase): @@ -632,17 +632,15 @@ def ref_rand(size, mu, b): pymc3_random(pm.Laplace, {"mu": R, "b": Rplus}, ref_rand=ref_rand) def test_laplace_asymmetric(self): - def ref_rand(size, kappa, b): + def ref_rand(size, kappa, b, mu): u = np.random.uniform(size=size) - x = -np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) * ( - u > ((kappa ** 2) / (1 + kappa ** 2)) - ) + kappa * np.log(u * (1 + kappa ** 2) / (kappa ** 2)) / b * ( - u < ((kappa ** 2) / (1 + kappa ** 2)) - ) - - return x + switch = kappa ** 2 / (1 + kappa ** 2) + non_positive_x = mu + kappa * np.log(u * (1 / switch)) / b + positive_x = mu - np.log((1 - u) * (1 + kappa ** 2)) / (kappa * b) + draws = non_positive_x * (u <= switch) + positive_x * (u > switch) + return draws - pymc3_random(pm.AsymmetricLaplace, {"b": Rplus, "kappa": Rplus}, ref_rand=ref_rand) + pymc3_random(pm.AsymmetricLaplace, {"b": Rplus, "kappa": Rplus, "mu": R}, ref_rand=ref_rand) def test_lognormal(self): def ref_rand(size, mu, tau): From a19c8f9c5bf04fe4c30feb3a653c4a2b764b4141 Mon Sep 17 00:00:00 2001 From: Chandan Kumar Roy <chandanroy559@outlook.com> Date: Tue, 5 Jan 2021 19:52:20 +0530 Subject: [PATCH 6/7] RELEASE-NOTES.md and continuous.rst updated --- RELEASE-NOTES.md | 2 +- docs/source/api/distributions/continuous.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index a313d6cc33..4cbfb52e73 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -21,7 +21,7 @@ It also brings some dreadfully awaited fixes, so be sure to go through the chang - `plot_posterior_predictive_glm` now works with `arviz.InferenceData` as well (see [#4234](https://github.com/pymc-devs/pymc3/pull/4234)) - Add `logcdf` method to all univariate discrete distributions (see [#4387](https://github.com/pymc-devs/pymc3/pull/4387)). - Add `random` method to `MvGaussianRandomWalk` (see [#4388](https://github.com/pymc-devs/pymc3/pull/4388)) - +- `AsymmetricLaplace` distribution added (see [#4392](https://github.com/pymc-devs/pymc3/pull/4392)). ### Maintenance - Fixed bug whereby partial traces returns after keyboard interrupt during parallel sampling had fewer draws than would've been available [#4318](https://github.com/pymc-devs/pymc3/pull/4318) - Make `sample_shape` same across all contexts in `draw_values` (see [#4305](https://github.com/pymc-devs/pymc3/pull/4305)). diff --git a/docs/source/api/distributions/continuous.rst b/docs/source/api/distributions/continuous.rst index fcc49d2e11..9222dcb797 100644 --- a/docs/source/api/distributions/continuous.rst +++ b/docs/source/api/distributions/continuous.rst @@ -16,6 +16,7 @@ Continuous Kumaraswamy Exponential Laplace + AsymmetricLaplace StudentT HalfStudentT Cauchy From 39146c78edb097951d4eaddf450641b70b792906 Mon Sep 17 00:00:00 2001 From: Chandan Kumar Roy <chandanroy559@outlook.com> Date: Tue, 5 Jan 2021 19:56:24 +0530 Subject: [PATCH 7/7] --- RELEASE-NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 4cbfb52e73..10c3e5a2ec 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -22,6 +22,7 @@ It also brings some dreadfully awaited fixes, so be sure to go through the chang - Add `logcdf` method to all univariate discrete distributions (see [#4387](https://github.com/pymc-devs/pymc3/pull/4387)). - Add `random` method to `MvGaussianRandomWalk` (see [#4388](https://github.com/pymc-devs/pymc3/pull/4388)) - `AsymmetricLaplace` distribution added (see [#4392](https://github.com/pymc-devs/pymc3/pull/4392)). + ### Maintenance - Fixed bug whereby partial traces returns after keyboard interrupt during parallel sampling had fewer draws than would've been available [#4318](https://github.com/pymc-devs/pymc3/pull/4318) - Make `sample_shape` same across all contexts in `draw_values` (see [#4305](https://github.com/pymc-devs/pymc3/pull/4305)).