How to write complicated Deterministic?

I’m trying to translate examples in “Bayesian methods for hackers” into pyMC v5. Actually it is really painful since many documents from google are mixed with v2, v3, v4. Even I have read the migration guide of v4, there’s still a lot I don’t understand.

To be specific, in the SMS frequency example, how can I construct lambda_ that involves array index manipulation?

lambda_1 = pm.Exponential("lambda_1", alpha)
lambda_2 = pm.Exponential("lambda_2", alpha)
tau = pm.DiscreteUniform("tau", lower=0, upper=n_count_data)

@pm.deterministic
def lambda_(tau=tau, lambda_1=lambda_1, lambda_2=lambda_2):
    out = np.zeros(n_count_data)
    out[:tau] = lambda_1  # lambda before tau is lambda1
    out[tau:] = lambda_2  # lambda after tau is lambda2
    return out

observation = pm.Poisson("obs", lambda_, value=count_data, observed=True)
model = pm.Model([observation, lambda_1, lambda_2, tau])

I tried the following:

def mixed_lambda(lambda1=lambda1, lambda2=lambda2, tau=tau):
    out = np.zeros(len(real_pv))
    t = tau.eval()
    out[:t] = lambda1.eval()
    out[t:] = lambda2.eval()
    return out

with model:
    mixed_lambda = pm.Deterministic("mixed_lambda", mixed_lambda())

However pyMC blames incompatible between np.array and aesara.tensor. Could you tell me the right way to do it? Any related tip and best practice are welcome. Thank you in advance.

The book has an official PyMC3 version: GitHub - CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers: aka "Bayesian Methods for Hackers": An introduction to Bayesian methods + probabilistic programming with a computation/understanding-first, mathematics-second point of view. All in pure Python ;)

That API should be much more similar to V4/V5.

In general you can write complex deterministics just like you would write Numpy (replacing np by pt or at), except for some special operations like branching, looping and writing to arrays (as you are doing with indexing). Writing to arrays is supported via set_subtensor, but it’s probably easier to use where which works just like in numpy.

You should not use eval. That’s for debugging only, as it tries to evaluate the expression you have. You should always give PyMC an unevaluated expression.

import pytensor.tensor as pt

with pm.Model() as m:
  lambda_1 = pm.Exponential("lambda_1", alpha)
  lambda_2 = pm.Exponential("lambda_2", alpha)
  tau = pm.DiscreteUniform("tau", lower=0, upper=n_count_data)

  lambda_ = pt.where(tau <= pt.arange(len(count_data)), lambda_1, lambda_2)
  observation = pm.Poisson("obs", lambda_, observed=count_data)
1 Like

Thank you for the reply, it works! pt.where and .eval concepts helps me a lot.

However, when I change pytensor to aesara, it blames at tau <= at.arange(len(real_pv)) part for:

NotImplementedError: Cannot convert ARange{dtype='int64'}.0 to a tensor variable.

File ~/pyenv/lib/python3.10/site-packages/pytensor/tensor/var.py:45, in _tensor_py_operators.__le__(self, other)
     44 def __le__(self, other):
---> 45     rval = at.math.le(self, other)
     46     rval._is_nonzero = False
     47     return rval

File ~/pyenv/lib/python3.10/site-packages/pytensor/graph/op.py:296, in Op.__call__(self, *inputs, **kwargs)
    254 r"""Construct an `Apply` node using :meth:`Op.make_node` and return its outputs.
    255 
    256 This method is just a wrapper around :meth:`Op.make_node`.
   (...)
    293 
    294 """
    295 return_list = kwargs.pop("return_list", False)
--> 296 node = self.make_node(*inputs, **kwargs)
    298 if config.compute_test_value != "off":
    299     compute_test_value(node)
...

It seems the tensor in pytensor and aesara are slightly different. The backend of pyMC changes a lot and really complicated. Why does pyMC mix two or more tensor structure? Is there any preference or idiom about tensor types for daily usage?

You cannot mix Aesara and Pytensor!!!

PyMC 4.x uses Aesara
PyMC 5.x uses PyTensor

Otherwise they work the same (PyTensor is a fork of the Aesara project maintained by the PyMC developers)

1 Like