diff --git a/GPy/core/parameter.py b/GPy/core/parameter.py index 28539a28..85ec15c8 100644 --- a/GPy/core/parameter.py +++ b/GPy/core/parameter.py @@ -6,6 +6,7 @@ Created on 4 Sep 2013 import itertools import numpy from transformations import Logexp, NegativeLogexp, Logistic +from GPy.util.misc import fast_array_equal ###### printing __constraints_name__ = "Constraint" @@ -44,11 +45,13 @@ class Param(numpy.ndarray): obj._name_ = name obj._parent_ = None obj._parent_index_ = None + obj._updates_parent_ = None obj._gradient_ = gradient obj._current_slice_ = (slice(obj.shape[0]),) obj._realshape_ = obj.shape obj._realsize_ = obj.size obj._realndim_ = obj.ndim + obj._updated_ = False from index_operations import ParamDict obj._tied_to_me_ = ParamDict(set) obj._tied_to_ = [] @@ -67,7 +70,10 @@ class Param(numpy.ndarray): self._realshape_ = getattr(obj, '_realshape_', None) self._realsize_ = getattr(obj, '_realsize_', None) self._realndim_ = getattr(obj, '_realndim_', None) + self._updated_ = getattr(obj, '_updated_', None) self._original_ = getattr(obj, '_original_', None) + self._updates_parent_ = getattr(obj, '_updates_parent_', None) + def __array_wrap__(self, out_arr, context=None): return out_arr.view(numpy.ndarray) #=========================================================================== @@ -79,22 +85,30 @@ class Param(numpy.ndarray): (self._name_, self._parent_, self._parent_index_, + self._updates_parent_, self._gradient_, self._current_slice_, self._realshape_, self._realsize_, self._realndim_, - ) + self._tied_to_me_, + self._tied_to_, + self._updated_, + ) ) def __setstate__(self, state): super(Param, self).__setstate__(state[0]) state = list(state[1]) + self._updated_ = state.pop() + self._tied_to_ = state.pop() + self._tied_to_me_ = state.pop() self._realndim_ = state.pop() self._realsize_ = state.pop() self._realshape_ = state.pop() self._current_slice_ = state.pop() - self._parent_index_ = state.pop() self._gradient_ = state.pop() + self._updates_parent_ = state.pop() + self._parent_index_ = state.pop() self._parent_ = state.pop() self._name_ = state.pop() #=========================================================================== @@ -145,7 +159,7 @@ class Param(numpy.ndarray): #=========================================================================== # Constrain operations -> done #=========================================================================== - def constrain(self, transform, warning=True): + def constrain(self, transform, warning=True, update=True): """ :param transform: the :py:class:`GPy.core.transformations.Transformation` to constrain the this parameter to. @@ -155,10 +169,12 @@ class Param(numpy.ndarray): :py:class:`GPy.core.transformations.Transformation`. """ if self._original_: # this happens when indexing created a copy of the array - self.__setitem__(slice(None), transform.initialize(self)) + self.__setitem__(slice(None), transform.initialize(self), update=False) else: - self._parent_._get_original(self)[self._current_slice_] = transform.initialize(self) + self._parent_._get_original(self).__setitem__(self._current_slice_, transform.initialize(self), update=False) self._parent_._add_constrain(self, transform, warning) + if update: + self._updates_parent_.parameters_changed() def constrain_positive(self, warning=True): """ :param warning: print a warning if re-constraining parameters. @@ -236,11 +252,8 @@ class Param(numpy.ndarray): """ remove tie of this parameter to ties it was tied to. """ - [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_._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_==self._parent_._get_original(t)._parent_index_] self._parent_._set_unfixed(self) # self._parent_._remove_tie(self, *params) def _fire_changed(self): @@ -250,17 +263,25 @@ class Param(numpy.ndarray): 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()) - if len(self._tied_to_me_[t]) == 0: + if t._parent_index_ == to_remove._parent_index_: + new_index = list(set(t._raveled_index()) - set(to_remove._raveled_index())) + if new_index: + tmp = self._parent_._get_original(t)[numpy.unravel_index(new_index,t._realshape_)] + self._tied_to_me_[tmp] = self._tied_to_me_[t] + del self._tied_to_me_[t] + if len(self._tied_to_me_[tmp]) == 0: + del self._tied_to_me_[tmp] + else: del self._tied_to_me_[t] def _on_change(self, val, ind): - if not numpy.all(self==val[ind]): + if not self._updated_: #not fast_array_equal(self, val[ind]): + self._updated_ = True if self._original_: - self[:] = val[ind] + self.__setitem__(slice(None), val[ind], update=False) else: # this happens when indexing created a copy of the array - self._parent_._get_original(self)[self._current_slice_] = val[ind] + self._parent_._get_original(self).__setitem__(self._current_slice_, val[ind], update=False) self._fire_changed() + self._updated_ = False #=========================================================================== # Prior Operations #=========================================================================== @@ -294,10 +315,13 @@ class Param(numpy.ndarray): return new_arr def __getslice__(self, start, stop): return self.__getitem__(slice(start, stop)) - def __setitem__(self, *args, **kwargs): - numpy.ndarray.__setitem__(self, *args, **kwargs) + def __setslice__(self, start, stop, val): + return self.__setitem__(slice(start, stop), val) + def __setitem__(self, s, val, update=True): + numpy.ndarray.__setitem__(self, s, val) self._fire_changed() - self._parent_.parameters_changed() + if update: + self._updates_parent_.parameters_changed() #=========================================================================== # Index Operations: #=========================================================================== @@ -322,7 +346,7 @@ class Param(numpy.ndarray): extended_realshape = numpy.cumprod((1,) + self._realshape_[:0:-1])[::-1] ind = self._indices(slice_index) if ind.ndim < 2: ind=ind[:,None] - return numpy.apply_along_axis(lambda x: numpy.sum(extended_realshape*x), 1, ind) + return numpy.asarray(numpy.apply_along_axis(lambda x: numpy.sum(extended_realshape*x), 1, ind), dtype=int) def _expand_index(self, slice_index=None): # this calculates the full indexing arrays from the slicing objects given by get_item for _real..._ attributes # it basically translates slices to their respective index arrays and turns negative indices around @@ -428,19 +452,22 @@ class ParamConcatenation(object): self.params = params self._param_sizes = [p.size for p in self.params] startstops = numpy.cumsum([0] + self._param_sizes) - self._param_slices = [slice(start, stop) for start,stop in zip(startstops, startstops[1:])] + self._param_slices_ = [slice(start, stop) for start,stop in zip(startstops, startstops[1:])] #=========================================================================== # Get/set items, enable broadcasting #=========================================================================== def __getitem__(self, s): ind = numpy.zeros(sum(self._param_sizes), dtype=bool); ind[s] = True; - params = [p.flatten()[ind[ps]] for p,ps in zip(self.params, self._param_slices) if numpy.any(p.flat[ind[ps]])] + params = [p.flatten()[ind[ps]] for p,ps in zip(self.params, self._param_slices_) if numpy.any(p.flat[ind[ps]])] if len(params)==1: return params[0] return ParamConcatenation(params) - def __setitem__(self, s, val): + def __setitem__(self, s, val, update=True): 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]) for p, ps in zip(self.params, self._param_slices)] + [numpy.place(p, ind[ps], vals[ps]) and p._fire_changed() + for p, ps in zip(self.params, self._param_slices_)] + if update: + self.params[0]._updates_parent_.parameters_changed() def _vals(self): return numpy.hstack([p._get_params() for p in self.params]) #=========================================================================== @@ -502,7 +529,7 @@ class ParamConcatenation(object): if __name__ == '__main__': from GPy.core.parameterized import Parameterized #X = numpy.random.randn(2,3,1,5,2,4,3) - X = numpy.random.randn(100,20) + X = numpy.random.randn(1000,20) print "random done" p = Param("q_mean", X, None) p1 = Param("q_variance", numpy.random.rand(*p.shape), None) diff --git a/GPy/core/parameterized.py b/GPy/core/parameterized.py index 81ef5fa8..3ad8be0c 100644 --- a/GPy/core/parameterized.py +++ b/GPy/core/parameterized.py @@ -2,15 +2,16 @@ # Licensed under the BSD 3-clause license (see LICENSE.txt) -import numpy +import numpy; np = numpy import copy import cPickle from parameter import ParamConcatenation, Param from index_operations import ParameterIndexOperations,\ index_empty +import transformations import itertools from re import compile, _pattern_type -import sys +import re #=============================================================================== # Printing: @@ -69,8 +70,9 @@ class Parameterized(object): """ def __init__(self): self._constraints_ = ParameterIndexOperations() - #self._ties_ = TieIndexOperations(self) - self._ties_fixes_ = None + #self._fixes_ = TieIndexOperations(self) + #self._fixes_ = None + self._fixes_ = None self._in_init_ = True if not hasattr(self, "_parameters_"): self._parameters_ = [] @@ -79,7 +81,7 @@ class Parameterized(object): #=========================================================================== # Parameter connection for model creation: #=========================================================================== - def set_as_parameter(self, name, array, gradient, index=None): + def set_as_parameter(self, name, array, gradient, index=None, highest_parent=None): """ :param name: name of the parameter (in print and plots), can be callable without parameters :type name: str, callable @@ -89,6 +91,12 @@ class Parameterized(object): :type gradient: callable :param index: (optional) index of the parameter when printing + (:param highest_parent: connect these parameters to this class, but tell + updates to highest_parent, this is needed when parameterized classes + contain parameterized classes, but want to access the parameters + of their children) + + Set array (e.g. self.X) as parameter with name and gradient. I.e: self.set_as_parameter('curvature', self.lengthscale, self.dK_dlengthscale) @@ -99,11 +107,13 @@ class Parameterized(object): self._parameters_.append(Param(name, array, gradient)) else: self._parameters_.insert(index, Param(name, array, gradient)) - self._connect_parameters() + self._connect_parameters(highest_parent=highest_parent) def set_as_parameters(self, *parameters, **kwargs): """ :param parameters: the parameters to add :param index: index of where to put parameters + + Add all parameters to this parameter class, you can insert parameters at any given point using the :py:func:`list.insert` syntax """ @@ -111,23 +121,23 @@ class Parameterized(object): self._parameters_.extend(parameters) else: self._parameters_.insert(kwargs['index'], parameters) - self._connect_parameters() - def remove_parameter(self, *names_params_indices): - """ - :param names_params_indices: mix of parameter_names, parameter objects, or indices - to remove from being a parameter of this parameterized object. - - note: if it is a string object it will be regexp-matched automatically - """ - self._parameters_ = [p for p in self._parameters_ - if not (p._parent_index_ in names_params_indices - or p.name in names_params_indices - or p in names_params_indices)] - self._connect_parameters() + self._connect_parameters(highest_parent=kwargs.get('highest_parent', self)) +# def remove_parameter(self, *names_params_indices): +# """ +# :param names_params_indices: mix of parameter_names, parameter objects, or indices +# to remove from being a parameter of this parameterized object. +# +# note: if it is a string object it will be regexp-matched automatically +# """ +# self._parameters_ = [p for p in self._parameters_ +# if not (p._parent_index_ in names_params_indices +# or p.name in names_params_indices +# or p in names_params_indices)] +# self._connect_parameters() def parameters_changed(self): # will be called as soon as paramters have changed pass - def _connect_parameters(self): + def _connect_parameters(self, highest_parent=None): # connect parameterlist to this parameterized object # This just sets up the right connection for the params objects # to be used as parameters @@ -135,19 +145,21 @@ class Parameterized(object): # no parameters for this class return sizes = numpy.cumsum([0] + self._parameter_sizes_) - self._param_slices = [slice(start, stop) for start,stop in zip(sizes, sizes[1:])] + self._parameter_size_ = sizes[-1] + self._param_slices_ = [slice(start, stop) for start,stop in zip(sizes, sizes[1:])] for i, p in enumerate(self._parameters_): p._parent_ = self p._parent_index_ = i + p._updates_parent_ = highest_parent or self not_unique = [] if p.name in self.__dict__: - if p.base is self.__dict__[p.name]: + if p.base is self.__dict__[p.name] or p is self.__dict__[p.name]: self.__dict__[p.name] = p else: not_unique.append(p.name) del self.__dict__[p.name] elif not (p.name in not_unique): - self.__dict__[p.name] = p + self.__dict__[p.name] = p #=========================================================================== # Pickling operations #=========================================================================== @@ -187,49 +199,43 @@ class Parameterized(object): Allways append the state of the inherited object and call down to the inherited object in setstate!! """ - return [self._ties_, + return [ + self._fixes_, self._constraints_, + self._priors_, self._parameters_, ] + def setstate(self, state): self._parameters_ = state.pop() self._connect_parameters() self._priors = state.pop() self._constraints_ = state.pop() - self._ties_ = state.pop() - self._ties_fixes_ = numpy.ones(self._parameter_size_, dtype=bool) - for f, (fixed, ind) in itertools.izip_longest(self._ties_.iter_from_indices(), self._constraints_.iteritems()): - if f is not None: - self._ties_fixes_[f] = FIXED - if fixed == __fixed__: - self._ties_fixes_[ind] = FIXED - if numpy.all(self._ties_fixes_): # ==UNFIXED - self._ties_fixes_ = None + self._fixes_ = state.pop() self.parameters_changed() #=========================================================================== # Optimization handles: #=========================================================================== + def _get_param_names_transformed(self): + return numpy.array([p.name+str(i) for p in self._parameters_ for i in p._indices()])[self._fixes_][0] + def _get_params(self): # don't overwrite this anymore! return numpy.hstack([x._get_params() for x in self._parameters_])#numpy.fromiter(itertools.chain(*itertools.imap(lambda x: x._get_params(), self._parameters_)), dtype=numpy.float64, count=sum(self._parameter_sizes_)) def _set_params(self, params): # don't overwrite this anymore! - [p._set_params(params[s]) 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 _get_params_transformed(self): p = self._get_params() [numpy.put(p, ind, c.finv(p[ind])) for c,ind in self._constraints_.iteritems() if c != __fixed__] - if self._ties_fixes_ is not None: - return p[self._ties_fixes_] + if self._fixes_ is not None: + return p[self._fixes_] return p def _set_params_transformed(self, p): - if self._ties_fixes_ is not None: tmp = self._get_params(); tmp[self._ties_fixes_] = p; p = tmp; del tmp + if self._fixes_ is not None: tmp = self._get_params(); tmp[self._fixes_] = p; p = tmp; del tmp [numpy.put(p, ind, c.f(p[ind])) for c,ind in self._constraints_.iteritems() if c != __fixed__] - [numpy.put(p, f, p[t]) for f,t in self._ties_.iter_from_to_indices()] self._set_params(p) - def _handle_ties(self): - if not hasattr(self, '_in_init_'): - self._set_params_transformed(self._get_params_transformed()) def _name_changed(self, param, old_name): if hasattr(self, old_name): delattr(self, old_name) @@ -239,51 +245,51 @@ class Parameterized(object): #=========================================================================== def _backtranslate_index(self, param, ind): # translate an index in parameterized indexing into the index of param - ind = ind-self._offset(param) + ind = ind-self._offset_for(param) ind = ind[ind >= 0] internal_offset = param._internal_offset() ind = ind[ind < param.size + internal_offset] return ind - def _offset(self, param): + def _offset_for(self, param): # get the offset in the parameterized index array for param - return self._param_slices[param._parent_index_].start + return self._param_slices_[param._parent_index_].start def _raveled_index_for(self, param): - return param._raveled_index() + self._offset(param) + return param._raveled_index() + self._offset_for(param) #=========================================================================== # Handle ties: #=========================================================================== def _set_fixed(self, param_or_index): - if self._ties_fixes_ is None: self._ties_fixes_ = numpy.ones(self._parameter_size_, dtype=bool) + if self._fixes_ is None: self._fixes_ = numpy.ones(self._parameter_size_, dtype=bool) try: param_or_index = self._raveled_index_for(param_or_index) except AttributeError: pass - self._ties_fixes_[param_or_index] = FIXED - if numpy.all(self._ties_fixes_): self._ties_fixes_ = None # ==UNFIXED + self._fixes_[param_or_index] = FIXED + if numpy.all(self._fixes_): self._fixes_ = None # ==UNFIXED def _set_unfixed(self, param_or_index): - if self._ties_fixes_ is None: self._ties_fixes_ = numpy.ones(self._parameter_size_, dtype=bool) + if self._fixes_ is None: self._fixes_ = numpy.ones(self._parameter_size_, dtype=bool) try: param_or_index = self._raveled_index_for(param_or_index) except AttributeError: pass - self._ties_fixes_[param_or_index] = UNFIXED - if numpy.all(self._ties_fixes_): self._ties_fixes_ = None # ==UNFIXED + self._fixes_[param_or_index] = UNFIXED + if numpy.all(self._fixes_): self._fixes_ = None # ==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 +# f, _ = self._fixes_.add(param, tied_to) +# if self._fixes_ is None: self._fixes_ = numpy.ones(self._parameter_size_, dtype=bool) +# self._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() +# params = self._fixes_.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 +# _, t = self._fixes_.remove(param, p) +# self._fixes_[t] = True +# if numpy.all(self._fixes_): self._fixes_ = None # ==UNFIXED # def _ties_iter_items(self, param): -# for tied_to, ind in self._ties_.iter_from_items(): +# for tied_to, ind in self._fixes_.iter_from_items(): # ind = self._backtranslate_index(param, ind) # if not index_empty(ind): # yield tied_to, ind @@ -294,7 +300,7 @@ class Parameterized(object): # 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)) +# return self._fixes_.from_to_for(rav_index+self._offset_for(param)) #=========================================================================== # Fixing parameters: #=========================================================================== @@ -302,7 +308,7 @@ class Parameterized(object): f = self._add_constrain(param, __fixed__, warning) self._set_fixed(f) def _unfix(self, param): - if self._ties_fixes_ is not None: + if self._fixes_ is not None: f = self._remove_constrain(param, __fixed__) self._set_unfixed(f) #=========================================================================== @@ -310,9 +316,9 @@ class Parameterized(object): #=========================================================================== def _is_fixed(self, param): # returns if the whole parameter is fixed - if self._ties_fixes_ is None: + if self._fixes_ is None: return False - return not self._ties_fixes_[self._offset(param): self._offset(param)+param._realsize_].any() + return not self._fixes_[self._offset_for(param): self._offset_for(param)+param._realsize_].any() def _get_original(self, param): # if advanced indexing is activated it happens that the array is a copy # you can retrieve the original parameter through this method, by passing @@ -324,7 +330,7 @@ class Parameterized(object): def _add_constrain(self, param, transform, warning=True): rav_i = self._raveled_index_for(param) reconstrained = self._remove_constrain(param, index=rav_i) # remove constraints before - # if removing constraints before adding new is not wanted, just delete the above line! + # if removing constraints before adding new is not wanted, just delete the above line! self._constraints_.add(transform, rav_i) if warning and any(reconstrained): # if you want to print the whole params object, which was reconstrained use: @@ -356,7 +362,7 @@ class Parameterized(object): def _constraint_indices(self, param, constraint): return self._backtranslate_index(param, self._constraints_[constraint]) def _constraints_for(self, param, rav_index): - return self._constraints_.properties_for(rav_index+self._offset(param)) + return self._constraints_.properties_for(rav_index+self._offset_for(param)) #=========================================================================== # Get/set parameters: #=========================================================================== @@ -371,21 +377,22 @@ class Parameterized(object): paramlist = self.grep_param_names(name) if len(paramlist) < 1: raise AttributeError, name if len(paramlist) == 1: return paramlist[-1] - return ParamConcatenation(paramlist) + return ParamConcatenation(paramlist) def __setitem__(self, name, value, paramlist=None): try: param = self.__getitem__(name, paramlist) except AttributeError as a: raise a param[:] = value - self._handle_ties() - def __getattribute__(self, name): - try: - return object.__getattribute__(self, name) - except AttributeError: - _, a, tb = sys.exc_info() - try: - return self.__getitem__(name) - except AttributeError: - raise AttributeError, a.message, tb + def __getattr__(self, name): + return self.__getitem__(name) +# def __getattribute__(self, name): +# #try: +# return object.__getattribute__(self, name) + #except AttributeError: + # _, a, tb = sys.exc_info() + # try: + # return self.__getitem__(name) + # except AttributeError: + # raise AttributeError, a.message, tb def __setattr__(self, name, val): # override the default behaviour, if setting a parameter, so broadcasting can by used if hasattr(self, "_parameters_"): @@ -399,14 +406,11 @@ class Parameterized(object): def parameter_names(self): return [x.name for x in self._parameters_] @property - def _parameter_size_(self): - return sum(self._parameter_sizes_) - @property def _parameter_sizes_(self): return [x.size for x in self._parameters_] @property def _parameter_size_transformed_(self): - return sum(self._ties_fixes_) + return sum(self._fixes_) @property def _parameter_shapes_(self): return [x.shape for x in self._parameters_] @@ -436,462 +440,462 @@ class Parameterized(object): 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