Skip to content

Commit bce6a22

Browse files
farhanreynaldotwiecki
authored andcommitted
refactor Moyal distribution (#4704)
* refactor Moyal distribution * fixed ndim_params on moyal Co-authored-by: Farhan Reynaldo <[email protected]>
1 parent 503cfae commit bce6a22

File tree

3 files changed

+36
-47
lines changed

3 files changed

+36
-47
lines changed

pymc3/distributions/continuous.py

+23-38
Original file line numberDiff line numberDiff line change
@@ -3959,6 +3959,21 @@ def _distr_parameters_for_repr(self):
39593959
return []
39603960

39613961

3962+
class MoyalRV(RandomVariable):
3963+
name = "moyal"
3964+
ndim_supp = 0
3965+
ndims_params = [0, 0]
3966+
dtype = "floatX"
3967+
_print_name = ("Moyal", "\\operatorname{Moyal}")
3968+
3969+
@classmethod
3970+
def rng_fn(cls, rng, mu, sigma, size=None):
3971+
return stats.moyal.rvs(mu, sigma, size=size, random_state=rng)
3972+
3973+
3974+
moyal = MoyalRV()
3975+
3976+
39623977
class Moyal(Continuous):
39633978
r"""
39643979
Moyal log-likelihood.
@@ -4006,43 +4021,18 @@ class Moyal(Continuous):
40064021
sigma: float
40074022
Scale parameter (sigma > 0).
40084023
"""
4024+
rv_op = moyal
40094025

4010-
def __init__(self, mu=0, sigma=1.0, *args, **kwargs):
4011-
self.mu = at.as_tensor_variable(floatX(mu))
4012-
self.sigma = at.as_tensor_variable(floatX(sigma))
4026+
@classmethod
4027+
def dist(cls, mu=0, sigma=1.0, *args, **kwargs):
4028+
mu = at.as_tensor_variable(floatX(mu))
4029+
sigma = at.as_tensor_variable(floatX(sigma))
40134030

40144031
assert_negative_support(sigma, "sigma", "Moyal")
40154032

4016-
self.mean = self.mu + self.sigma * (np.euler_gamma + at.log(2))
4017-
self.median = self.mu - self.sigma * at.log(2 * at.erfcinv(1 / 2) ** 2)
4018-
self.mode = self.mu
4019-
self.variance = (np.pi ** 2 / 2.0) * self.sigma ** 2
4020-
4021-
super().__init__(*args, **kwargs)
4022-
4023-
def random(self, point=None, size=None):
4024-
"""
4025-
Draw random values from Moyal distribution.
4026-
4027-
Parameters
4028-
----------
4029-
point: dict, optional
4030-
Dict of variable values on which random values are to be
4031-
conditioned (uses default point if not specified).
4032-
size: int, optional
4033-
Desired size of random sample (returns one sample if not
4034-
specified).
4035-
4036-
Returns
4037-
-------
4038-
array
4039-
"""
4040-
# mu, sigma = draw_values([self.mu, self.sigma], point=point, size=size)
4041-
# return generate_samples(
4042-
# stats.moyal.rvs, loc=mu, scale=sigma, dist_shape=self.shape, size=size
4043-
# )
4033+
return super().dist([mu, sigma], *args, **kwargs)
40444034

4045-
def logp(self, value):
4035+
def logp(value, mu, sigma):
40464036
"""
40474037
Calculate log-probability of Moyal distribution at specified value.
40484038
@@ -4056,15 +4046,13 @@ def logp(self, value):
40564046
-------
40574047
TensorVariable
40584048
"""
4059-
mu = self.mu
4060-
sigma = self.sigma
40614049
scaled = (value - mu) / sigma
40624050
return bound(
40634051
(-(1 / 2) * (scaled + at.exp(-scaled)) - at.log(sigma) - (1 / 2) * at.log(2 * np.pi)),
40644052
0 < sigma,
40654053
)
40664054

4067-
def logcdf(self, value):
4055+
def logcdf(value, mu, sigma):
40684056
"""
40694057
Compute the log of the cumulative distribution function for Moyal distribution
40704058
at the specified value.
@@ -4079,9 +4067,6 @@ def logcdf(self, value):
40794067
-------
40804068
TensorVariable
40814069
"""
4082-
mu = self.mu
4083-
sigma = self.sigma
4084-
40854070
scaled = (value - mu) / sigma
40864071
return bound(
40874072
at.log(at.erfc(at.exp(-scaled / 2) * (2 ** -0.5))),

pymc3/tests/test_distributions.py

-2
Original file line numberDiff line numberDiff line change
@@ -2547,7 +2547,6 @@ def test_rice(self):
25472547
lambda value, b, sigma: sp.rice.logpdf(value, b=b, loc=0, scale=sigma),
25482548
)
25492549

2550-
@pytest.mark.xfail(reason="Distribution not refactored yet")
25512550
def test_moyal_logp(self):
25522551
# Using a custom domain, because the standard `R` domain undeflows with scipy in float64
25532552
value_domain = Domain([-inf, -1.5, -1, -0.01, 0.0, 0.01, 1, 1.5, inf])
@@ -2558,7 +2557,6 @@ def test_moyal_logp(self):
25582557
lambda value, mu, sigma: floatX(sp.moyal.logpdf(value, mu, sigma)),
25592558
)
25602559

2561-
@pytest.mark.xfail(reason="Distribution not refactored yet")
25622560
@pytest.mark.xfail(
25632561
condition=(aesara.config.floatX == "float32"),
25642562
reason="Pymc3 underflows earlier than scipy on float32",

pymc3/tests/test_distributions_random.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,6 @@ class TestZeroInflatedBinomial(BaseTestCases.BaseTestCase):
319319
params = {"n": 10, "p": 0.6, "psi": 0.3}
320320

321321

322-
@pytest.mark.xfail(reason="This distribution has not been refactored for v4")
323-
class TestMoyal(BaseTestCases.BaseTestCase):
324-
distribution = pm.Moyal
325-
params = {"mu": 0.0, "sigma": 1.0}
326-
327-
328322
class BaseTestDistribution(SeededTest):
329323
pymc_dist: Optional[Callable] = None
330324
pymc_dist_params = dict()
@@ -491,6 +485,19 @@ class TestStudentT(BaseTestDistribution):
491485
]
492486

493487

488+
class TestMoyal(BaseTestDistribution):
489+
pymc_dist = pm.Moyal
490+
pymc_dist_params = {"mu": 0.0, "sigma": 1.0}
491+
expected_rv_op_params = {"mu": 0.0, "sigma": 1.0}
492+
reference_dist_params = {"loc": 0.0, "scale": 1.0}
493+
reference_dist = seeded_scipy_distribution_builder("moyal")
494+
tests_to_run = [
495+
"check_pymc_params_match_rv_op",
496+
"check_pymc_draws_match_reference",
497+
"check_rv_size",
498+
]
499+
500+
494501
class TestStudentTLam(BaseTestDistribution):
495502
pymc_dist = pm.StudentT
496503
lam, sigma = get_tau_sigma(tau=2.0)
@@ -1391,7 +1398,6 @@ def ref_rand(size, mu, sigma):
13911398

13921399
pymc3_random(pm.LogitNormal, {"mu": R, "sigma": Rplus}, ref_rand=ref_rand)
13931400

1394-
@pytest.mark.xfail(reason="This distribution has not been refactored for v4")
13951401
def test_moyal(self):
13961402
def ref_rand(size, mu, sigma):
13971403
return st.moyal.rvs(loc=mu, scale=sigma, size=size)

0 commit comments

Comments
 (0)