diff --git a/GPy/core/gp.py b/GPy/core/gp.py index 8b52699b..6fc127ea 100644 --- a/GPy/core/gp.py +++ b/GPy/core/gp.py @@ -222,15 +222,16 @@ class GP(Model): """ - return Model._getstate(self) + [self.X, - self.num_data, - self.input_dim, - self.kern, - self.likelihood, - self.output_dim, - ] + return []#Model._getstate(self) + [self.X, +# self.num_data, +# self.input_dim, +# self.kern, +# self.likelihood, +# self.output_dim, +# ] def _setstate(self, state): + return self.output_dim = state.pop() self.likelihood = state.pop() self.kern = state.pop() diff --git a/GPy/core/parameterization/lists_and_dicts.py b/GPy/core/parameterization/lists_and_dicts.py index ca0589c9..31235952 100644 --- a/GPy/core/parameterization/lists_and_dicts.py +++ b/GPy/core/parameterization/lists_and_dicts.py @@ -28,4 +28,11 @@ 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 diff --git a/GPy/core/parameterization/parameter_core.py b/GPy/core/parameterization/parameter_core.py index 1cdeee0b..b804a61a 100644 --- a/GPy/core/parameterization/parameter_core.py +++ b/GPy/core/parameterization/parameter_core.py @@ -902,15 +902,19 @@ class Parameterizable(OptimizationHandlable): #=========================================================================== def copy(self): """Returns a (deep) copy of the current model""" - raise NotImplementedError, "Copy is not yet implemented, TODO: Observable hierarchy" + #raise NotImplementedError, "Copy is not yet implemented, TODO: Observable hierarchy" import copy from .index_operations import ParameterIndexOperations, ParameterIndexOperationsView from .lists_and_dicts import ArrayList + + param_mapping = [[] for _ in range(self.num_params)] 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)): + if v in self._parameters_: + param_mapping[self._parameters_.index(v)] += [k] + elif isinstance(v, (Constrainable, ParameterIndexOperations, ParameterIndexOperationsView)): dc[k] = v.copy() else: dc[k] = copy.deepcopy(v) @@ -928,9 +932,10 @@ class Parameterizable(OptimizationHandlable): s = self.__new__(self.__class__) s.__dict__ = dc - for p in params: + for p, mlist in zip(params, param_mapping): s.add_parameter(p, _ignore_added_names=True) - + for m in mlist: + setattr(s, m, p) return s #=========================================================================== diff --git a/GPy/core/parameterization/parameterized.py b/GPy/core/parameterization/parameterized.py index bc83d8c8..529d3733 100644 --- a/GPy/core/parameterization/parameterized.py +++ b/GPy/core/parameterization/parameterized.py @@ -110,29 +110,15 @@ class Parameterized(Parameterizable, Pickleable): 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_, - ] + return [] 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 = super(Parameterized, self).copy() c.add_observer(c, c._parameters_changed_notification, -100) return c diff --git a/GPy/kern/_src/kernel_slice_operations.py b/GPy/kern/_src/kernel_slice_operations.py index 9beb40ab..21421cc0 100644 --- a/GPy/kern/_src/kernel_slice_operations.py +++ b/GPy/kern/_src/kernel_slice_operations.py @@ -5,130 +5,134 @@ Created on 11 Mar 2014 ''' from ...core.parameterization.parameterized import ParametersChangedMeta import numpy as np +from functools import wraps + +def put_clean(dct, name, func): + if name in dct: + dct['_clean_{}'.format(name)] = dct[name] + dct[name] = func(dct[name]) class KernCallsViaSlicerMeta(ParametersChangedMeta): - def __call__(self, *args, **kw): - instance = super(ParametersChangedMeta, self).__call__(*args, **kw) - instance.K = _slice_wrapper(instance, instance.K) - instance.Kdiag = _slice_wrapper(instance, instance.Kdiag, diag=True) - instance.update_gradients_full = _slice_wrapper(instance, instance.update_gradients_full, diag=False, derivative=True) - instance.update_gradients_diag = _slice_wrapper(instance, instance.update_gradients_diag, diag=True, derivative=True) - instance.gradients_X = _slice_wrapper(instance, instance.gradients_X, diag=False, derivative=True, ret_X=True) - instance.gradients_X_diag = _slice_wrapper(instance, instance.gradients_X_diag, diag=True, derivative=True, ret_X=True) - instance.psi0 = _slice_wrapper(instance, instance.psi0, diag=False, derivative=False) - instance.psi1 = _slice_wrapper(instance, instance.psi1, diag=False, derivative=False) - instance.psi2 = _slice_wrapper(instance, instance.psi2, diag=False, derivative=False) - instance.update_gradients_expectations = _slice_wrapper(instance, instance.update_gradients_expectations, derivative=True, psi_stat=True) - instance.gradients_Z_expectations = _slice_wrapper(instance, instance.gradients_Z_expectations, derivative=True, psi_stat_Z=True, ret_X=True) - instance.gradients_qX_expectations = _slice_wrapper(instance, instance.gradients_qX_expectations, derivative=True, psi_stat=True, ret_X=True) - instance.parameters_changed() - return instance + def __new__(cls, name, bases, dct): + put_clean(dct, 'K', _slice_K) + put_clean(dct, 'Kdiag', _slice_Kdiag) + put_clean(dct, 'update_gradients_full', _slice_update_gradients_full) + put_clean(dct, 'update_gradients_diag', _slice_update_gradients_diag) + put_clean(dct, 'gradients_X', _slice_gradients_X) + put_clean(dct, 'gradients_X_diag', _slice_gradients_X_diag) -def _slice_wrapper(kern, operation, diag=False, derivative=False, psi_stat=False, psi_stat_Z=False, ret_X=False): - """ - This method wraps the functions in kernel to make sure all kernels allways see their respective input dimension. - The different switches are: - diag: if X2 exists - derivative: if first arg is dL_dK - psi_stat: if first 3 args are dL_dpsi0..2 - psi_stat_Z: if first 2 args are dL_dpsi1..2 - """ - if derivative: - if diag: - def x_slice_wrapper(dL_dKdiag, X): - ret_X_not_sliced = ret_X and kern._sliced_X == 0 - if ret_X_not_sliced: - ret = np.zeros(X.shape) - X = kern._slice_X(X) if not kern._sliced_X else X - # if the return value is of shape X.shape, we need to make sure to return the right shape - kern._sliced_X += 1 - try: - if ret_X_not_sliced: ret[:, kern.active_dims] = operation(dL_dKdiag, X) - else: ret = operation(dL_dKdiag, X) - except: - raise - finally: - kern._sliced_X -= 1 - return ret - elif psi_stat: - def x_slice_wrapper(dL_dpsi0, dL_dpsi1, dL_dpsi2, Z, variational_posterior): - ret_X_not_sliced = ret_X and kern._sliced_X == 0 - if ret_X_not_sliced: - ret1, ret2 = np.zeros(variational_posterior.shape), np.zeros(variational_posterior.shape) - Z, variational_posterior = kern._slice_X(Z) if not kern._sliced_X else Z, kern._slice_X(variational_posterior) if not kern._sliced_X else variational_posterior - kern._sliced_X += 1 - # if the return value is of shape X.shape, we need to make sure to return the right shape - try: - if ret_X_not_sliced: - ret = list(operation(dL_dpsi0, dL_dpsi1, dL_dpsi2, Z, variational_posterior)) - r2 = ret[:2] - ret[0] = ret1 - ret[1] = ret2 - ret[0][:, kern.active_dims] = r2[0] - ret[1][:, kern.active_dims] = r2[1] - del r2 - else: ret = operation(dL_dpsi0, dL_dpsi1, dL_dpsi2, Z, variational_posterior) - except: - raise - finally: - kern._sliced_X -= 1 - return ret - elif psi_stat_Z: - def x_slice_wrapper(dL_dpsi1, dL_dpsi2, Z, variational_posterior): - ret_X_not_sliced = ret_X and kern._sliced_X == 0 - if ret_X_not_sliced: ret = np.zeros(Z.shape) - Z, variational_posterior = kern._slice_X(Z) if not kern._sliced_X else Z, kern._slice_X(variational_posterior) if not kern._sliced_X else variational_posterior - kern._sliced_X += 1 - try: - if ret_X_not_sliced: - ret[:, kern.active_dims] = operation(dL_dpsi1, dL_dpsi2, Z, variational_posterior) - else: ret = operation(dL_dpsi1, dL_dpsi2, Z, variational_posterior) - except: - raise - finally: - kern._sliced_X -= 1 - return ret + put_clean(dct, 'psi0', _slice_psi) + put_clean(dct, 'psi1', _slice_psi) + put_clean(dct, 'psi2', _slice_psi) + put_clean(dct, 'update_gradients_expectations', _slice_update_gradients_expectations) + put_clean(dct, 'gradients_Z_expectations', _slice_gradients_Z_expectations) + put_clean(dct, 'gradients_qX_expectations', _slice_gradients_qX_expectations) + return super(KernCallsViaSlicerMeta, cls).__new__(cls, name, bases, dct) + +class _Slice_wrap(object): + def __init__(self, k, X, X2=None): + self.k = k + self.shape = X.shape + if self.k._sliced_X == 0: + self.X = self.k._slice_X(X) + self.X2 = self.k._slice_X(X2) if X2 is not None else None + self.ret = True else: - def x_slice_wrapper(dL_dK, X, X2=None): - ret_X_not_sliced = ret_X and kern._sliced_X == 0 - if ret_X_not_sliced: - ret = np.zeros(X.shape) - X, X2 = kern._slice_X(X) if not kern._sliced_X else X, kern._slice_X(X2) if X2 is not None and not kern._sliced_X else X2 - kern._sliced_X += 1 - try: - if ret_X_not_sliced: ret[:, kern.active_dims] = operation(dL_dK, X, X2) - else: ret = operation(dL_dK, X, X2) - except: - raise - finally: - kern._sliced_X -= 1 - return ret - else: - if diag: - def x_slice_wrapper(X, *args, **kw): - X = kern._slice_X(X) if not kern._sliced_X else X - kern._sliced_X += 1 - try: - ret = operation(X, *args, **kw) - except: - raise - finally: - kern._sliced_X -= 1 - return ret - else: - def x_slice_wrapper(X, X2=None, *args, **kw): - X, X2 = kern._slice_X(X) if not kern._sliced_X else X, kern._slice_X(X2) if X2 is not None and not kern._sliced_X else X2 - kern._sliced_X += 1 - try: - ret = operation(X, X2, *args, **kw) - except: raise - finally: - kern._sliced_X -= 1 - return ret - x_slice_wrapper._operation = operation - x_slice_wrapper.__name__ = ("slicer("+str(operation) - +(","+str(bool(diag)) if diag else'') - +(','+str(bool(derivative)) if derivative else '') - +')') - x_slice_wrapper.__doc__ = "**sliced**\n" + (operation.__doc__ or "") - return x_slice_wrapper \ No newline at end of file + self.X = X + self.X2 = X2 + self.ret = False + def __enter__(self): + self.k._sliced_X += 1 + return self + def __exit__(self, *a): + self.k._sliced_X -= 1 + def handle_return_array(self, return_val): + if self.ret: + ret = np.zeros(self.shape) + ret[:, self.k.active_dims] = return_val + return ret + return return_val + +def _slice_K(f): + @wraps(f) + def wrap(self, X, X2 = None, *a, **kw): + with _Slice_wrap(self, X, X2) as s: + ret = f(self, s.X, s.X2, *a, **kw) + return ret + return wrap + +def _slice_Kdiag(f): + @wraps(f) + def wrap(self, X, *a, **kw): + with _Slice_wrap(self, X, None) as s: + ret = f(self, s.X, *a, **kw) + return ret + return wrap + +def _slice_update_gradients_full(f): + @wraps(f) + def wrap(self, dL_dK, X, X2=None): + with _Slice_wrap(self, X, X2) as s: + ret = f(self, dL_dK, s.X, s.X2) + return ret + return wrap + +def _slice_update_gradients_diag(f): + @wraps(f) + def wrap(self, dL_dKdiag, X): + with _Slice_wrap(self, X, None) as s: + ret = f(self, dL_dKdiag, s.X) + return ret + return wrap + +def _slice_gradients_X(f): + @wraps(f) + def wrap(self, dL_dK, X, X2=None): + with _Slice_wrap(self, X, X2) as s: + ret = s.handle_return_array(f(self, dL_dK, s.X, s.X2)) + return ret + return wrap + +def _slice_gradients_X_diag(f): + @wraps(f) + def wrap(self, dL_dKdiag, X): + with _Slice_wrap(self, X, None) as s: + ret = s.handle_return_array(f(self, dL_dKdiag, s.X)) + return ret + return wrap + +def _slice_psi(f): + @wraps(f) + def wrap(self, Z, variational_posterior): + with _Slice_wrap(self, Z, variational_posterior) as s: + ret = f(self, s.X, s.X2) + return ret + return wrap + +def _slice_update_gradients_expectations(f): + @wraps(f) + def wrap(self, dL_dpsi0, dL_dpsi1, dL_dpsi2, Z, variational_posterior): + with _Slice_wrap(self, Z, variational_posterior) as s: + ret = f(self, dL_dpsi0, dL_dpsi1, dL_dpsi2, s.X, s.X2) + return ret + return wrap + +def _slice_gradients_Z_expectations(f): + @wraps(f) + def wrap(self, dL_dpsi1, dL_dpsi2, Z, variational_posterior): + with _Slice_wrap(self, Z, variational_posterior) as s: + ret = s.handle_return_array(f(self, dL_dpsi1, dL_dpsi2, s.X, s.X2)) + return ret + return wrap + +def _slice_gradients_qX_expectations(f): + @wraps(f) + def wrap(self, dL_dpsi0, dL_dpsi1, dL_dpsi2, Z, variational_posterior): + with _Slice_wrap(self, variational_posterior, Z) as s: + ret = list(f(self, dL_dpsi0, dL_dpsi1, dL_dpsi2, s.X2, s.X)) + r2 = ret[:2] + ret[0] = s.handle_return_array(r2[0]) + ret[1] = s.handle_return_array(r2[1]) + del r2 + return ret + return wrap diff --git a/GPy/util/caching.py b/GPy/util/caching.py index 282c9f8c..0886d0c6 100644 --- a/GPy/util/caching.py +++ b/GPy/util/caching.py @@ -48,7 +48,7 @@ class Cacher(object): if k in kw and kw[k] is not None: return self.operation(*args, **kw) # TODO: WARNING !!! Cache OFFSWITCH !!! WARNING - #return self.operation(*args) + # return self.operation(*args, **kw) #if the result is cached, return the cached computation state = [all(a is b for a, b in itertools.izip_longest(args, cached_i)) for cached_i in self.cached_inputs] @@ -101,7 +101,7 @@ class Cacher(object): def __name__(self): return self.operation.__name__ -from functools import partial +from functools import partial, update_wrapper class Cacher_wrap(object): def __init__(self, f, limit, ignore_args, force_kwargs): @@ -109,6 +109,7 @@ class Cacher_wrap(object): self.ignore_args = ignore_args self.force_kwargs = force_kwargs self.f = f + update_wrapper(self, self.f) def __get__(self, obj, objtype=None): return partial(self, obj) def __call__(self, *args, **kwargs):