From 65fd6dd24e0cd840106783ac9c82452298bb7c1a Mon Sep 17 00:00:00 2001 From: Max Zwiessele Date: Wed, 26 Feb 2014 15:46:14 +0000 Subject: [PATCH] observable pattern through and thorugh --- GPy/core/parameterization/array_core.py | 5 +- GPy/core/parameterization/param.py | 17 +++--- GPy/core/parameterization/parameter_core.py | 27 +++++---- GPy/core/parameterization/parameterized.py | 4 +- GPy/kern/_src/linear.py | 1 - GPy/kern/_src/rbf.py | 5 +- GPy/kern/_src/ssrbf.py | 5 +- GPy/kern/_src/stationary.py | 5 ++ GPy/models/gplvm.py | 2 +- GPy/models/sparse_gp_regression.py | 6 +- GPy/util/caching.py | 67 +++++++-------------- 11 files changed, 64 insertions(+), 80 deletions(-) diff --git a/GPy/core/parameterization/array_core.py b/GPy/core/parameterization/array_core.py index e8be0f77..e5b2ac07 100644 --- a/GPy/core/parameterization/array_core.py +++ b/GPy/core/parameterization/array_core.py @@ -59,11 +59,10 @@ class ObservableArray(np.ndarray, Observable): else: return s.size != 0 - def __setitem__(self, s, val, update=True): + def __setitem__(self, s, val): if self._s_not_empty(s): super(ObservableArray, self).__setitem__(s, val) - if update: - self._notify_observers() + self._notify_observers() def __getslice__(self, start, stop): return self.__getitem__(slice(start, stop)) diff --git a/GPy/core/parameterization/param.py b/GPy/core/parameterization/param.py index 8eac69da..53b2260e 100644 --- a/GPy/core/parameterization/param.py +++ b/GPy/core/parameterization/param.py @@ -137,8 +137,6 @@ class Param(Constrainable, ObservableArray, Gradcheckable): #=========================================================================== def _set_params(self, param, update=True): self.flat = param - #self._notify_tied_parameters() - self._notify_observers() def _get_params(self): return self.flat @@ -161,12 +159,10 @@ class Param(Constrainable, ObservableArray, Gradcheckable): try: new_arr._current_slice_ = s; new_arr._original_ = self.base is new_arr.base except AttributeError: pass # returning 0d array or float, double etc return new_arr - def __setitem__(self, s, val, update=True): - super(Param, self).__setitem__(s, val, update=update) - #self._notify_tied_parameters() - if update and self._s_not_empty(s): - self._notify_parameters_changed() - + def __setitem__(self, s, val): + super(Param, self).__setitem__(s, val) + #self._notify_observers() + #=========================================================================== # Index Operations: #=========================================================================== @@ -185,6 +181,7 @@ class Param(Constrainable, ObservableArray, Gradcheckable): 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 # of this object @@ -354,7 +351,7 @@ class ParamConcatenation(object): val = val._vals() ind = numpy.zeros(sum(self._param_sizes), dtype=bool); ind[s] = True; vals = self._vals(); vals[s] = val; del val - [numpy.place(p, ind[ps], vals[ps]) and update and p._notify_parameters_changed() + [numpy.place(p, ind[ps], vals[ps]) and update and p._notify_observers() for p, ps in zip(self.params, self._param_slices_)] def _vals(self): return numpy.hstack([p._get_params() for p in self.params]) @@ -363,7 +360,7 @@ class ParamConcatenation(object): #=========================================================================== def update_all_params(self): for p in self.params: - p._notify_parameters_changed() + p._notify_observers() def constrain(self, constraint, warning=True): [param.constrain(constraint, update=False) for param in self.params] diff --git a/GPy/core/parameterization/parameter_core.py b/GPy/core/parameterization/parameter_core.py index 28d63b02..e1e5c21c 100644 --- a/GPy/core/parameterization/parameter_core.py +++ b/GPy/core/parameterization/parameter_core.py @@ -18,8 +18,13 @@ class Observable(object): def add_observer(self, observer, callble): self._observer_callables_[observer].append(callble) - def remove_observer(self, observer, callble): - del self._observer_callables_[observer][callble] + def remove_observer(self, observer, callble=None): + if callble is None: + del self._observer_callables_[observer] + else: + self._observer_callables_[observer].remove(callble) + if len(self._observer_callables_[observer]) == 0: + self.remove_observer(observer) def _notify_observers(self): [[callble(self) for callble in callables] @@ -72,9 +77,8 @@ class Parentable(object): return self._direct_parent_._highest_parent_ def _notify_parameters_changed(self): - if self.has_parent(): - self._direct_parent_._notify_parameters_changed() - + raise NotImplementedError, "shouldnt happen, abstract superclass" + class Nameable(Parentable): def __init__(self, name, *a, **kw): super(Nameable, self).__init__(*a, **kw) @@ -309,7 +313,7 @@ class Constrainable(Nameable, Indexable): print "WARNING: reconstraining parameters {}".format(self.parameter_names() or self.name) which.add(transform, self._raveled_index()) if update: - self._notify_parameters_changed() + self._notify_observers() def _remove_from_index_operations(self, which, transforms): if len(transforms) == 0: @@ -325,7 +329,7 @@ class Constrainable(Nameable, Indexable): return removed -class Parameterizable(Constrainable): +class Parameterizable(Constrainable, Observable): def __init__(self, *args, **kwargs): super(Parameterizable, self).__init__(*args, **kwargs) from GPy.core.parameterization.array_core import ParamList @@ -386,7 +390,7 @@ class Parameterizable(Constrainable): def _set_params(self, params, update=True): # don't overwrite this anymore! import itertools - [p._set_params(params[s], update=update) for p, s in itertools.izip(self._parameters_, self._param_slices_)] + [p._set_params(params[s]) for p, s in itertools.izip(self._parameters_, self._param_slices_)] self.parameters_changed() def copy(self): @@ -420,11 +424,10 @@ class Parameterizable(Constrainable): return s - def _notify_parameters_changed(self): + def _notify_parameters_changed(self, which): self.parameters_changed() - if self.has_parent(): - self._direct_parent_._notify_parameters_changed() - + self._notify_observers() + def parameters_changed(self): """ This method gets called when parameters have changed. diff --git a/GPy/core/parameterization/parameterized.py b/GPy/core/parameterization/parameterized.py index d463ed43..16ae43a7 100644 --- a/GPy/core/parameterization/parameterized.py +++ b/GPy/core/parameterization/parameterized.py @@ -11,7 +11,7 @@ from parameter_core import Constrainable, Pickleable, Parentable, Observable, Pa from transformations import __fixed__ from array_core import ParamList -class Parameterized(Parameterizable, Pickleable, Observable, Gradcheckable): +class Parameterized(Parameterizable, Pickleable, Gradcheckable): """ Parameterized class @@ -92,6 +92,7 @@ class Parameterized(Parameterizable, Pickleable, Observable, Gradcheckable): self.constraints.update(param.constraints, start) self.priors.update(param.priors, start) self._parameters_.insert(index, param) + param.add_observer(self, self._notify_parameters_changed) self.size += param.size else: raise RuntimeError, """Parameter exists already added and no copy made""" @@ -120,6 +121,7 @@ class Parameterized(Parameterizable, Pickleable, Observable, Gradcheckable): del self._parameters_[param._parent_index_] param._disconnect_parent() + param.remove_observer(self, self._notify_parameters_changed) self.constraints.shift_left(start, param.size) self._connect_fixes() self._connect_parameters() diff --git a/GPy/kern/_src/linear.py b/GPy/kern/_src/linear.py index 44f71d85..2c82eb7a 100644 --- a/GPy/kern/_src/linear.py +++ b/GPy/kern/_src/linear.py @@ -9,7 +9,6 @@ from ...util.linalg import tdot from ...util.misc import fast_array_equal, param_to_array from ...core.parameterization import Param from ...core.parameterization.transformations import Logexp -from ...util.caching import cache_this class Linear(Kern): """ diff --git a/GPy/kern/_src/rbf.py b/GPy/kern/_src/rbf.py index 666a79f4..5e973aee 100644 --- a/GPy/kern/_src/rbf.py +++ b/GPy/kern/_src/rbf.py @@ -6,6 +6,7 @@ import numpy as np from scipy import weave from ...util.misc import param_to_array from stationary import Stationary +from GPy.util.caching import Cache_this class RBF(Stationary): """ @@ -166,7 +167,7 @@ class RBF(Stationary): return target - #@cache_this TODO + @Cache_this(limit=1) def _psi1computations(self, Z, vp): mu, S = vp.mean, vp.variance l2 = self.lengthscale **2 @@ -179,7 +180,7 @@ class RBF(Stationary): - #@cache_this TODO + @Cache_this(limit=1) def _psi2computations(self, Z, vp): mu, S = vp.mean, vp.variance diff --git a/GPy/kern/_src/ssrbf.py b/GPy/kern/_src/ssrbf.py index 60df7225..cd921acb 100644 --- a/GPy/kern/_src/ssrbf.py +++ b/GPy/kern/_src/ssrbf.py @@ -6,7 +6,6 @@ from kern import Kern import numpy as np from ...util.linalg import tdot from ...util.config import * -from ...util.caching import cache_this from stationary import Stationary class SSRBF(Stationary): @@ -155,7 +154,7 @@ class SSRBF(Stationary): # Precomputations # #---------------------------------------# - @cache_this(1) + #@cache_this(1) def _K_computations(self, X, X2): """ K(X,X2) - X is NxQ @@ -175,7 +174,7 @@ class SSRBF(Stationary): self._K_dist2 = -2.*np.dot(X, X2.T) + (np.sum(np.square(X), axis=1)[:, None] + np.sum(np.square(X2), axis=1)[None, :]) self._K_dvar = np.exp(-0.5 * self._K_dist2) - @cache_this(1) + #@cache_this(1) def _psi_computations(self, Z, mu, S, gamma): """ Z - MxQ diff --git a/GPy/kern/_src/stationary.py b/GPy/kern/_src/stationary.py index b3c648fc..ebc6b880 100644 --- a/GPy/kern/_src/stationary.py +++ b/GPy/kern/_src/stationary.py @@ -9,6 +9,7 @@ from ...util.linalg import tdot from ... import util import numpy as np from scipy import integrate +from ...util.caching import Cache_this class Stationary(Kern): def __init__(self, input_dim, variance, lengthscale, ARD, name): @@ -39,15 +40,18 @@ class Stationary(Kern): def dK_dr(self, r): raise NotImplementedError, "implement the covaraiance function as a fn of r to use this class" + @Cache_this(limit=5, ignore_args=()) def K(self, X, X2=None): r = self._scaled_dist(X, X2) return self.K_of_r(r) + @Cache_this(limit=5, ignore_args=(0,)) def _dist(self, X, X2): if X2 is None: X2 = X return X[:, None, :] - X2[None, :, :] + @Cache_this(limit=5, ignore_args=(0,)) def _unscaled_dist(self, X, X2=None): """ Compute the square distance between each row of X and X2, or between @@ -61,6 +65,7 @@ class Stationary(Kern): X2sq = np.sum(np.square(X2),1) return np.sqrt(-2.*np.dot(X, X2.T) + (X1sq[:,None] + X2sq[None,:])) + @Cache_this(limit=5, ignore_args=()) def _scaled_dist(self, X, X2=None): """ Efficiently compute the scaled distance, r. diff --git a/GPy/models/gplvm.py b/GPy/models/gplvm.py index c2fd46fe..ba270dad 100644 --- a/GPy/models/gplvm.py +++ b/GPy/models/gplvm.py @@ -41,7 +41,7 @@ class GPLVM(GP): def parameters_changed(self): super(GPLVM, self).parameters_changed() - self.X.gradient = self.kern.gradients_X(self._dL_dK, self.X, None) + self.X.gradient = self.kern.gradients_X(self.dL_dK, self.X, None) def _getstate(self): return GP._getstate(self) diff --git a/GPy/models/sparse_gp_regression.py b/GPy/models/sparse_gp_regression.py index 54c89a89..6a76df3f 100644 --- a/GPy/models/sparse_gp_regression.py +++ b/GPy/models/sparse_gp_regression.py @@ -8,6 +8,7 @@ from .. import likelihoods from .. import kern from ..inference.latent_function_inference import VarDTC from ..util.misc import param_to_array +from ..core.parameterization.variational import VariationalPosterior class SparseGPRegression(SparseGP): """ @@ -44,7 +45,10 @@ class SparseGPRegression(SparseGP): assert Z.shape[1] == input_dim likelihood = likelihoods.Gaussian() - + + if not (X_variance is None): + X = VariationalPosterior(X,X_variance) + SparseGP.__init__(self, X, Y, Z, kernel, likelihood, inference_method=VarDTC()) def _getstate(self): diff --git a/GPy/util/caching.py b/GPy/util/caching.py index 55e546df..2c7be9bd 100644 --- a/GPy/util/caching.py +++ b/GPy/util/caching.py @@ -1,21 +1,23 @@ from ..core.parameterization.parameter_core import Observable class Cacher(object): - def __init__(self, operation, limit=5, reset_on_first=False): + def __init__(self, operation, limit=5, ignore_args=()): self.limit = int(limit) - self._reset_on_first = reset_on_first + self.ignore_args = ignore_args self.operation=operation self.cached_inputs = [] self.cached_outputs = [] self.inputs_changed = [] def __call__(self, *args): - if self._reset_on_first: - assert isinstance(args[0], Observable) - args[0].add_observer(self, self.reset) - cached_args = args + if len(self.ignore_args) != 0: + ca = [a for i,a in enumerate(args) if i not in self.ignore_args] + cached_args = [] + for a in ca: + if not any(a is ai for ai in cached_args): + cached_args.append(a) else: - cached_args = args[1:] + cached_args = args if not all([isinstance(arg, Observable) for arg in cached_args]): @@ -36,7 +38,7 @@ class Cacher(object): self.cached_inputs.append(cached_args) self.cached_outputs.append(self.operation(*args)) self.inputs_changed.append(False) - [a.add_observer(self, self.on_cache_changed) for a in args] + [a.add_observer(self, self.on_cache_changed) for a in cached_args] return self.cached_outputs[-1] def on_cache_changed(self, arg): @@ -48,42 +50,15 @@ class Cacher(object): self.cached_outputs = [] self.inputs_changed = [] - - - -def cache_this(limit=5, reset_on_self=False): - def limited_cache(f): - c = Cacher(f, limit, reset_on_first=reset_on_self) +class Cache_this(object): + def __init__(self, limit=5, ignore_args=()): + self.limit = limit + self.ignore_args = ignore_args + self.c = None + def __call__(self, f): def f_wrap(*args): - return c(*args) - f_wrap._cacher = c - return f_wrap - return limited_cache - - - - - - - - - - - - - #Xbase = X - #while Xbase is not None: - #try: - #i = self.cached_inputs.index(X) - #break - #except ValueError: - #Xbase = X.base - #continue - #self.inputs_changed[i] = True - - - - - - - + if self.c is None: + self.c = Cacher(f, self.limit, ignore_args=self.ignore_args) + return self.c(*args) + f_wrap._cacher = self + return f_wrap \ No newline at end of file