Skip to content

Commit e5dc51b

Browse files
Merge pull request #106 from brandonwillard/fix-pymc3-rv-params
Fix PyMC3 RV Parameterizations
2 parents 6e319d1 + 32187ed commit e5dc51b

File tree

4 files changed

+45
-25
lines changed

4 files changed

+45
-25
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def get_long_description():
3636
"scipy>=1.4.0",
3737
"Theano>=1.0.4",
3838
"tf-estimator-nightly==2.1.0.dev2020012309",
39-
"tf-nightly==2.2.0.dev20200201",
39+
"tf-nightly==2.2.0.dev20200301",
4040
"tfp-nightly==0.10.0.dev20200201",
4141
"multipledispatch>=0.6.0",
4242
"logical-unification>=0.4.3",

symbolic_pymc/theano/ops.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,6 @@ def perform(self, node, inputs, outputs):
306306
"""Draw samples using Numpy/SciPy."""
307307
rng_out, smpl_out = outputs
308308

309-
# Draw from `rng` if `self.inplace` is `True`, and from a copy of `rng`
310-
# otherwise.
311309
args = list(inputs)
312310
rng = args.pop()
313311
size = args.pop()
@@ -327,6 +325,8 @@ def perform(self, node, inputs, outputs):
327325
else:
328326
size = tuple(size)
329327

328+
# Draw from `rng` if `self.inplace` is `True`, and from a copy of `rng`
329+
# otherwise.
330330
if not self.inplace:
331331
rng = copy(rng)
332332

symbolic_pymc/theano/pymc3.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def _convert_rv_to_dist_MvNormal(op, rv):
149149
@convert_dist_to_rv.register(pm.Gamma, object)
150150
def convert_dist_to_rv_Gamma(dist, rng):
151151
size = dist.shape.astype(int)[GammaRV.ndim_supp :]
152-
res = GammaRV(dist.alpha, tt.inv(dist.beta), size=size, rng=rng)
152+
res = GammaRV(dist.alpha, dist.beta, size=size, rng=rng)
153153
return res
154154

155155

@@ -162,14 +162,13 @@ def _convert_rv_to_dist_Gamma(op, rv):
162162
@convert_dist_to_rv.register(pm.InverseGamma, object)
163163
def convert_dist_to_rv_InverseGamma(dist, rng):
164164
size = dist.shape.astype(int)[InvGammaRV.ndim_supp :]
165-
res = InvGammaRV(dist.alpha, scale=dist.beta, size=size, rng=rng)
165+
res = InvGammaRV(dist.alpha, rate=dist.beta, size=size, rng=rng)
166166
return res
167167

168168

169169
@_convert_rv_to_dist.register(InvGammaRVType, Apply)
170170
def _convert_rv_to_dist_InvGamma(op, rv):
171-
assert not np.any(tt_get_values(rv.inputs[1]))
172-
params = {"alpha": rv.inputs[0], "beta": rv.inputs[2]}
171+
params = {"alpha": rv.inputs[0], "beta": rv.inputs[1]}
173172
return pm.InverseGamma, params
174173

175174

symbolic_pymc/theano/random_variables.py

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import numpy as np
12
import theano
23
import scipy
34
import theano.tensor as tt
@@ -7,7 +8,6 @@
78
from .ops import RandomVariable, param_supp_shape_fn
89

910

10-
# Continuous Numpy-generated variates
1111
class UniformRVType(RandomVariable):
1212
print_name = ("U", "\\operatorname{U}")
1313

@@ -71,10 +71,19 @@ class GammaRVType(RandomVariable):
7171
print_name = ("Gamma", "\\operatorname{Gamma}")
7272

7373
def __init__(self):
74-
super().__init__("gamma", theano.config.floatX, 0, [0, 0], "gamma", inplace=True)
74+
super().__init__(
75+
"gamma",
76+
theano.config.floatX,
77+
0,
78+
[0, 0],
79+
lambda rng, shape, rate, size: scipy.stats.gamma.rvs(
80+
shape, scale=1.0 / rate, size=size, random_state=rng
81+
),
82+
inplace=True,
83+
)
7584

76-
def make_node(self, shape, scale, size=None, rng=None, name=None):
77-
return super().make_node(shape, scale, size=size, rng=rng, name=name)
85+
def make_node(self, shape, rate, size=None, rng=None, name=None):
86+
return super().make_node(shape, rate, size=size, rng=rng, name=name)
7887

7988

8089
GammaRV = GammaRVType()
@@ -93,20 +102,27 @@ def make_node(self, scale, size=None, rng=None, name=None):
93102
ExponentialRV = ExponentialRVType()
94103

95104

96-
# One with multivariate support
97105
class MvNormalRVType(RandomVariable):
98106
print_name = ("N", "\\operatorname{N}")
99107

100108
def __init__(self):
101109
super().__init__(
102-
"multivariate_normal",
103-
theano.config.floatX,
104-
1,
105-
[1, 2],
106-
"multivariate_normal",
107-
inplace=True,
110+
"multivariate_normal", theano.config.floatX, 1, [1, 2], self._smpl_fn, inplace=True,
108111
)
109112

113+
@classmethod
114+
def _smpl_fn(cls, rng, mean, cov, size):
115+
res = np.atleast_1d(
116+
scipy.stats.multivariate_normal(mean=mean, cov=cov, allow_singular=True).rvs(
117+
size=size, random_state=rng
118+
)
119+
)
120+
121+
if size is not None:
122+
res = res.reshape(list(size) + [-1])
123+
124+
return res
125+
110126
def make_node(self, mean, cov, size=None, rng=None, name=None):
111127
return super().make_node(mean, cov, size=size, rng=rng, name=name)
112128

@@ -127,7 +143,6 @@ def make_node(self, alpha, size=None, rng=None, name=None):
127143
DirichletRV = DirichletRVType()
128144

129145

130-
# A discrete Numpy-generated variate
131146
class PoissonRVType(RandomVariable):
132147
print_name = ("Pois", "\\operatorname{Pois}")
133148

@@ -141,7 +156,6 @@ def make_node(self, rate, size=None, rng=None, name=None):
141156
PoissonRV = PoissonRVType()
142157

143158

144-
# A SciPy-generated variate
145159
class CauchyRVType(RandomVariable):
146160
print_name = ("C", "\\operatorname{C}")
147161

@@ -191,12 +205,14 @@ def __init__(self):
191205
theano.config.floatX,
192206
0,
193207
[0, 0, 0],
194-
lambda rng, *args: scipy.stats.invgamma.rvs(*args, random_state=rng),
208+
lambda rng, shape, rate, size: scipy.stats.invgamma.rvs(
209+
shape, scale=rate, size=size, random_state=rng
210+
),
195211
inplace=True,
196212
)
197213

198-
def make_node(self, a, loc=0.0, scale=1.0, size=None, rng=None, name=None):
199-
return super().make_node(a, loc, scale, size=size, rng=rng, name=name)
214+
def make_node(self, shape, rate=1.0, size=None, rng=None, name=None):
215+
return super().make_node(shape, rate, size=size, rng=rng, name=name)
200216

201217

202218
InvGammaRV = InvGammaRVType()
@@ -277,9 +293,14 @@ def make_node(self, n, a, b, size=None, rng=None, name=None):
277293
BetaBinomialRV = BetaBinomialRVType()
278294

279295

280-
# Support shape is determined by the first dimension in the *second* parameter
281-
# (i.e. the probabilities vector)
282296
class MultinomialRVType(RandomVariable):
297+
"""A Multinomial random variable type.
298+
299+
FYI: Support shape is determined by the first dimension in the *second*
300+
parameter (i.e. the probabilities vector).
301+
302+
"""
303+
283304
print_name = ("MN", "\\operatorname{MN}")
284305

285306
def __init__(self):

0 commit comments

Comments
 (0)