From 851e6ec8e92a2bf2136b6ff90b525fb9a7cdc8b8 Mon Sep 17 00:00:00 2001 From: Max Zwiessele Date: Tue, 12 Nov 2013 12:17:14 +0000 Subject: [PATCH] simple tieing now working, still buggy though, progress with care --- GPy/core/parameter.py | 76 ++- GPy/core/parameterized.py | 922 ++++++++++++++++++------------------ GPy/testing/kernel_tests.py | 21 +- 3 files changed, 531 insertions(+), 488 deletions(-) diff --git a/GPy/core/parameter.py b/GPy/core/parameter.py index d9bed164..4f064eb5 100644 --- a/GPy/core/parameter.py +++ b/GPy/core/parameter.py @@ -284,19 +284,26 @@ class Param(ObservableArray, Nameable, Pickleable): #=========================================================================== def tie_to(self, param): """ - :param param: the parameter object to tie this parameter to. + :param param: the parameter object to tie this parameter to. + Can be ParamConcatenation (retrieved by regexp search) Tie this parameter to the given parameter. - Broadcasting is allowed, so you can tie a whole dimension to + Broadcasting is not allowed, but you can tie a whole dimension to one parameter: self[:,0].tie_to(other), where other is a one-value parameter. - Note: this method will tie to the parameter which is the last in - the chain of ties. Thus, if you tie to a tied parameter, - this tie will be created to the parameter the param is tied - to. + Note: For now only one parameter can have ties, so all of a parameter + will be removed, when re-tieing! """ + #Note: this method will tie to the parameter which is the last in + # the chain of ties. Thus, if you tie to a tied parameter, + # this tie will be created to the parameter the param is tied + # to. + assert isinstance(param, Param), "Argument {1} not of type {0}".format(Param,param.__class__) + param = numpy.atleast_1d(param) + if param.size != 1: + raise NotImplementedError, "Broadcast tying is not implemented yet" try: if self._original_: self[:] = param @@ -306,35 +313,55 @@ class Param(ObservableArray, Nameable, Pickleable): raise ValueError("Trying to tie {} with shape {} to {} with shape {}".format(self.name, self.shape, param.name, param.shape)) if param is self: raise RuntimeError, 'Cyclic tieing is not allowed' - if len(param._tied_to_) > 0: - self.tie_to(param._tied_to_[0]) - return - self._direct_parent_._get_original(self)._tied_to_ += [param] +# if len(param._tied_to_) > 0: +# if (self._direct_parent_._get_original(self) is param._direct_parent_._get_original(param) +# and len(set(self._raveled_index())&set(param._tied_to_[0]._raveled_index()))!=0): +# raise RuntimeError, 'Cyclic tieing is not allowed' +# self.tie_to(param._tied_to_[0]) +# return + if not param in self._direct_parent_._get_original(self)._tied_to_: + self._direct_parent_._get_original(self)._tied_to_ += [param] param._add_tie_listener(self) self._highest_parent_._set_fixed(self) - for t in self._tied_to_me_.iterkeys(): - if t is not self: - t.untie(self) - t.tie_to(param) -# self._direct_parent_._add_tie(self, param) +# for t in self._tied_to_me_.keys(): +# if t is not self: +# t.untie(self) +# t.tie_to(param) def untie(self, *ties): """ - remove tie of this parameter to ties it was tied to. + remove all ties. """ [t._direct_parent_._get_original(t)._remove_tie_listener(self) for t in self._tied_to_] - self._tied_to_ = [tied_to for tied_to in self._tied_to_ for t in tied_to._tied_to_me_ if self._parent_index_==t._direct_parent_._get_original(t)._parent_index_] - self._highest_parent_._set_unfixed(self) + new_ties = [] + for t in self._direct_parent_._get_original(self)._tied_to_: + for tied in t._tied_to_me_.keys(): + if t._parent_index_ is tied._parent_index_: + new_ties.append(tied) + self._direct_parent_._get_original(self)._tied_to_ = new_ties + self._direct_parent_._get_original(self)._highest_parent_._set_unfixed(self) # self._direct_parent_._remove_tie(self, *params) def _notify_tied_parameters(self): for tied, ind in self._tied_to_me_.iteritems(): tied._on_tied_parameter_changed(self.base, list(ind)) def _add_tie_listener(self, tied_to_me): - self._tied_to_me_[tied_to_me] |= set(self._raveled_index()) + for t in self._tied_to_me_.keys(): + if tied_to_me._parent_index_ is t._parent_index_: + t_rav_i = t._raveled_index() + tr_rav_i = tied_to_me._raveled_index() + new_index = list(set(t_rav_i) | set(tr_rav_i)) + tmp = t._direct_parent_._get_original(t)[numpy.unravel_index(new_index,t._realshape_)] + self._tied_to_me_[tmp] = self._tied_to_me_[t] | set(self._raveled_index()) + del self._tied_to_me_[t] + return + 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_ == to_remove._parent_index_: - new_index = list(set(t._raveled_index()) - set(to_remove._raveled_index())) + t_rav_i = t._raveled_index() + tr_rav_i = to_remove._raveled_index() + import ipdb;ipdb.set_trace() + new_index = list(set(t_rav_i) - set(tr_rav_i)) if new_index: tmp = t._direct_parent_._get_original(t)[numpy.unravel_index(new_index,t._realshape_)] self._tied_to_me_[tmp] = self._tied_to_me_[t] @@ -345,6 +372,7 @@ class Param(ObservableArray, Nameable, Pickleable): del self._tied_to_me_[t] def _on_tied_parameter_changed(self, val, ind): if not self._updated_: #not fast_array_equal(self, val[ind]): + val = numpy.atleast_1d(val) self._updated_ = True if self._original_: self.__setitem__(slice(None), val[ind], update=False) @@ -479,14 +507,16 @@ class Param(ObservableArray, Nameable, Pickleable): x=self.name_hirarchical) return name + super(Param, self).__repr__(*args,**kwargs) def _ties_for(self, rav_index): + size = sum(p.size for p in self._tied_to_) ties = numpy.empty(shape=(len(self._tied_to_), numpy.size(rav_index)), dtype=Param) for i, tied_to in enumerate(self._tied_to_): - for t in tied_to._tied_to_me_.iterkeys(): + for t, ind in tied_to._tied_to_me_.iteritems(): if t._parent_index_ == self._parent_index_: matches = numpy.where(rav_index[:,None] == t._raveled_index()[None, :]) tt_rav_index = tied_to._raveled_index() - ties[i, matches[0]] = numpy.take(tt_rav_index, matches[1], mode='wrap') - #[ties.__setitem__(i, ties[i] + [tied_to]) for i in t._raveled_index()] + ind_rav_matches = numpy.where(tt_rav_index == numpy.array(list(ind)))[0] + if len(ind) != 1: ties[i, matches[0][ind_rav_matches]] = numpy.take(tt_rav_index, matches[1], mode='wrap')[ind_rav_matches] + else: ties[i, matches[0]] = numpy.take(tt_rav_index, matches[1], mode='wrap') return map(lambda a: sum(a,[]), zip(*[[[tie.flatten()] if tx!=None else [] for tx in t] for t,tie in zip(ties,self._tied_to_)])) def _constraints_for(self, rav_index): return self._highest_parent_._constraints_for(self, rav_index) diff --git a/GPy/core/parameterized.py b/GPy/core/parameterized.py index 137af24a..fbfaae83 100644 --- a/GPy/core/parameterized.py +++ b/GPy/core/parameterized.py @@ -633,7 +633,7 @@ class Parameterized(Nameable, Pickleable, Observable): return [xi for x in self._parameters_ for xi in x._description_str] @property def _ties_str(self): - return [xi for x in self._parameters_ for xi in x._ties_str] + return [','.join(x._ties_str) for x in self.flattened_parameters] def __str__(self, header=True): constrs = self._constraints_str; ts = self._ties_str desc = self._description_str; names = self.parameter_names @@ -654,463 +654,463 @@ class Parameterized(Nameable, Pickleable, Observable): return '\n'.format(sep).join(to_print) pass - -class Parameterized_old(object): - def __init__(self): - """ - This is the base class for model and kernel. Mostly just handles tieing and constraining of parameters - """ - self.tied_indices = [] - self.fixed_indices = [] - self.fixed_values = [] - self.constrained_indices = [] - self.constraints = [] - - def _get_params(self): - raise NotImplementedError, "this needs to be implemented to use the Parameterized class" - def _set_params(self, x): - raise NotImplementedError, "this needs to be implemented to use the Parameterized class" - - def _get_param_names(self): - raise NotImplementedError, "this needs to be implemented to use the Parameterized class" - #def _get_print_names(self): - # """ Override for which parameter_names to print out, when using print m """ - # return self._get_param_names() - - def pickle(self, filename, protocol=None): - if protocol is None: - if self._has_get_set_state(): - protocol = 0 - else: - protocol = -1 - with open(filename, 'w') as f: - cPickle.dump(self, f, protocol) - - def copy(self): - """Returns a (deep) copy of the current model """ - return copy.deepcopy(self) - - 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) # set state - self._set_params(self._get_params()) # restore all values - return - self.__dict__ = state - - def _has_get_set_state(self): - return 'getstate' in vars(self.__class__) and 'setstate' in vars(self.__class__) - - 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.tied_indices, - self.fixed_indices, - self.fixed_values, - self.constrained_indices, - self.constraints] - - def setstate(self, state): - self.constraints = state.pop() - self.constrained_indices = state.pop() - self.fixed_values = state.pop() - self.fixed_indices = state.pop() - self.tied_indices = state.pop() - - def __getitem__(self, regexp, return_names=False): - """ - Get a model parameter by name. The name is applied as a regular - expression and all parameters that match that regular expression are - returned. - """ - matches = self.grep_param_names(regexp) - if len(matches): - if return_names: - return self._get_params()[matches], np.asarray(self._get_param_names())[matches].tolist() - else: - return self._get_params()[matches] - else: - raise AttributeError, "no parameter matches %s" % regexp - - def __setitem__(self, name, val): - """ - Set model parameter(s) by name. The name is provided as a regular - expression. All parameters matching that regular expression are set to - the given value. - """ - matches = self.grep_param_names(name) - if len(matches): - val = np.array(val) - assert (val.size == 1) or val.size == len(matches), "Shape mismatch: {}:({},)".format(val.size, len(matches)) - x = self._get_params() - x[matches] = val - self._set_params(x) - else: - raise AttributeError, "no parameter matches %s" % name - - def tie_params(self, regexp): - """ - Tie (all!) parameters matching the regular expression `regexp`. - """ - matches = self.grep_param_names(regexp) - assert matches.size > 0, "need at least something to tie together" - if len(self.tied_indices): - assert not np.any(matches[:, None] == np.hstack(self.tied_indices)), "Some indices are already tied!" - self.tied_indices.append(matches) - # TODO only one of the priors will be evaluated. Give a warning message if the priors are not identical - if hasattr(self, 'prior'): - pass - - self._set_params_transformed(self._get_params_transformed()) # sets tied parameters to single value - - def untie_everything(self): - """Unties all parameters by setting tied_indices to an empty list.""" - self.tied_indices = [] - - def grep_param_names(self, regexp, transformed=False, search=False): - """ - :param regexp: regular expression to select parameter parameter_names - :type regexp: re | str | int - :rtype: the indices of self._get_param_names which match the regular expression. - - Note:- - Other objects are passed through - i.e. integers which weren't meant for grepping - """ - - if transformed: - parameter_names = self._get_param_names_transformed() - else: - parameter_names = self._get_param_names() - - if type(regexp) in [str, np.string_, np.str]: - regexp = re.compile(regexp) - elif type(regexp) is re._pattern_type: - pass - else: - return regexp - if search: - return np.nonzero([regexp.search(name) for name in parameter_names])[0] - else: - return np.nonzero([regexp.match(name) for name in parameter_names])[0] - - def num_params_transformed(self): - removed = 0 - for tie in self.tied_indices: - removed += tie.size - 1 - - for fix in self.fixed_indices: - removed += fix.size - - return len(self._get_params()) - removed - - def unconstrain(self, regexp): - """Unconstrain matching parameters. Does not untie parameters""" - matches = self.grep_param_names(regexp) - - # tranformed contraints: - for match in matches: - self.constrained_indices = [i[i <> match] for i in self.constrained_indices] - - # remove empty constraints - tmp = zip(*[(i, t) for i, t in zip(self.constrained_indices, self.constraints) if len(i)]) - if tmp: - self.constrained_indices, self.constraints = zip(*[(i, t) for i, t in zip(self.constrained_indices, self.constraints) if len(i)]) - self.constrained_indices, self.constraints = list(self.constrained_indices), list(self.constraints) - - # fixed: - self.fixed_values = [np.delete(values, np.nonzero(np.sum(indices[:, None] == matches[None, :], 1))[0]) for indices, values in zip(self.fixed_indices, self.fixed_values)] - self.fixed_indices = [np.delete(indices, np.nonzero(np.sum(indices[:, None] == matches[None, :], 1))[0]) for indices in self.fixed_indices] - - # remove empty elements - tmp = [(i, v) for i, v in zip(self.fixed_indices, self.fixed_values) if len(i)] - if tmp: - self.fixed_indices, self.fixed_values = zip(*tmp) - self.fixed_indices, self.fixed_values = list(self.fixed_indices), list(self.fixed_values) - else: - self.fixed_indices, self.fixed_values = [], [] - - def constrain_negative(self, regexp, warning=True): - """ Set negative constraints. """ - self.constrain(regexp, transformations.NegativeLogexp(), warning) - - def constrain_positive(self, regexp, warning=True): - """ Set positive constraints. """ - self.constrain(regexp, transformations.Logexp(), warning) - - def constrain_bounded(self, regexp, lower, upper, warning=True): - """ Set bounded constraints. """ - self.constrain(regexp, transformations.Logistic(lower, upper), warning) - - def all_constrained_indices(self): - if len(self.constrained_indices) or len(self.fixed_indices): - return np.hstack(self.constrained_indices + self.fixed_indices) - else: - return np.empty(shape=(0,)) - - def constrain(self, regexp, transform, warning=True): - assert isinstance(transform, transformations.Transformation) - - matches = self.grep_param_names(regexp) - overlap = set(matches).intersection(set(self.all_constrained_indices())) - if overlap: - self.unconstrain(np.asarray(list(overlap))) - if warning: - print 'Warning: re-constraining these parameters' - pn = self._get_param_names() - for i in overlap: - print pn[i] - - self.constrained_indices.append(matches) - self.constraints.append(transform) - x = self._get_params() - x[matches] = transform.initialize(x[matches]) - self._set_params(x) - - def constrain_fixed(self, regexp, value=None, warning=True): - """ - - :param regexp: which parameters need to be fixed. - :type regexp: ndarray(dtype=int) or regular expression object or string - :param value: the vlaue to fix the parameters to. If the value is not specified, - the parameter is fixed to the current value - :type value: float - - **Notes** - - Fixing a parameter which is tied to another, or constrained in some way will result in an error. - - To fix multiple parameters to the same value, simply pass a regular expression which matches both parameter parameter_names, or pass both of the indexes. - - """ - matches = self.grep_param_names(regexp) - overlap = set(matches).intersection(set(self.all_constrained_indices())) - if overlap: - self.unconstrain(np.asarray(list(overlap))) - if warning: - print 'Warning: re-constraining these parameters' - pn = self._get_param_names() - for i in overlap: - print pn[i] - - self.fixed_indices.append(matches) - if value != None: - self.fixed_values.append(value) - else: - self.fixed_values.append(self._get_params()[self.fixed_indices[-1]]) - - # self.fixed_values.append(value) - self._set_params_transformed(self._get_params_transformed()) - - def _get_params_transformed(self): - """use self._get_params to get the 'true' parameters of the model, which are then tied, constrained and fixed""" - x = self._get_params() - [np.put(x, i, t.finv(x[i])) for i, t in zip(self.constrained_indices, self.constraints)] - - to_remove = self.fixed_indices + [t[1:] for t in self.tied_indices] - if len(to_remove): - return np.delete(x, np.hstack(to_remove)) - else: - return x - - def _set_params_transformed(self, x): - """ takes the vector x, which is then modified (by untying, reparameterising or inserting fixed values), and then call self._set_params""" - self._set_params(self._untransform_params(x)) - - def _untransform_params(self, x): - """ - The Transformation required for _set_params_transformed. - - This moves the vector x seen by the optimiser (unconstrained) to the - valid parameter vector seen by the model - - Note: - - This function is separate from _set_params_transformed for downstream flexibility - """ - # work out how many places are fixed, and where they are. tricky logic! - fix_places = self.fixed_indices + [t[1:] for t in self.tied_indices] - if len(fix_places): - fix_places = np.hstack(fix_places) - Nfix_places = fix_places.size - else: - Nfix_places = 0 - - free_places = np.setdiff1d(np.arange(Nfix_places + x.size, dtype=np.int), fix_places) - - # put the models values in the vector xx - xx = np.zeros(Nfix_places + free_places.size, dtype=np.float64) - - xx[free_places] = x - [np.put(xx, i, v) for i, v in zip(self.fixed_indices, self.fixed_values)] - [np.put(xx, i, v) for i, v in [(t[1:], xx[t[0]]) for t in self.tied_indices] ] - - [np.put(xx, i, t.f(xx[i])) for i, t in zip(self.constrained_indices, self.constraints)] - if hasattr(self, 'debug'): - stop # @UndefinedVariable - - return xx - - def _get_param_names_transformed(self): - """ - Returns the parameter parameter_names as propagated after constraining, - tying or fixing, i.e. a list of the same length as _get_params_transformed() - """ - n = self._get_param_names() - - # remove/concatenate the tied parameter parameter_names - if len(self.tied_indices): - for t in self.tied_indices: - n[t[0]] = "".join([n[tt] for tt in t]) - remove = np.hstack([t[1:] for t in self.tied_indices]) - else: - remove = np.empty(shape=(0,), dtype=np.int) - - # also remove the fixed params - if len(self.fixed_indices): - remove = np.hstack((remove, np.hstack(self.fixed_indices))) - - # add markers to show that some variables are constrained - for i, t in zip(self.constrained_indices, self.constraints): - for ii in i: - n[ii] = n[ii] + t.__str__() - - n = [nn for i, nn in enumerate(n) if not i in remove] - return n - - #@property - #def all(self): - # return self.__str__(self._get_param_names()) - - - #def __str__(self, parameter_names=None, nw=30): - def __str__(self, nw=30): - """ - Return a string describing the parameter parameter_names and their ties and constraints - """ - parameter_names = self._get_param_names() - #if parameter_names is None: - # parameter_names = self._get_print_names() - #name_indices = self.grep_param_names("|".join(parameter_names)) - N = len(parameter_names) - - if not N: - return "This object has no free parameters." - header = ['Name', 'Value', 'Constraints', 'Ties'] - values = self._get_params() # map(str,self._get_params()) - #values = self._get_params()[name_indices] # map(str,self._get_params()) - # sort out the constraints - constraints = [''] * len(parameter_names) - #constraints = [''] * len(self._get_param_names()) - for i, t in zip(self.constrained_indices, self.constraints): - for ii in i: - constraints[ii] = t.__str__() - for i in self.fixed_indices: - for ii in i: - constraints[ii] = 'Fixed' - # sort out the ties - ties = [''] * len(parameter_names) - for i, tie in enumerate(self.tied_indices): - for j in tie: - ties[j] = '(' + str(i) + ')' - - if values.size == 1: - values = ['%.4f' %float(values)] - else: - values = ['%.4f' % float(v) for v in values] - max_names = max([len(parameter_names[i]) for i in range(len(parameter_names))] + [len(header[0])]) - max_values = max([len(values[i]) for i in range(len(values))] + [len(header[1])]) - max_constraint = max([len(constraints[i]) for i in range(len(constraints))] + [len(header[2])]) - max_ties = max([len(ties[i]) for i in range(len(ties))] + [len(header[3])]) - cols = np.array([max_names, max_values, max_constraint, max_ties]) + 4 - # columns = cols.sum() - - header_string = ["{h:^{col}}".format(h=header[i], col=cols[i]) for i in range(len(cols))] - header_string = map(lambda x: '|'.join(x), [header_string]) - separator = '-' * len(header_string[0]) - param_string = ["{n:^{c0}}|{v:^{c1}}|{c:^{c2}}|{t:^{c3}}".format(n=parameter_names[i], v=values[i], c=constraints[i], t=ties[i], c0=cols[0], c1=cols[1], c2=cols[2], c3=cols[3]) for i in range(len(values))] - - - return ('\n'.join([header_string[0], separator] + param_string)) + '\n' - - def grep_model(self,regexp): - regexp_indices = self.grep_param_names(regexp) - all_names = self._get_param_names() - - parameter_names = [all_names[pj] for pj in regexp_indices] - N = len(parameter_names) - - if not N: - return "Match not found." - - header = ['Name', 'Value', 'Constraints', 'Ties'] - all_values = self._get_params() - values = np.array([all_values[pj] for pj in regexp_indices]) - constraints = [''] * len(parameter_names) - - _constrained_indices,aux = self._pick_elements(regexp_indices,self.constrained_indices) - _constraints_ = [self.constraints[pj] for pj in aux] - - for i, t in zip(_constrained_indices, _constraints_): - for ii in i: - iii = regexp_indices.tolist().index(ii) - constraints[iii] = t.__str__() - - _fixed_indices,aux = self._pick_elements(regexp_indices,self.fixed_indices) - for i in _fixed_indices: - for ii in i: - iii = regexp_indices.tolist().index(ii) - constraints[ii] = 'Fixed' - - _tied_indices,aux = self._pick_elements(regexp_indices,self.tied_indices) - ties = [''] * len(parameter_names) - for i,ti in zip(_tied_indices,aux): - for ii in i: - iii = regexp_indices.tolist().index(ii) - ties[iii] = '(' + str(ti) + ')' - - if values.size == 1: - values = ['%.4f' %float(values)] - else: - values = ['%.4f' % float(v) for v in values] - - max_names = max([len(parameter_names[i]) for i in range(len(parameter_names))] + [len(header[0])]) - max_values = max([len(values[i]) for i in range(len(values))] + [len(header[1])]) - max_constraint = max([len(constraints[i]) for i in range(len(constraints))] + [len(header[2])]) - max_ties = max([len(ties[i]) for i in range(len(ties))] + [len(header[3])]) - cols = np.array([max_names, max_values, max_constraint, max_ties]) + 4 - - header_string = ["{h:^{col}}".format(h=header[i], col=cols[i]) for i in range(len(cols))] - header_string = map(lambda x: '|'.join(x), [header_string]) - separator = '-' * len(header_string[0]) - param_string = ["{n:^{c0}}|{v:^{c1}}|{c:^{c2}}|{t:^{c3}}".format(n=parameter_names[i], v=values[i], c=constraints[i], t=ties[i], c0=cols[0], c1=cols[1], c2=cols[2], c3=cols[3]) for i in range(len(values))] - - print header_string[0] - print separator - for string in param_string: - print string - - def _pick_elements(self,regexp_ind,array_list): - """Removes from array_list the elements different from regexp_ind""" - new_array_list = [] #New list with elements matching regexp_ind - array_indices = [] #Indices that matches the arrays in new_array_list and array_list - - array_index = 0 - for array in array_list: - _new = [] - for ai in array: - if ai in regexp_ind: - _new.append(ai) - if len(_new): - new_array_list.append(np.array(_new)) - array_indices.append(array_index) - array_index += 1 - return new_array_list, array_indices +# +# class Parameterized_old(object): +# def __init__(self): +# """ +# This is the base class for model and kernel. Mostly just handles tieing and constraining of parameters +# """ +# self.tied_indices = [] +# self.fixed_indices = [] +# self.fixed_values = [] +# self.constrained_indices = [] +# self.constraints = [] +# +# def _get_params(self): +# raise NotImplementedError, "this needs to be implemented to use the Parameterized class" +# def _set_params(self, x): +# raise NotImplementedError, "this needs to be implemented to use the Parameterized class" +# +# def _get_param_names(self): +# raise NotImplementedError, "this needs to be implemented to use the Parameterized class" +# #def _get_print_names(self): +# # """ Override for which parameter_names to print out, when using print m """ +# # return self._get_param_names() +# +# def pickle(self, filename, protocol=None): +# if protocol is None: +# if self._has_get_set_state(): +# protocol = 0 +# else: +# protocol = -1 +# with open(filename, 'w') as f: +# cPickle.dump(self, f, protocol) +# +# def copy(self): +# """Returns a (deep) copy of the current model """ +# return copy.deepcopy(self) +# +# 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) # set state +# self._set_params(self._get_params()) # restore all values +# return +# self.__dict__ = state +# +# def _has_get_set_state(self): +# return 'getstate' in vars(self.__class__) and 'setstate' in vars(self.__class__) +# +# 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.tied_indices, +# self.fixed_indices, +# self.fixed_values, +# self.constrained_indices, +# self.constraints] +# +# def setstate(self, state): +# self.constraints = state.pop() +# self.constrained_indices = state.pop() +# self.fixed_values = state.pop() +# self.fixed_indices = state.pop() +# self.tied_indices = state.pop() +# +# def __getitem__(self, regexp, return_names=False): +# """ +# Get a model parameter by name. The name is applied as a regular +# expression and all parameters that match that regular expression are +# returned. +# """ +# matches = self.grep_param_names(regexp) +# if len(matches): +# if return_names: +# return self._get_params()[matches], np.asarray(self._get_param_names())[matches].tolist() +# else: +# return self._get_params()[matches] +# else: +# raise AttributeError, "no parameter matches %s" % regexp +# +# def __setitem__(self, name, val): +# """ +# Set model parameter(s) by name. The name is provided as a regular +# expression. All parameters matching that regular expression are set to +# the given value. +# """ +# matches = self.grep_param_names(name) +# if len(matches): +# val = np.array(val) +# assert (val.size == 1) or val.size == len(matches), "Shape mismatch: {}:({},)".format(val.size, len(matches)) +# x = self._get_params() +# x[matches] = val +# self._set_params(x) +# else: +# raise AttributeError, "no parameter matches %s" % name +# +# def tie_params(self, regexp): +# """ +# Tie (all!) parameters matching the regular expression `regexp`. +# """ +# matches = self.grep_param_names(regexp) +# assert matches.size > 0, "need at least something to tie together" +# if len(self.tied_indices): +# assert not np.any(matches[:, None] == np.hstack(self.tied_indices)), "Some indices are already tied!" +# self.tied_indices.append(matches) +# # TODO only one of the priors will be evaluated. Give a warning message if the priors are not identical +# if hasattr(self, 'prior'): +# pass +# +# self._set_params_transformed(self._get_params_transformed()) # sets tied parameters to single value +# +# def untie_everything(self): +# """Unties all parameters by setting tied_indices to an empty list.""" +# self.tied_indices = [] +# +# def grep_param_names(self, regexp, transformed=False, search=False): +# """ +# :param regexp: regular expression to select parameter parameter_names +# :type regexp: re | str | int +# :rtype: the indices of self._get_param_names which match the regular expression. +# +# Note:- +# Other objects are passed through - i.e. integers which weren't meant for grepping +# """ +# +# if transformed: +# parameter_names = self._get_param_names_transformed() +# else: +# parameter_names = self._get_param_names() +# +# if type(regexp) in [str, np.string_, np.str]: +# regexp = re.compile(regexp) +# elif type(regexp) is re._pattern_type: +# pass +# else: +# return regexp +# if search: +# return np.nonzero([regexp.search(name) for name in parameter_names])[0] +# else: +# return np.nonzero([regexp.match(name) for name in parameter_names])[0] +# +# def num_params_transformed(self): +# removed = 0 +# for tie in self.tied_indices: +# removed += tie.size - 1 +# +# for fix in self.fixed_indices: +# removed += fix.size +# +# return len(self._get_params()) - removed +# +# def unconstrain(self, regexp): +# """Unconstrain matching parameters. Does not untie parameters""" +# matches = self.grep_param_names(regexp) +# +# # tranformed contraints: +# for match in matches: +# self.constrained_indices = [i[i <> match] for i in self.constrained_indices] +# +# # remove empty constraints +# tmp = zip(*[(i, t) for i, t in zip(self.constrained_indices, self.constraints) if len(i)]) +# if tmp: +# self.constrained_indices, self.constraints = zip(*[(i, t) for i, t in zip(self.constrained_indices, self.constraints) if len(i)]) +# self.constrained_indices, self.constraints = list(self.constrained_indices), list(self.constraints) +# +# # fixed: +# self.fixed_values = [np.delete(values, np.nonzero(np.sum(indices[:, None] == matches[None, :], 1))[0]) for indices, values in zip(self.fixed_indices, self.fixed_values)] +# self.fixed_indices = [np.delete(indices, np.nonzero(np.sum(indices[:, None] == matches[None, :], 1))[0]) for indices in self.fixed_indices] +# +# # remove empty elements +# tmp = [(i, v) for i, v in zip(self.fixed_indices, self.fixed_values) if len(i)] +# if tmp: +# self.fixed_indices, self.fixed_values = zip(*tmp) +# self.fixed_indices, self.fixed_values = list(self.fixed_indices), list(self.fixed_values) +# else: +# self.fixed_indices, self.fixed_values = [], [] +# +# def constrain_negative(self, regexp, warning=True): +# """ Set negative constraints. """ +# self.constrain(regexp, transformations.NegativeLogexp(), warning) +# +# def constrain_positive(self, regexp, warning=True): +# """ Set positive constraints. """ +# self.constrain(regexp, transformations.Logexp(), warning) +# +# def constrain_bounded(self, regexp, lower, upper, warning=True): +# """ Set bounded constraints. """ +# self.constrain(regexp, transformations.Logistic(lower, upper), warning) +# +# def all_constrained_indices(self): +# if len(self.constrained_indices) or len(self.fixed_indices): +# return np.hstack(self.constrained_indices + self.fixed_indices) +# else: +# return np.empty(shape=(0,)) +# +# def constrain(self, regexp, transform, warning=True): +# assert isinstance(transform, transformations.Transformation) +# +# matches = self.grep_param_names(regexp) +# overlap = set(matches).intersection(set(self.all_constrained_indices())) +# if overlap: +# self.unconstrain(np.asarray(list(overlap))) +# if warning: +# print 'Warning: re-constraining these parameters' +# pn = self._get_param_names() +# for i in overlap: +# print pn[i] +# +# self.constrained_indices.append(matches) +# self.constraints.append(transform) +# x = self._get_params() +# x[matches] = transform.initialize(x[matches]) +# self._set_params(x) +# +# def constrain_fixed(self, regexp, value=None, warning=True): +# """ +# +# :param regexp: which parameters need to be fixed. +# :type regexp: ndarray(dtype=int) or regular expression object or string +# :param value: the vlaue to fix the parameters to. If the value is not specified, +# the parameter is fixed to the current value +# :type value: float +# +# **Notes** +# +# Fixing a parameter which is tied to another, or constrained in some way will result in an error. +# +# To fix multiple parameters to the same value, simply pass a regular expression which matches both parameter parameter_names, or pass both of the indexes. +# +# """ +# matches = self.grep_param_names(regexp) +# overlap = set(matches).intersection(set(self.all_constrained_indices())) +# if overlap: +# self.unconstrain(np.asarray(list(overlap))) +# if warning: +# print 'Warning: re-constraining these parameters' +# pn = self._get_param_names() +# for i in overlap: +# print pn[i] +# +# self.fixed_indices.append(matches) +# if value != None: +# self.fixed_values.append(value) +# else: +# self.fixed_values.append(self._get_params()[self.fixed_indices[-1]]) +# +# # self.fixed_values.append(value) +# self._set_params_transformed(self._get_params_transformed()) +# +# def _get_params_transformed(self): +# """use self._get_params to get the 'true' parameters of the model, which are then tied, constrained and fixed""" +# x = self._get_params() +# [np.put(x, i, t.finv(x[i])) for i, t in zip(self.constrained_indices, self.constraints)] +# +# to_remove = self.fixed_indices + [t[1:] for t in self.tied_indices] +# if len(to_remove): +# return np.delete(x, np.hstack(to_remove)) +# else: +# return x +# +# def _set_params_transformed(self, x): +# """ takes the vector x, which is then modified (by untying, reparameterising or inserting fixed values), and then call self._set_params""" +# self._set_params(self._untransform_params(x)) +# +# def _untransform_params(self, x): +# """ +# The Transformation required for _set_params_transformed. +# +# This moves the vector x seen by the optimiser (unconstrained) to the +# valid parameter vector seen by the model +# +# Note: +# - This function is separate from _set_params_transformed for downstream flexibility +# """ +# # work out how many places are fixed, and where they are. tricky logic! +# fix_places = self.fixed_indices + [t[1:] for t in self.tied_indices] +# if len(fix_places): +# fix_places = np.hstack(fix_places) +# Nfix_places = fix_places.size +# else: +# Nfix_places = 0 +# +# free_places = np.setdiff1d(np.arange(Nfix_places + x.size, dtype=np.int), fix_places) +# +# # put the models values in the vector xx +# xx = np.zeros(Nfix_places + free_places.size, dtype=np.float64) +# +# xx[free_places] = x +# [np.put(xx, i, v) for i, v in zip(self.fixed_indices, self.fixed_values)] +# [np.put(xx, i, v) for i, v in [(t[1:], xx[t[0]]) for t in self.tied_indices] ] +# +# [np.put(xx, i, t.f(xx[i])) for i, t in zip(self.constrained_indices, self.constraints)] +# if hasattr(self, 'debug'): +# stop # @UndefinedVariable +# +# return xx +# +# def _get_param_names_transformed(self): +# """ +# Returns the parameter parameter_names as propagated after constraining, +# tying or fixing, i.e. a list of the same length as _get_params_transformed() +# """ +# n = self._get_param_names() +# +# # remove/concatenate the tied parameter parameter_names +# if len(self.tied_indices): +# for t in self.tied_indices: +# n[t[0]] = "".join([n[tt] for tt in t]) +# remove = np.hstack([t[1:] for t in self.tied_indices]) +# else: +# remove = np.empty(shape=(0,), dtype=np.int) +# +# # also remove the fixed params +# if len(self.fixed_indices): +# remove = np.hstack((remove, np.hstack(self.fixed_indices))) +# +# # add markers to show that some variables are constrained +# for i, t in zip(self.constrained_indices, self.constraints): +# for ii in i: +# n[ii] = n[ii] + t.__str__() +# +# n = [nn for i, nn in enumerate(n) if not i in remove] +# return n +# +# #@property +# #def all(self): +# # return self.__str__(self._get_param_names()) +# +# +# #def __str__(self, parameter_names=None, nw=30): +# def __str__(self, nw=30): +# """ +# Return a string describing the parameter parameter_names and their ties and constraints +# """ +# parameter_names = self._get_param_names() +# #if parameter_names is None: +# # parameter_names = self._get_print_names() +# #name_indices = self.grep_param_names("|".join(parameter_names)) +# N = len(parameter_names) +# +# if not N: +# return "This object has no free parameters." +# header = ['Name', 'Value', 'Constraints', 'Ties'] +# values = self._get_params() # map(str,self._get_params()) +# #values = self._get_params()[name_indices] # map(str,self._get_params()) +# # sort out the constraints +# constraints = [''] * len(parameter_names) +# #constraints = [''] * len(self._get_param_names()) +# for i, t in zip(self.constrained_indices, self.constraints): +# for ii in i: +# constraints[ii] = t.__str__() +# for i in self.fixed_indices: +# for ii in i: +# constraints[ii] = 'Fixed' +# # sort out the ties +# ties = [''] * len(parameter_names) +# for i, tie in enumerate(self.tied_indices): +# for j in tie: +# ties[j] = '(' + str(i) + ')' +# +# if values.size == 1: +# values = ['%.4f' %float(values)] +# else: +# values = ['%.4f' % float(v) for v in values] +# max_names = max([len(parameter_names[i]) for i in range(len(parameter_names))] + [len(header[0])]) +# max_values = max([len(values[i]) for i in range(len(values))] + [len(header[1])]) +# max_constraint = max([len(constraints[i]) for i in range(len(constraints))] + [len(header[2])]) +# max_ties = max([len(ties[i]) for i in range(len(ties))] + [len(header[3])]) +# cols = np.array([max_names, max_values, max_constraint, max_ties]) + 4 +# # columns = cols.sum() +# +# header_string = ["{h:^{col}}".format(h=header[i], col=cols[i]) for i in range(len(cols))] +# header_string = map(lambda x: '|'.join(x), [header_string]) +# separator = '-' * len(header_string[0]) +# param_string = ["{n:^{c0}}|{v:^{c1}}|{c:^{c2}}|{t:^{c3}}".format(n=parameter_names[i], v=values[i], c=constraints[i], t=ties[i], c0=cols[0], c1=cols[1], c2=cols[2], c3=cols[3]) for i in range(len(values))] +# +# +# return ('\n'.join([header_string[0], separator] + param_string)) + '\n' +# +# def grep_model(self,regexp): +# regexp_indices = self.grep_param_names(regexp) +# all_names = self._get_param_names() +# +# parameter_names = [all_names[pj] for pj in regexp_indices] +# N = len(parameter_names) +# +# if not N: +# return "Match not found." +# +# header = ['Name', 'Value', 'Constraints', 'Ties'] +# all_values = self._get_params() +# values = np.array([all_values[pj] for pj in regexp_indices]) +# constraints = [''] * len(parameter_names) +# +# _constrained_indices,aux = self._pick_elements(regexp_indices,self.constrained_indices) +# _constraints_ = [self.constraints[pj] for pj in aux] +# +# for i, t in zip(_constrained_indices, _constraints_): +# for ii in i: +# iii = regexp_indices.tolist().index(ii) +# constraints[iii] = t.__str__() +# +# _fixed_indices,aux = self._pick_elements(regexp_indices,self.fixed_indices) +# for i in _fixed_indices: +# for ii in i: +# iii = regexp_indices.tolist().index(ii) +# constraints[ii] = 'Fixed' +# +# _tied_indices,aux = self._pick_elements(regexp_indices,self.tied_indices) +# ties = [''] * len(parameter_names) +# for i,ti in zip(_tied_indices,aux): +# for ii in i: +# iii = regexp_indices.tolist().index(ii) +# ties[iii] = '(' + str(ti) + ')' +# +# if values.size == 1: +# values = ['%.4f' %float(values)] +# else: +# values = ['%.4f' % float(v) for v in values] +# +# max_names = max([len(parameter_names[i]) for i in range(len(parameter_names))] + [len(header[0])]) +# max_values = max([len(values[i]) for i in range(len(values))] + [len(header[1])]) +# max_constraint = max([len(constraints[i]) for i in range(len(constraints))] + [len(header[2])]) +# max_ties = max([len(ties[i]) for i in range(len(ties))] + [len(header[3])]) +# cols = np.array([max_names, max_values, max_constraint, max_ties]) + 4 +# +# header_string = ["{h:^{col}}".format(h=header[i], col=cols[i]) for i in range(len(cols))] +# header_string = map(lambda x: '|'.join(x), [header_string]) +# separator = '-' * len(header_string[0]) +# param_string = ["{n:^{c0}}|{v:^{c1}}|{c:^{c2}}|{t:^{c3}}".format(n=parameter_names[i], v=values[i], c=constraints[i], t=ties[i], c0=cols[0], c1=cols[1], c2=cols[2], c3=cols[3]) for i in range(len(values))] +# +# print header_string[0] +# print separator +# for string in param_string: +# print string +# +# def _pick_elements(self,regexp_ind,array_list): +# """Removes from array_list the elements different from regexp_ind""" +# new_array_list = [] #New list with elements matching regexp_ind +# array_indices = [] #Indices that matches the arrays in new_array_list and array_list +# +# array_index = 0 +# for array in array_list: +# _new = [] +# for ai in array: +# if ai in regexp_ind: +# _new.append(ai) +# if len(_new): +# new_array_list.append(np.array(_new)) +# array_indices.append(array_index) +# array_index += 1 +# return new_array_list, array_indices diff --git a/GPy/testing/kernel_tests.py b/GPy/testing/kernel_tests.py index 2707f047..1fb6b848 100644 --- a/GPy/testing/kernel_tests.py +++ b/GPy/testing/kernel_tests.py @@ -17,12 +17,16 @@ except ImportError: class KernelTests(unittest.TestCase): def test_kerneltie(self): K = GPy.kern.rbf(5, ARD=True) - K.rbf.lengthscale[:2].tie_to(K.rbf.lengthscale[2:4]) + K.rbf.lengthscale[0].tie_to(K.rbf.lengthscale[2]) + K.rbf.lengthscale[1].tie_to(K.rbf.lengthscale[3]) K.rbf.lengthscale[2].constrain_fixed() - import ipdb;ipdb.set_trace() + K.rbf.lengthscale[3].tie_to(K.rbf.variance) X = np.random.rand(5,5) Y = np.ones((5,1)) m = GPy.models.GPRegression(X,Y,K) + self.assertRaises(RuntimeError, lambda: m.kern.rbf.lengthscale[3].tie_to(m.kern.rbf.lengthscale[1])) + self.assertRaises(RuntimeError, lambda: m.kern.rbf.lengthscale[3].tie_to(m.kern.rbf.lengthscale[0])) + self.assertRaises(RuntimeError, lambda: m.kern.rbf.lengthscale.tie_to(m.kern.rbf.lengthscale)) import ipdb;ipdb.set_trace() self.assertTrue(m.checkgrad()) @@ -117,5 +121,14 @@ class KernelTests(unittest.TestCase): if __name__ == "__main__": - print "Running unit tests, please be (very) patient..." - unittest.main() + K = GPy.kern.rbf(5, ARD=True) + K.rbf.lengthscale[0].tie_to(K.rbf.lengthscale[2]) + K.rbf.lengthscale[1].tie_to(K.rbf.lengthscale[3]) + K.rbf.lengthscale[2].constrain_fixed() + K.rbf.lengthscale[2:].tie_to(K.rbf.variance) + X = np.random.rand(5,5) + Y = np.ones((5,1)) + m = GPy.models.GPRegression(X,Y,K) + + #print "Running unit tests, please be (very) patient..." + #unittest.main()