simple tieing now working, still buggy though, progress with care

This commit is contained in:
Max Zwiessele 2013-11-12 12:17:14 +00:00
parent 6c60eef4bd
commit 851e6ec8e9
3 changed files with 531 additions and 488 deletions

View file

@ -284,19 +284,26 @@ class Param(ObservableArray, Nameable, Pickleable):
#=========================================================================== #===========================================================================
def tie_to(self, param): 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. 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 one parameter: self[:,0].tie_to(other), where other is a one-value
parameter. parameter.
Note: this method will tie to the parameter which is the last in Note: For now only one parameter can have ties, so all of a parameter
the chain of ties. Thus, if you tie to a tied parameter, will be removed, when re-tieing!
this tie will be created to the parameter the param is tied
to.
""" """
#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__) 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: try:
if self._original_: if self._original_:
self[:] = param 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)) raise ValueError("Trying to tie {} with shape {} to {} with shape {}".format(self.name, self.shape, param.name, param.shape))
if param is self: if param is self:
raise RuntimeError, 'Cyclic tieing is not allowed' raise RuntimeError, 'Cyclic tieing is not allowed'
if len(param._tied_to_) > 0: # if len(param._tied_to_) > 0:
self.tie_to(param._tied_to_[0]) # if (self._direct_parent_._get_original(self) is param._direct_parent_._get_original(param)
return # and len(set(self._raveled_index())&set(param._tied_to_[0]._raveled_index()))!=0):
self._direct_parent_._get_original(self)._tied_to_ += [param] # 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) param._add_tie_listener(self)
self._highest_parent_._set_fixed(self) self._highest_parent_._set_fixed(self)
for t in self._tied_to_me_.iterkeys(): # for t in self._tied_to_me_.keys():
if t is not self: # if t is not self:
t.untie(self) # t.untie(self)
t.tie_to(param) # t.tie_to(param)
# self._direct_parent_._add_tie(self, param)
def untie(self, *ties): 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_] [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_] new_ties = []
self._highest_parent_._set_unfixed(self) 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) # self._direct_parent_._remove_tie(self, *params)
def _notify_tied_parameters(self): def _notify_tied_parameters(self):
for tied, ind in self._tied_to_me_.iteritems(): for tied, ind in self._tied_to_me_.iteritems():
tied._on_tied_parameter_changed(self.base, list(ind)) tied._on_tied_parameter_changed(self.base, list(ind))
def _add_tie_listener(self, tied_to_me): 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): def _remove_tie_listener(self, to_remove):
for t in self._tied_to_me_.keys(): for t in self._tied_to_me_.keys():
if t._parent_index_ == to_remove._parent_index_: 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: if new_index:
tmp = t._direct_parent_._get_original(t)[numpy.unravel_index(new_index,t._realshape_)] tmp = t._direct_parent_._get_original(t)[numpy.unravel_index(new_index,t._realshape_)]
self._tied_to_me_[tmp] = self._tied_to_me_[t] 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] del self._tied_to_me_[t]
def _on_tied_parameter_changed(self, val, ind): def _on_tied_parameter_changed(self, val, ind):
if not self._updated_: #not fast_array_equal(self, val[ind]): if not self._updated_: #not fast_array_equal(self, val[ind]):
val = numpy.atleast_1d(val)
self._updated_ = True self._updated_ = True
if self._original_: if self._original_:
self.__setitem__(slice(None), val[ind], update=False) self.__setitem__(slice(None), val[ind], update=False)
@ -479,14 +507,16 @@ class Param(ObservableArray, Nameable, Pickleable):
x=self.name_hirarchical) x=self.name_hirarchical)
return name + super(Param, self).__repr__(*args,**kwargs) return name + super(Param, self).__repr__(*args,**kwargs)
def _ties_for(self, rav_index): 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) ties = numpy.empty(shape=(len(self._tied_to_), numpy.size(rav_index)), dtype=Param)
for i, tied_to in enumerate(self._tied_to_): 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_: if t._parent_index_ == self._parent_index_:
matches = numpy.where(rav_index[:,None] == t._raveled_index()[None, :]) matches = numpy.where(rav_index[:,None] == t._raveled_index()[None, :])
tt_rav_index = tied_to._raveled_index() tt_rav_index = tied_to._raveled_index()
ties[i, matches[0]] = numpy.take(tt_rav_index, matches[1], mode='wrap') ind_rav_matches = numpy.where(tt_rav_index == numpy.array(list(ind)))[0]
#[ties.__setitem__(i, ties[i] + [tied_to]) for i in t._raveled_index()] 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_)])) 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): def _constraints_for(self, rav_index):
return self._highest_parent_._constraints_for(self, rav_index) return self._highest_parent_._constraints_for(self, rav_index)

