mirror of
https://github.com/SheffieldML/GPy.git
synced 2026-05-30 14:35:15 +02:00
merge the current devel into psi2
This commit is contained in:
commit
785c580032
49 changed files with 1839 additions and 581 deletions
|
|
@ -77,8 +77,18 @@ class ObserverList(object):
|
|||
self._poc.insert(ins, (priority, weakref.ref(observer), callble))
|
||||
|
||||
def __str__(self):
|
||||
from . import ObsAr, Param
|
||||
from parameter_core import Parameterizable
|
||||
ret = []
|
||||
curr_p = None
|
||||
|
||||
def frmt(o):
|
||||
if isinstance(o, ObsAr):
|
||||
return 'ObsArr <{}>'.format(hex(id(o)))
|
||||
elif isinstance(o, (Param,Parameterizable)):
|
||||
return '{}'.format(o.hierarchy_name())
|
||||
else:
|
||||
return repr(o)
|
||||
for p, o, c in self:
|
||||
curr = ''
|
||||
if curr_p != p:
|
||||
|
|
@ -87,8 +97,9 @@ class ObserverList(object):
|
|||
else: curr_pre = " "*len(pre)
|
||||
curr_p = p
|
||||
curr += curr_pre
|
||||
ret.append(curr + ", ".join(map(repr, [o,c])))
|
||||
return '\n'.join(ret)
|
||||
|
||||
ret.append(curr + ", ".join([frmt(o), str(c)]))
|
||||
return '\n'.join(ret)
|
||||
|
||||
def flush(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -30,16 +30,22 @@ class ObsAr(np.ndarray, Pickleable, Observable):
|
|||
def __array_wrap__(self, out_arr, context=None):
|
||||
return out_arr.view(np.ndarray)
|
||||
|
||||
def _setup_observers(self):
|
||||
# do not setup anything, as observable arrays do not have default observers
|
||||
pass
|
||||
|
||||
def copy(self):
|
||||
from lists_and_dicts import ObserverList
|
||||
memo = {}
|
||||
memo[id(self)] = self
|
||||
memo[id(self.observers)] = ObserverList()
|
||||
return self.__deepcopy__(memo)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
s = self.__new__(self.__class__, input_array=self.view(np.ndarray).copy())
|
||||
memo[id(self)] = s
|
||||
import copy
|
||||
s.__dict__.update(copy.deepcopy(self.__dict__, memo))
|
||||
Pickleable.__setstate__(s, copy.deepcopy(self.__getstate__(), memo))
|
||||
return s
|
||||
|
||||
def __reduce__(self):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import itertools
|
||||
import numpy
|
||||
np = numpy
|
||||
from parameter_core import Parameterizable, adjust_name_for_printing
|
||||
from parameter_core import Parameterizable, adjust_name_for_printing, Pickleable
|
||||
from observable_array import ObsAr
|
||||
|
||||
###### printing
|
||||
|
|
@ -173,36 +173,6 @@ class Param(Parameterizable, ObsAr):
|
|||
def _ensure_fixes(self):
|
||||
if not self._has_fixes(): self._fixes_ = numpy.ones(self._realsize_, dtype=bool)
|
||||
|
||||
#===========================================================================
|
||||
# parameterizable
|
||||
#===========================================================================
|
||||
def traverse(self, visit, *args, **kwargs):
|
||||
"""
|
||||
Traverse the hierarchy performing visit(self, *args, **kwargs) at every node passed by.
|
||||
See "visitor pattern" in literature. This is implemented in pre-order fashion.
|
||||
|
||||
This will function will just call visit on self, as Param are leaf nodes.
|
||||
"""
|
||||
self.__visited = True
|
||||
visit(self, *args, **kwargs)
|
||||
self.__visited = False
|
||||
|
||||
def traverse_parents(self, visit, *args, **kwargs):
|
||||
"""
|
||||
Traverse the hierarchy upwards, visiting all parents and their children, except self.
|
||||
See "visitor pattern" in literature. This is implemented in pre-order fashion.
|
||||
|
||||
Example:
|
||||
|
||||
parents = []
|
||||
self.traverse_parents(parents.append)
|
||||
print parents
|
||||
"""
|
||||
if self.has_parent():
|
||||
self.__visited = True
|
||||
self._parent_._traverse_parents(visit, *args, **kwargs)
|
||||
self.__visited = False
|
||||
|
||||
#===========================================================================
|
||||
# Convenience
|
||||
#===========================================================================
|
||||
|
|
@ -217,14 +187,24 @@ class Param(Parameterizable, ObsAr):
|
|||
#===========================================================================
|
||||
# Pickling and copying
|
||||
#===========================================================================
|
||||
def copy(self):
|
||||
return Parameterizable.copy(self, which=self)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
s = self.__new__(self.__class__, name=self.name, input_array=self.view(numpy.ndarray).copy())
|
||||
memo[id(self)] = s
|
||||
memo[id(self)] = s
|
||||
import copy
|
||||
s.__dict__.update(copy.deepcopy(self.__dict__, memo))
|
||||
Pickleable.__setstate__(s, copy.deepcopy(self.__getstate__(), memo))
|
||||
return s
|
||||
|
||||
|
||||
def _setup_observers(self):
|
||||
"""
|
||||
Setup the default observers
|
||||
|
||||
1: pass through to parent, if present
|
||||
"""
|
||||
if self.has_parent():
|
||||
self.add_observer(self._parent_, self._parent_._pass_through_notify_observers, -np.inf)
|
||||
|
||||
#===========================================================================
|
||||
# Printing -> done
|
||||
#===========================================================================
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ Observable Pattern for patameterization
|
|||
from transformations import Logexp, NegativeLogexp, Logistic, __fixed__, FIXED, UNFIXED
|
||||
import numpy as np
|
||||
import re
|
||||
import logging
|
||||
|
||||
__updated__ = '2014-05-20'
|
||||
__updated__ = '2014-05-21'
|
||||
|
||||
class HierarchyError(Exception):
|
||||
"""
|
||||
|
|
@ -49,7 +50,6 @@ class Observable(object):
|
|||
as an observer. Every time the observable changes, it sends a notification with
|
||||
self as only argument to all its observers.
|
||||
"""
|
||||
_updated = True
|
||||
_updates = True
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Observable, self).__init__()
|
||||
|
|
@ -58,26 +58,32 @@ class Observable(object):
|
|||
|
||||
@property
|
||||
def updates(self):
|
||||
self._updates = self._highest_parent_._updates
|
||||
p = getattr(self, '_highest_parent_', None)
|
||||
if p is not None:
|
||||
self._updates = p._updates
|
||||
return self._updates
|
||||
|
||||
@updates.setter
|
||||
def updates(self, ups):
|
||||
assert isinstance(ups, bool), "updates are either on (True) or off (False)"
|
||||
self._highest_parent_._updates = ups
|
||||
p = getattr(self, '_highest_parent_', None)
|
||||
if p is not None:
|
||||
p._updates = ups
|
||||
else:
|
||||
self._updates = ups
|
||||
if ups:
|
||||
self._trigger_params_changed()
|
||||
|
||||
def add_observer(self, observer, callble, priority=0):
|
||||
"""
|
||||
Add an observer `observer` with the callback `callble`
|
||||
Add an observer `observer` with the callback `callble`
|
||||
and priority `priority` to this observers list.
|
||||
"""
|
||||
self.observers.add(priority, observer, callble)
|
||||
|
||||
def remove_observer(self, observer, callble=None):
|
||||
"""
|
||||
Either (if callble is None) remove all callables,
|
||||
Either (if callble is None) remove all callables,
|
||||
which were added alongside observer,
|
||||
or remove callable `callble` which was added alongside
|
||||
the observer `observer`.
|
||||
|
|
@ -86,7 +92,7 @@ class Observable(object):
|
|||
for poc in self.observers:
|
||||
_, obs, clble = poc
|
||||
if callble is not None:
|
||||
if (obs == observer) and (callble == clble):
|
||||
if (obs is observer) and (callble == clble):
|
||||
to_remove.append(poc)
|
||||
else:
|
||||
if obs is observer:
|
||||
|
|
@ -172,6 +178,7 @@ class Pickleable(object):
|
|||
"""
|
||||
def __init__(self, *a, **kw):
|
||||
super(Pickleable, self).__init__()
|
||||
|
||||
#===========================================================================
|
||||
# Pickling operations
|
||||
#===========================================================================
|
||||
|
|
@ -192,37 +199,46 @@ class Pickleable(object):
|
|||
#===========================================================================
|
||||
# copy and pickling
|
||||
#===========================================================================
|
||||
def copy(self):
|
||||
def copy(self, memo=None, which=None):
|
||||
"""
|
||||
Returns a (deep) copy of the current parameter handle.
|
||||
Returns a (deep) copy of the current parameter handle.
|
||||
|
||||
All connections to parents of the copy will be cut.
|
||||
|
||||
:param dict memo: memo for deepcopy
|
||||
:param Parameterized which: parameterized object which started the copy process [default: self]
|
||||
"""
|
||||
#raise NotImplementedError, "Copy is not yet implemented, TODO: Observable hierarchy"
|
||||
if memo is None:
|
||||
memo = {}
|
||||
import copy
|
||||
memo = {}
|
||||
# the next part makes sure that we do not include parents in any form:
|
||||
parents = []
|
||||
self.traverse_parents(parents.append) # collect parents
|
||||
if which is None:
|
||||
which = self
|
||||
which.traverse_parents(parents.append) # collect parents
|
||||
for p in parents:
|
||||
memo[id(p)] = None # set all parents to be None, so they will not be copied
|
||||
memo[id(self.gradient)] = None # reset the gradient
|
||||
memo[id(self.param_array)] = None # and param_array
|
||||
memo[id(self._fixes_)] = None # fixes have to be reset, as this is now highest parent
|
||||
c = copy.deepcopy(self, memo) # and start the copy
|
||||
c._parent_index_ = None
|
||||
return c
|
||||
if not memo.has_key(id(p)):memo[id(p)] = None # set all parents to be None, so they will not be copied
|
||||
if not memo.has_key(id(self.gradient)):memo[id(self.gradient)] = None # reset the gradient
|
||||
if not memo.has_key(id(self._fixes_)):memo[id(self._fixes_)] = None # fixes have to be reset, as this is now highest parent
|
||||
copy = copy.deepcopy(self, memo) # and start the copy
|
||||
copy._parent_index_ = None
|
||||
copy._trigger_params_changed()
|
||||
return copy
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
s = self.__new__(self.__class__) # fresh instance
|
||||
memo[id(self)] = s # be sure to break all cycles --> self is already done
|
||||
import copy
|
||||
s.__dict__.update(copy.deepcopy(self.__dict__, memo)) # standard copy
|
||||
s.__setstate__(copy.deepcopy(self.__getstate__(), memo)) # standard copy
|
||||
return s
|
||||
|
||||
def __getstate__(self):
|
||||
ignore_list = ['_param_array_', # parameters get set from bottom to top
|
||||
'_gradient_array_', # as well as gradients
|
||||
'_optimizer_copy_',
|
||||
'logger',
|
||||
'observers',
|
||||
'_fixes_', # and fixes
|
||||
'_Cacher_wrap__cachers', # never pickle cachers
|
||||
]
|
||||
|
|
@ -231,10 +247,14 @@ class Pickleable(object):
|
|||
if k not in ignore_list:
|
||||
dc[k] = v
|
||||
return dc
|
||||
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.__dict__.update(state)
|
||||
return self
|
||||
from lists_and_dicts import ObserverList
|
||||
self.observers = ObserverList()
|
||||
self._setup_observers()
|
||||
self._optimizer_copy_transformed = False
|
||||
|
||||
|
||||
class Gradcheckable(Pickleable, Parentable):
|
||||
"""
|
||||
|
|
@ -261,7 +281,7 @@ class Gradcheckable(Pickleable, Parentable):
|
|||
"""
|
||||
if self.has_parent():
|
||||
return self._highest_parent_._checkgrad(self, verbose=verbose, step=step, tolerance=tolerance)
|
||||
return self._checkgrad(self[''], verbose=verbose, step=step, tolerance=tolerance)
|
||||
return self._checkgrad(self, verbose=verbose, step=step, tolerance=tolerance)
|
||||
|
||||
def _checkgrad(self, param, verbose=0, step=1e-6, tolerance=1e-3):
|
||||
"""
|
||||
|
|
@ -352,8 +372,9 @@ class Indexable(Nameable, Observable):
|
|||
basically just sums up the parameter sizes which come before param.
|
||||
"""
|
||||
if param.has_parent():
|
||||
if param._parent_._get_original(param) in self.parameters:
|
||||
return self._param_slices_[param._parent_._get_original(param)._parent_index_].start
|
||||
p = param._parent_._get_original(param)
|
||||
if p in self.parameters:
|
||||
return reduce(lambda a,b: a + b.size, self.parameters[:p._parent_index_], 0)
|
||||
return self._offset_for(param._parent_) + param._parent_._offset_for(param)
|
||||
return 0
|
||||
|
||||
|
|
@ -387,7 +408,6 @@ class Indexable(Nameable, Observable):
|
|||
if value is not None:
|
||||
self[:] = value
|
||||
|
||||
#index = self._raveled_index()
|
||||
index = self.unconstrain()
|
||||
index = self._add_to_index_operations(self.constraints, index, __fixed__, warning)
|
||||
self._highest_parent_._set_fixed(self, index)
|
||||
|
|
@ -423,12 +443,12 @@ class Indexable(Nameable, Observable):
|
|||
if np.all(self._fixes_): self._fixes_ = None # ==UNFIXED
|
||||
|
||||
def _connect_fixes(self):
|
||||
from ties_and_remappings import Tie
|
||||
self._ensure_fixes()
|
||||
[np.put(self._fixes_, ind, FIXED) for c, ind in self.constraints.iteritems()
|
||||
if c == __fixed__ or isinstance(c,Tie)]
|
||||
if np.all(self._fixes_): self._fixes_ = None
|
||||
if self.constraints[__fixed__]==0:
|
||||
fixed_indices = self.constraints[__fixed__]
|
||||
if fixed_indices.size > 0:
|
||||
self._ensure_fixes()
|
||||
self._fixes_[fixed_indices] = FIXED
|
||||
else:
|
||||
self._fixes_ = None
|
||||
del self.constraints[__fixed__]
|
||||
|
||||
#===========================================================================
|
||||
|
|
@ -495,32 +515,6 @@ class Indexable(Nameable, Observable):
|
|||
#===========================================================================
|
||||
# Constrain operations -> done
|
||||
#===========================================================================
|
||||
|
||||
def tie(self, name):
|
||||
from ties_and_remappings import Tie
|
||||
#remove any constraints
|
||||
old_const = [c for c in self.constraints.properties() if not isinstance(c,Tie)]
|
||||
self.unconstrain()
|
||||
|
||||
#see if a tie exists with that name
|
||||
if name in self._highest_parent_.ties:
|
||||
t = self._highest_parent_.ties[name]
|
||||
else:
|
||||
#create a tie object
|
||||
value = np.atleast_1d(self.param_array)[0]*1
|
||||
t = Tie(value=value, name=name)
|
||||
|
||||
#add the new tie object to the global index
|
||||
self._highest_parent_.ties[name] = t
|
||||
self._highest_parent_.add_parameter(t)
|
||||
|
||||
#constrain the tie as we were constrained
|
||||
if len(old_const)>0:
|
||||
t.constrain(old_const[0])
|
||||
|
||||
self.constraints.add(t, self._raveled_index())
|
||||
t.add_tied_parameter(self)
|
||||
self._highest_parent_._connect_fixes()
|
||||
|
||||
def constrain(self, transform, warning=True, trigger_parent=True):
|
||||
"""
|
||||
|
|
@ -638,48 +632,78 @@ class OptimizationHandlable(Indexable):
|
|||
"""
|
||||
This enables optimization handles on an Object as done in GPy 0.4.
|
||||
|
||||
`..._transformed`: make sure the transformations and constraints etc are handled
|
||||
`..._optimizer_copy_transformed`: make sure the transformations and constraints etc are handled
|
||||
"""
|
||||
def __init__(self, name, default_constraint=None, *a, **kw):
|
||||
super(OptimizationHandlable, self).__init__(name, default_constraint=default_constraint, *a, **kw)
|
||||
self._optimizer_copy_ = None
|
||||
self._optimizer_copy_transformed = False
|
||||
|
||||
def _get_params_transformed(self):
|
||||
# transformed parameters (apply un-transformation rules)
|
||||
p = self.param_array.copy()
|
||||
from ties_and_remappings import Tie
|
||||
[np.put(p, ind, c.finv(p[ind])) for c, ind in self.constraints.iteritems() if c != __fixed__ and not isinstance(c,Tie)]
|
||||
if self.has_parent() and self.constraints[__fixed__].size != 0:
|
||||
fixes = np.ones(self.size).astype(bool)
|
||||
[np.put(fixes,ind,FIXED) for c, ind in self.constraints.iteritems()
|
||||
if c == __fixed__ or isinstance(c,Tie)]
|
||||
return p[fixes]
|
||||
elif self._has_fixes():
|
||||
return p[self._fixes_]
|
||||
return p
|
||||
#===========================================================================
|
||||
# Optimizer copy
|
||||
#===========================================================================
|
||||
@property
|
||||
def optimizer_array(self):
|
||||
"""
|
||||
Array for the optimizer to work on.
|
||||
This array always lives in the space for the optimizer.
|
||||
Thus, it is untransformed, going from Transformations.
|
||||
|
||||
def _set_params_transformed(self, p):
|
||||
Setting this array, will make sure the transformed parameters for this model
|
||||
will be set accordingly. It has to be set with an array, retrieved from
|
||||
this method, as e.g. fixing will resize the array.
|
||||
|
||||
The optimizer should only interfere with this array, such that transofrmations
|
||||
are secured.
|
||||
"""
|
||||
Set parameters p, but make sure they get transformed before setting.
|
||||
This means, the optimizer sees p, whereas the model sees transformed(p),
|
||||
such that, the parameters the model sees are in the right domain.
|
||||
"""
|
||||
from ties_and_remappings import Tie
|
||||
if not(p is self.param_array):
|
||||
if self.__dict__.get('_optimizer_copy_', None) is None or self.size != self._optimizer_copy_.size:
|
||||
self._optimizer_copy_ = np.empty(self.size)
|
||||
|
||||
if not self._optimizer_copy_transformed:
|
||||
self._optimizer_copy_.flat = self.param_array.flat
|
||||
[np.put(self._optimizer_copy_, ind, c.finv(self.param_array[ind])) for c, ind in self.constraints.iteritems() if c != __fixed__]
|
||||
if self.has_parent() and self.constraints[__fixed__].size != 0:
|
||||
fixes = np.ones(self.size).astype(bool)
|
||||
# fixes[self.constraints[__fixed__]] = FIXED
|
||||
for c, ind in self.constraints.iteritems():
|
||||
if c == __fixed__ or isinstance(c,Tie):
|
||||
fixes[ind] = FIXED
|
||||
self.param_array.flat[fixes] = p
|
||||
elif self._has_fixes(): self.param_array.flat[self._fixes_] = p
|
||||
else: self.param_array.flat = p
|
||||
[np.put(self.param_array, ind, c.f(self.param_array.flat[ind]))
|
||||
for c, ind in self.constraints.iteritems() if c != __fixed__ and not isinstance(c,Tie)]
|
||||
[np.put(self.param_array, ind, c.val)
|
||||
for c, ind in self.constraints.iteritems() if isinstance(c,Tie)]
|
||||
fixes[self.constraints[__fixed__]] = FIXED
|
||||
return self._optimizer_copy_[fixes]
|
||||
elif self._has_fixes():
|
||||
return self._optimizer_copy_[self._fixes_]
|
||||
self._optimizer_copy_transformed = True
|
||||
|
||||
return self._optimizer_copy_
|
||||
|
||||
@optimizer_array.setter
|
||||
def optimizer_array(self, p):
|
||||
"""
|
||||
Make sure the optimizer copy does not get touched, thus, we only want to
|
||||
set the values *inside* not the array itself.
|
||||
|
||||
Also we want to update param_array in here.
|
||||
"""
|
||||
f = None
|
||||
if self.has_parent() and self.constraints[__fixed__].size != 0:
|
||||
f = np.ones(self.size).astype(bool)
|
||||
f[self.constraints[__fixed__]] = FIXED
|
||||
elif self._has_fixes():
|
||||
f = self._fixes_
|
||||
if f is None:
|
||||
self.param_array.flat = p
|
||||
[np.put(self.param_array, ind, c.f(self.param_array.flat[ind]))
|
||||
for c, ind in self.constraints.iteritems() if c != __fixed__]
|
||||
else:
|
||||
self.param_array.flat[f] = p
|
||||
[np.put(self.param_array, ind[f[ind]], c.f(self.param_array.flat[ind[f[ind]]]))
|
||||
for c, ind in self.constraints.iteritems() if c != __fixed__]
|
||||
|
||||
self._optimizer_copy_transformed = False
|
||||
self._trigger_params_changed()
|
||||
|
||||
def _get_params_transformed(self):
|
||||
raise DeprecationWarning, "_get|set_params{_optimizer_copy_transformed} is deprecated, use self.optimizer array insetad!"
|
||||
#
|
||||
def _set_params_transformed(self, p):
|
||||
raise DeprecationWarning, "_get|set_params{_optimizer_copy_transformed} is deprecated, use self.optimizer array insetad!"
|
||||
|
||||
def _trigger_params_changed(self, trigger_parent=True):
|
||||
"""
|
||||
First tell all children to update,
|
||||
|
|
@ -687,7 +711,7 @@ class OptimizationHandlable(Indexable):
|
|||
|
||||
If trigger_parent is True, we will tell the parent, otherwise not.
|
||||
"""
|
||||
[p._trigger_params_changed(trigger_parent=False) for p in self.parameters]
|
||||
[p._trigger_params_changed(trigger_parent=False) for p in self.parameters if not p.is_fixed]
|
||||
self.notify_observers(None, None if trigger_parent else -np.inf)
|
||||
|
||||
def _size_transformed(self):
|
||||
|
|
@ -702,11 +726,7 @@ class OptimizationHandlable(Indexable):
|
|||
Transform the gradients by multiplying the gradient factor for each
|
||||
constraint to it.
|
||||
"""
|
||||
if self.has_parent():
|
||||
return g
|
||||
from ties_and_remappings import Tie
|
||||
[np.put(g, self._raveled_index_for(c.val), g[i].sum()) for c, i in self.constraints.iteritems() if isinstance(c,Tie)]
|
||||
[np.put(g, i, g[i] * c.gradfactor(self.param_array[i])) for c, i in self.constraints.iteritems() if c != __fixed__ and not isinstance(c,Tie)]
|
||||
[np.put(g, i, g[i] * c.gradfactor(self.param_array[i])) for c, i in self.constraints.iteritems() if c != __fixed__]
|
||||
if self._has_fixes(): return g[self._fixes_]
|
||||
return g
|
||||
|
||||
|
|
@ -746,7 +766,7 @@ class OptimizationHandlable(Indexable):
|
|||
#===========================================================================
|
||||
# Randomizeable
|
||||
#===========================================================================
|
||||
def randomize(self, rand_gen=np.random.normal, loc=0, scale=1, *args, **kwargs):
|
||||
def randomize(self, rand_gen=np.random.normal, *args, **kwargs):
|
||||
"""
|
||||
Randomize the model.
|
||||
Make this draw from the prior if one exists, else draw from given random generator
|
||||
|
|
@ -757,10 +777,10 @@ class OptimizationHandlable(Indexable):
|
|||
:param args, kwargs: will be passed through to random number generator
|
||||
"""
|
||||
# first take care of all parameters (from N(0,1))
|
||||
x = rand_gen(loc=loc, scale=scale, size=self._size_transformed(), *args, **kwargs)
|
||||
x = rand_gen(size=self._size_transformed(), *args, **kwargs)
|
||||
# now draw from prior where possible
|
||||
[np.put(x, ind, p.rvs(ind.size)) for p, ind in self.priors.iteritems() if not p is None]
|
||||
self._set_params_transformed(x) # makes sure all of the tied parameters get the same init (since there's only one prior object...)
|
||||
self.optimizer_array = x # makes sure all of the tied parameters get the same init (since there's only one prior object...)
|
||||
|
||||
#===========================================================================
|
||||
# For shared memory arrays. This does nothing in Param, but sets the memory
|
||||
|
|
@ -788,6 +808,11 @@ class OptimizationHandlable(Indexable):
|
|||
1.) connect param_array of children to self.param_array
|
||||
2.) tell all children to propagate further
|
||||
"""
|
||||
if self.param_array.size != self.size:
|
||||
self._param_array_ = np.empty(self.size, dtype=np.float64)
|
||||
if self.gradient.size != self.size:
|
||||
self._gradient_array_ = np.empty(self.size, dtype=np.float64)
|
||||
|
||||
pi_old_size = 0
|
||||
for pi in self.parameters:
|
||||
pislice = slice(pi_old_size, pi_old_size + pi.size)
|
||||
|
|
@ -801,6 +826,9 @@ class OptimizationHandlable(Indexable):
|
|||
pi._propagate_param_grad(parray[pislice], garray[pislice])
|
||||
pi_old_size += pi.size
|
||||
|
||||
def _connect_parameters(self):
|
||||
pass
|
||||
|
||||
class Parameterizable(OptimizationHandlable):
|
||||
"""
|
||||
A parameterisable class.
|
||||
|
|
@ -819,26 +847,48 @@ class Parameterizable(OptimizationHandlable):
|
|||
self.parameters = ArrayList()
|
||||
self._param_array_ = None
|
||||
self._added_names_ = set()
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
self.__visited = False # for traversing in reverse order we need to know if we were here already
|
||||
self.ties = {}
|
||||
|
||||
@property
|
||||
def param_array(self):
|
||||
"""
|
||||
Array representing the parameters of this class.
|
||||
There is only one copy of all parameters in memory, two during optimization.
|
||||
|
||||
!WARNING!: setting the parameter array MUST always be done in memory:
|
||||
m.param_array[:] = m_copy.param_array
|
||||
"""
|
||||
if self.__dict__.get('_param_array_', None) is None:
|
||||
self._param_array_ = np.empty(self.size, dtype=np.float64)
|
||||
return self._param_array_
|
||||
|
||||
@property
|
||||
def unfixed_param_array(self):
|
||||
"""
|
||||
Array representing the parameters of this class.
|
||||
There is only one copy of all parameters in memory, two during optimization.
|
||||
|
||||
!WARNING!: setting the parameter array MUST always be done in memory:
|
||||
m.param_array[:] = m_copy.param_array
|
||||
"""
|
||||
if self.__dict__.get('_param_array_', None) is None:
|
||||
self._param_array_ = np.empty(self.size, dtype=np.float64)
|
||||
|
||||
if self.constraints[__fixed__].size !=0:
|
||||
fixes = np.ones(self.size).astype(bool)
|
||||
fixes[self.constraints[__fixed__]] = FIXED
|
||||
return self._param_array_[fixes]
|
||||
else:
|
||||
return self._param_array_
|
||||
|
||||
@param_array.setter
|
||||
def param_array(self, arr):
|
||||
self._param_array_ = arr
|
||||
|
||||
def traverse(self, visit, *args, **kwargs):
|
||||
"""
|
||||
Traverse the hierarchy performing visit(self, *args, **kwargs)
|
||||
Traverse the hierarchy performing visit(self, *args, **kwargs)
|
||||
at every node passed by downwards. This function includes self!
|
||||
|
||||
See "visitor pattern" in literature. This is implemented in pre-order fashion.
|
||||
|
|
@ -930,14 +980,33 @@ class Parameterizable(OptimizationHandlable):
|
|||
self._remove_parameter_name(None, old_name)
|
||||
self._add_parameter_name(param)
|
||||
|
||||
def __setstate__(self, state):
|
||||
super(Parameterizable, self).__setstate__(state)
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
return self
|
||||
|
||||
#===========================================================================
|
||||
# notification system
|
||||
#===========================================================================
|
||||
def _parameters_changed_notification(self, me, which=None):
|
||||
"""
|
||||
In parameterizable we just need to make sure, that the next call to optimizer_array
|
||||
will update the optimizer_array to the latest parameters
|
||||
"""
|
||||
self._optimizer_copy_transformed = False # tells the optimizer array to update on next request
|
||||
self.parameters_changed()
|
||||
def _pass_through_notify_observers(self, me, which=None):
|
||||
self.notify_observers(which=which)
|
||||
def _setup_observers(self):
|
||||
"""
|
||||
Setup the default observers
|
||||
|
||||
1: parameters_changed_notify
|
||||
2: pass through to parent, if present
|
||||
"""
|
||||
self.add_observer(self, self._parameters_changed_notification, -100)
|
||||
if self.has_parent():
|
||||
self.add_observer(self._parent_, self._parent_._pass_through_notify_observers, -np.inf)
|
||||
#===========================================================================
|
||||
# From being parentable, we have to define the parent_change notification
|
||||
#===========================================================================
|
||||
|
|
@ -956,4 +1025,3 @@ class Parameterizable(OptimizationHandlable):
|
|||
updates get passed through. See :py:function:``GPy.core.param.Observable.add_observer``
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,23 @@ from re import compile, _pattern_type
|
|||
from param import ParamConcatenation
|
||||
from parameter_core import HierarchyError, Parameterizable, adjust_name_for_printing
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger("parameters changed meta")
|
||||
|
||||
class ParametersChangedMeta(type):
|
||||
def __call__(self, *args, **kw):
|
||||
instance = super(ParametersChangedMeta, self).__call__(*args, **kw)
|
||||
instance.parameters_changed()
|
||||
return instance
|
||||
self._in_init_ = True
|
||||
#import ipdb;ipdb.set_trace()
|
||||
self = super(ParametersChangedMeta, self).__call__(*args, **kw)
|
||||
logger.debug("finished init")
|
||||
self._in_init_ = False
|
||||
logger.debug("connecting parameters")
|
||||
self._highest_parent_._connect_parameters()
|
||||
self._highest_parent_._notify_parent_change()
|
||||
self._highest_parent_._connect_fixes()
|
||||
logger.debug("calling parameters changed")
|
||||
self.parameters_changed()
|
||||
return self
|
||||
|
||||
class Parameterized(Parameterizable):
|
||||
"""
|
||||
|
|
@ -57,21 +69,19 @@ class Parameterized(Parameterizable):
|
|||
and concatenate them. Printing m[''] will result in printing of all parameters in detail.
|
||||
"""
|
||||
#===========================================================================
|
||||
# Metaclass for parameters changed after init.
|
||||
# Metaclass for parameters changed after init.
|
||||
# This makes sure, that parameters changed will always be called after __init__
|
||||
# **Never** call parameters_changed() yourself
|
||||
# **Never** call parameters_changed() yourself
|
||||
__metaclass__ = ParametersChangedMeta
|
||||
#===========================================================================
|
||||
def __init__(self, name=None, parameters=[], *a, **kw):
|
||||
super(Parameterized, self).__init__(name=name, *a, **kw)
|
||||
self._in_init_ = True
|
||||
self.size = sum(p.size for p in self.parameters)
|
||||
self.add_observer(self, self._parameters_changed_notification, -100)
|
||||
if not self._has_fixes():
|
||||
self._fixes_ = None
|
||||
self._param_slices_ = []
|
||||
self._connect_parameters()
|
||||
del self._in_init_
|
||||
#self._connect_parameters()
|
||||
self.add_parameters(*parameters)
|
||||
|
||||
def build_pydot(self, G=None):
|
||||
|
|
@ -125,6 +135,9 @@ class Parameterized(Parameterizable):
|
|||
param._parent_.remove_parameter(param)
|
||||
# make sure the size is set
|
||||
if index is None:
|
||||
start = sum(p.size for p in self.parameters)
|
||||
self.constraints.shift_right(start, param.size)
|
||||
self.priors.shift_right(start, param.size)
|
||||
self.constraints.update(param.constraints, self.size)
|
||||
self.priors.update(param.priors, self.size)
|
||||
self.parameters.append(param)
|
||||
|
|
@ -143,14 +156,16 @@ class Parameterized(Parameterizable):
|
|||
parent.size += param.size
|
||||
parent = parent._parent_
|
||||
|
||||
self._connect_parameters()
|
||||
if not self._in_init_:
|
||||
self._connect_parameters()
|
||||
self._notify_parent_change()
|
||||
|
||||
self._highest_parent_._connect_parameters(ignore_added_names=_ignore_added_names)
|
||||
self._highest_parent_._notify_parent_change()
|
||||
self._highest_parent_._connect_fixes()
|
||||
self._highest_parent_._connect_parameters(ignore_added_names=_ignore_added_names)
|
||||
self._highest_parent_._notify_parent_change()
|
||||
self._highest_parent_._connect_fixes()
|
||||
|
||||
else:
|
||||
raise HierarchyError, """Parameter exists already and no copy made"""
|
||||
raise HierarchyError, """Parameter exists already, try making a copy"""
|
||||
|
||||
|
||||
def add_parameters(self, *parameters):
|
||||
|
|
@ -198,26 +213,28 @@ class Parameterized(Parameterizable):
|
|||
# no parameters for this class
|
||||
return
|
||||
if self.param_array.size != self.size:
|
||||
self.param_array = np.empty(self.size, dtype=np.float64)
|
||||
self._param_array_ = np.empty(self.size, dtype=np.float64)
|
||||
if self.gradient.size != self.size:
|
||||
self._gradient_array_ = np.empty(self.size, dtype=np.float64)
|
||||
|
||||
old_size = 0
|
||||
self._param_slices_ = []
|
||||
for i, p in enumerate(self.parameters):
|
||||
if not p.param_array.flags['C_CONTIGUOUS']:
|
||||
raise ValueError, "This should not happen! Please write an email to the developers with the code, which reproduces this error. All parameter arrays must be C_CONTIGUOUS"
|
||||
|
||||
p._parent_ = self
|
||||
p._parent_index_ = i
|
||||
|
||||
pslice = slice(old_size, old_size + p.size)
|
||||
|
||||
# first connect all children
|
||||
p._propagate_param_grad(self.param_array[pslice], self.gradient_full[pslice])
|
||||
|
||||
# then connect children to self
|
||||
self.param_array[pslice] = p.param_array.flat # , requirements=['C', 'W']).ravel(order='C')
|
||||
self.gradient_full[pslice] = p.gradient_full.flat # , requirements=['C', 'W']).ravel(order='C')
|
||||
|
||||
if not p.param_array.flags['C_CONTIGUOUS']:
|
||||
raise ValueError, "This should not happen! Please write an email to the developers with the code, which reproduces this error. All parameter arrays must be C_CONTIGUOUS"
|
||||
|
||||
p.param_array.data = self.param_array[pslice].data
|
||||
p.gradient_full.data = self.gradient_full[pslice].data
|
||||
|
||||
|
|
@ -292,12 +309,16 @@ class Parameterized(Parameterizable):
|
|||
except Exception as e:
|
||||
print "WARNING: caught exception {!s}, trying to continue".format(e)
|
||||
|
||||
def copy(self):
|
||||
c = super(Parameterized, self).copy()
|
||||
c._connect_parameters()
|
||||
c._connect_fixes()
|
||||
c._notify_parent_change()
|
||||
return c
|
||||
def copy(self, memo=None):
|
||||
if memo is None:
|
||||
memo = {}
|
||||
memo[id(self.optimizer_array)] = None # and param_array
|
||||
memo[id(self.param_array)] = None # and param_array
|
||||
copy = super(Parameterized, self).copy(memo)
|
||||
copy._connect_parameters()
|
||||
copy._connect_fixes()
|
||||
copy._notify_parent_change()
|
||||
return copy
|
||||
|
||||
#===========================================================================
|
||||
# Printing:
|
||||
|
|
@ -328,7 +349,7 @@ class Parameterized(Parameterizable):
|
|||
def __str__(self, header=True):
|
||||
|
||||
name = adjust_name_for_printing(self.name) + "."
|
||||
constrs = self._constraints_str;
|
||||
constrs = self._constraints_str;
|
||||
ts = self._ties_str
|
||||
prirs = self._priors_str
|
||||
desc = self._description_str; names = self.parameter_names()
|
||||
|
|
|
|||
|
|
@ -76,11 +76,11 @@ class Uniform(Prior):
|
|||
o = super(Prior, cls).__new__(cls, lower, upper)
|
||||
cls._instances.append(weakref.ref(o))
|
||||
return cls._instances[-1]()
|
||||
|
||||
|
||||
def __init__(self, lower, upper):
|
||||
self.lower = float(lower)
|
||||
self.upper = float(upper)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "[" + str(np.round(self.lower)) + ', ' + str(np.round(self.upper)) + ']'
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ class Uniform(Prior):
|
|||
|
||||
def rvs(self, n):
|
||||
return np.random.uniform(self.lower, self.upper, size=n)
|
||||
|
||||
|
||||
class LogGaussian(Prior):
|
||||
"""
|
||||
Implementation of the univariate *log*-Gaussian probability function, coupled with random variables.
|
||||
|
|
@ -246,7 +246,7 @@ class Gamma(Prior):
|
|||
"""
|
||||
Creates an instance of a Gamma Prior by specifying the Expected value(s)
|
||||
and Variance(s) of the distribution.
|
||||
|
||||
|
||||
:param E: expected value
|
||||
:param V: variance
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class SpikeAndSlabPrior(VariationalPrior):
|
|||
super(VariationalPrior, self).__init__(name=name, **kw)
|
||||
self.pi = Param('pi', pi, Logistic(1e-10,1.-1e-10))
|
||||
self.variance = Param('variance',variance)
|
||||
self.learnPi = learnPi
|
||||
if learnPi:
|
||||
self.add_parameters(self.pi)
|
||||
|
||||
|
|
@ -58,12 +59,13 @@ class SpikeAndSlabPrior(VariationalPrior):
|
|||
gamma.gradient -= np.log((1-self.pi)/self.pi*gamma/(1.-gamma))+((np.square(mu)+S)/self.variance-np.log(S)+np.log(self.variance)-1.)/2.
|
||||
mu.gradient -= gamma*mu/self.variance
|
||||
S.gradient -= (1./self.variance - 1./S) * gamma /2.
|
||||
if len(self.pi)==1:
|
||||
self.pi.gradient = (gamma/self.pi - (1.-gamma)/(1.-self.pi)).sum()
|
||||
if len(self.pi.shape)==1:
|
||||
self.pi.gradient = (gamma/self.pi - (1.-gamma)/(1.-self.pi)).sum(axis=0)
|
||||
else:
|
||||
self.pi.gradient = (gamma/self.pi - (1.-gamma)/(1.-self.pi))
|
||||
if self.learnPi:
|
||||
if len(self.pi)==1:
|
||||
self.pi.gradient = (gamma/self.pi - (1.-gamma)/(1.-self.pi)).sum()
|
||||
elif len(self.pi.shape)==1:
|
||||
self.pi.gradient = (gamma/self.pi - (1.-gamma)/(1.-self.pi)).sum(axis=0)
|
||||
else:
|
||||
self.pi.gradient = (gamma/self.pi - (1.-gamma)/(1.-self.pi))
|
||||
|
||||
class VariationalPosterior(Parameterized):
|
||||
def __init__(self, means=None, variances=None, name='latent space', *a, **kw):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue