[GPU] GPU version of varDTC is ready

This commit is contained in:
Zhenwen Dai 2014-04-07 10:25:16 +01:00
commit bbcba2553c
59 changed files with 2012 additions and 1186 deletions

View file

@ -24,12 +24,6 @@ class ParameterIndexOperations(object):
for t, i in constraints.iteritems():
self.add(t, i)
def __getstate__(self):
return self._properties
def __setstate__(self, state):
self._properties = state
def iteritems(self):
return self._properties.iteritems()
@ -92,13 +86,18 @@ class ParameterIndexOperations(object):
for i, v in parameter_index_view.iteritems():
self.add(i, v+offset)
def copy(self):
return self.__deepcopy__(None)
def __deepcopy__(self, memo):
return ParameterIndexOperations(dict(self.iteritems()))
def __getitem__(self, prop):
return self._properties[prop]
def __delitem__(self, prop):
del self._properties[prop]
def __str__(self, *args, **kwargs):
import pprint
return pprint.pformat(dict(self._properties))
@ -183,7 +182,7 @@ class ParameterIndexOperationsView(object):
def remove(self, prop, indices):
removed = self._param_index_ops.remove(prop, indices+self._offset)
removed = self._param_index_ops.remove(prop, numpy.array(indices)+self._offset)
if removed.size > 0:
return removed - self._size + 1
return removed
@ -193,6 +192,9 @@ class ParameterIndexOperationsView(object):
ind = self._filter_index(self._param_index_ops[prop])
return ind
def __delitem__(self, prop):
self.remove(prop, self[prop])
def __str__(self, *args, **kwargs):
import pprint
return pprint.pformat(dict(self.iteritems()))
@ -203,6 +205,9 @@ class ParameterIndexOperationsView(object):
def copy(self):
return self.__deepcopy__(None)
def __deepcopy__(self, memo):
return ParameterIndexOperations(dict(self.iteritems()))
pass

View file

