 # DensityDist conditional random() method

Hi pymc Comunity,
I’d like to do posterior predictive checks on a DensityDist.
I’m implementing a random method for the custom DensityDist distribution, however, the ‘random’ values produced are conditional on other observed data. How can I get this data into the random() method? Maybe something to do with the point input?

I have a simplified version on my code below, where a value (next_epoch) is sampled categorically, conditioned on another value (current_epoch)

``````with pm.Model() as model:
BoundedNormal = pm.Bound(pm.Normal, lower=0, upper=1)
trans_baserate = BoundedNormal('trans_baserate', mu=0.5,
sd=1e5, shape=(nstages, nstages))

def logp(current_epoch, next_epoch):
current_idx = tt.cast(current_epoch, 'int16')
next_idx = tt.cast(next_epoch, 'int16')
cat = pm.Categorical.dist(p=trans_baserate[current_idx, :])
return cat.logp(next_idx)

def random(point=None, size=None):
trans_baserate_ = pm.distributions.multivariate.draw_values([trans_baserate], point=point, size=size)
def _random(point, trans_baserate, size=None):
trans_p = trans_baserate[point['current_epoch'], :] #Complains here :frowning:
return pm.distributions.dist_math.random_choice(p=trans_p, size=size)

return pm.distributions.multivariate.generate_samples(_random,
point=point,
trans_baserate=trans_baserate_,
size=size)

next_epoch = pm.DensityDist('next_epoch', logp,
observed={'current_epoch': data['current_epoch'],
'next_epoch': data['next_epoch']},
random=random)
``````

The low response rate here makes me wonder if this is even possible? Maybe shared theano variables could help?

1 Like

Hi, can any of the devs help me here please? Or is this not possible? Conversely, I will raise this on github.

There is no FreeRV in your model call ‘current_epoch’ - that’s why it complains.
How about casting `current_epoch` to theano shared variable and just use it like in the logp: `trans_p = trans_baserate[current_epoch, :]`?

1 Like

Amazing!
Seems a little hacky using a variable from the parent scope, but it works Further, I had to call `.eval()` on the shared `current_epoch` in the `random()` function, as the random function should be using numpy arrays and not theano tensors.

I dont think it is a good practice to call `.eval()` - try `draw_valued` instead:

Thanks,
I tried that but:

``````current_epoch_values = draw_values(current_epoch_shared)
``````

throws:

``````
File "/home/bdyetton/PSleep/src/modeling/sleep_stage_models.py", line 208, in _random
current_epoch_values = draw_values(current_epoch_shared)
File "/home/bdyetton/anaconda3/envs/psleep/lib/python3.7/site-packages/pymc3/distributions/distribution.py", line 283, in draw_values
params = dict(enumerate(params))
File "/home/bdyetton/anaconda3/envs/psleep/lib/python3.7/site-packages/theano/tensor/var.py", line 628, in __iter__
for i in xrange(theano.tensor.basic.get_vector_length(self)):
File "/home/bdyetton/anaconda3/envs/psleep/lib/python3.7/site-packages/theano/tensor/basic.py", line 4828, in get_vector_length
raise ValueError("length not known: %s" % msg)
ValueError: length not known: <TensorType(int64, vector)> [id A]
``````

Giving a size input to draw values does not help either.

I think it needs a list as input, try:

``````current_epoch_values = draw_values([current_epoch_shared])
``````

Ahhh, silly me. Thanks!
+1 for type hints in pymc4 1 Like