Copying and changing pymc models #14

Closed
yarden opened this Issue Mar 16, 2015 · 6 comments

Projects

None yet

3 participants

@yarden

Hi,

I'm writing a small extension to pymc for inference in DBNs that unrolls a DBN into a static graphical model, so that it can be solved with pymc. To do this, it's helpful to copy Model instances and change their variables, but I'm not sure how to correctly copy models and how to modify their contents? For example, I'd like to copy a Model instance and change the names of its variables in place, and which nodes are parents, keeping all else in tact. E.g.:

import pymc
import copy
rain = pymc.Bernoulli("rain", 0.5)
model = pymc.Model([rain])
# This is not a copy of model!
new_model = model.value
# This is not a copy of model either
new_model = copy.copy(model.value)
new_rain = new_model.get_node("rain")
# This modified original model
new_rain.__name__ = "new_rain"
print "We modified original model: "
print model.variables
print "Before replacement: "
# How to modify the variable of a model?
for n in new_model.variables:
    print n, n.__name__
new_model.replace(None, "rain", new_model.get_node("rain"))
print "After replacement: "
for n in new_model.variables:
    print n, n.__name__

What is the best way to copy Model instances and alter them? Sorry if this is obvious, I couldn't find an example in the documentation and looking at ObjectContainer and related classes didn't reveal the answer. Thanks very much.

@fonnesbeck fonnesbeck self-assigned this Mar 17, 2015
@yarden

Sorry to bug, but does anyone have hints on this? I'm pretty sure I'm missing something very simple about how PyMC objects can be mutated/copied, and have not been able to get around it. Thanks again!

@fonnesbeck
PyMC member

There really isn't any functionality built into PyMC to do this, though it ought to be easy to wrap something along the lines that you have into a function. All we have are facilities for serializing model states. We would happily accept a pull request to this effect.

@yarden

Thank you Chris. I'd love to implement this; just let me know if there are any subtle 'gotchas' with copying a node from a model without breaking inference. It seems like the pythonic solution is just to implement a __copy__ method for a Stochastic node (and for a Model) that will copy the relevant attributes, like the parents and children, etc.

@yarden

I'm revisiting this and still puzzled as to how to correctly copy Model objects. deepcopy on any variable instance fails with this:

import pymc
import copy
rain = pymc.Bernoulli("rain", 0.5)
model = pymc.Model([rain])
# Get a variable from a model and try to copy it
var = next(iter(model.variables))
# Deep copy of variable
new_node = copy.deepcopy(var)
# This fails
print "new node: "
print new_node

The error is below.

I don't really follow what is happening with ParentDict and how distribution objects are made, and that seems to be the barrier to making a copy a Model object.

Can someone give a hint as to how to copy these objects? All I'm trying to do is copy a Model object correctly. (Happy to serialize model and read back in if that's the best way to do this but I don't see the serialization methods @fonnesbeck mentioned?).

This is the error from above code snippet:

Traceback (most recent call last):
  File "pymc_test.py", line 78, in <module>
    new_node = copy.deepcopy(var)
  File "/Users/yarden/anaconda/lib/python2.7/copy.py", line 190, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  File "/Users/yarden/anaconda/lib/python2.7/copy.py", line 334, in _reconstruct
    state = deepcopy(state, memo)
  File "/Users/yarden/anaconda/lib/python2.7/copy.py", line 163, in deepcopy
    y = copier(x, memo)
  File "/Users/yarden/anaconda/lib/python2.7/copy.py", line 257, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/yarden/anaconda/lib/python2.7/copy.py", line 190, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  File "/Users/yarden/anaconda/lib/python2.7/copy.py", line 358, in _reconstruct
    y[key] = value
  File "/Users/yarden/Software/pymc/pymc/PyMCObjects.py", line 151, in __setitem__
    old_parent = self[key]
KeyError: 'p'
@twiecki
PyMC member

I'm not sure if this helps, but in kabuki I managed to make an object pickable (but it essentially got recreated): https://github.com/hddm-devs/kabuki/blob/master/kabuki/hierarchical.py#L361

@yarden

It's not a PyMC object though that gets copied in __getstate__ (in the Hierarchical class) as far as I can tell? Or did you mean that I should try to pickle a PyMC object, serialize it out, and then read it back in as a way to generate a copy?

Any attempt to make deepcopy of a PyMC node gives an error, e.g.:

rain_pymc = pymc.Bernoulli("rain", 0.5)
from copy import deepcopy
deepcopy(rain_pymc)

gives:

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-145-7ca6ed433e2e> in <module>()
----> 1 deepcopy(rain_pymc)

/Users/yarden/anaconda/lib/python2.7/copy.pyc in deepcopy(x, memo, _nil)
    188                             raise Error(
    189                                 "un(deep)copyable object of type %s" % cls)
--> 190                 y = _reconstruct(x, rv, 1, memo)
    191 
    192     memo[d] = y

/Users/yarden/anaconda/lib/python2.7/copy.pyc in _reconstruct(x, info, deep, memo)
    332     if state:
    333         if deep:
--> 334             state = deepcopy(state, memo)
    335         if hasattr(y, '__setstate__'):
    336             y.__setstate__(state)

/Users/yarden/anaconda/lib/python2.7/copy.pyc in deepcopy(x, memo, _nil)
    161     copier = _deepcopy_dispatch.get(cls)
    162     if copier:
--> 163         y = copier(x, memo)
    164     else:
    165         try:

/Users/yarden/anaconda/lib/python2.7/copy.pyc in _deepcopy_dict(x, memo)
    255     memo[id(x)] = y
    256     for key, value in x.iteritems():
--> 257         y[deepcopy(key, memo)] = deepcopy(value, memo)
    258     return y
    259 d[dict] = _deepcopy_dict

/Users/yarden/anaconda/lib/python2.7/copy.pyc in deepcopy(x, memo, _nil)
    188                             raise Error(
    189                                 "un(deep)copyable object of type %s" % cls)
--> 190                 y = _reconstruct(x, rv, 1, memo)
    191 
    192     memo[d] = y

/Users/yarden/anaconda/lib/python2.7/copy.pyc in _reconstruct(x, info, deep, memo)
    356                 key = deepcopy(key, memo)
    357                 value = deepcopy(value, memo)
--> 358             y[key] = value
    359     return y
    360 

/Users/yarden/Software/pymc/pymc/PyMCObjects.py in __setitem__(self, key, new_parent)
    149 
    150     def __setitem__(self, key, new_parent):
--> 151         old_parent = self[key]
    152 
    153         # Possibly remove owner from old parent's children set.

KeyError: 'p'
@yarden yarden closed this Nov 12, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment