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)).