From 152f61acf00ec02948ebf5ca3d03454a55f8fc50 Mon Sep 17 00:00:00 2001 From: Max Zwiessele Date: Fri, 11 Oct 2013 16:44:34 +0100 Subject: [PATCH] parameterized first beta test --- GPy/core/parameter.py | 568 +++++++++++++++++++++++++----------------- 1 file changed, 346 insertions(+), 222 deletions(-) diff --git a/GPy/core/parameter.py b/GPy/core/parameter.py index a2ac2bb2..21872a32 100644 --- a/GPy/core/parameter.py +++ b/GPy/core/parameter.py @@ -7,7 +7,8 @@ import re import itertools import numpy from GPy.core.transformations import Logexp, NegativeLogexp -from GPy.core.index_operations import ParameterIndexOperations +from GPy.core.index_operations import ConstraintIndexOperations,\ + create_raveled_indices, index_empty, TieIndexOperations from types import FunctionType from re import compile, _pattern_type _index_re = re.compile('(?:_(\d+))+') # pattern match for indices @@ -17,319 +18,440 @@ __constraints_name__ = "Constraint" __index_name__ = "Index" __tie_name__ = "Tied to" __precision__ = numpy.get_printoptions()['precision'] # numpy printing precision used, sublassing numpy ndarray after all +__fixed__ = "fixed" ###### -def translate_param_names_to_parameters(param_names): - """ - Naive translation from _get_param_names return to Parameterized object. - Assumptions: - - array indices are at the and matching _\d+_\d+... - - names are in order and names match field names - """ - - class Parameterized(object): + """ + Parameterized class + + Say m is a handle to a parameterized class. + + Printing parameters: + + >>> print m + # prints a nice summary over all parameters + >>> print m.name + # prints all the parameters which start with name + + Getting and setting parameters: + + Two ways to get parameters: + + - m.name regular expression matches all parameters beginning with name + - m['name'] regular expression matches all parameters with name + + Handling of constraining, fixing and tieing parameters together: + + + + """ def __init__(self, parameterlist, prefix=None, *args, **kwargs): + self._init = True self._params = [] for p in parameterlist: if isinstance(p, Parameterized): self._params.extend(p._params) else: self._params.append(p) - sizes = numpy.cumsum([0] + self.sizes) - self._param_slices = itertools.starmap(lambda start,stop: slice(start, stop), zip(sizes, sizes[1:])) - for p in self._params: + self._constraints = ConstraintIndexOperations() + self._ties = TieIndexOperations(self) + self._fixes = ConstraintIndexOperations + self._ties_fixes = None + self._connect_parameters() + self._init = False + def _connect_parameters(self): + sizes = numpy.cumsum([0] + self.parameter_sizes) + self._param_slices = [slice(start, stop) for start,stop in zip(sizes, sizes[1:])] + for i, p in enumerate(self._params): p._parent = self - self.__setattr__(p.name, p) + p._parent_index = i + not_unique = [] + if p.name in self.__dict__: + not_unique.append(p.name) + del self.__dict__[p.name] + elif not (p.name in not_unique): + self.__dict__[p.name] = p + + #=========================================================================== + # Optimization handling: + #=========================================================================== def _get_params(self): - return numpy.hstack([x._get_params() for x in self._params])#numpy.fromiter(itertools.chain(*itertools.imap(lambda x: x._get_params(), self._params)), dtype=numpy.float64, count=sum(self.sizes)) + return numpy.hstack([x._get_params() for x in self._params])#numpy.fromiter(itertools.chain(*itertools.imap(lambda x: x._get_params(), self._params)), dtype=numpy.float64, count=sum(self.parameter_sizes)) def _set_params(self, params): [p._set_params(params[s]) for p,s in itertools.izip(self._params,self._param_slices)] def _get_params_transformed(self): - return numpy.hstack([x._get_params_transformed() for x in self._params]) - return numpy.fromiter(itertools.chain(*itertools.imap(lambda x: x._get_params_transformed(), self._params)), dtype=numpy.float64, count=self.num_params_transformed) + p = self._get_params() + [numpy.put(p, ind, c.finv(p[ind])) for c,ind in self._constraints.iteritems() if c is not __fixed__] + if self._ties_fixes is not None: + return p[self._ties_fixes] + return p - def _set_params_transformed(self, params): - current_index = 0 - for p in self._params: - s = p.num_params_transformed - p._set_params_transformed(params[current_index:current_index+s]) - current_index += s - self._handle_ties() - + 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 + [numpy.put(p, ind, c.f(p[ind])) for c,ind in self._constraints.iteritems() if c is not __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): - [p._handle_ties() for p in self._params] - - def __getitem__(self, name): - return self.__getattr__(name) - + if not self._init: + self._set_params_transformed(self._get_params_transformed()) + #=========================================================================== + # Index Handling + #=========================================================================== + def _backtranslate_index(self, param, ind): + ind = ind-self._offset(param) + ind = ind[ind >= 0] + internal_offset = (numpy.arange(param._realsize).reshape(param._realshape)[param._current_slice]).flat[0] + ind = ind[ind < param.size + internal_offset] + return ind - internal_offset + #=========================================================================== + # Handle ties: + #=========================================================================== + def _add_tie(self, param, tied_to): + try: + param[...] = tied_to + except ValueError: + raise ValueError("Trying to tie {} with shape {} to {} with shape {}".format(self.name, self.shape, param.name, param.shape)) + self._ties.add(param, tied_to) + if self._ties_fixes is None: self._ties_fixes = numpy.ones(self.parameter_size, dtype=bool) + f = create_raveled_indices(param._current_slice, param._realshape, self._offset(param)) + self._ties_fixes[f] = False + def _remove_tie(self, param, *params): + if len(params) == 0: + params = self._ties.properties() + for p in self._ties.properties(): + if any((p == x).all() for x in params): + ind = create_raveled_indices(p._current_slice, param._realshape, self._offset(param)) + self._ties.remove(param, p) + self._ties_fixes[ind] = True + if numpy.all(self._ties_fixes): self._ties_fixes = None + def _ties_iter_items(self, param): + for tied_to, ind in self._ties.iter_from_items(): + ind = self._backtranslate_index(param, ind) + if not index_empty(ind): + yield tied_to, ind + def _ties_iter(self, param): + for constr, _ in self._ties_iter_items(param): + yield constr + def _ties_iter_indices(self, param): + for _, ind in self._ties_iter_items(param): + yield ind + #=========================================================================== + # Fixing parameters: + #=========================================================================== + def _fix(self, param): + reconstrained = self._remove_constrain(param, None) + if any(reconstrained): + # to print whole params: m = str(param[self._backtranslate_index(param, reconstrained)]) + m = param.name + if param._realsize > 1: + m += str("".join(map(str,param._indices()[self._backtranslate_index(param, reconstrained)]))) + print "Reconstrained parameters:\n{}".format(m) + self._constraints.add(__fixed__, param._current_slice, param._realshape, self._offset(param)) + if self._ties_fixes is None: self._ties_fixes = numpy.ones(self.parameter_size, dtype=bool) + f = create_raveled_indices(param._current_slice, param._realshape, self._offset(param)) + self._ties_fixes[f] = False + def _unfix(self, param): + self._remove_constrain(param, __fixed__) + ind = create_raveled_indices(p._current_slice, param._realshape, self._offset(param)) + self._ties_fixes[ind] = True + if numpy.all(self._ties_fixes): self._ties_fixes = None + #=========================================================================== + # Constraint Handling: + #=========================================================================== + def constrain(self, regexp, constraint): + self[regexp].constrain(constraint) + def _offset(self, param): + # offset = reduce(lambda a, b:a + (b.stop - b.start), self._param_slices[:param._parent_index], 0) + return self._param_slices[param._parent_index].start + def _add_constrain(self, param, transform): + reconstrained = self._remove_constrain(param, None) + self._constraints.add(transform, param._current_slice, param._realshape, self._offset(param)) + if any(reconstrained): + # to print whole params: m = str(param[self._backtranslate_index(param, reconstrained)]) + m = param.name + str("".join(map(str,param._indices()[self._backtranslate_index(param, reconstrained)]))) + print "Reconstrained parameters:\n{}".format(m) + def _remove_constrain(self, param, transforms): + if transforms is None: + transforms = self._constraints.properties() + elif not isinstance(transforms, (tuple, list, numpy.ndarray)): + transforms = [transforms] + removed_indices = numpy.array([]).astype(int) + for constr in transforms: + removed = self._constraints.remove(constr, param._current_slice, param._realshape, self._offset(param)) + removed_indices = numpy.union1d(removed_indices, removed) + return removed_indices + def _constraints_iter_items(self, param): + for constr, ind in self._constraints.iteritems(): + ind = self._backtranslate_index(param, ind) + if not index_empty(ind): + yield constr, ind + def _constraints_iter(self, param): + for constr, _ in self._constraints_iter_items(param): + yield constr + def _contraints_iter_indices(self, param): + for _, ind in self._constraints_iter_items(param): + yield ind + def _constraint_indices(self, param, constraint): + return self._backtranslate_index(param, self._constraints[constraint]) + #=========================================================================== + # Get/set parameters: + #=========================================================================== def grep_param_names(self, regexp): - if not isinstance(regexp, _pattern_type): - regexp = compile(regexp) - paramlist = [param for param in self._params if regexp.search(param.name) is not None] + if not isinstance(regexp, _pattern_type): regexp = compile(regexp) + paramlist = [param for param in self._params if regexp.match(param.name) is not None] return paramlist - - def tie_params(self, regexp): - paramlist = self.grep_param_names(regexp) - - - def __getattr__(self, name, *args, **kwargs): - if name in self.__dict__: - return object.__getattribute__(self, name, *args, **kwargs) - else: + def __getitem__(self, name, paramlist=None): + if paramlist is None: paramlist = self.grep_param_names(name) - if len(paramlist) < 1: - raise AttributeError("'{:s}' object has no attribute '{:s}'".format(str(self.__class__.__name__), name)) - if len(paramlist) == 1: - return paramlist[-1] - return ParamConcatenation(paramlist) - + if len(paramlist) < 1: raise AttributeError, name + if len(paramlist) == 1: return paramlist[-1] + return ParamConcatenation(paramlist) + def __setitem__(self, name, value, paramlist=None): + try: param = self.__getitem__(name, paramlist) + except AttributeError as a: raise a + param[...] = value + def __getattr__(self, name, *args, **kwargs): + return self.__getitem__(name) + def __setattr__(self, name, val): + if hasattr(self, "_params"): + paramlist = self.grep_param_names(name) + if len(paramlist) > 1: object.__setattr__(self, name, val); return# raise AttributeError("Non-unique params identified {}".format([p.name for p in paramlist])) + if len(paramlist) == 1: self.__setitem__(name, val, paramlist); return + object.__setattr__(self, name, val); + #=========================================================================== + # Printing: + #=========================================================================== @property def names(self): return [x.name for x in self._params] @property - def size(self): - return sum(self.sizes) + def parameter_size(self): + return sum(self.parameter_sizes) @property - def sizes(self): + def parameter_sizes(self): return [x.size for x in self._params] @property - def sizes_transformed(self): - return [x.num_params_transformed() for x in self._params] + def parameter_size_transformed(self): + return sum(self._ties_fixes) @property - def num_params_transformed(self): - return reduce(lambda a,b: a+b.num_params_transformed, self._params, 0) - @property - def constraints(self): - return [x.constraints for x in self._params] - @property - def shapes(self): + def parameter_shapes(self): return [x.shape for x in self._params] @property def _constrs(self): - return [x._constr for x in self._params] + return [p._constr for p in self._params] @property def _descs(self): return [x._desc for x in self._params] - + @property + def _ts(self): + return [x._t for x in self._params] def __str__(self, header=True): nl = max([len(str(x)) for x in self.names + ["Name"]]) sl = max([len(str(x)) for x in self._descs + ["Value"]]) cl = max([len(str(x)) if x else 0 for x in self._constrs + ["Constraint"]]) - format_spec = " \033[1m{{self.name:^{0}s}}\033[0;0m | {{self._desc:^{1}s}} | {{self._constr:^{2}s}} ".format(nl, sl, cl) + tl = max([len(str(x)) if x else 0 for x in self._ts + ["Tied to"]]) + format_spec = " \033[1m{{p.name:^{0}s}}\033[0;0m | {{p._desc:^{1}s}} | {{p._constr:^{2}s}} | {{p._t:^{2}s}}".format(nl, sl, cl) + to_print = [format_spec.format(p=p) for p in self._params] + sep = '-'*len(to_print[0]) if header: - header = " {{0:^{0}s}} | {{1:^{1}s}} | {{2:^{2}s}} ".format(nl, sl, cl).format("Name", "Value", "Constraint") - header += '\n' + '-'*len(header) - return '\n'.join([header]+[x.__str__(format_spec=format_spec) for x in self._params]) - return '\n'.join([x.__str__(format_spec=format_spec) for x in self._params]) + header = " {{0:^{0}s}} | {{1:^{1}s}} | {{2:^{2}s}} | {{3:^{3}s}}".format(nl, sl, cl, tl).format("Name", "Value", "Constraint", "Tied to") + header += '\n' + sep + to_print.insert(0, header) + return '\n'.format(sep).join(to_print) pass class Param(numpy.ndarray): """ Parameter object for GPy models. - - - constraining singular entries: - - param[1:2,1:2].constrain{_..}() to keep the shape - - param[1,1,...].constrain{_..}(), which tells numpy to keep the shapes - - param[[1],[1]] to trigger advanced indexin, which keeps shapes - - this is because of numpy's ndarray functionality: - it gives back a float, when indexing singular entries - we want a Param object though, with the right shape - workarounds: - + - slice paramters and constrain the slices. - unconstrain slices - """ fixed = False # if this parameter is fixed - __array_priority__ = -1 # Only give back a param if slicing - - def __new__(cls, name, input_array, constraints=None, ties=None): + __array_priority__ = -1. # Allways give back Param + def __new__(cls, name, input_array): obj = numpy.atleast_1d(numpy.array(input_array)).view(cls) obj.name = name + obj._parent = None + obj._parent_index = None obj._current_slice = slice(None) obj._realshape = obj.shape - #obj.parameters = parameters - if constraints is None: - obj.constraints = ParameterIndexOperations(obj) - else: - obj.constraints = constraints - if ties is None: - obj.ties = ParameterIndexOperations(obj) - else: - obj.ties = ties - return obj - + obj._realsize = obj.size + return obj def __array_finalize__(self, obj): # see InfoArray.__array_finalize__ for comments if obj is None: return self.name = getattr(obj, 'name', None) - self._realshape = getattr(obj, '_realshape', None) - self.constraints = getattr(obj, 'constraints', None) - self._parent = getattr(obj, '_parent', None) - self.ties = getattr(obj, 'ties', None) self._current_slice = getattr(obj, '_current_slice', None) - - @property - def value(self): - return self#self.base[self._current_slice] - - @property - def _desc(self): - if self.size <= 1: - return "%f"%self.value - else: - return self.shape - @property - def _constr(self): - return ' '.join([str(c) if c else '' for c in self.constraints.properties()]) - + self._parent = getattr(obj, '_parent', None) + self._parent_index = getattr(obj, '_parent_index', None) + self._realshape = getattr(obj, '_realshape', None) + self._realsize = getattr(obj, '_realsize', None) + #=========================================================================== + # get/set parameters + #=========================================================================== def _set_params(self, param): - self.value.flat = param - + self.flat = param def _get_params(self): - return self.value.flat - - @property - def num_params_transformed(self): - return self.size - self.ties.size() - - def _get_params_transformed(self): - params = self.base.flatten() - [numpy.put(params, indices, constr.finv(params[indices])) for constr, indices in self.constraints.iteritems()] - return numpy.delete(params, reduce(numpy.union1d, self.ties.iterindices(), numpy.array([],dtype=self.dtype))) - - def _set_params_transformed(self, params): - numpy.put(self.base, numpy.setdiff1d(numpy.r_[:self.size],reduce(numpy.union1d, self.ties.iterindices(), numpy.array([],dtype=self.dtype))), params) - [numpy.put(self.base, indices, constraint.f(self.base.flat[indices])) for constraint, indices in self.constraints.iteritems()] - - def _handle_ties(self): - # handle all tied values - for tie, indices in self.ties.iteritems(): - self.base.flat[indices] = tie.flat - + return self.flat + #=========================================================================== + # Fixing Parameters: + #=========================================================================== + def constrain_fixed(self): + self._parent._fix(self) + def unconstrain_fixed(self): + self._parent._unfix(self) + #=========================================================================== + # Constrain operations -> done + #=========================================================================== + def constrain(self, transform): + self._parent._add_constrain(self, transform) + self[...] = transform.initialize(self) + def constrain_positive(self): + self.constrain(Logexp()) + def constrain_negative(self): + self.constrain(NegativeLogexp()) + def unconstrain(self, transforms=None): + self._parent._remove_constrain(self, transforms) + def unconstrain_positive(self): + self.unconstrain(Logexp()) + def unconstrain_negative(self): + self.unconstrain(NegativeLogexp()) + #=========================================================================== + # Tying operations -> done + #=========================================================================== def tie_to(self, param): assert isinstance(param, Param), "Argument {1} not of type {0}".format(Param,param.__class__) try: - rav_index = self.ties.create_raveled_indices(self._current_slice) - self.base.flat[rav_index] = param.flat - self.ties.add(param, self._current_slice) - self._handle_ties() + self[...] = param + self._parent._add_tie(self, param) except ValueError: - raise ValueError("Trying to tie params with shape {} to params with shape {}".format(self.shape, param.shape)) - + raise ValueError("Trying to tie {} with shape {} to {} with shape {}".format(self.name, self.shape, param.name, param.shape)) def untie(self, *params): if len(params) == 0: - params = self.ties.properties() - for p in self.ties.properties(): + params = self._parent._ties.properties() + for p in self._parent._ties.properties(): if any((p == x).all() for x in params): - self.ties.remove(p, self._current_slice) - - def constrain(self, transform): - self.constraints.add(transform, self._current_slice) - self[:] = transform.initialize(self) - - def constrain_positive(self): - self.constrain(Logexp()) - - def constrain_negative(self): - self.constrain(NegativeLogexp()) - - def unconstrain(self, transforms=None): - if transforms is None: - transforms = self.constraints.properties() - elif not isinstance(transforms, (tuple, list, numpy.ndarray)): - transforms = [transforms] - for constr in transforms: - self.constraints.remove(constr, self._current_slice) - - def unconstrain_positive(self): - self.unconstrain(Logexp()) - - def unconstrain_negative(self): - self.unconstrain(NegativeLogexp()) - + self._parent._remove_tie(self, params) + #=========================================================================== + # Array operations -> done + #=========================================================================== def __getitem__(self, s, *args, **kwargs): + if not isinstance(s, tuple): + s = (s,) + if not( Ellipsis in s): + s = (s + (Ellipsis,)) new_arr = numpy.ndarray.__getitem__(self, s, *args, **kwargs) - try: - new_arr._current_slice = s - except AttributeError: - # returning 0d array or float, double etc: - pass + try: new_arr._current_slice = s + except AttributeError: pass# returning 0d array or float, double etc 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) self._parent._handle_ties() - - def __repr__(self, *args, **kwargs): - return super(Param, self).__repr__(*args, **kwargs) - view = repr(self.value) + #=========================================================================== + # Printing -> + #=========================================================================== + @property + def _desc(self): + if self.size <= 1: return "%f"%self + else: return self.shape + @property + def _constr(self): + return ' '.join(map(lambda c: str(c[0]) if len(c[1])==self._realsize else "{"+str(c[0])+"}", self._parent._constraints_iter_items(self))) + @property + def _t(self): + # indices one by one: "".join(map(str,c[0]._indices())) + return ' '.join(map(lambda c: c[0].name if len(c[1])==self._realsize else c[0].name+"[...]", self._parent._ties_iter_items(self))) + def round(self, decimals=0, out=None): + view = super(Param, self).round(decimals, out).view(Param) + view.__array_finalize__(self) return view - + round.__doc__ = numpy.round.__doc__ + def __repr__(self, *args, **kwargs): + return "\033[1m{x:s}\033[0;0m:\n".format(x=self.name)+super(Param, self).__repr__(*args,**kwargs) def _constr_matrix_str(self): - constr_matrix = numpy.empty(self._realshape, dtype=object) # we need the whole constraints matrix + constr_matrix = numpy.empty(self._realsize, dtype=object) # we need the whole constraints matrix constr_matrix[:] = '' - for constr, indices in self.constraints.iteritems(): # put in all the constraints: - constr_matrix[indices] = numpy.vectorize(lambda x:" ".join([x, str(constr)]) if x else str(constr))(constr_matrix[indices]) - return constr_matrix.astype(numpy.string_)[self._current_slice] # and get the slice we did before + for constr, indices in self._parent._constraints_iter_items(self): # put in all the constraints: + cstr = ""+str(constr)+"" + constr_matrix[indices] = numpy.vectorize(lambda x:" ".join([x, cstr]) if x else cstr, otypes=[str])(constr_matrix[indices]) + return constr_matrix.astype(numpy.string_).reshape(self._realshape)[self._current_slice].flatten() # and get the slice we did before def _ties_matrix_str(self): - ties_matr = numpy.empty(self._realshape, dtype=object) # we need the whole constraints matrix + ties_matr = numpy.empty(self._realsize, dtype=object) # we need the whole constraints matrix ties_matr[:] = '' - for tie, indices in self.ties.iteritems(): # go through all ties: - tie_cycle = itertools.cycle(tie._indices()) + for tie, indices in self._parent._ties_iter_items(self): # go through all ties: + tie_cycle = itertools.cycle(tie._indices()) if tie._realsize > 1 else itertools.repeat('') ties_matr[indices] = numpy.vectorize(lambda x:" ".join([x, str(tie.name) + str(str(tie_cycle.next()))]) if x else str(tie.name)+str(str(tie_cycle.next())), otypes=[str])(ties_matr[indices]) - return ties_matr.astype(numpy.string_)[self._current_slice] # and get the slice we did before + return ties_matr.astype(numpy.string_).reshape(*(self._realshape+(-1,)))[self._current_slice] # and get the slice we did before def _indices(self): - return numpy.array(list(itertools.product(*itertools.imap(range, self._realshape))))[self.constraints.create_raveled_indices(self._current_slice),...] # find out which indices to print + flat_indices = numpy.array(list(itertools.product(*itertools.imap(range, self._realshape)))).reshape(self._realshape + (-1,)) + return flat_indices[self._current_slice].reshape(self.size, -1) # find out which indices to print def _max_len_names(self, constr_matrix, header): return max(reduce(lambda a, b:max(a, len(b)), constr_matrix.flat, 0), len(header)) def _max_len_values(self): - return max(reduce(lambda a, b:max(a, len("{x:=.{0}G}".format(__precision__, x=b))), self.value.flat, 0), len(self.name)) + return max(reduce(lambda a, b:max(a, len("{x:=.{0}G}".format(__precision__, x=b))), self.flat, 0), len(self.name)) def _max_len_index(self, ind): return max(reduce(lambda a, b:max(a, len(str(b))), ind, 0), len(__index_name__)) - def __str__(self, format_spec=None, constr_matrix=None, indices=None, ties=None, lc=None, lx=None, li=None, lt=None): - if format_spec is None: - if indices is None: - indices = self._indices() - if constr_matrix is None: - constr_matrix = self._constr_matrix_str() - if ties is None: - ties = self._ties_matrix_str() - if lc is None: - lc = self._max_len_names(constr_matrix, __constraints_name__) - if lx is None: - lx = self._max_len_values() - if li is None: - li = self._max_len_index(indices) - if lt is None: - lt = self._max_len_names(ties, __tie_name__) + def __str__(self, constr_matrix=None, indices=None, ties=None, lc=None, lx=None, li=None, lt=None): + try: + if indices is None: indices = self._indices() + if constr_matrix is None: constr_matrix = self._constr_matrix_str() + if ties is None: ties = self._ties_matrix_str() + if lc is None: lc = self._max_len_names(constr_matrix, __constraints_name__) + if lx is None: lx = self._max_len_values() + if li is None: li = self._max_len_index(indices) + if lt is None: lt = self._max_len_names(ties, __tie_name__) constr = constr_matrix.flat ties = ties.flat header = " {i:^{2}s} | \033[1m{x:^{1}s}\033[0;0m | {c:^{0}s} | {t:^{3}s}".format(lc,lx,li,lt, x=self.name, c=__constraints_name__, i=__index_name__, t=__tie_name__) # nice header for printing - return "\n".join([header]+[" {i:^{3}s} | {x: >{1}.{2}G} | {c:^{0}s} | {t:^{4}} ".format(lc,lx,__precision__,li,lt, x=x, c=constr.next(), t=ties.next(), i=i) for i,x in itertools.izip(indices,self.value.flat)]) # return all the constraints with right indices - return format_spec.format(self=self) - + return "\n".join([header]+[" {i:^{3}s} | {x: >{1}.{2}G} | {c:^{0}s} | {t:^{4}} ".format(lc,lx,__precision__,li,lt, x=x, c=constr.next(), t=ties.next(), i=i) for i,x in itertools.izip(indices,self.flat)]) # return all the constraints with right indices + except: return super(Param, self).__str__() class ParamConcatenation(object): def __init__(self, params): + """ + Parameter concatenation for convienience of printing regular expression matched arrays + you can index this concatenation as if it was the flattened concatenation + of all the parameters it contains, same for setting parameters + """ 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:])] def __getitem__(self, s): - raise AttributeError("Cannot index a concatenation of parameters!") + 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]])] + if len(params)==1: return params[0] + return ParamConcatenation(params) + def __setitem__(self, s, val): + 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)] + def _vals(self): + return numpy.hstack([p._get_params() for p in self.params]) def constrain(self, constraint): [param.constrain(constraint) for param in self.params] def constrain_positive(self): [param.constrain_positive() for param in self.params] + def constrain_fixed(self): + [param.constrain_fixed() for param in self.params] def constrain_negative(self): [param.constrain_negative() for param in self.params] def unconstrain(self, constraints=None): [param.unconstrain(constraints) for param in self.params] def unconstrain_negative(self): [param.unconstrain_negative() for param in self.params] + def unconstrain_positive(self): + [param.unconstrain_positive() for param in self.params] + def unconstrain_fixed(self): + [param.unconstrain_fixed() for param in self.params] def __str__(self, *args, **kwargs): constr_matrices = [p._constr_matrix_str() for p in self.params] ties_matrices = [p._ties_matrix_str() for p in self.params] @@ -338,21 +460,23 @@ class ParamConcatenation(object): lx = max([p._max_len_values() for p in self.params]) li = max([p._max_len_index(i) for p, i in itertools.izip(self.params, indices)]) lt = max([p._max_len_names(tm, __tie_name__) for p, tm in itertools.izip(self.params, ties_matrices)]) - return "\n".join([p.__str__(None, cm, i, tm, lc, lx, li, lt) for p, cm, i, tm in itertools.izip(self.params,constr_matrices,indices,ties_matrices)]) + strings = [p.__str__(cm, i, tm, lc, lx, li, lt) for p, cm, i, tm in itertools.izip(self.params,constr_matrices,indices,ties_matrices)] + return "\n{}\n".format(" -"+"- | -".join(['-'*l for l in [li,lx,lc,lt]])).join(strings) def __repr__(self): - return self.__str__() + return "\n".join(map(repr,self.params)) if __name__ == '__main__': - X = numpy.random.randn(100,8) - p = Param("X", X) - p1 = Param("X_variance", numpy.random.rand(*X.shape)) - p2 = Param("Y", numpy.random.randn(3,1)) - p3 = Param("rbf_variance", numpy.random.rand(1)) + X = numpy.random.randn(3,3,2) + p = Param("q_mean", X) + p1 = Param("q_variance", numpy.random.rand(*p.shape)) + p2 = Param("Y", numpy.random.randn(p.shape[0],1)) + p3 = Param("rbf_variance", numpy.random.rand()) p4 = Param("rbf_lengthscale", numpy.random.rand(2)) - params = Parameterized([p,p1,p2,p3,p4]) - params.rbf.constrain_positive() - params[".*variance"].constrain_positive() - params.rbf_l.tie_to(params.rbf_va) - pt = numpy.array(params._get_params_transformed()) - ptr = numpy.random.randn(*pt.shape) + m = Parameterized([p,p1,p2,p3,p4]) + m[".*variance"].constrain_positive() + m.rbf.constrain_positive() + m.q_v.tie_to(m.rbf_v) +# m.rbf_l.tie_to(m.rbf_va) + # pt = numpy.array(params._get_params_transformed()) + # ptr = numpy.random.randn(*pt.shape) # params.X.tie_to(params.rbf_v) \ No newline at end of file