Skip to content

Add sample_generative function #2983

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 18, 2018
Prev Previous commit
Next Next commit
WIP
  • Loading branch information
ColCarroll committed Jun 17, 2018
commit e70b3cb057e75549fcb2b078572ac76c2c755e37
13 changes: 5 additions & 8 deletions pymc3/distributions/discrete.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from pymc3.util import get_variable_name
from .dist_math import bound, factln, binomln, betaln, logpow
from .distribution import Discrete, draw_values, generate_samples, reshape_sampled
from .distribution import Discrete, draw_values, generate_samples
from pymc3.math import tround, sigmoid, logaddexp, logit, log1pexp


Expand Down Expand Up @@ -495,7 +495,7 @@ def random(self, point=None, size=None):
dist_shape=self.shape,
size=size)
g[g == 0] = np.finfo(float).eps # Just in case
return reshape_sampled(stats.poisson.rvs(g), size, self.shape)
return stats.poisson.rvs(g)

def logp(self, value):
mu = self.mu
Expand Down Expand Up @@ -849,8 +849,7 @@ def random(self, point=None, size=None):
g = generate_samples(stats.poisson.rvs, theta,
dist_shape=self.shape,
size=size)
sampled = g * (np.random.random(np.squeeze(g.shape)) < psi)
return reshape_sampled(sampled, size, self.shape)
return g * (np.random.random(np.squeeze(g.shape)) < psi)

def logp(self, value):
psi = self.psi
Expand Down Expand Up @@ -942,8 +941,7 @@ def random(self, point=None, size=None):
g = generate_samples(stats.binom.rvs, n, p,
dist_shape=self.shape,
size=size)
sampled = g * (np.random.random(np.squeeze(g.shape)) < psi)
return reshape_sampled(sampled, size, self.shape)
return g * (np.random.random(np.squeeze(g.shape)) < psi)

def logp(self, value):
psi = self.psi
Expand Down Expand Up @@ -1061,8 +1059,7 @@ def random(self, point=None, size=None):
dist_shape=self.shape,
size=size)
g[g == 0] = np.finfo(float).eps # Just in case
sampled = stats.poisson.rvs(g) * (np.random.random(np.squeeze(g.shape)) < psi)
return reshape_sampled(sampled, size, self.shape)
return stats.poisson.rvs(g) * (np.random.random(np.squeeze(g.shape)) < psi)

def logp(self, value):
alpha = self.alpha
Expand Down
53 changes: 19 additions & 34 deletions pymc3/distributions/distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,12 +390,16 @@ def _draw_value(param, point=None, givens=None, size=None):
else:
variables = values = []
func = _compile_theano_function(param, variables)
return func(*values)
if size and all(len(v) == size for v in values):
return np.array([func(*v) for v in zip(*values)])
else:
return func(*values)
else:
raise ValueError('Unexpected type in draw_value: %s' % type(param))


def infer_shape(shape):
def to_tuple(shape):
"""Convert ints, arrays, and Nones to tuples"""
try:
shape = tuple(shape or ())
except TypeError: # If size is an int
Expand All @@ -405,27 +409,6 @@ def infer_shape(shape):
return shape


def reshape_sampled(sampled, size, dist_shape):
dist_shape = infer_shape(dist_shape)
repeat_shape = infer_shape(size)

if np.size(sampled) == 1 or repeat_shape or dist_shape:
return np.reshape(sampled, repeat_shape + dist_shape)
else:
return sampled.squeeze()


def replicate_samples(generator, size, repeats, *args, **kwargs):
n = int(np.prod(repeats))
if n == 1:
samples = generator(size=size, *args, **kwargs)
else:
samples = np.array([generator(size=size, *args, **kwargs)
for _ in range(n)])
samples = np.reshape(samples, tuple(repeats) + tuple(size))
return samples


def generate_samples(generator, *args, **kwargs):
"""Generate samples from the distribution of a random variable.

Expand Down Expand Up @@ -458,7 +441,6 @@ def generate_samples(generator, *args, **kwargs):
dist_shape = kwargs.pop('dist_shape', ())
size = kwargs.pop('size', None)
broadcast_shape = kwargs.pop('broadcast_shape', None)

if size is None:
size = 1

Expand All @@ -472,24 +454,27 @@ def generate_samples(generator, *args, **kwargs):
inputs = args + tuple(kwargs.values())
broadcast_shape = np.broadcast(*inputs).shape # size of generator(size=1)

dist_shape = infer_shape(dist_shape)
broadcast_shape = (1,) + infer_shape(broadcast_shape)
size_tup = infer_shape(size)

if broadcast_shape == (1,):
dist_shape = to_tuple(dist_shape)
broadcast_shape = to_tuple(broadcast_shape)
size_tup = to_tuple(size)
if broadcast_shape == ():
samples = generator(size=size_tup + dist_shape, *args, **kwargs)
elif broadcast_shape[-len(dist_shape):] == dist_shape:
samples = generator(*args, **kwargs)
if size == 1 or (broadcast_shape == size_tup + dist_shape):
samples = generator(size=broadcast_shape, *args, **kwargs)
else:
samples = generator(*args, **kwargs)
elif dist_shape == broadcast_shape:
samples = generator(size=size, *args, **kwargs)
elif broadcast_shape[1:1 + len(size_tup)] == size_tup:
suffix = broadcast_shape[1 + len(size_tup):] + dist_shape
samples = [generator(*args, **kwargs).reshape(size_tup + (1,)) for _ in range(np.prod(suffix))]
elif broadcast_shape[:len(size_tup)] == size_tup:
suffix = broadcast_shape[len(size_tup):] + dist_shape
samples = [generator(*args, **kwargs).reshape(size_tup + (1,)) for _ in range(np.prod(suffix, dtype=int))]
samples = np.hstack(samples).reshape(size_tup + suffix)
else:
raise TypeError(f'''Attempted to generate values with incompatible shapes:
size: {size}
dist_shape: {dist_shape}
broadcast_shape: {broadcast_shape}
''')
return samples.squeeze()
samples = samples.squeeze()
return samples
1 change: 0 additions & 1 deletion pymc3/distributions/multivariate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1469,7 +1469,6 @@ def _setup_random(self):
elif self._cov_type == 'evd':
covs = []
for eig, Q in zip(self.eigs_sep, self.Qs):
# print()
cov_i = tt.dot(Q, tt.dot(tt.diag(eig), Q.T))
covs.append(cov_i)
cov = kronecker(*covs)
Expand Down
6 changes: 3 additions & 3 deletions pymc3/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def get_named_nodes_and_relations(graph):
is a theano named node, and the corresponding value is the set
of theano named nodes that are children of the node. These child
relations skip unnamed intermediate nodes.

"""
if graph.name is not None:
node_parents = {graph: set()}
Expand Down Expand Up @@ -1017,7 +1017,7 @@ def check_test_point(self, test_point=None, round_vals=2):
if test_point is None:
test_point = self.test_point

return Series({RV.name:np.round(RV.logp(self.test_point), round_vals) for RV in self.basic_RVs},
return Series({RV.name:np.round(RV.logp(self.test_point), round_vals) for RV in self.basic_RVs},
name='Log-probability of test_point')

def _repr_latex_(self, name=None, dist=None):
Expand Down Expand Up @@ -1244,7 +1244,7 @@ def pandas_to_array(data):
ret = generator(data)
else:
ret = np.asarray(data)
return pm.smartfloatX(ret)
return pm.floatX(ret)


def as_tensor(data, name, model, distribution):
Expand Down
4 changes: 0 additions & 4 deletions pymc3/tests/test_distributions.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,10 +916,6 @@ def test_multinomial_mode(self, p, n):
[.25, .25, .25, .25]], (2, 4), 13],
[[[.25, .25, .25, .25],
[.25, .25, .25, .25]], (2, 4), [17, 19]],
[[[.25, .25, .25, .25],
[.25, .25, .25, .25]], (1, 2, 4), [23, 29]],
[[[.25, .25, .25, .25],
[.25, .25, .25, .25]], (10, 2, 4), [31, 37]],
])
def test_multinomial_random(self, p, shape, n):
p = np.asarray(p)
Expand Down