Compare commits

..
Author SHA1 Message Date
Rowan Cockett 7976e9a498 Serialize the Properties. 2016-02-09 14:20:44 -08:00
Rowan Cockett ffffcb4b7b Initial work on pickling for #226 2016-02-09 13:41:49 -08:00
3 changed files with 53 additions and 67 deletions
+47 -49
View File
@@ -12,6 +12,7 @@ class Property(object):
# Set the default after all other params are set
self.doc = doc
Utils.setKwargs(self, **kwargs)
self._kwargs = kwargs
@property
def propertyLink(self):
@@ -34,18 +35,6 @@ class Property(object):
setattr(self, '_%sMap'%prop.name, val)
return property(fget=fget, fset=fset, doc=prop.doc)
def _getDefaultProperty(self):
prop = self
def fget(self):
return getattr(self, '_%sDefault'%prop.name, None)
def fset(self, val):
if prop.propertyLink is not None:
linkName, linkMap = prop.propertyLink
assert getattr(self, '%sDefault'%linkName, None) is None, 'Cannot set both sides of a linked property.'
assert isinstance(val, np.ndarray) or np.isscalar(val), 'Default must be a scalar or a numpy array.'
setattr(self, '_%sDefault'%prop.name, val)
return property(fget=fget, fset=fset, doc=prop.doc)
def _getIndexProperty(self):
prop = self
def fget(self):
@@ -59,17 +48,12 @@ class Property(object):
def fget(self):
mapping = getattr(self, '%sMap'%prop.name)
if mapping is None and prop.propertyLink is None:
return getattr(self, '%sDefault'%prop.name)
return prop.defaultVal
if mapping is None and prop.propertyLink is not None:
linkName, linkMapClass = prop.propertyLink
linkMap = linkMapClass(None)
# *
print linkName, getattr(self.propMap, '_%sDefault'%linkName, None)
if getattr(self, '%sMap'%linkName, None) is None and getattr(self.propMap, '_%sDefault'%linkName, None) is not None:
# We have a default
return linkMap * getattr(self, '%sDefault'%linkName, None)
elif getattr(self, '%sMap'%linkName, None) is None:
if getattr(self, '%sMap'%linkName, None) is None:
return prop.defaultVal
m = getattr(self, '%s'%linkName)
return linkMap * m
@@ -127,12 +111,11 @@ class Property(object):
return getattr(self.propMap, '_%sMap'%prop.name, None)
return property(fget=fget)
def _getModelDefaultProperty(self):
prop = self
def fget(self):
return getattr(self.propMap, '_%sDefault'%prop.name, prop.defaultVal)
return property(fget=fget)
def toJSON(self):
out = dict(doc=self.doc)
for k in self._kwargs:
out[k] = self._kwargs[k]
return out
class PropModel(object):
@@ -173,9 +156,8 @@ class _PropMapMetaClass(type):
for attr in keys:
if isinstance(attrs[attr], Property):
attrs[attr].name = attr
attrs[attr + 'Map' ] = attrs[attr]._getMapProperty()
attrs[attr + 'Default'] = attrs[attr]._getDefaultProperty()
attrs[attr + 'Index' ] = attrs[attr]._getIndexProperty()
attrs[attr + 'Map' ] = attrs[attr]._getMapProperty()
attrs[attr + 'Index'] = attrs[attr]._getIndexProperty()
_properties[attr] = attrs[attr]
attrs.pop(attr)
@@ -205,15 +187,20 @@ class _PropMapMetaClass(type):
for attr in _properties:
prop = _properties[attr]
attrs[attr ] = prop._getProperty()
attrs[attr + 'Map' ] = prop._getModelMapProperty()
attrs[attr + 'Default'] = prop._getModelDefaultProperty()
attrs[attr + 'Proj' ] = prop._getModelProjProperty()
attrs[attr + 'Model' ] = prop._getModelProperty()
attrs[attr + 'Deriv' ] = prop._getModelDerivProperty()
attrs[attr ] = prop._getProperty()
attrs[attr + 'Map' ] = prop._getModelMapProperty()
attrs[attr + 'Proj' ] = prop._getModelProjProperty()
attrs[attr + 'Model'] = prop._getModelProperty()
attrs[attr + 'Deriv'] = prop._getModelDerivProperty()
return type(name.replace('PropMap', 'PropModel'), (PropModel, ), attrs)
def fromPickle(name, properties, maps, slices):
attrs = dict()
for p in properties:
attrs[p] = Property(**properties[p])
PM = type(name, (PropMap,), attrs)
return PM(dict(maps=maps, slices=slices))
class PropMap(object):
__metaclass__ = _PropMapMetaClass
@@ -222,9 +209,10 @@ class PropMap(object):
"""
PropMap takes a multi parameter model and maps it to the equivalent PropModel
"""
if type(mappings) is dict:
assert np.all([k in ['maps', 'slices', 'defaults'] for k in mappings]), 'Dict must only have properties "maps", "slices" and "defaults"'
self.setup(mappings['maps'], slices=mappings.get('slices',{}), defaults=mappings.get('defaults',{}))
assert np.all([k in ['maps', 'slices'] for k in mappings]), 'Dict must only have properties "maps" and "slices"'
self.setup(mappings['maps'], slices=mappings['slices'])
elif type(mappings) is list:
self.setup(mappings)
elif isinstance(mappings, Maps.IdentityMap):
@@ -233,7 +221,7 @@ class PropMap(object):
raise Exception('mappings must be a dict, a mapping, or a list of tuples.')
def setup(self, maps, slices=None, defaults=None):
def setup(self, maps, slices=None):
"""
Sets up the maps and slices for the PropertyMap
@@ -256,13 +244,6 @@ class PropMap(object):
s in self._properties and
(type(slices[s]) in [slice, list] or isinstance(slices[s], np.ndarray))
for s in slices]), 'Slices must be for each property'
if defaults is None:
defaults = dict()
else:
assert np.all([
s in self._properties and
(np.isscalar(defaults[s]) or isinstance(defaults[s], np.ndarray))
for s in defaults]), 'Defaults must be for each property'
self.clearMaps()
@@ -271,13 +252,12 @@ class PropMap(object):
setattr(self, '%sMap'%name, mapping)
setattr(self, '%sIndex'%name, slices.get(name, slice(nP, nP + mapping.nP)))
nP += mapping.nP
self._maps = maps
self._slices = slices
self.nP = nP
for key in defaults:
setattr(self, '%sDefault'%key, defaults[key])
@property
def defaultInvProp(self):
for name in self._properties:
@@ -290,9 +270,27 @@ class PropMap(object):
setattr(self, '%sMap'%name, None)
setattr(self, '%sIndex'%name, None)
self._maps = None
self._slices = None
def __call__(self, vec):
return self.PropModel(self, vec)
def __contains__(self, val):
activeMaps = [name for name in self._properties if getattr(self, '%sMap'%name) is not None]
return val in activeMaps
def __reduce__(self):
import cPickle
props = dict()
for p in self._properties:
props[p] = self._properties[p].toJSON()
className = self.__class__.__name__
pickledMaps = []
for name, mapping in self._maps:
pickledMaps += [name, cPickle.dumps(mapping)]
return (fromPickle, (className, props, self._maps, self._slices))
+2 -2
View File
@@ -65,8 +65,8 @@ def setKwargs(obj, ignore=[], **kwargs):
else:
raise Exception('%s attr is not recognized' % attr)
hook(obj,hook, silent=True)
hook(obj,setKwargs, silent=True)
# hook(obj,hook, silent=True)
# hook(obj,setKwargs, silent=True)
def printTitles(obj, printers, name='Print Titles', pad=''):
titles = ''
+4 -16
View File
@@ -1,6 +1,7 @@
import unittest
from SimPEG import *
from scipy.constants import mu_0
import cPickle
class MyPropMap(Maps.PropMap):
@@ -28,9 +29,10 @@ class TestPropMaps(unittest.TestCase):
PM3 = MyPropMap({'maps':[('sigma', expMap)], 'slices':{'sigma':slice(0,3)}})
for PM in [PM1,PM2,PM3]:
PM = cPickle.loads( cPickle.dumps(PM) )
assert PM.defaultInvProp == 'sigma'
assert PM.sigmaMap is not None
assert PM.sigmaMap is expMap
assert PM.sigmaMap.__class__ is expMap.__class__
assert PM.sigmaIndex == slice(0,3)
assert getattr(PM, 'sigma', None) is None
assert PM.muMap is None
@@ -52,26 +54,12 @@ class TestPropMaps(unittest.TestCase):
assert m.muDeriv is None
assert np.all(m.sigmaModel == np.r_[1.,2,3])
assert m.sigmaMap is expMap
assert m.sigmaMap.__class__ is expMap.__class__
assert np.all(m.sigma == np.exp(np.r_[1.,2,3]))
assert m.sigmaDeriv is not None
assert m.mu == mu_0
assert m.nP == 3
def test_defaultOverride(self):
expMap = Maps.ExpMap(Mesh.TensorMesh((3,)))
PM = MyReciprocalPropMap({'maps':[('sigma', expMap)], 'defaults':{'mu':mu_0*2}})
self.assertRaises(Exception, MyReciprocalPropMap, {'maps':[('sigma', expMap)], 'defaults':{'mu':mu_0*2, 'mui':5}}) # Cannot set both sides of the default
m = PM(np.r_[1.,2,3])
assert np.all(m.sigmaModel == np.r_[1,2,3])
self.assertEqual(m.mu, mu_0 * 2)
# self.assertEqual(m.mui, 1/(mu_0 * 2))
def test_slices(self):
expMap = Maps.ExpMap(Mesh.TensorMesh((3,)))
PM = MyPropMap({'maps':[('sigma', expMap)], 'slices':{'sigma':[2,1,0]}})