From 95b07146ac521c5a364562a05fd6df214c8790ce Mon Sep 17 00:00:00 2001 From: Max Zwiessele Date: Thu, 17 Oct 2013 14:33:41 +0100 Subject: [PATCH] WARNING: half way through commit, this is a non working middle thing! everything should be in place now, figure tieing and printing with broadcasting --- GPy/core/index_operations.py | 57 +++++++------- GPy/core/parameter.py | 146 +++++++++++++++-------------------- GPy/core/parameterized.py | 89 ++++++++++++++------- 3 files changed, 155 insertions(+), 137 deletions(-) diff --git a/GPy/core/index_operations.py b/GPy/core/index_operations.py index 15bea339..020fd9ef 100644 --- a/GPy/core/index_operations.py +++ b/GPy/core/index_operations.py @@ -6,19 +6,23 @@ Created on Oct 2, 2013 import numpy from numpy.lib.function_base import vectorize from parameter import Param +from collections import defaultdict -class ParamDict(dict): +class ParamDict(defaultdict): + def __init__(self): + defaultdict.__init__(self, lambda: numpy.array([], dtype=int)) + def __getitem__(self, key): try: - return super(ParamDict, self).__getitem__(key) + return defaultdict.__getitem__(self, key) except KeyError: for a in self.iterkeys(): if numpy.all(a==key) and a._parent_index_==key._parent_index_: - return super(ParamDict, self).__getitem__(a) + return defaultdict.__getitem__(self, a) raise def __contains__(self, key): - if super(ParamDict, self).__contains__(key): + if defaultdict.__contains__(self, key): return True for a in self.iterkeys(): if numpy.all(a==key) and a._parent_index_==key._parent_index_: @@ -31,7 +35,7 @@ class ParamDict(dict): if numpy.all(a==key) and a._parent_index_==key._parent_index_: return super(ParamDict, self).__setitem__(a, value) raise KeyError, key - super(ParamDict, self).__setitem__(key, value) + defaultdict.__setitem__(self, key, value) class ParameterIndexOperations(object): @@ -84,23 +88,16 @@ class ParameterIndexOperations(object): # yield already_seen[ni] return vectorize(lambda i: [prop for prop in self.iter_properties() if i in self._properties[prop]], otypes=[list])(index) - def add(self, prop, indices, shape, offset=False): - ind = create_raveled_indices(indices, shape, offset) - #[self._reverse[i].__add__(prop) for i in ind] + def add(self, prop, indices): try: - self._properties[prop] = combine_indices(self._properties[prop], ind) + self._properties[prop] = combine_indices(self._properties[prop], indices) except KeyError: -# for a in self.properties(): -# if numpy.all(a==prop) and a._parent_index_ == prop._parent_index_: -# self._properties[a] = combine_indices(self._properties[a], ind) -# return - self._properties[prop] = ind + self._properties[prop] = indices - def remove(self, prop, indices, shape, offset=False): + def remove(self, prop, indices): if prop in self._properties: - ind = create_raveled_indices(indices, shape, offset) - diff = remove_indices(self[prop], ind) - removed = numpy.intersect1d(self[prop], ind, True) + diff = remove_indices(self[prop], indices) + removed = numpy.intersect1d(self[prop], indices, True) if not index_empty(diff): self._properties[prop] = diff else: @@ -129,11 +126,17 @@ class TieIndexOperations(object): self.tied_from = ParameterIndexOperations() self.tied_to = ParameterIndexOperations() def add(self, tied_from, tied_to): - self.tied_from.add(tied_to, tied_from._current_slice_, tied_from._realshape_, self.params._offset(tied_from)) - self.tied_to.add(tied_to, tied_to._current_slice_, tied_to._realshape_, self.params._offset(tied_to)) + rav_from = self.params._raveled_index_for(tied_from) + rav_to = self.params._raveled_index_for(tied_to) + self.tied_from.add(tied_to, rav_from) + self.tied_to.add(tied_to, rav_to) + return rav_from, rav_to def remove(self, tied_from, tied_to): - self.tied_from.remove(tied_to, tied_from._current_slice_, tied_from._realshape_, self.params._offset(tied_from)) - self.tied_to.remove(tied_to, tied_to._current_slice_, tied_to._realshape_, self.params._offset(tied_to)) + rav_from = self.params._raveled_index_for(tied_from) + rav_to = self.params._raveled_index_for(tied_to) + self.tied_from.remove(tied_to, rav_from) + self.tied_to.remove(tied_to, rav_to) + return rav_from, rav_to def from_to_for(self, index): return self.tied_from.properties_for(index), self.tied_to.properties_for(index) def iter_from_to_indices(self): @@ -153,11 +156,11 @@ class TieIndexOperations(object): def from_to_indices(self, param): return self.tied_from[param], self.tied_to[param] -def create_raveled_indices(index, shape, offset=0): - if isinstance(index, (tuple, list)): i = [slice(None)] + list(index) - else: i = [slice(None), index] - ind = numpy.array(numpy.ravel_multi_index(numpy.indices(shape)[i], shape)).flat + numpy.int_(offset) - return ind +# def create_raveled_indices(index, shape, offset=0): +# if isinstance(index, (tuple, list)): i = [slice(None)] + list(index) +# else: i = [slice(None), index] +# ind = numpy.array(numpy.ravel_multi_index(numpy.indices(shape)[i], shape)).flat + numpy.int_(offset) +# return ind def combine_indices(arr1, arr2): return numpy.union1d(arr1, arr2) diff --git a/GPy/core/parameter.py b/GPy/core/parameter.py index f731f151..e5f6b706 100644 --- a/GPy/core/parameter.py +++ b/GPy/core/parameter.py @@ -5,9 +5,7 @@ Created on 4 Sep 2013 ''' import itertools import numpy -from transformations import Logexp, NegativeLogexp -from GPy.core.transformations import Logistic -import collections +from transformations import Logexp, NegativeLogexp, Logistic ###### printing __constraints_name__ = "Constraint" @@ -23,7 +21,9 @@ class Param(numpy.ndarray): :param name: name of the parameter to be printed :param input_array: array which this parameter handles :param gradient: callable with one argument, which is the model of this parameter - + :param args: additional arguments to gradient + :param kwargs: additional keyword arguments to gradient + You can add/remove constraints by calling the constrain on the parameter itself, e.g: - self[:,1].constrain_positive() @@ -38,17 +38,17 @@ class Param(numpy.ndarray): See :py:class:`GPy.core.parameterized.Parameterized` for more details. """ __array_priority__ = -numpy.inf # Never give back Param - def __new__(cls, name, input_array, gradient): + def __new__(cls, name, input_array, gradient, *args, **kwargs): obj = numpy.atleast_1d(numpy.array(input_array)).view(cls) obj._name_ = name obj._parent_ = None obj._parent_index_ = None obj._gradient_ = gradient - obj._current_slice_ = [slice(obj.shape[0])] + obj._current_slice_ = (slice(obj.shape[0]),) obj._realshape_ = obj.shape obj._realsize_ = obj.size obj._realndim_ = obj.ndim - obj._flat_indices_ = None + obj._original_ = True return obj def __array_finalize__(self, obj): # see InfoArray.__array_finalize__ for comments @@ -57,11 +57,11 @@ class Param(numpy.ndarray): self._current_slice_ = getattr(obj, '_current_slice_', None) self._parent_ = getattr(obj, '_parent_', None) self._parent_index_ = getattr(obj, '_parent_index_', None) - self._flat_indices_ = getattr(obj, '_flat_indices_', None) self._gradient_ = getattr(obj, '_gradient_', None) self._realshape_ = getattr(obj, '_realshape_', None) self._realsize_ = getattr(obj, '_realsize_', None) self._realndim_ = getattr(obj, '_realndim_', None) + self._original_ = getattr(obj, '_original_', None) def __array_wrap__(self, out_arr, context=None): return out_arr.view(numpy.ndarray) #=========================================================================== @@ -131,6 +131,12 @@ class Param(numpy.ndarray): self._parent_._unfix(self) unfix = unconstrain_fixed #=========================================================================== + # Convenience methods: + #=========================================================================== + @property + def is_fixed(self): + return self._parent_._is_fixed(self) + #=========================================================================== # Constrain operations -> done #=========================================================================== def constrain(self, transform, warning=True): @@ -142,7 +148,10 @@ class Param(numpy.ndarray): Constrain the parameter to the given :py:class:`GPy.core.transformations.Transformation`. """ - self[...] = transform.initialize(self) + if self._original_: # this happens when indexing created a copy of the array + self.__setitem__(slice(None), transform.initialize(self)) + else: + self._parent_._get_original(self)[self._current_slice_] = transform.initialize(self) self._parent_._add_constrain(self, transform, warning) def constrain_positive(self, warning=True): """ @@ -205,12 +214,14 @@ class Param(numpy.ndarray): """ assert isinstance(param, Param), "Argument {1} not of type {0}".format(Param,param.__class__) try: - self[...] = param - self._parent_._add_tie(self, param) - if self.base is None: # this happens when indexing created a copy of the array - self._parent_._handle_ties() + if self._original_: # this happens when indexing created a copy of the array + self[:] = param + else: + self._parent_._get_original(self)[self._current_slice_] = param except ValueError: raise ValueError("Trying to tie {} with shape {} to {} with shape {}".format(self.name, self.shape, param.name, param.shape)) + self._parent_._add_tie(self, param) + def untie(self, *params): """ :param params: parameters to untie from @@ -219,7 +230,7 @@ class Param(numpy.ndarray): """ if len(params) == 0: params = self._parent_._ties_.properties() - self._parent_._remove_tie(self, params) + self._parent_._remove_tie(self, *params) #=========================================================================== # Prior Operations #=========================================================================== @@ -248,7 +259,7 @@ class Param(numpy.ndarray): if not reduce(lambda a,b: a or numpy.any(b is Ellipsis), s, False): s += (Ellipsis,) new_arr = numpy.ndarray.__getitem__(self, s, *args, **kwargs) - try: new_arr._current_slice_ = s + try: new_arr._current_slice_ = s; new_arr._original_ = self.base is new_arr.base except AttributeError: pass# returning 0d array or float, double etc return new_arr def __getslice__(self, start, stop): @@ -274,17 +285,19 @@ class Param(numpy.ndarray): a = self._realshape_[i]+a internal_offset += a * extended_realshape[i] return internal_offset - def _raveled_index(self): + def _raveled_index(self, slice_index=None): # return an index array on the raveled array, which is formed by the current_slice # of this object extended_realshape = numpy.cumprod((1,) + self._realshape_[:0:-1])[::-1] - ind = self._indices() + 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) - def _expand_index(self): + 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 # it tells you in the second return argument if it has only seen arrays as indices + if slice_index is None: + slice_index = self._current_slice_ def f(a): a, b = a if a not in (slice(None), Ellipsis): @@ -298,7 +311,7 @@ class Param(numpy.ndarray): a = b+a return numpy.r_[a] return numpy.r_[:b] - return itertools.imap(f, itertools.izip_longest(self._current_slice_[:self._realndim_], self._realshape_, fillvalue=slice(None))) + return itertools.imap(f, itertools.izip_longest(slice_index[:self._realndim_], self._realshape_, fillvalue=slice(None))) #=========================================================================== # Printing -> done #=========================================================================== @@ -309,15 +322,6 @@ class Param(numpy.ndarray): @property def _constr(self): return ' '.join(map(lambda c: str(c[0]) if c[1].size==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())) - def decide(c): - if c[0]._realsize_ > 1 and not c[0].size==self.size: - return c[0].name + "".join(map(str,c[0]._indices())) - else: - return c[0].name - return ' '.join(map(lambda c: decide(c), 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) @@ -325,58 +329,21 @@ class Param(numpy.ndarray): 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): - # create an iterator, which shows the constraints of all indices -# cons_turnaround = collections.defaultdict(list) -# for c, index in self._parent_._constraints_iter_items(self): -# for i in index: -# cons_turnaround[i] += [str(c)] -# offset = self._internal_offset() -# return [" ".join(cons_turnaround[i]) for i in xrange(offset, offset+self.size)] -# self._str_dummy_ = numpy.empty(self._realshape_, dtype=object) -# constr_matrix = self._str_dummy_ # we need the whole constraints matrix -# constr_matrix[:] = '' -# 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): - # create an iterator, which shows the ties of all indices -# ties_turnaround = collections.defaultdict(list) -# for tie, index in self._parent_._ties_iter_items(self): -# for i in index: -# ties_turnaround[i] += [str(tie)] -# offset = self._internal_offset() -# return [" ".join(ties_turnaround[i]) for i in xrange(offset, offset+self.size)] -# for i in xrange(self.size): -# yield " ".join(ties_turnaround[i]) -# if self._str_dummy_ is None: -# self._str_dummy_ = numpy.empty(self._realshape_, dtype=object) -# ties_matr = self._str_dummy_; ties_matr[:] = '' -# 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_).reshape(*(self._realshape_+(-1,)))[self._current_slice_] # and get the slice we did before - -# def _in_index(self, i, index): -# if isinstance(index, slice): -# start,stop,step = index.indices() -# return i>=start and i 4: indstr = ','.join(map(str,ind[:2])) + "..." + ','.join(map(str,ind[-2:])) - else: indstr = ','.join(map(str,ind)) + ind = self._indices(slice_index) + if ind.size > 4: indstr = ','.join(map(str,ind[:2])) + "..." + ','.join(map(str,ind[-2:])) + else: indstr = ','.join(map(str,ind)) return self.name+'['+indstr+']' def __str__(self, constr_matrix=None, indices=None, ties=None, lc=None, lx=None, li=None, lt=None): if indices is None: indices = self._indices() @@ -400,6 +368,13 @@ class Param(numpy.ndarray): if lx is None: lx = self._max_len_values() if li is None: li = self._max_len_index(self._indices()) if lt is None: lt = self._max_len_names(ties[0], __tie_name__) + from index_operations import ParamDict + keep_track_of_broadcasting = ParamDict() + def tie_broadcasting(tie): + if tie in keep_track_of_broadcasting: + return keep_track_of_broadcasting[tie].next() + else: + pass header = " {i:^{2}s} | \033[1m{x:^{1}s}\033[0;0m | {c:^{0}s} | {t:^{3}s}".format(lc,lx,li,lt, x=self.name, c=__constraints_name__, i=__index_name__, t=__tie_name__) # nice header for printing return "\n".join([header]+[" {i!s:^{3}s} | {x: >{1}.{2}G} | {c:^{0}s} | {t:^{4}} ".format(lc,lx,__precision__,li,lt, x=x, c=" ".join(map(str,c)), t=" ".join([tie._short() for tie in t]), i=i) for i,x,c,t in itertools.izip(indices,self.flat,constr_matrix,ties[0])]) # return all the constraints with right indices #except: return super(Param, self).__str__() @@ -473,8 +448,10 @@ class ParamConcatenation(object): __gt__ = lambda self, val: self._vals()>val __ge__ = lambda self, val: self._vals()>=val 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] + def f(p): + ind = p._raveled_index() + return p._constraints_for(ind), p._ties_for(ind) + constr_matrices, ties_matrices = zip(*map(f, self.params)) indices = [p._indices() for p in self.params] lc = max([p._max_len_names(cm, __constraints_name__) for p, cm in itertools.izip(self.params, constr_matrices)]) lx = max([p._max_len_values() for p in self.params]) @@ -488,17 +465,22 @@ 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(800,1e4) + X = numpy.random.randn(2,3) + print "random done" p = Param("q_mean", X, None) p1 = Param("q_variance", numpy.random.rand(*p.shape), None) p2 = Param("Y", numpy.random.randn(p.shape[0],1), None) p3 = Param("rbf_variance", numpy.random.rand(), None) p4 = Param("rbf_lengthscale", numpy.random.rand(2), None) m = Parameterized() + print "setting params" m.set_as_parameters(p,p1,p2,p3,p4) #print m.q_v[3:5,[1,4,5]] + print "constraining variance" m[".*variance"].constrain_positive() + print "constraining rbf" m.rbf.constrain_positive() + m.q_variance[:,[0,2]].tie_to(m.rbf_l) #m.q_v.tie_to(m.rbf_v) # m.rbf_l.tie_to(m.rbf_va) # pt = numpy.array(params._get_params_transformed()) diff --git a/GPy/core/parameterized.py b/GPy/core/parameterized.py index 5a543d41..94915b2d 100644 --- a/GPy/core/parameterized.py +++ b/GPy/core/parameterized.py @@ -7,7 +7,7 @@ import copy import cPickle from parameter import ParamConcatenation, Param from index_operations import ParameterIndexOperations,\ - TieIndexOperations, create_raveled_indices, index_empty + TieIndexOperations, index_empty import itertools from re import compile, _pattern_type import sys @@ -17,6 +17,22 @@ import sys __fixed__ = "fixed" #=============================================================================== +#=============================================================================== +# constants +class _F_(object): + def __bool__(self): + return False + def __str__(self): + return "FIXED" +FIXED = _F_() +class _U_(object): + def __bool__(self): + return True + def __str__(self): + return "UNFIXED" +UNFIXED = _U_() +del _U_, _F_ +#=============================================================================== class Parameterized(object): """ @@ -195,9 +211,9 @@ class Parameterized(object): 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] = False + self._ties_fixes_[f] = FIXED if fixed == __fixed__: - self._ties_fixes_[ind] = False + self._ties_fixes_[ind] = FIXED if numpy.all(self._ties_fixes_): self._ties_fixes_ = None self.parameters_changed() @@ -237,30 +253,29 @@ class Parameterized(object): ind = ind-self._offset(param) ind = ind[ind >= 0] internal_offset = param._internal_offset() - internal_offset_old = (numpy.arange(param._realsize_).reshape(param._realshape_)[param._current_slice_]).flat[0] - assert internal_offset == internal_offset_old ind = ind[ind < param.size + internal_offset] return ind def _offset(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) #=========================================================================== # Handle ties: #=========================================================================== def _add_tie(self, param, tied_to): # tie param to tie_to, if the values match (with broadcasting) - self._ties_.add(param, tied_to) + 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) - f = create_raveled_indices(param._current_slice_, param._realshape_, self._offset(param)) self._ties_fixes_[f] = False def _remove_tie(self, param, *params): # remove the tie from param to all *params (can be None, so all ties get deleted for param) if len(params) == 0: params = self._ties_.properties() for p in params: - ind = create_raveled_indices(p._current_slice_, param._realshape_, self._offset(param)) - self._ties_.remove(param, p) - self._ties_fixes_[ind] = True + _, t = self._ties_.remove(param, p) + self._ties_fixes_[t] = True if numpy.all(self._ties_fixes_): self._ties_fixes_ = None def _ties_iter_items(self, param): for tied_to, ind in self._ties_.iter_from_items(): @@ -273,38 +288,56 @@ class Parameterized(object): def _ties_iter_indices(self, param): for _, ind in self._ties_iter_items(param): yield ind - def _ties_for(self, param, index): - return self._ties_.from_to_for(index+self._offset(param)) - #=========================================================================== + def _ties_for(self, param, rav_index): + return self._ties_.from_to_for(rav_index+self._offset(param)) + #=========================================================================== # Fixing parameters: #=========================================================================== def _fix(self, param, warning=True): - self._add_constrain(param, __fixed__, warning) + f = self._add_constrain(param, __fixed__, warning) 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(param._current_slice_, param._realshape_, self._offset(param)) - self._ties_fixes_[ind] = True - if numpy.all(self._ties_fixes_): self._ties_fixes_ = None + if self._ties_fixes_ is not None: + self._remove_constrain(param, __fixed__) + ind = self._raveled_index_for(param) + self._ties_fixes_[ind] = UNFIXED + if numpy.all(self._ties_fixes_==UNFIXED): self._ties_fixes_ = None + self._handle_ties() + #=========================================================================== + # Convenience for fixed, tied checking of parameter: + #=========================================================================== + def _is_fixed(self, param): + # returns if the whole parameter is fixed + if self._ties_fixes_ is None: + return False + return not self._ties_fixes_[self._offset(param): self._offset(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 + # the copy here + return self._parameters_[param._parent_index_] #=========================================================================== # Constraint Handling: #=========================================================================== def _add_constrain(self, param, transform, warning=True): - reconstrained = self._remove_constrain(param) # remove constraints before + 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! - self._constraints_.add(transform, param._current_slice_, param._realshape_, self._offset(param)) + self._constraints_.add(transform, rav_i) if warning and any(reconstrained): # if you want to print the whole params object, which was reconstrained use: # m = str(param[self._backtranslate_index(param, reconstrained)]) print "Warning: re-constraining parameters:\n{}".format(param._short()) - def _remove_constrain(self, param, *transforms): + return rav_i + def _remove_constrain(self, param, *transforms, **kwargs): if transforms is (): transforms = self._constraints_.properties() removed_indices = numpy.array([]).astype(int) + if "index" in kwargs: index = kwargs['index'] + else: index = self._raveled_index_for(param) for constr in transforms: - removed = self._constraints_.remove(constr, param._current_slice_, param._realshape_, self._offset(param)) + removed = self._constraints_.remove(constr, index) removed_indices = numpy.union1d(removed_indices, removed) return removed_indices # convienience for iterating over items @@ -321,8 +354,8 @@ class Parameterized(object): yield ind def _constraint_indices(self, param, constraint): return self._backtranslate_index(param, self._constraints_[constraint]) - def _constraints_for(self, param, index): - return self._constraints_.properties_for(index+self._offset(param)) + def _constraints_for(self, param, rav_index): + return self._constraints_.properties_for(rav_index+self._offset(param)) #=========================================================================== # Get/set parameters: #=========================================================================== @@ -384,7 +417,7 @@ class Parameterized(object): return [x._desc for x in self._parameters_] @property def _ts(self): - return [x._t for x in self._parameters_] + return [' '.join([t._short() for t in self._ties_iter(x)]) for x in self._parameters_] def __str__(self, header=True): nl = max([len(str(x)) for x in self.parameter_names + ["Name"]]) sl = max([len(str(x)) for x in self._descs + ["Value"]]) @@ -393,7 +426,7 @@ class Parameterized(object): tl = max([len(str(x)) if x else 0 for x in ts + ["Tied to"]]) format_spec = " \033[1m{{p.name:^{0}s}}\033[0;0m | {{p._desc:^{1}s}} | {{const:^{2}s}} | {{t:^{2}s}}".format(nl, sl, cl) to_print = [format_spec.format(p=p, const=c, t=t) for p, c, t in itertools.izip(self._parameters_, constrs, ts)] - sep = '-'*len(to_print[0]) + sep = '-'*(nl+sl+cl+tl+8*2+3) if header: 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