From 6be05de791cd9bb1000ff6a46f7d71790f3410eb Mon Sep 17 00:00:00 2001 From: Max Zwiessele Date: Thu, 17 Oct 2013 20:47:41 +0100 Subject: [PATCH] still todo: untie, gradients, priors, print ties --- GPy/core/index_operations.py | 90 +++++++++++++++++++----------------- GPy/core/parameter.py | 62 +++++++++++++++++-------- GPy/core/parameterized.py | 82 +++++++++++++++++--------------- 3 files changed, 135 insertions(+), 99 deletions(-) diff --git a/GPy/core/index_operations.py b/GPy/core/index_operations.py index 020fd9ef..6c342c6e 100644 --- a/GPy/core/index_operations.py +++ b/GPy/core/index_operations.py @@ -9,8 +9,8 @@ from parameter import Param from collections import defaultdict class ParamDict(defaultdict): - def __init__(self): - defaultdict.__init__(self, lambda: numpy.array([], dtype=int)) + def __init__(self, default=lambda: numpy.array([], dtype=int)): + defaultdict.__init__(self, default) def __getitem__(self, key): try: @@ -120,47 +120,51 @@ class ParameterIndexOperations(object): def __getitem__(self, prop): return self._properties[prop] -class TieIndexOperations(object): - def __init__(self, params): - self.params = params - self.tied_from = ParameterIndexOperations() - self.tied_to = ParameterIndexOperations() - def add(self, tied_from, tied_to): - rav_from = self.params._raveled_index_for(tied_from) - rav_to = self.params._raveled_index_for(tied_to) - self.tied_from.add(tied_to, rav_from) - self.tied_to.add(tied_to, rav_to) - return rav_from, rav_to - def remove(self, tied_from, tied_to): - rav_from = self.params._raveled_index_for(tied_from) - rav_to = self.params._raveled_index_for(tied_to) - self.tied_from.remove(tied_to, rav_from) - self.tied_to.remove(tied_to, rav_to) - return rav_from, rav_to - def from_to_for(self, index): - return self.tied_from.properties_for(index), self.tied_to.properties_for(index) - def iter_from_to_indices(self): - for k, f in self.tied_from.iteritems(): - yield f, self.tied_to[k] - def iter_to_indices(self): - return self.tied_to.iterindices() - def iter_from_indices(self): - return self.tied_from.iterindices() - def iter_from_items(self): - for f, i in self.tied_from.iteritems(): - yield f, i - def iter_properties(self): - return self.tied_from.iter_properties() - def properties(self): - return self.tied_from.properties() - def from_to_indices(self, param): - return self.tied_from[param], self.tied_to[param] - -# def create_raveled_indices(index, shape, offset=0): -# if isinstance(index, (tuple, list)): i = [slice(None)] + list(index) -# else: i = [slice(None), index] -# ind = numpy.array(numpy.ravel_multi_index(numpy.indices(shape)[i], shape)).flat + numpy.int_(offset) -# return ind +# class TieIndexOperations(object): +# def __init__(self, params): +# self.params = params +# self.tied_from = ParameterIndexOperations() +# self.tied_to = ParameterIndexOperations() +# def add(self, tied_from, tied_to): +# rav_from = self.params._raveled_index_for(tied_from) +# rav_to = self.params._raveled_index_for(tied_to) +# self.tied_from.add(tied_to, rav_from) +# self.tied_to.add(tied_to, rav_to) +# return rav_from, rav_to +# def remove(self, tied_from, tied_to): +# rav_from = self.params._raveled_index_for(tied_from) +# rav_to = self.params._raveled_index_for(tied_to) +# rem_from = self.tied_from.remove(tied_to, rav_from) +# rem_to = self.tied_to.remove(tied_to, rav_to) +# left_from = self.tied_from._properties.pop(tied_to) +# left_to = self.tied_to._properties.pop(tied_to) +# self.tied_from[numpy.delete(tied_to, rem_from)] = left_from +# self.tied_to[numpy.delete(tied_to, rem_to)] = left_to +# return rav_from, rav_to +# def from_to_for(self, index): +# return self.tied_from.properties_for(index), self.tied_to.properties_for(index) +# def iter_from_to_indices(self): +# for k, f in self.tied_from.iteritems(): +# yield f, self.tied_to[k] +# def iter_to_indices(self): +# return self.tied_to.iterindices() +# def iter_from_indices(self): +# return self.tied_from.iterindices() +# def iter_from_items(self): +# for f, i in self.tied_from.iteritems(): +# yield f, i +# def iter_properties(self): +# return self.tied_from.iter_properties() +# def properties(self): +# return self.tied_from.properties() +# def from_to_indices(self, param): +# return self.tied_from[param], self.tied_to[param] +# +# # def create_raveled_indices(index, shape, offset=0): +# # if isinstance(index, (tuple, list)): i = [slice(None)] + list(index) +# # else: i = [slice(None), index] +# # ind = numpy.array(numpy.ravel_multi_index(numpy.indices(shape)[i], shape)).flat + numpy.int_(offset) +# # return ind def combine_indices(arr1, arr2): return numpy.union1d(arr1, arr2) diff --git a/GPy/core/parameter.py b/GPy/core/parameter.py index e5f6b706..1be315ff 100644 --- a/GPy/core/parameter.py +++ b/GPy/core/parameter.py @@ -48,6 +48,9 @@ class Param(numpy.ndarray): obj._realshape_ = obj.shape obj._realsize_ = obj.size obj._realndim_ = obj.ndim + from index_operations import ParamDict + obj._tied_to_me_ = ParamDict(set) + obj._tied_to_ = [] obj._original_ = True return obj def __array_finalize__(self, obj): @@ -58,6 +61,8 @@ class Param(numpy.ndarray): self._parent_ = getattr(obj, '_parent_', None) self._parent_index_ = getattr(obj, '_parent_index_', None) self._gradient_ = getattr(obj, '_gradient_', None) + self._tied_to_me_ = getattr(obj, '_tied_to_me_', None) + self._tied_to_ = getattr(obj, '_tied_to_', None) self._realshape_ = getattr(obj, '_realshape_', None) self._realsize_ = getattr(obj, '_realsize_', None) self._realndim_ = getattr(obj, '_realndim_', None) @@ -220,17 +225,37 @@ class Param(numpy.ndarray): self._parent_._get_original(self)[self._current_slice_] = param except ValueError: raise ValueError("Trying to tie {} with shape {} to {} with shape {}".format(self.name, self.shape, param.name, param.shape)) - self._parent_._add_tie(self, param) + + self._parent_._get_original(self)._tied_to_ += [param] + param._add_tie_listener(self) + self._parent_._set_fixed(param) +# self._parent_._add_tie(self, param) - def untie(self, *params): + def untie(self, *ties): """ - :param params: parameters to untie from - - remove ties to parameters given. + remove tie of this parameter to ties it was tied to. """ - if len(params) == 0: - params = self._parent_._ties_.properties() - self._parent_._remove_tie(self, *params) + [t._remove_tie_listener(self) for t in self._tied_to_] + def set_index(tied_to,untie_from): + tied_to._current_slice_ = numpy.array(set(tied_to._raveled_index()) & set(untie_from._raveled_index())) + return tied_to + self._tied_to_ = [tied_to for tied_to in self._tied_to_ for untie_from in ties if tied_to._parent_index_ == untie_from._parent_index_ and set_index(tied_to)._current_slice_.size > 0] + self._parent_._set_unfixed(self) +# self._parent_._remove_tie(self, *params) + def _fire_changed(self): + for tied, ind in self._tied_to_me_.iteritems(): + tied._on_change(self[list(ind)]) + def _add_tie_listener(self, tied_to_me): + self._tied_to_me_[tied_to_me] |= set(self._raveled_index()) + def _remove_tie_listener(self, to_remove): + for t in self._tied_to_me_.keys(): + if t._parent_index_ == self._parent_index_: + self._tied_to_me_[t] &= set(t._raveled_index()) + def _on_change(self, val): + if self._original_: # this happens when indexing created a copy of the array + self[:] = val + else: + self._parent_._get_original(self)[self._current_slice_] = val #=========================================================================== # Prior Operations #=========================================================================== @@ -266,6 +291,7 @@ class Param(numpy.ndarray): return self.__getitem__(slice(start, stop)) def __setitem__(self, *args, **kwargs): numpy.ndarray.__setitem__(self, *args, **kwargs) + self._fire_changed() self._parent_.parameters_changed() #=========================================================================== # Index Operations: @@ -330,7 +356,12 @@ class Param(numpy.ndarray): def __repr__(self, *args, **kwargs): return "\033[1m{x:s}\033[0;0m:\n".format(x=self.name)+super(Param, self).__repr__(*args,**kwargs) def _ties_for(self, rav_index): - return self._parent_._ties_for(self, rav_index) + ties = [[]] * numpy.size(rav_index) + for tied_to in self._tied_to_: + for t in tied_to._tied_to_me_.iterkeys(): + if t._parent_index_ == self._parent_index_: + [ties.__setitem__(i, ties[i] + [tied_to]) for i in t._raveled_index()] + return ties def _constraints_for(self, rav_index): return self._parent_._constraints_for(self, rav_index) def _indices(self, slice_index=None): @@ -367,16 +398,9 @@ class Param(numpy.ndarray): if lc is None: lc = self._max_len_names(constr_matrix, __constraints_name__) if lx is None: lx = self._max_len_values() if li is None: li = self._max_len_index(self._indices()) - if lt is None: lt = self._max_len_names(ties[0], __tie_name__) - from index_operations import ParamDict - keep_track_of_broadcasting = ParamDict() - def tie_broadcasting(tie): - if tie in keep_track_of_broadcasting: - return keep_track_of_broadcasting[tie].next() - else: - pass + if lt is None: lt = self._max_len_names([t._short() for ti in ties for t in ti], __tie_name__) header = " {i:^{2}s} | \033[1m{x:^{1}s}\033[0;0m | {c:^{0}s} | {t:^{3}s}".format(lc,lx,li,lt, x=self.name, c=__constraints_name__, i=__index_name__, t=__tie_name__) # nice header for printing - return "\n".join([header]+[" {i!s:^{3}s} | {x: >{1}.{2}G} | {c:^{0}s} | {t:^{4}} ".format(lc,lx,__precision__,li,lt, x=x, c=" ".join(map(str,c)), t=" ".join([tie._short() for tie in t]), i=i) for i,x,c,t in itertools.izip(indices,self.flat,constr_matrix,ties[0])]) # return all the constraints with right indices + return "\n".join([header]+[" {i!s:^{3}s} | {x: >{1}.{2}G} | {c:^{0}s} | {t:^{4}} ".format(lc,lx,__precision__,li,lt, x=x, c=" ".join(map(str,c)), t=" ".join([tie._short() for tie in t]), i=i) for i,x,c,t in itertools.izip(indices,self.flat,constr_matrix,ties)]) # return all the constraints with right indices #except: return super(Param, self).__str__() class ParamConcatenation(object): @@ -480,7 +504,7 @@ if __name__ == '__main__': m[".*variance"].constrain_positive() print "constraining rbf" m.rbf.constrain_positive() - m.q_variance[:,[0,2]].tie_to(m.rbf_l) + m.q_variance[:,:2].tie_to(m.rbf_l) #m.q_v.tie_to(m.rbf_v) # m.rbf_l.tie_to(m.rbf_va) # pt = numpy.array(params._get_params_transformed()) diff --git a/GPy/core/parameterized.py b/GPy/core/parameterized.py index 94915b2d..04dfe29c 100644 --- a/GPy/core/parameterized.py +++ b/GPy/core/parameterized.py @@ -7,7 +7,7 @@ import copy import cPickle from parameter import ParamConcatenation, Param from index_operations import ParameterIndexOperations,\ - TieIndexOperations, index_empty + index_empty import itertools from re import compile, _pattern_type import sys @@ -80,7 +80,7 @@ class Parameterized(object): """ def __init__(self): self._constraints_ = ParameterIndexOperations() - self._ties_ = TieIndexOperations(self) + #self._ties_ = TieIndexOperations(self) self._ties_fixes_ = None self._in_init_ = True if not hasattr(self, "_parameters_"): @@ -214,7 +214,7 @@ class Parameterized(object): self._ties_fixes_[f] = FIXED if fixed == __fixed__: self._ties_fixes_[ind] = FIXED - if numpy.all(self._ties_fixes_): + if numpy.all(self._ties_fixes_): # ==UNFIXED self._ties_fixes_ = None self.parameters_changed() #=========================================================================== @@ -263,47 +263,55 @@ class Parameterized(object): #=========================================================================== # Handle ties: #=========================================================================== - def _add_tie(self, param, tied_to): - # tie param to tie_to, if the values match (with broadcasting) - self._remove_tie(param) # delete if multiple ties should be allowed - f, _ = self._ties_.add(param, tied_to) + def _set_fixed(self, param_or_index): if self._ties_fixes_ is None: self._ties_fixes_ = numpy.ones(self._parameter_size_, dtype=bool) - self._ties_fixes_[f] = False - def _remove_tie(self, param, *params): - # remove the tie from param to all *params (can be None, so all ties get deleted for param) - if len(params) == 0: - params = self._ties_.properties() - for p in params: - _, t = self._ties_.remove(param, p) - self._ties_fixes_[t] = True - if numpy.all(self._ties_fixes_): self._ties_fixes_ = None - def _ties_iter_items(self, param): - for tied_to, ind in self._ties_.iter_from_items(): - ind = self._backtranslate_index(param, ind) - if not index_empty(ind): - yield tied_to, ind - def _ties_iter(self, param): - for constr, _ in self._ties_iter_items(param): - yield constr - def _ties_iter_indices(self, param): - for _, ind in self._ties_iter_items(param): - yield ind - def _ties_for(self, param, rav_index): - return self._ties_.from_to_for(rav_index+self._offset(param)) + try: + param_or_index = self._raveled_index_for(param_or_index) + except AttributeError: + self._ties_fixes_[param_or_index] = FIXED + def _set_unfixed(self, param_or_index): + if self._ties_fixes_ is None: self._ties_fixes_ = numpy.ones(self._parameter_size_, dtype=bool) + try: + param_or_index = self._raveled_index_for(param_or_index) + except AttributeError: + self._ties_fixes_[param_or_index] = UNFIXED +# def _add_tie(self, param, tied_to): +# # tie param to tie_to, if the values match (with broadcasting) +# self._remove_tie(param) # delete if multiple ties should be allowed +# f, _ = self._ties_.add(param, tied_to) +# if self._ties_fixes_ is None: self._ties_fixes_ = numpy.ones(self._parameter_size_, dtype=bool) +# self._ties_fixes_[f] = False +# def _remove_tie(self, param, *params): +# # remove the tie from param to all *params (can be None, so all ties get deleted for param) +# if len(params) == 0: +# params = self._ties_.properties() +# for p in params: +# _, t = self._ties_.remove(param, p) +# self._ties_fixes_[t] = True +# if numpy.all(self._ties_fixes_): self._ties_fixes_ = None # ==UNFIXED +# def _ties_iter_items(self, param): +# for tied_to, ind in self._ties_.iter_from_items(): +# ind = self._backtranslate_index(param, ind) +# if not index_empty(ind): +# yield tied_to, ind +# def _ties_iter(self, param): +# for constr, _ in self._ties_iter_items(param): +# yield constr +# def _ties_iter_indices(self, param): +# for _, ind in self._ties_iter_items(param): +# yield ind +# def _ties_for(self, param, rav_index): +# return self._ties_.from_to_for(rav_index+self._offset(param)) #=========================================================================== # Fixing parameters: #=========================================================================== def _fix(self, param, warning=True): f = self._add_constrain(param, __fixed__, warning) - if self._ties_fixes_ is None: self._ties_fixes_ = numpy.ones(self._parameter_size_, dtype=bool) - self._ties_fixes_[f] = False + self._set_fixed(f) def _unfix(self, param): if self._ties_fixes_ is not None: - self._remove_constrain(param, __fixed__) - ind = self._raveled_index_for(param) - self._ties_fixes_[ind] = UNFIXED - if numpy.all(self._ties_fixes_==UNFIXED): self._ties_fixes_ = None - self._handle_ties() + f = self._remove_constrain(param, __fixed__) + self._set_unfixed(f) #=========================================================================== # Convenience for fixed, tied checking of parameter: #=========================================================================== @@ -424,7 +432,7 @@ class Parameterized(object): constrs = self._constrs; ts = self._ts cl = max([len(str(x)) if x else 0 for x in constrs + ["Constraint"]]) tl = max([len(str(x)) if x else 0 for x in ts + ["Tied to"]]) - format_spec = " \033[1m{{p.name:^{0}s}}\033[0;0m | {{p._desc:^{1}s}} | {{const:^{2}s}} | {{t:^{2}s}}".format(nl, sl, cl) + format_spec = " \033[1m{{p.name:^{0}s}}\033[0;0m | {{p._desc:^{1}s}} | {{const:^{2}s}} | {{t:^{3}s}}".format(nl, sl, cl, tl) to_print = [format_spec.format(p=p, const=c, t=t) for p, c, t in itertools.izip(self._parameters_, constrs, ts)] sep = '-'*(nl+sl+cl+tl+8*2+3) if header: