Deleting / replacing RVs

Is it possible to replace a RV after it has been created the usual way? e.g. var = pm.Beta('var', 1, 1)

Suppose later on, I wish to redefine var = pm.Normal('var', 0, 1)

Currently, doing so would invoke an error:

>>>ValueError: Variable name var already exists.

I can’t seem to see how to delete RV from PyMC3 either.

Is this currently possible in PyMC3? I suspect there may be an issue in implementing this sort of functionality due to the way in which PyMC3 deals with RVs, automatically building it into pm.Model().

Thoughts?

I think you can use theano.clone to modify the underly theano computational graph, but I dont know exactly how you can to do that as well.

@junpenglao @lucianopaz I’m interested in doing what @cvigoe described. Do we now know whether or not this is possible with theano.clone? If so, do you know of an example illustrating this or can you sketch the syntax?

@npschafer, no, it is not possible at the moment. You can clone a theano computational graph and replace a node in it with another one. The problem is that a pymc3 Model has a computational graph that only represents the calculations underlying the logp, but the distributions are almost completely disconnected from this graph after each RV is created. This means that even if you managed to change the models logp using clone, this would only have an effect when you try to do inference (sample), but it wouldn’t do anything when you do forward sampling (sample_prior_predictive or sample_posterior_predictive).

This will change once we move pymc random variables to use Random variable operators (this work was introduced in symbolic pymc and it is being refined in theano-pymc itself). Once we are able to do that, the pymc model will have the full computational graph that will be responsible for inference and forward sampling, so it will be possible to clone and replace nodes in it, as you would with any other theano graph. In fact, symbolic pymc showed that this could be used to implement automatic model reparametrizations that had a more robust sampling behavior.

1 Like

Thanks for the thorough explanation, @lucianopaz. Much appreciated!

Hi @lucianopaz, any update on this in pymc v4 ?
I’d find it convenient to write a code that looks like:

with pm.Model() as model:
    a = pm.Normal('a', 0, 1)
    trace = pm.sample()

with model:
    i = pm.Categorical('i', np.arange(1000)/1000)
    a_trace = pm.Data('a_trace', trace.posterior['a'].sel(chain=0).values)
    pm.update_RVs({'a': a_trace[i]})
    pm.sample()

or alternatively:

with model2:
    i = pm.Categorical('i', np.arange(1000)/1000)
    a_trace = pm.Data('a_trace', trace.posterior['a'].sel(chain=0).values)
    pm.clone_from(model, update={'a', a_trace})
    pm.sample()

I currently use a model factory function with if/then/else, which is fine, but I think something like the above would be more readable (if easily done internally of course – otherwise not worth the extra machinery).

Do you want to replace a free variable by a fixed value?

1 Like

I do! Is there a way to do this? I have tried variations of:

with pm.Model() as model:
    beta = pm.LogNormal(...)
    ...
    # If I find out, for a certain dataset, this should be a fixed value...
    del globals()["beta"] 
    beta = fixed_value # Didn't work--"beta" is still sampled

    setattr(model, name, fixed_value) # Also didn't work--"beta" is still sampled

You can use pm.do to replace variables by constants: pymc.model.transform.conditioning.do — PyMC 5.11.0 documentation

1 Like

This is great, thanks!

If the random variable(s) to be fixed are not known until runtime (for example, given as a string, rv_name):

with pm.Model() as model:
    ...

fixed = dict()
fixed[getattr(model, rv_name)] = fixed_value
model2 = pm.do(model, fixed)

I think the method also accepts string with the variable names as keys