View file

@ -633,7 +633,7 @@ class Parameterized(Nameable, Pickleable, Observable):
return [xi for x in self._parameters_ for xi in x._description_str] return [xi for x in self._parameters_ for xi in x._description_str]
@property @property
def _ties_str(self): 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): def __str__(self, header=True):
constrs = self._constraints_str; ts = self._ties_str constrs = self._constraints_str; ts = self._ties_str
desc = self._description_str; names = self.parameter_names desc = self._description_str; names = self.parameter_names
@ -654,463 +654,463 @@ class Parameterized(Nameable, Pickleable, Observable):
return '\n'.format(sep).join(to_print) return '\n'.format(sep).join(to_print)
pass pass
#
class Parameterized_old(object): # class Parameterized_old(object):
def __init__(self): # def __init__(self):
""" # """
This is the base class for model and kernel. Mostly just handles tieing and constraining of parameters # This is the base class for model and kernel. Mostly just handles tieing and constraining of parameters
""" # """
self.tied_indices = [] # self.tied_indices = []
self.fixed_indices = [] # self.fixed_indices = []
self.fixed_values = [] # self.fixed_values = []
self.constrained_indices = [] # self.constrained_indices = []
self.constraints = [] # self.constraints = []
#
def _get_params(self): # def _get_params(self):
raise NotImplementedError, "this needs to be implemented to use the Parameterized class" # raise NotImplementedError, "this needs to be implemented to use the Parameterized class"
def _set_params(self, x): # def _set_params(self, x):
raise NotImplementedError, "this needs to be implemented to use the Parameterized class" # raise NotImplementedError, "this needs to be implemented to use the Parameterized class"
#
def _get_param_names(self): # def _get_param_names(self):
raise NotImplementedError, "this needs to be implemented to use the Parameterized class" # raise NotImplementedError, "this needs to be implemented to use the Parameterized class"
#def _get_print_names(self): # #def _get_print_names(self):
# """ Override for which parameter_names to print out, when using print m """ # # """ Override for which parameter_names to print out, when using print m """
# return self._get_param_names() # # return self._get_param_names()
#
def pickle(self, filename, protocol=None): # def pickle(self, filename, protocol=None):
if protocol is None: # if protocol is None:
if self._has_get_set_state(): # if self._has_get_set_state():
protocol = 0 # protocol = 0
else: # else:
protocol = -1 # protocol = -1
with open(filename, 'w') as f: # with open(filename, 'w') as f:
cPickle.dump(self, f, protocol) # cPickle.dump(self, f, protocol)
#
def copy(self): # def copy(self):
"""Returns a (deep) copy of the current model """ # """Returns a (deep) copy of the current model """
return copy.deepcopy(self) # return copy.deepcopy(self)
#
def __getstate__(self): # def __getstate__(self):
if self._has_get_set_state(): # if self._has_get_set_state():
return self.getstate() # return self.getstate()
return self.__dict__ # return self.__dict__
#
def __setstate__(self, state): # def __setstate__(self, state):
if self._has_get_set_state(): # if self._has_get_set_state():
self.setstate(state) # set state # self.setstate(state) # set state
self._set_params(self._get_params()) # restore all values # self._set_params(self._get_params()) # restore all values
return # return
self.__dict__ = state # self.__dict__ = state
#
def _has_get_set_state(self): # def _has_get_set_state(self):
return 'getstate' in vars(self.__class__) and 'setstate' in vars(self.__class__) # return 'getstate' in vars(self.__class__) and 'setstate' in vars(self.__class__)
#
def getstate(self): # def getstate(self):
""" # """
Get the current state of the class, # Get the current state of the class,
here just all the indices, rest can get recomputed # here just all the indices, rest can get recomputed
For inheriting from Parameterized: # For inheriting from Parameterized:
#
Allways append the state of the inherited object # Allways append the state of the inherited object
and call down to the inherited object in setstate!! # and call down to the inherited object in setstate!!
""" # """
return [self.tied_indices, # return [self.tied_indices,
self.fixed_indices, # self.fixed_indices,
self.fixed_values, # self.fixed_values,
self.constrained_indices, # self.constrained_indices,
self.constraints] # self.constraints]
#
def setstate(self, state): # def setstate(self, state):
self.constraints = state.pop() # self.constraints = state.pop()
self.constrained_indices = state.pop() # self.constrained_indices = state.pop()
self.fixed_values = state.pop() # self.fixed_values = state.pop()
self.fixed_indices = state.pop() # self.fixed_indices = state.pop()
self.tied_indices = state.pop() # self.tied_indices = state.pop()
#
def __getitem__(self, regexp, return_names=False): # def __getitem__(self, regexp, return_names=False):
""" # """
Get a model parameter by name. The name is applied as a regular # Get a model parameter by name. The name is applied as a regular
expression and all parameters that match that regular expression are # expression and all parameters that match that regular expression are
returned. # returned.
""" # """
matches = self.grep_param_names(regexp) # matches = self.grep_param_names(regexp)
if len(matches): # if len(matches):
if return_names: # if return_names:
return self._get_params()[matches], np.asarray(self._get_param_names())[matches].tolist() # return self._get_params()[matches], np.asarray(self._get_param_names())[matches].tolist()
else: # else:
return self._get_params()[matches] # return self._get_params()[matches]
else: # else:
raise AttributeError, "no parameter matches %s" % regexp # raise AttributeError, "no parameter matches %s" % regexp
#
def __setitem__(self, name, val): # def __setitem__(self, name, val):
""" # """
Set model parameter(s) by name. The name is provided as a regular # Set model parameter(s) by name. The name is provided as a regular
expression. All parameters matching that regular expression are set to # expression. All parameters matching that regular expression are set to
the given value. # the given value.
""" # """
matches = self.grep_param_names(name) # matches = self.grep_param_names(name)
if len(matches): # if len(matches):
val = np.array(val) # val = np.array(val)
assert (val.size == 1) or val.size == len(matches), "Shape mismatch: {}:({},)".format(val.size, len(matches)) # assert (val.size == 1) or val.size == len(matches), "Shape mismatch: {}:({},)".format(val.size, len(matches))
x = self._get_params() # x = self._get_params()
x[matches] = val # x[matches] = val
self._set_params(x) # self._set_params(x)
else: # else:
raise AttributeError, "no parameter matches %s" % name # raise AttributeError, "no parameter matches %s" % name
#
def tie_params(self, regexp): # def tie_params(self, regexp):
""" # """
Tie (all!) parameters matching the regular expression `regexp`. # Tie (all!) parameters matching the regular expression `regexp`.
""" # """
matches = self.grep_param_names(regexp) # matches = self.grep_param_names(regexp)
assert matches.size > 0, "need at least something to tie together" # assert matches.size > 0, "need at least something to tie together"
if len(self.tied_indices): # if len(self.tied_indices):
assert not np.any(matches[:, None] == np.hstack(self.tied_indices)), "Some indices are already tied!" # assert not np.any(matches[:, None] == np.hstack(self.tied_indices)), "Some indices are already tied!"
self.tied_indices.append(matches) # self.tied_indices.append(matches)
# TODO only one of the priors will be evaluated. Give a warning message if the priors are not identical # # TODO only one of the priors will be evaluated. Give a warning message if the priors are not identical
if hasattr(self, 'prior'): # if hasattr(self, 'prior'):
pass # pass
#
self._set_params_transformed(self._get_params_transformed()) # sets tied parameters to single value # self._set_params_transformed(self._get_params_transformed()) # sets tied parameters to single value
#
def untie_everything(self): # def untie_everything(self):
"""Unties all parameters by setting tied_indices to an empty list.""" # """Unties all parameters by setting tied_indices to an empty list."""
self.tied_indices = [] # self.tied_indices = []
#
def grep_param_names(self, regexp, transformed=False, search=False): # def grep_param_names(self, regexp, transformed=False, search=False):
""" # """
:param regexp: regular expression to select parameter parameter_names # :param regexp: regular expression to select parameter parameter_names
:type regexp: re | str | int # :type regexp: re | str | int
:rtype: the indices of self._get_param_names which match the regular expression. # :rtype: the indices of self._get_param_names which match the regular expression.
#
Note:- # Note:-
Other objects are passed through - i.e. integers which weren't meant for grepping # Other objects are passed through - i.e. integers which weren't meant for grepping
""" # """
#
if transformed: # if transformed:
parameter_names = self._get_param_names_transformed() # parameter_names = self._get_param_names_transformed()
else: # else:
parameter_names = self._get_param_names() # parameter_names = self._get_param_names()
#
if type(regexp) in [str, np.string_, np.str]: # if type(regexp) in [str, np.string_, np.str]:
regexp = re.compile(regexp) # regexp = re.compile(regexp)
elif type(regexp) is re._pattern_type: # elif type(regexp) is re._pattern_type:
pass # pass
else: # else:
return regexp # return regexp
if search: # if search:
return np.nonzero([regexp.search(name) for name in parameter_names])[0] # return np.nonzero([regexp.search(name) for name in parameter_names])[0]
else: # else:
return np.nonzero([regexp.match(name) for name in parameter_names])[0] # return np.nonzero([regexp.match(name) for name in parameter_names])[0]
#
def num_params_transformed(self): # def num_params_transformed(self):
removed = 0 # removed = 0
for tie in self.tied_indices: # for tie in self.tied_indices:
removed += tie.size - 1 # removed += tie.size - 1
#
for fix in self.fixed_indices: # for fix in self.fixed_indices:
removed += fix.size # removed += fix.size
#
return len(self._get_params()) - removed # return len(self._get_params()) - removed
#
def unconstrain(self, regexp): # def unconstrain(self, regexp):
"""Unconstrain matching parameters. Does not untie parameters""" # """Unconstrain matching parameters. Does not untie parameters"""
matches = self.grep_param_names(regexp) # matches = self.grep_param_names(regexp)
#
# tranformed contraints: # # tranformed contraints:
for match in matches: # for match in matches:
self.constrained_indices = [i[i <> match] for i in self.constrained_indices] # self.constrained_indices = [i[i <> match] for i in self.constrained_indices]
#
# remove empty constraints # # remove empty constraints
tmp = zip(*[(i, t) for i, t in zip(self.constrained_indices, self.constraints) if len(i)]) # tmp = zip(*[(i, t) for i, t in zip(self.constrained_indices, self.constraints) if len(i)])
if tmp: # 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 = 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) # self.constrained_indices, self.constraints = list(self.constrained_indices), list(self.constraints)
#
# fixed: # # 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_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] # 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 # # remove empty elements
tmp = [(i, v) for i, v in zip(self.fixed_indices, self.fixed_values) if len(i)] # tmp = [(i, v) for i, v in zip(self.fixed_indices, self.fixed_values) if len(i)]
if tmp: # if tmp:
self.fixed_indices, self.fixed_values = zip(*tmp) # self.fixed_indices, self.fixed_values = zip(*tmp)
self.fixed_indices, self.fixed_values = list(self.fixed_indices), list(self.fixed_values) # self.fixed_indices, self.fixed_values = list(self.fixed_indices), list(self.fixed_values)
else: # else:
self.fixed_indices, self.fixed_values = [], [] # self.fixed_indices, self.fixed_values = [], []
#
def constrain_negative(self, regexp, warning=True): # def constrain_negative(self, regexp, warning=True):
""" Set negative constraints. """ # """ Set negative constraints. """
self.constrain(regexp, transformations.NegativeLogexp(), warning) # self.constrain(regexp, transformations.NegativeLogexp(), warning)
#
def constrain_positive(self, regexp, warning=True): # def constrain_positive(self, regexp, warning=True):
""" Set positive constraints. """ # """ Set positive constraints. """
self.constrain(regexp, transformations.Logexp(), warning) # self.constrain(regexp, transformations.Logexp(), warning)
#
def constrain_bounded(self, regexp, lower, upper, warning=True): # def constrain_bounded(self, regexp, lower, upper, warning=True):
""" Set bounded constraints. """ # """ Set bounded constraints. """
self.constrain(regexp, transformations.Logistic(lower, upper), warning) # self.constrain(regexp, transformations.Logistic(lower, upper), warning)
#
def all_constrained_indices(self): # def all_constrained_indices(self):
if len(self.constrained_indices) or len(self.fixed_indices): # if len(self.constrained_indices) or len(self.fixed_indices):
return np.hstack(self.constrained_indices + self.fixed_indices) # return np.hstack(self.constrained_indices + self.fixed_indices)
else: # else:
return np.empty(shape=(0,)) # return np.empty(shape=(0,))
#
def constrain(self, regexp, transform, warning=True): # def constrain(self, regexp, transform, warning=True):
assert isinstance(transform, transformations.Transformation) # assert isinstance(transform, transformations.Transformation)
#
matches = self.grep_param_names(regexp) # matches = self.grep_param_names(regexp)
overlap = set(matches).intersection(set(self.all_constrained_indices())) # overlap = set(matches).intersection(set(self.all_constrained_indices()))
if overlap: # if overlap:
self.unconstrain(np.asarray(list(overlap))) # self.unconstrain(np.asarray(list(overlap)))
if warning: # if warning:
print 'Warning: re-constraining these parameters' # print 'Warning: re-constraining these parameters'
pn = self._get_param_names() # pn = self._get_param_names()
for i in overlap: # for i in overlap:
print pn[i] # print pn[i]
#
self.constrained_indices.append(matches) # self.constrained_indices.append(matches)
self.constraints.append(transform) # self.constraints.append(transform)
x = self._get_params() # x = self._get_params()
x[matches] = transform.initialize(x[matches]) # x[matches] = transform.initialize(x[matches])
self._set_params(x) # self._set_params(x)
#
def constrain_fixed(self, regexp, value=None, warning=True): # def constrain_fixed(self, regexp, value=None, warning=True):
""" # """
#
:param regexp: which parameters need to be fixed. # :param regexp: which parameters need to be fixed.
:type regexp: ndarray(dtype=int) or regular expression object or string # :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, # :param value: the vlaue to fix the parameters to. If the value is not specified,
the parameter is fixed to the current value # the parameter is fixed to the current value
:type value: float # :type value: float
#
**Notes** # **Notes**
#
Fixing a parameter which is tied to another, or constrained in some way will result in an error. # 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. # 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) # matches = self.grep_param_names(regexp)
overlap = set(matches).intersection(set(self.all_constrained_indices())) # overlap = set(matches).intersection(set(self.all_constrained_indices()))
if overlap: # if overlap:
self.unconstrain(np.asarray(list(overlap))) # self.unconstrain(np.asarray(list(overlap)))
if warning: # if warning:
print 'Warning: re-constraining these parameters' # print 'Warning: re-constraining these parameters'
pn = self._get_param_names() # pn = self._get_param_names()
for i in overlap: # for i in overlap:
print pn[i] # print pn[i]
#
self.fixed_indices.append(matches) # self.fixed_indices.append(matches)
if value != None: # if value != None:
self.fixed_values.append(value) # self.fixed_values.append(value)
else: # else:
self.fixed_values.append(self._get_params()[self.fixed_indices[-1]]) # self.fixed_values.append(self._get_params()[self.fixed_indices[-1]])
#
# self.fixed_values.append(value) # # self.fixed_values.append(value)
self._set_params_transformed(self._get_params_transformed()) # self._set_params_transformed(self._get_params_transformed())
#
def _get_params_transformed(self): # def _get_params_transformed(self):
"""use self._get_params to get the 'true' parameters of the model, which are then tied, constrained and fixed""" # """use self._get_params to get the 'true' parameters of the model, which are then tied, constrained and fixed"""
x = self._get_params() # x = self._get_params()
[np.put(x, i, t.finv(x[i])) for i, t in zip(self.constrained_indices, self.constraints)] # [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] # to_remove = self.fixed_indices + [t[1:] for t in self.tied_indices]
if len(to_remove): # if len(to_remove):
return np.delete(x, np.hstack(to_remove)) # return np.delete(x, np.hstack(to_remove))
else: # else:
return x # return x
#
def _set_params_transformed(self, 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""" # """ 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)) # self._set_params(self._untransform_params(x))
#
def _untransform_params(self, x): # def _untransform_params(self, x):
""" # """
The Transformation required for _set_params_transformed. # The Transformation required for _set_params_transformed.
#
This moves the vector x seen by the optimiser (unconstrained) to the # This moves the vector x seen by the optimiser (unconstrained) to the
valid parameter vector seen by the model # valid parameter vector seen by the model
#
Note: # Note:
- This function is separate from _set_params_transformed for downstream flexibility # - This function is separate from _set_params_transformed for downstream flexibility
""" # """
# work out how many places are fixed, and where they are. tricky logic! # # 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] # fix_places = self.fixed_indices + [t[1:] for t in self.tied_indices]
if len(fix_places): # if len(fix_places):
fix_places = np.hstack(fix_places) # fix_places = np.hstack(fix_places)
Nfix_places = fix_places.size # Nfix_places = fix_places.size
else: # else:
Nfix_places = 0 # Nfix_places = 0
#
free_places = np.setdiff1d(np.arange(Nfix_places + x.size, dtype=np.int), fix_places) # free_places = np.setdiff1d(np.arange(Nfix_places + x.size, dtype=np.int), fix_places)
#
# put the models values in the vector xx # # put the models values in the vector xx
xx = np.zeros(Nfix_places + free_places.size, dtype=np.float64) # xx = np.zeros(Nfix_places + free_places.size, dtype=np.float64)
#
xx[free_places] = x # 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 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, 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)] # [np.put(xx, i, t.f(xx[i])) for i, t in zip(self.constrained_indices, self.constraints)]
if hasattr(self, 'debug'): # if hasattr(self, 'debug'):
stop # @UndefinedVariable # stop # @UndefinedVariable
#
return xx # return xx
#
def _get_param_names_transformed(self): # def _get_param_names_transformed(self):
""" # """
Returns the parameter parameter_names as propagated after constraining, # Returns the parameter parameter_names as propagated after constraining,
tying or fixing, i.e. a list of the same length as _get_params_transformed() # tying or fixing, i.e. a list of the same length as _get_params_transformed()
""" # """
n = self._get_param_names() # n = self._get_param_names()
#
# remove/concatenate the tied parameter parameter_names # # remove/concatenate the tied parameter parameter_names
if len(self.tied_indices): # if len(self.tied_indices):
for t in self.tied_indices: # for t in self.tied_indices:
n[t[0]] = "<tie>".join([n[tt] for tt in t]) # n[t[0]] = "<tie>".join([n[tt] for tt in t])
remove = np.hstack([t[1:] for t in self.tied_indices]) # remove = np.hstack([t[1:] for t in self.tied_indices])
else: # else:
remove = np.empty(shape=(0,), dtype=np.int) # remove = np.empty(shape=(0,), dtype=np.int)
#
# also remove the fixed params # # also remove the fixed params
if len(self.fixed_indices): # if len(self.fixed_indices):
remove = np.hstack((remove, np.hstack(self.fixed_indices))) # remove = np.hstack((remove, np.hstack(self.fixed_indices)))
#
# add markers to show that some variables are constrained # # add markers to show that some variables are constrained
for i, t in zip(self.constrained_indices, self.constraints): # for i, t in zip(self.constrained_indices, self.constraints):
for ii in i: # for ii in i:
n[ii] = n[ii] + t.__str__() # n[ii] = n[ii] + t.__str__()
#
n = [nn for i, nn in enumerate(n) if not i in remove] # n = [nn for i, nn in enumerate(n) if not i in remove]
return n # return n
#
#@property # #@property
#def all(self): # #def all(self):
# return self.__str__(self._get_param_names()) # # return self.__str__(self._get_param_names())
#
#
#def __str__(self, parameter_names=None, nw=30): # #def __str__(self, parameter_names=None, nw=30):
def __str__(self, nw=30): # def __str__(self, nw=30):
""" # """
Return a string describing the parameter parameter_names and their ties and constraints # Return a string describing the parameter parameter_names and their ties and constraints
""" # """
parameter_names = self._get_param_names() # parameter_names = self._get_param_names()
#if parameter_names is None: # #if parameter_names is None:
# parameter_names = self._get_print_names() # # parameter_names = self._get_print_names()
#name_indices = self.grep_param_names("|".join(parameter_names)) # #name_indices = self.grep_param_names("|".join(parameter_names))
N = len(parameter_names) # N = len(parameter_names)
#
if not N: # if not N:
return "This object has no free parameters." # return "This object has no free parameters."
header = ['Name', 'Value', 'Constraints', 'Ties'] # header = ['Name', 'Value', 'Constraints', 'Ties']
values = self._get_params() # map(str,self._get_params()) # values = self._get_params() # map(str,self._get_params())
#values = self._get_params()[name_indices] # map(str,self._get_params()) # #values = self._get_params()[name_indices] # map(str,self._get_params())
# sort out the constraints # # sort out the constraints
constraints = [''] * len(parameter_names) # constraints = [''] * len(parameter_names)
#constraints = [''] * len(self._get_param_names()) # #constraints = [''] * len(self._get_param_names())
for i, t in zip(self.constrained_indices, self.constraints): # for i, t in zip(self.constrained_indices, self.constraints):
for ii in i: # for ii in i:
constraints[ii] = t.__str__() # constraints[ii] = t.__str__()
for i in self.fixed_indices: # for i in self.fixed_indices:
for ii in i: # for ii in i:
constraints[ii] = 'Fixed' # constraints[ii] = 'Fixed'
# sort out the ties # # sort out the ties
ties = [''] * len(parameter_names) # ties = [''] * len(parameter_names)
for i, tie in enumerate(self.tied_indices): # for i, tie in enumerate(self.tied_indices):
for j in tie: # for j in tie:
ties[j] = '(' + str(i) + ')' # ties[j] = '(' + str(i) + ')'
#
if values.size == 1: # if values.size == 1:
values = ['%.4f' %float(values)] # values = ['%.4f' %float(values)]
else: # else:
values = ['%.4f' % float(v) for v in values] # 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_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_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_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])]) # 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 # cols = np.array([max_names, max_values, max_constraint, max_ties]) + 4
# columns = cols.sum() # # columns = cols.sum()
#
header_string = ["{h:^{col}}".format(h=header[i], col=cols[i]) for i in range(len(cols))] # 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]) # header_string = map(lambda x: '|'.join(x), [header_string])
separator = '-' * len(header_string[0]) # 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))] # 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' # return ('\n'.join([header_string[0], separator] + param_string)) + '\n'
#
def grep_model(self,regexp): # def grep_model(self,regexp):
regexp_indices = self.grep_param_names(regexp) # regexp_indices = self.grep_param_names(regexp)
all_names = self._get_param_names() # all_names = self._get_param_names()
#
parameter_names = [all_names[pj] for pj in regexp_indices] # parameter_names = [all_names[pj] for pj in regexp_indices]
N = len(parameter_names) # N = len(parameter_names)
#
if not N: # if not N:
return "Match not found." # return "Match not found."
#
header = ['Name', 'Value', 'Constraints', 'Ties'] # header = ['Name', 'Value', 'Constraints', 'Ties']
all_values = self._get_params() # all_values = self._get_params()
values = np.array([all_values[pj] for pj in regexp_indices]) # values = np.array([all_values[pj] for pj in regexp_indices])
constraints = [''] * len(parameter_names) # constraints = [''] * len(parameter_names)
#
_constrained_indices,aux = self._pick_elements(regexp_indices,self.constrained_indices) # _constrained_indices,aux = self._pick_elements(regexp_indices,self.constrained_indices)
_constraints_ = [self.constraints[pj] for pj in aux] # _constraints_ = [self.constraints[pj] for pj in aux]
#
for i, t in zip(_constrained_indices, _constraints_): # for i, t in zip(_constrained_indices, _constraints_):
for ii in i: # for ii in i:
iii = regexp_indices.tolist().index(ii) # iii = regexp_indices.tolist().index(ii)
constraints[iii] = t.__str__() # constraints[iii] = t.__str__()
#
_fixed_indices,aux = self._pick_elements(regexp_indices,self.fixed_indices) # _fixed_indices,aux = self._pick_elements(regexp_indices,self.fixed_indices)
for i in _fixed_indices: # for i in _fixed_indices:
for ii in i: # for ii in i:
iii = regexp_indices.tolist().index(ii) # iii = regexp_indices.tolist().index(ii)
constraints[ii] = 'Fixed' # constraints[ii] = 'Fixed'
#
_tied_indices,aux = self._pick_elements(regexp_indices,self.tied_indices) # _tied_indices,aux = self._pick_elements(regexp_indices,self.tied_indices)
ties = [''] * len(parameter_names) # ties = [''] * len(parameter_names)
for i,ti in zip(_tied_indices,aux): # for i,ti in zip(_tied_indices,aux):
for ii in i: # for ii in i:
iii = regexp_indices.tolist().index(ii) # iii = regexp_indices.tolist().index(ii)
ties[iii] = '(' + str(ti) + ')' # ties[iii] = '(' + str(ti) + ')'
#
if values.size == 1: # if values.size == 1:
values = ['%.4f' %float(values)] # values = ['%.4f' %float(values)]
else: # else:
values = ['%.4f' % float(v) for v in values] # 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_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_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_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])]) # 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 # 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 = ["{h:^{col}}".format(h=header[i], col=cols[i]) for i in range(len(cols))]
header_string = map(lambda x: '|'.join(x), [header_string]) # header_string = map(lambda x: '|'.join(x), [header_string])
separator = '-' * len(header_string[0]) # 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))] # 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 header_string[0]
print separator # print separator
for string in param_string: # for string in param_string:
print string # print string
#
def _pick_elements(self,regexp_ind,array_list): # def _pick_elements(self,regexp_ind,array_list):
"""Removes from array_list the elements different from regexp_ind""" # """Removes from array_list the elements different from regexp_ind"""
new_array_list = [] #New list with elements matching 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_indices = [] #Indices that matches the arrays in new_array_list and array_list
#
array_index = 0 # array_index = 0
for array in array_list: # for array in array_list:
_new = [] # _new = []
for ai in array: # for ai in array:
if ai in regexp_ind: # if ai in regexp_ind:
_new.append(ai) # _new.append(ai)
if len(_new): # if len(_new):
new_array_list.append(np.array(_new)) # new_array_list.append(np.array(_new))
array_indices.append(array_index) # array_indices.append(array_index)
array_index += 1 # array_index += 1
return new_array_list, array_indices # return new_array_list, array_indices

View file

@ -17,12 +17,16 @@ except ImportError:
class KernelTests(unittest.TestCase): class KernelTests(unittest.TestCase):
def test_kerneltie(self): def test_kerneltie(self):
K = GPy.kern.rbf(5, ARD=True) 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() 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) X = np.random.rand(5,5)
Y = np.ones((5,1)) Y = np.ones((5,1))
m = GPy.models.GPRegression(X,Y,K) 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() import ipdb;ipdb.set_trace()
self.assertTrue(m.checkgrad()) self.assertTrue(m.checkgrad())
@ -117,5 +121,14 @@ class KernelTests(unittest.TestCase):
if __name__ == "__main__": if __name__ == "__main__":
print "Running unit tests, please be (very) patient..." K = GPy.kern.rbf(5, ARD=True)
unittest.main() 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()