@ -5,6 +5,7 @@ Created on 27 Feb 2014
'''
from collections import defaultdict
import weakref
def intarray_default_factory():
import numpy as np
@ -28,4 +29,90 @@ class ArrayList(list):
return True
return False
def index(self, item):
index = 0
for el in self:
if el is item:
return index
index += 1
raise ValueError, "{} is not in list".format(item)
pass
class ObservablesList(object):
def __init__(self):
self._poc = []
def __getitem__(self, ind):
p,o,c = self._poc[ind]
return p, o(), c
def remove(self, priority, observable, callble):
"""
"""
self.flush()
for i in range(len(self) - 1, -1, -1):
p,o,c = self[i]
if priority==p and observable==o and callble==c:
del self._poc[i]
def __repr__(self):
return self._poc.__repr__()
def add(self, priority, observable, callble):
ins = 0
for pr, _, _ in self:
if priority > pr:
break
ins += 1
self._poc.insert(ins, (priority, weakref.ref(observable), callble))
def __str__(self):
ret = []
curr_p = None
for p, o, c in self:
curr = ''
if curr_p != p:
pre = "{!s}: ".format(p)
curr_pre = pre
else: curr_pre = " "*len(pre)
curr_p = p
curr += curr_pre
ret.append(curr + ", ".join(map(repr, [o,c])))
return '\n'.join(ret)
def flush(self):
self._poc = [(p,o,c) for p,o,c in self._poc if o() is not None]
def __iter__(self):
self.flush()
for p, o, c in self._poc:
if o() is not None:
yield p, o(), c
def __len__(self):
self.flush()
return self._poc.__len__()
def __deepcopy__(self, memo):
self.flush()
s = ObservablesList()
import copy
s._poc = copy.deepcopy(self._poc, memo)
return s
def __getstate__(self):
self.flush()
from ...util.caching import Cacher
obs = []
for p, o, c in self:
if (getattr(o, c.__name__, None) is not None
and not isinstance(o, Cacher)):
obs.append((p,o,c.__name__))
return obs
def __setstate__(self, state):
self._poc = []
for p, o, c in state:
self.add(p,o,getattr(o, c))
pass

View file

@ -1,12 +1,12 @@
# Copyright (c) 2012, GPy authors (see AUTHORS.txt).
# Licensed under the BSD 3-clause license (see LICENSE.txt)
__updated__ = '2014-03-21'
__updated__ = '2014-03-31'
import numpy as np
from parameter_core import Observable
from parameter_core import Observable, Pickleable
class ObsAr(np.ndarray, Observable):
class ObsAr(np.ndarray, Pickleable, Observable):
"""
An ndarray which reports changes to its observers.
The observers can add themselves with a callable, which
@ -25,37 +25,34 @@ class ObsAr(np.ndarray, Observable):
def __array_finalize__(self, obj):
# see InfoArray.__array_finalize__ for comments
if obj is None: return
self._observer_callables_ = getattr(obj, '_observer_callables_', None)
self.observers = getattr(obj, 'observers', None)
def __array_wrap__(self, out_arr, context=None):
return out_arr.view(np.ndarray)
def copy(self):
memo = {}
memo[id(self)] = self
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))
return s
def __reduce__(self):
func, args, state = np.ndarray.__reduce__(self)
return func, args, (state, Observable._getstate(self))
func, args, state = super(ObsAr, self).__reduce__()
return func, args, (state, Pickleable.__getstate__(self))
def __setstate__(self, state):
np.ndarray.__setstate__(self, state[0])
Observable._setstate(self, state[1])
def _s_not_empty(self, s):
# this checks whether there is something picked by this slice.
return True
# TODO: disarmed, for performance increase,
if not isinstance(s, (list,tuple,np.ndarray)):
return True
if isinstance(s, (list,tuple)):
return len(s)!=0
if isinstance(s, np.ndarray):
if s.dtype is bool:
return np.all(s)
else:
return s.size != 0
Pickleable.__setstate__(self, state[1])
def __setitem__(self, s, val):
if self._s_not_empty(s):
super(ObsAr, self).__setitem__(s, val)
self.notify_observers()
super(ObsAr, self).__setitem__(s, val)
self.notify_observers()
def __getslice__(self, start, stop):
return self.__getitem__(slice(start, stop))
@ -63,12 +60,6 @@ class ObsAr(np.ndarray, Observable):
def __setslice__(self, start, stop, val):
return self.__setitem__(slice(start, stop), val)
def __copy__(self, *args):
return ObsAr(self.view(np.ndarray).copy())
def copy(self, *args):
return self.__copy__(*args)
def __ilshift__(self, *args, **kwargs):
r = np.ndarray.__ilshift__(self, *args, **kwargs)
self.notify_observers()
@ -143,77 +134,4 @@ class ObsAr(np.ndarray, Observable):
def __imul__(self, *args, **kwargs):
r = np.ndarray.__imul__(self, *args, **kwargs)
self.notify_observers()
return r
# def __rrshift__(self, *args, **kwargs):
# r = np.ndarray.__rrshift__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __ror__(self, *args, **kwargs):
# r = np.ndarray.__ror__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __rxor__(self, *args, **kwargs):
# r = np.ndarray.__rxor__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __rdivmod__(self, *args, **kwargs):
# r = np.ndarray.__rdivmod__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __radd__(self, *args, **kwargs):
# r = np.ndarray.__radd__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __rdiv__(self, *args, **kwargs):
# r = np.ndarray.__rdiv__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __rtruediv__(self, *args, **kwargs):
# r = np.ndarray.__rtruediv__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __rshift__(self, *args, **kwargs):
# r = np.ndarray.__rshift__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __rmul__(self, *args, **kwargs):
# r = np.ndarray.__rmul__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __rpow__(self, *args, **kwargs):
# r = np.ndarray.__rpow__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __rsub__(self, *args, **kwargs):
# r = np.ndarray.__rsub__(self, *args, **kwargs)
# self.notify_observers()
# return r
# def __rfloordiv__(self, *args, **kwargs):
# r = np.ndarray.__rfloordiv__(self, *args, **kwargs)
# self.notify_observers()
# return r
return r

View file

@ -4,7 +4,7 @@
import itertools
import numpy
from parameter_core import OptimizationHandlable, adjust_name_for_printing
from array_core import ObsAr
from observable_array import ObsAr
###### printing
__constraints_name__ = "Constraint"
@ -43,14 +43,13 @@ class Param(OptimizationHandlable, ObsAr):
_fixes_ = None
_parameters_ = []
def __new__(cls, name, input_array, default_constraint=None):
obj = numpy.atleast_1d(super(Param, cls).__new__(cls, input_array=input_array, name=name, default_constraint=default_constraint))
obj = numpy.atleast_1d(super(Param, cls).__new__(cls, input_array=input_array))
cls.__name__ = "Param"
obj._current_slice_ = (slice(obj.shape[0]),)
obj._realshape_ = obj.shape
obj._realsize_ = obj.size
obj._realndim_ = obj.ndim
obj._original_ = True
obj._gradient_array_ = numpy.zeros(obj.shape, dtype=numpy.float64)
return obj
def __init__(self, name, input_array, default_constraint=None, *a, **kw):
@ -60,7 +59,7 @@ class Param(OptimizationHandlable, ObsAr):
import pydot
node = pydot.Node(id(self), shape='record', label=self.name)
G.add_node(node)
for o in self._observer_callables_.keys():
for o in self.observers.keys():
label = o.name if hasattr(o, 'name') else str(o)
observed_node = pydot.Node(id(o), label=label)
G.add_node(observed_node)
@ -87,74 +86,24 @@ class Param(OptimizationHandlable, ObsAr):
self.priors = getattr(obj, 'priors', None)
@property
def _param_array_(self):
def param_array(self):
return self
@property
def gradient(self):
"""
Return a view on the gradient, which is in the same shape as this parameter is.
Note: this is not the real gradient array, it is just a view on it.
To work on the real gradient array use: self.full_gradient
"""
if getattr(self, '_gradient_array_', None) is None:
self._gradient_array_ = numpy.empty(self._realshape_, dtype=numpy.float64)
return self._gradient_array_[self._current_slice_]
@gradient.setter
def gradient(self, val):
self.gradient[:] = val
#===========================================================================
# Pickling operations
#===========================================================================
def __reduce__(self):
func, args, state = super(Param, self).__reduce__()
return func, args, (state,
(self._name,
self._parent_,
self._parent_index_,
self._default_constraint_,
self._current_slice_,
self._realshape_,
self._realsize_,
self._realndim_,
self.constraints,
self.priors
)
)
def __setstate__(self, state):
super(Param, self).__setstate__(state[0])
state = list(state[1])
self.priors = state.pop()
self.constraints = state.pop()
self._realndim_ = state.pop()
self._realsize_ = state.pop()
self._realshape_ = state.pop()
self._current_slice_ = state.pop()
self._default_constraint_ = state.pop()
self._parent_index_ = state.pop()
self._parent_ = state.pop()
self._name = state.pop()
def copy(self, *args):
constr = self.constraints.copy()
priors = self.priors.copy()
p = Param(self.name, self.view(numpy.ndarray).copy(), self._default_constraint_)
p.constraints = constr
p.priors = priors
return p
#===========================================================================
# get/set parameters
#===========================================================================
# def _set_params(self, param, trigger_parent=True):
# self.flat = param
# if trigger_parent: min_priority = None
# else: min_priority = -numpy.inf
# self.notify_observers(None, min_priority)
#
# def _get_params(self):
# return self.flat
#
# def _collect_gradient(self, target):
# target += self.gradient.flat
#
# def _set_gradient(self, g):
# self.gradient = g.reshape(self._realshape_)
self._gradient_array_[self._current_slice_] = val
#===========================================================================
# Array operations -> done
@ -172,24 +121,6 @@ class Param(OptimizationHandlable, ObsAr):
def __setitem__(self, s, val):
super(Param, self).__setitem__(s, val)
#===========================================================================
# Index Operations:
#===========================================================================
#def _internal_offset(self):
# internal_offset = 0
# extended_realshape = numpy.cumprod((1,) + self._realshape_[:0:-1])[::-1]
# for i, si in enumerate(self._current_slice_[:self._realndim_]):
# if numpy.all(si == Ellipsis):
# continue
# if isinstance(si, slice):
# a = si.indices(self._realshape_[i])[0]
# elif isinstance(si, (list,numpy.ndarray,tuple)):
# a = si[0]
# else: a = si
# if a < 0:
# a = self._realshape_[i] + a
# internal_offset += a * extended_realshape[i]
# return internal_offset
def _raveled_index(self, slice_index=None):
# return an index array on the raveled array, which is formed by the current_slice
@ -235,13 +166,21 @@ class Param(OptimizationHandlable, ObsAr):
def is_fixed(self):
from transformations import __fixed__
return self.constraints[__fixed__].size == self.size
#def round(self, decimals=0, out=None):
# view = super(Param, self).round(decimals, out).view(Param)
# view.__array_finalize__(self)
# return view
#round.__doc__ = numpy.round.__doc__
def _get_original(self, param):
return self
#===========================================================================
# Pickling and copying
#===========================================================================
def __deepcopy__(self, memo):
s = self.__new__(self.__class__, name=self.name, input_array=self.view(numpy.ndarray).copy())
memo[id(self)] = s
import copy
s.__dict__.update(copy.deepcopy(self.__dict__, memo))
return s
#===========================================================================
# Printing -> done
#===========================================================================
@ -250,7 +189,8 @@ class Param(OptimizationHandlable, ObsAr):
if self.size <= 1:
return [str(self.view(numpy.ndarray)[0])]
else: return [str(self.shape)]
def parameter_names(self, add_self=False, adjust_for_printing=False):
def parameter_names(self, add_self=False, adjust_for_printing=False, recursive=True):
# this is just overwrighting the parameterized calls to parameter names, in order to maintain OOP
if adjust_for_printing:
return [adjust_name_for_printing(self.name)]
return [self.name]
@ -261,6 +201,9 @@ class Param(OptimizationHandlable, ObsAr):
def parameter_shapes(self):
return [self.shape]
@property
def num_params(self):
return 0
@property
def _constraints_str(self):
return [' '.join(map(lambda c: str(c[0]) if c[1].size == self._realsize_ else "{" + str(c[0]) + "}", self.constraints.iteritems()))]
@property
@ -282,8 +225,8 @@ class Param(OptimizationHandlable, ObsAr):
if isinstance(slice_index, (tuple, list)):
clean_curr_slice = [s for s in slice_index if numpy.any(s != Ellipsis)]
for i in range(self._realndim_-len(clean_curr_slice)):
i+=len(clean_curr_slice)
clean_curr_slice += range(self._realshape_[i])
i+=1
clean_curr_slice += [range(self._realshape_[i])]
if (all(isinstance(n, (numpy.ndarray, list, tuple)) for n in clean_curr_slice)
and len(set(map(len, clean_curr_slice))) <= 1):
return numpy.fromiter(itertools.izip(*clean_curr_slice),
@ -368,7 +311,7 @@ class ParamConcatenation(object):
#===========================================================================
def __getitem__(self, s):
ind = numpy.zeros(sum(self._param_sizes), dtype=bool); ind[s] = True;
params = [p._param_array_[ind[ps]] for p,ps in zip(self.params, self._param_slices_) if numpy.any(p._param_array_[ind[ps]])]
params = [p.param_array[ind[ps]] for p,ps in zip(self.params, self._param_slices_) if numpy.any(p.param_array[ind[ps]])]
if len(params)==1: return params[0]
return ParamConcatenation(params)
def __setitem__(self, s, val, update=True):
@ -381,7 +324,7 @@ class ParamConcatenation(object):
if update:
self.update_all_params()
def values(self):
return numpy.hstack([p._param_array_ for p in self.params])
return numpy.hstack([p.param_array.flat for p in self.params])
#===========================================================================
# parameter operations:
#===========================================================================

View file

@ -13,10 +13,10 @@ Observable Pattern for patameterization
"""
from transformations import Transformation, Logexp, NegativeLogexp, Logistic, __fixed__, FIXED, UNFIXED
from transformations import Logexp, NegativeLogexp, Logistic, __fixed__, FIXED, UNFIXED
import numpy as np
__updated__ = '2014-03-24'
__updated__ = '2014-03-31'
class HierarchyError(Exception):
"""
@ -31,71 +31,8 @@ def adjust_name_for_printing(name):
return name.replace(" ", "_").replace(".", "_").replace("-", "_m_").replace("+", "_p_").replace("!", "_I_").replace("**", "_xx_").replace("*", "_x_").replace("/", "_l_").replace("@", '_at_')
return ''
class InterfacePickleFunctions(object):
def __init__(self, *a, **kw):
super(InterfacePickleFunctions, self).__init__()
def _getstate(self):
"""
Returns the state of this class in a memento pattern.
The state must be a list-like structure of all the fields
this class needs to run.
See python doc "pickling" (`__getstate__` and `__setstate__`) for details.
"""
raise NotImplementedError, "To be able to use pickling you need to implement this method"
def _setstate(self, state):
"""
Set the state (memento pattern) of this class to the given state.
Usually this is just the counterpart to _getstate, such that
an object is a copy of another when calling
copy = <classname>.__new__(*args,**kw)._setstate(<to_be_copied>._getstate())
See python doc "pickling" (`__getstate__` and `__setstate__`) for details.
"""
raise NotImplementedError, "To be able to use pickling you need to implement this method"
class Pickleable(InterfacePickleFunctions):
"""
Make an object pickleable (See python doc 'pickling').
This class allows for pickling support by Memento pattern.
_getstate returns a memento of the class, which gets pickled.
_setstate(<memento>) (re-)sets the state of the class to the memento
"""
def __init__(self, *a, **kw):
super(Pickleable, self).__init__()
#===========================================================================
# Pickling operations
#===========================================================================
def pickle(self, f, protocol=-1):
"""
:param f: either filename or open file object to write to.
if it is an open buffer, you have to make sure to close
it properly.
:param protocol: pickling protocol to use, python-pickle for details.
"""
import cPickle
if isinstance(f, str):
with open(f, 'w') as f:
cPickle.dump(self, f, protocol)
else:
cPickle.dump(self, f, protocol)
def __getstate__(self):
if self._has_get_set_state():
return self._getstate()
return self.__dict__
def __setstate__(self, state):
if self._has_get_set_state():
self._setstate(state)
# TODO: maybe parameters_changed() here?
return
self.__dict__ = state
def _has_get_set_state(self):
return '_getstate' in vars(self.__class__) and '_setstate' in vars(self.__class__)
class Observable(Pickleable):
class Observable(object):
"""
Observable pattern for parameterization.
@ -105,23 +42,25 @@ class Observable(Pickleable):
"""
_updated = True
def __init__(self, *args, **kwargs):
super(Observable, self).__init__(*args, **kwargs)
self._observer_callables_ = []
super(Observable, self).__init__()
from lists_and_dicts import ObservablesList
self.observers = ObservablesList()
def add_observer(self, observer, callble, priority=0):
self._insert_sorted(priority, observer, callble)
self.observers.add(priority, observer, callble)
def remove_observer(self, observer, callble=None):
to_remove = []
for p, obs, clble in self._observer_callables_:
for poc in self.observers:
_, obs, clble = poc
if callble is not None:
if (obs == observer) and (callble == clble):
to_remove.append((p, obs, clble))
to_remove.append(poc)
else:
if obs is observer:
to_remove.append((p, obs, clble))
to_remove.append(poc)
for r in to_remove:
self._observer_callables_.remove(r)
self.observers.remove(*r)
def notify_observers(self, which=None, min_priority=None):
"""
@ -136,32 +75,18 @@ class Observable(Pickleable):
if which is None:
which = self
if min_priority is None:
[callble(self, which=which) for _, _, callble in self._observer_callables_]
[callble(self, which=which) for _, _, callble in self.observers]
else:
for p, _, callble in self._observer_callables_:
for p, _, callble in self.observers:
if p <= min_priority:
break
callble(self, which=which)
def _insert_sorted(self, p, o, c):
ins = 0
for pr, _, _ in self._observer_callables_:
if p > pr:
break
ins += 1
self._observer_callables_.insert(ins, (p, o, c))
def _getstate(self):
return [self._observer_callables_]
def _setstate(self, state):
self._observer_callables_ = state.pop()
#===============================================================================
# Foundation framework for parameterized and param objects:
#===============================================================================
class Parentable(Observable):
class Parentable(object):
"""
Enable an Object to have a parent.
@ -171,7 +96,7 @@ class Parentable(Observable):
_parent_ = None
_parent_index_ = None
def __init__(self, *args, **kwargs):
super(Parentable, self).__init__(*args, **kwargs)
super(Parentable, self).__init__()
def has_parent(self):
"""
@ -207,7 +132,84 @@ class Parentable(Observable):
"""
pass
class Gradcheckable(Parentable):
class Pickleable(object):
"""
Make an object pickleable (See python doc 'pickling').
This class allows for pickling support by Memento pattern.
_getstate returns a memento of the class, which gets pickled.
_setstate(<memento>) (re-)sets the state of the class to the memento
"""
def __init__(self, *a, **kw):
super(Pickleable, self).__init__()
#===========================================================================
# Pickling operations
#===========================================================================
def pickle(self, f, protocol=-1):
"""
:param f: either filename or open file object to write to.
if it is an open buffer, you have to make sure to close
it properly.
:param protocol: pickling protocol to use, python-pickle for details.
"""
import cPickle as pickle
import pickle #TODO: cPickle
if isinstance(f, str):
with open(f, 'w') as f:
pickle.dump(self, f, protocol)
else:
pickle.dump(self, f, protocol)
#===========================================================================
# copy and pickling
#===========================================================================
def copy(self):
"""Returns a (deep) copy of the current model"""
#raise NotImplementedError, "Copy is not yet implemented, TODO: Observable hierarchy"
import copy
memo = {}
memo[id(self._parent_)] = None
memo[id(self.gradient)] = None
memo[id(self.param_array)] = None
memo[id(self._fixes_)] = None
c = copy.deepcopy(self, memo)
c._parent_index_ = None
return c
def __deepcopy__(self, memo):
s = self.__new__(self.__class__)
memo[id(self)] = s
import copy
s.__dict__.update(copy.deepcopy(self.__dict__, memo))
return s
def __getstate__(self):
ignore_list = ([#'_parent_', '_parent_index_',
#'observers',
'_param_array_', '_gradient_array_', '_fixes_',
'_Cacher_wrap__cachers']
#+ self.parameter_names(recursive=False)
)
dc = dict()
for k,v in self.__dict__.iteritems():
if k not in ignore_list:
#if hasattr(v, "__getstate__"):
#dc[k] = v.__getstate__()
#else:
dc[k] = v
return dc
def __setstate__(self, state):
self.__dict__.update(state)
return self
#def __getstate__(self, memo):
# raise NotImplementedError, "get state must be implemented to be able to pickle objects"
#def __setstate__(self, memo):
# raise NotImplementedError, "set state must be implemented to be able to pickle objects"
class Gradcheckable(Pickleable, Parentable):
"""
Adds the functionality for an object to be gradcheckable.
It is just a thin wrapper of a call to the highest parent for now.
@ -312,7 +314,7 @@ class Indexable(object):
raise NotImplementedError, "shouldnt happen, raveld index transformation required from non parameterization object?"
class Constrainable(Nameable, Indexable):
class Constrainable(Nameable, Indexable, Observable):
"""
Make an object constrainable with Priors and Transformations.
TODO: Mappings!!
@ -394,6 +396,7 @@ class Constrainable(Nameable, Indexable):
self._fixes_[fixed_indices] = FIXED
else:
self._fixes_ = None
del self.constraints[__fixed__]
def _has_fixes(self):
return hasattr(self, "_fixes_") and self._fixes_ is not None and self._fixes_.size == self.size
@ -429,14 +432,14 @@ class Constrainable(Nameable, Indexable):
def log_prior(self):
"""evaluate the prior"""
if self.priors.size > 0:
x = self._param_array_
x = self.param_array
return reduce(lambda a, b: a + b, (p.lnpdf(x[ind]).sum() for p, ind in self.priors.iteritems()), 0)
return 0.
def _log_prior_gradients(self):
"""evaluate the gradients of the priors"""
if self.priors.size > 0:
x = self._param_array_
x = self.param_array
ret = np.zeros(x.size)
[np.put(ret, ind, p.lnpdf_grad(x[ind])) for p, ind in self.priors.iteritems()]
return ret
@ -455,7 +458,7 @@ class Constrainable(Nameable, Indexable):
Constrain the parameter to the given
:py:class:`GPy.core.transformations.Transformation`.
"""
self._param_array_[:] = transform.initialize(self._param_array_)
self.param_array[:] = transform.initialize(self.param_array)
reconstrained = self.unconstrain()
self._add_to_index_operations(self.constraints, reconstrained, transform, warning)
self.notify_observers(self, None if trigger_parent else -np.inf)
@ -565,14 +568,14 @@ class OptimizationHandlable(Constrainable):
super(OptimizationHandlable, self).__init__(name, default_constraint=default_constraint, *a, **kw)
def transform(self):
[np.put(self._param_array_, ind, c.finv(self._param_array_.flat[ind])) for c, ind in self.constraints.iteritems() if c != __fixed__]
[np.put(self.param_array, ind, c.finv(self.param_array.flat[ind])) for c, ind in self.constraints.iteritems() if c != __fixed__]
def untransform(self):
[np.put(self._param_array_, ind, c.f(self._param_array_.flat[ind])) for c, ind in self.constraints.iteritems() if c != __fixed__]
[np.put(self.param_array, ind, c.f(self.param_array.flat[ind])) for c, ind in self.constraints.iteritems() if c != __fixed__]
def _get_params_transformed(self):
# transformed parameters (apply transformation rules)
p = self._param_array_.copy()
p = self.param_array.copy()
[np.put(p, ind, c.finv(p[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)
@ -583,14 +586,14 @@ class OptimizationHandlable(Constrainable):
return p
def _set_params_transformed(self, p):
if p is self._param_array_:
if p is self.param_array:
p = p.copy()
if self.has_parent() and self.constraints[__fixed__].size != 0:
fixes = np.ones(self.size).astype(bool)
fixes[self.constraints[__fixed__]] = FIXED
self._param_array_.flat[fixes] = p
elif self._has_fixes(): self._param_array_.flat[self._fixes_] = p
else: self._param_array_.flat = p
self.param_array.flat[fixes] = p
elif self._has_fixes(): self.param_array.flat[self._fixes_] = p
else: self.param_array.flat = p
self.untransform()
self._trigger_params_changed()
@ -600,36 +603,29 @@ class OptimizationHandlable(Constrainable):
def _size_transformed(self):
return self.size - self.constraints[__fixed__].size
#
# def _untransform_params(self, p):
# # inverse apply transformations for parameters
# #p = p.copy()
# if self._has_fixes(): tmp = self._get_params(); tmp[self._fixes_] = p; p = tmp; del tmp
# [np.put(p, ind, c.f(p[ind])) for c, ind in self.constraints.iteritems() if c != __fixed__]
# return p
#
# def _get_params(self):
# """
# get all parameters
# """
# return self._param_array_
# p = np.empty(self.size, dtype=np.float64)
# if self.size == 0:
# return p
# [np.put(p, ind, par._get_params()) for ind, par in itertools.izip(self._param)]
# return p
# def _set_params(self, params, trigger_parent=True):
# self._param_array_.flat = params
# if trigger_parent: min_priority = None
# else: min_priority = -np.inf
# self.notify_observers(None, min_priority)
# don't overwrite this anymore!
# raise NotImplementedError, "Abstract superclass: This needs to be implemented in Param and Parameterizable"
@property
def num_params(self):
"""
Return the number of parameters of this parameter_handle.
Param objects will allways return 0.
"""
raise NotImplemented, "Abstract, please implement in respective classes"
#===========================================================================
# Optimization handles:
#===========================================================================
def parameter_names(self, add_self=False, adjust_for_printing=False, recursive=True):
"""
Get the names of all parameters of this model.
:param bool add_self: whether to add the own name in front of names
:param bool adjust_for_printing: whether to call `adjust_name_for_printing` on names
:param bool recursive: whether to traverse through hierarchy and append leaf node names
"""
if adjust_for_printing: adjust = lambda x: adjust_name_for_printing(x)
else: adjust = lambda x: x
if recursive: names = [xi for x in self._parameters_ for xi in x.parameter_names(add_self=True, adjust_for_printing=adjust_for_printing)]
else: names = [adjust(x.name) for x in self._parameters_]
if add_self: names = map(lambda x: adjust(self.name) + "." + x, names)
return names
def _get_param_names(self):
n = np.array([p.hierarchy_name() + '[' + str(i) + ']' for p in self.flattened_parameters for i in p._indices()])
return n
@ -663,16 +659,30 @@ class OptimizationHandlable(Constrainable):
# For shared memory arrays. This does nothing in Param, but sets the memory
# for all parameterized objects
#===========================================================================
@property
def full_gradient(self):
"""
Note to users:
This does not return the gradient in the right shape! Use self.gradient
for the right gradient array.
To work on the gradient array, use this as the gradient handle.
This method exists for in memory use of parameters.
When trying to access the true gradient array, use this.
"""
self.gradient # <<< ensure _gradient_array_
return self._gradient_array_
def _propagate_param_grad(self, parray, garray):
pi_old_size = 0
for pi in self._parameters_:
pislice = slice(pi_old_size, pi_old_size + pi.size)
self._param_array_[pislice] = pi._param_array_.flat # , requirements=['C', 'W']).flat
self._gradient_array_[pislice] = pi._gradient_array_.flat # , requirements=['C', 'W']).flat
self.param_array[pislice] = pi.param_array.flat # , requirements=['C', 'W']).flat
self.full_gradient[pislice] = pi.full_gradient.flat # , requirements=['C', 'W']).flat
pi._param_array_.data = parray[pislice].data
pi._gradient_array_.data = garray[pislice].data
pi.param_array.data = parray[pislice].data
pi.full_gradient.data = garray[pislice].data
pi._propagate_param_grad(parray[pislice], garray[pislice])
pi_old_size += pi.size
@ -681,26 +691,32 @@ class Parameterizable(OptimizationHandlable):
def __init__(self, *args, **kwargs):
super(Parameterizable, self).__init__(*args, **kwargs)
from GPy.core.parameterization.lists_and_dicts import ArrayList
_parameters_ = ArrayList()
self._parameters_ = ArrayList()
self.size = 0
self._param_array_ = np.empty(self.size, dtype=np.float64)
self._gradient_array_ = np.empty(self.size, dtype=np.float64)
self._added_names_ = set()
def parameter_names(self, add_self=False, adjust_for_printing=False, recursive=True):
"""
Get the names of all parameters of this model.
@property
def param_array(self):
if not hasattr(self, '_param_array_'):
self._param_array_ = np.empty(self.size, dtype=np.float64)
return self._param_array_
:param bool add_self: whether to add the own name in front of names
:param bool adjust_for_printing: whether to call `adjust_name_for_printing` on names
:param bool recursive: whether to traverse through hierarchy and append leaf node names
"""
if adjust_for_printing: adjust = lambda x: adjust_name_for_printing(x)
else: adjust = lambda x: x
if recursive: names = [xi for x in self._parameters_ for xi in x.parameter_names(add_self=True, adjust_for_printing=adjust_for_printing)]
else: names = [adjust(x.name) for x in self._parameters_]
if add_self: names = map(lambda x: adjust(self.name) + "." + x, names)
return names
@param_array.setter
def param_array(self, arr):
self._param_array_ = arr
#=========================================================================
# Gradient handling
#=========================================================================
@property
def gradient(self):
if not hasattr(self, '_gradient_array_'):
self._gradient_array_ = np.empty(self.size, dtype=np.float64)
return self._gradient_array_
@gradient.setter
def gradient(self, val):
self._gradient_array_[:] = val
@property
def num_params(self):
@ -737,34 +753,6 @@ class Parameterizable(OptimizationHandlable):
self._remove_parameter_name(None, old_name)
self._add_parameter_name(param)
#=========================================================================
# Gradient handling
#=========================================================================
@property
def gradient(self):
return self._gradient_array_
@gradient.setter
def gradient(self, val):
self._gradient_array_[:] = val
#===========================================================================
# def _collect_gradient(self, target):
# [p._collect_gradient(target[s]) for p, s in itertools.izip(self._parameters_, self._param_slices_)]
#===========================================================================
#===========================================================================
# def _set_params(self, params, trigger_parent=True):
# [p._set_params(params[s], trigger_parent=False) for p, s in itertools.izip(self._parameters_, self._param_slices_)]
# if trigger_parent: min_priority = None
# else: min_priority = -np.inf
# self.notify_observers(None, min_priority)
#===========================================================================
#===========================================================================
# def _set_gradient(self, g):
# [p._set_gradient(g[s]) for p, s in itertools.izip(self._parameters_, self._param_slices_)]
#===========================================================================
def add_parameter(self, param, index=None, _ignore_added_names=False):
"""
:param parameters: the parameters to add
@ -864,7 +852,7 @@ class Parameterizable(OptimizationHandlable):
# no parameters for this class
return
old_size = 0
self._param_array_ = np.empty(self.size, dtype=np.float64)
self.param_array = np.empty(self.size, dtype=np.float64)
self._gradient_array_ = np.empty(self.size, dtype=np.float64)
self._param_slices_ = []
@ -874,15 +862,15 @@ class Parameterizable(OptimizationHandlable):
pslice = slice(old_size, old_size + p.size)
# first connect all children
p._propagate_param_grad(self._param_array_[pslice], self._gradient_array_[pslice])
p._propagate_param_grad(self.param_array[pslice], self.full_gradient[pslice])
# then connect children to self
self._param_array_[pslice] = p._param_array_.flat # , requirements=['C', 'W']).ravel(order='C')
self._gradient_array_[pslice] = p._gradient_array_.flat # , requirements=['C', 'W']).ravel(order='C')
self.param_array[pslice] = p.param_array.flat # , requirements=['C', 'W']).ravel(order='C')
self.full_gradient[pslice] = p.full_gradient.flat # , requirements=['C', 'W']).ravel(order='C')
if not p._param_array_.flags['C_CONTIGUOUS']:
import ipdb;ipdb.set_trace()
p._param_array_.data = self._param_array_[pslice].data
p._gradient_array_.data = self._gradient_array_[pslice].data
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.full_gradient.data = self.full_gradient[pslice].data
self._param_slices_.append(pslice)
@ -898,41 +886,22 @@ class Parameterizable(OptimizationHandlable):
self.notify_observers(which=which)
#===========================================================================
# TODO: not working yet
# Pickling
#===========================================================================
def __setstate__(self, state):
super(Parameterizable, self).__setstate__(state)
self._connect_parameters()
self._connect_fixes()
self._notify_parent_change()
self.parameters_changed()
def copy(self):
"""Returns a (deep) copy of the current model"""
raise NotImplementedError, "Copy is not yet implemented, TODO: Observable hierarchy"
import copy
from .index_operations import ParameterIndexOperations, ParameterIndexOperationsView
from .lists_and_dicts import ArrayList
dc = dict()
for k, v in self.__dict__.iteritems():
if k not in ['_parent_', '_parameters_', '_parent_index_', '_observer_callables_'] + self.parameter_names(recursive=False):
if isinstance(v, (Constrainable, ParameterIndexOperations, ParameterIndexOperationsView)):
dc[k] = v.copy()
else:
dc[k] = copy.deepcopy(v)
if k == '_parameters_':
params = [p.copy() for p in v]
dc['_parent_'] = None
dc['_parent_index_'] = None
dc['_observer_callables_'] = []
dc['_parameters_'] = ArrayList()
dc['constraints'].clear()
dc['priors'].clear()
dc['size'] = 0
s = self.__new__(self.__class__)
s.__dict__ = dc
for p in params:
s.add_parameter(p, _ignore_added_names=True)
return s
c = super(Parameterizable, self).copy()
c._connect_parameters()
c._connect_fixes()
c._notify_parent_change()
return c
#===========================================================================
# From being parentable, we have to define the parent_change notification
#===========================================================================

View file

@ -17,7 +17,7 @@ class ParametersChangedMeta(type):
instance.parameters_changed()
return instance
class Parameterized(Parameterizable, Pickleable):
class Parameterized(Parameterizable):
"""
Parameterized class
@ -63,7 +63,7 @@ class Parameterized(Parameterizable, Pickleable):
# Metaclass for parameters changed after init.
# This makes sure, that parameters changed will always be called after __init__
# **Never** call parameters_changed() yourself
__metaclass__ = ParametersChangedMeta
__metaclass__ = ParametersChangedMeta
#===========================================================================
def __init__(self, name=None, parameters=[], *a, **kw):
super(Parameterized, self).__init__(name=name, *a, **kw)
@ -90,7 +90,7 @@ class Parameterized(Parameterizable, Pickleable):
child_node = child.build_pydot(G)
G.add_edge(pydot.Edge(node, child_node))
for o in self._observer_callables_.keys():
for o in self.observers.keys():
label = o.name if hasattr(o, 'name') else str(o)
observed_node = pydot.Node(id(o), label=label)
G.add_node(observed_node)
@ -101,48 +101,13 @@ class Parameterized(Parameterizable, Pickleable):
return G
return node
def _getstate(self):
"""
Get the current state of the class,
here just all the indices, rest can get recomputed
For inheriting from Parameterized:
Allways append the state of the inherited object
and call down to the inherited object in _setstate!!
"""
return [
self._fixes_,
self.priors,
self.constraints,
self._parameters_,
self._name,
self._added_names_,
]
def _setstate(self, state):
self._added_names_ = state.pop()
self._name = state.pop()
self._parameters_ = state.pop()
self.constraints = state.pop()
self.priors = state.pop()
self._fixes_ = state.pop()
self._connect_parameters()
self.parameters_changed()
#===========================================================================
# Override copy to handle programmatically added observers
#===========================================================================
def copy(self):
c = super(Pickleable, self).copy()
c.add_observer(c, c._parameters_changed_notification, -100)
return c
#===========================================================================
# Gradient control
#===========================================================================
def _transform_gradients(self, g):
if self.has_parent():
return g
[numpy.put(g, i, g[i] * c.gradfactor(self._param_array_[i])) for c, i in self.constraints.iteritems() if c != __fixed__]
[numpy.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
@ -174,7 +139,7 @@ class Parameterized(Parameterizable, Pickleable):
this is not in the global view of things!
"""
return numpy.r_[:self.size]
#===========================================================================
# Convenience for fixed, tied checking of param:
#===========================================================================
@ -189,7 +154,7 @@ class Parameterized(Parameterizable, Pickleable):
# you can retrieve the original param through this method, by passing
# the copy here
return self._parameters_[param._parent_index_]
#===========================================================================
# Get/set parameters:
#===========================================================================
@ -206,7 +171,7 @@ class Parameterized(Parameterizable, Pickleable):
def __getitem__(self, name, paramlist=None):
if isinstance(name, (int, slice, tuple, np.ndarray)):
return self._param_array_[name]
return self.param_array[name]
else:
if paramlist is None:
paramlist = self.grep_param_names(name)
@ -222,7 +187,7 @@ class Parameterized(Parameterizable, Pickleable):
def __setitem__(self, name, value, paramlist=None):
if isinstance(name, (slice, tuple, np.ndarray)):
try:
self._param_array_[name] = value
self.param_array[name] = value
except:
raise ValueError, "Setting by slice or index only allowed with array-like"
self._trigger_params_changed()

View file

@ -66,7 +66,7 @@ class SpikeAndSlabPrior(VariationalPrior):
self.pi.gradient = (gamma/self.pi - (1.-gamma)/(1.-self.pi)).sum(axis=0)
class VariationalPosterior(Parameterized):
def __init__(self, means=None, variances=None, name=None, *a, **kw):
def __init__(self, means=None, variances=None, name='latent space', *a, **kw):
super(VariationalPosterior, self).__init__(name=name, *a, **kw)
self.mean = Param("mean", means)
self.variance = Param("variance", variances, Logexp())
@ -124,6 +124,7 @@ class NormalPosterior(VariationalPosterior):
import sys
assert "matplotlib" in sys.modules, "matplotlib package has not been imported."
from ...plotting.matplot_dep import variational_plots
import matplotlib
return variational_plots.plot(self,*args)
class SpikeAndSlabPosterior(VariationalPosterior):