mirror of
https://github.com/SheffieldML/GPy.git
synced 2026-06-02 14:45:15 +02:00
parameterized first beta test
This commit is contained in:
parent
4f56506aa6
commit
152f61acf0
1 changed files with 346 additions and 222 deletions
|
|
@ -7,7 +7,8 @@ import re
|
||||||
import itertools
|
import itertools
|
||||||
import numpy
|
import numpy
|
||||||
from GPy.core.transformations import Logexp, NegativeLogexp
|
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 types import FunctionType
|
||||||
from re import compile, _pattern_type
|
from re import compile, _pattern_type
|
||||||
_index_re = re.compile('(?:_(\d+))+') # pattern match for indices
|
_index_re = re.compile('(?:_(\d+))+') # pattern match for indices
|
||||||
|
|
@ -17,319 +18,440 @@ __constraints_name__ = "Constraint"
|
||||||
__index_name__ = "Index"
|
__index_name__ = "Index"
|
||||||
__tie_name__ = "Tied to"
|
__tie_name__ = "Tied to"
|
||||||
__precision__ = numpy.get_printoptions()['precision'] # numpy printing precision used, sublassing numpy ndarray after all
|
__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):
|
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):
|
def __init__(self, parameterlist, prefix=None, *args, **kwargs):
|
||||||
|
self._init = True
|
||||||
self._params = []
|
self._params = []
|
||||||
for p in parameterlist:
|
for p in parameterlist:
|
||||||
if isinstance(p, Parameterized):
|
if isinstance(p, Parameterized):
|
||||||
self._params.extend(p._params)
|
self._params.extend(p._params)
|
||||||
else:
|
else:
|
||||||
self._params.append(p)
|
self._params.append(p)
|
||||||
sizes = numpy.cumsum([0] + self.sizes)
|
self._constraints = ConstraintIndexOperations()
|
||||||
self._param_slices = itertools.starmap(lambda start,stop: slice(start, stop), zip(sizes, sizes[1:]))
|
self._ties = TieIndexOperations(self)
|
||||||
for p in self._params:
|
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
|
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):
|
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):
|
def _set_params(self, params):
|
||||||
[p._set_params(params[s]) for p,s in itertools.izip(self._params,self._param_slices)]
|
[p._set_params(params[s]) for p,s in itertools.izip(self._params,self._param_slices)]
|
||||||
|
|
||||||
def _get_params_transformed(self):
|
def _get_params_transformed(self):
|
||||||
return numpy.hstack([x._get_params_transformed() for x in self._params])
|
p = self._get_params()
|
||||||
return numpy.fromiter(itertools.chain(*itertools.imap(lambda x: x._get_params_transformed(), self._params)), dtype=numpy.float64, count=self.num_params_transformed)
|
[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):
|
def _set_params_transformed(self, p):
|
||||||
current_index = 0
|
if self._ties_fixes is not None: tmp = self._get_params(); tmp[self._ties_fixes] = p; p = tmp; del tmp
|
||||||
for p in self._params:
|
[numpy.put(p, ind, c.f(p[ind])) for c,ind in self._constraints.iteritems() if c is not __fixed__]
|
||||||
s = p.num_params_transformed
|
[numpy.put(p, f, p[t]) for f,t in self._ties.iter_from_to_indices()]
|
||||||
p._set_params_transformed(params[current_index:current_index+s])
|
self._set_params(p)
|
||||||
current_index += s
|
|
||||||
self._handle_ties()
|
|
||||||
|
|
||||||
def _handle_ties(self):
|
def _handle_ties(self):
|
||||||
[p._handle_ties() for p in self._params]
|
if not self._init:
|
||||||
|
self._set_params_transformed(self._get_params_transformed())
|
||||||
def __getitem__(self, name):
|
#===========================================================================
|
||||||
return self.__getattr__(name)
|
# 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):
|
def grep_param_names(self, regexp):
|
||||||
if not isinstance(regexp, _pattern_type):
|
if not isinstance(regexp, _pattern_type): regexp = compile(regexp)
|
||||||
regexp = compile(regexp)
|
paramlist = [param for param in self._params if regexp.match(param.name) is not None]
|
||||||
paramlist = [param for param in self._params if regexp.search(param.name) is not None]
|
|
||||||
return paramlist
|
return paramlist
|
||||||
|
def __getitem__(self, name, paramlist=None):
|
||||||
def tie_params(self, regexp):
|
if paramlist is None:
|
||||||
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:
|
|
||||||
paramlist = self.grep_param_names(name)
|
paramlist = self.grep_param_names(name)
|
||||||
if len(paramlist) < 1:
|
if len(paramlist) < 1: raise AttributeError, name
|
||||||
raise AttributeError("'{:s}' object has no attribute '{:s}'".format(str(self.__class__.__name__), name))
|
if len(paramlist) == 1: return paramlist[-1]
|
||||||
if len(paramlist) == 1:
|
return ParamConcatenation(paramlist)
|
||||||
return paramlist[-1]
|
def __setitem__(self, name, value, paramlist=None):
|
||||||
return ParamConcatenation(paramlist)
|
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
|
@property
|
||||||
def names(self):
|
def names(self):
|
||||||
return [x.name for x in self._params]
|
return [x.name for x in self._params]
|
||||||
@property
|
@property
|
||||||
def size(self):
|
def parameter_size(self):
|
||||||
return sum(self.sizes)
|
return sum(self.parameter_sizes)
|
||||||
@property
|
@property
|
||||||
def sizes(self):
|
def parameter_sizes(self):
|
||||||
return [x.size for x in self._params]
|
return [x.size for x in self._params]
|
||||||
@property
|
@property
|
||||||
def sizes_transformed(self):
|
def parameter_size_transformed(self):
|
||||||
return [x.num_params_transformed() for x in self._params]
|
return sum(self._ties_fixes)
|
||||||
@property
|
@property
|
||||||
def num_params_transformed(self):
|
def parameter_shapes(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):
|
|
||||||
return [x.shape for x in self._params]
|
return [x.shape for x in self._params]
|
||||||
@property
|
@property
|
||||||
def _constrs(self):
|
def _constrs(self):
|
||||||
return [x._constr for x in self._params]
|
return [p._constr for p in self._params]
|
||||||
@property
|
@property
|
||||||
def _descs(self):
|
def _descs(self):
|
||||||
return [x._desc for x in self._params]
|
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):
|
def __str__(self, header=True):
|
||||||
nl = max([len(str(x)) for x in self.names + ["Name"]])
|
nl = max([len(str(x)) for x in self.names + ["Name"]])
|
||||||
sl = max([len(str(x)) for x in self._descs + ["Value"]])
|
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"]])
|
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:
|
if header:
|
||||||
header = " {{0:^{0}s}} | {{1:^{1}s}} | {{2:^{2}s}} ".format(nl, sl, cl).format("Name", "Value", "Constraint")
|
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' + '-'*len(header)
|
header += '\n' + sep
|
||||||
return '\n'.join([header]+[x.__str__(format_spec=format_spec) for x in self._params])
|
to_print.insert(0, header)
|
||||||
return '\n'.join([x.__str__(format_spec=format_spec) for x in self._params])
|
return '\n'.format(sep).join(to_print)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Param(numpy.ndarray):
|
class Param(numpy.ndarray):
|
||||||
"""
|
"""
|
||||||
Parameter object for GPy models.
|
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.
|
- slice paramters and constrain the slices.
|
||||||
|
|
||||||
- unconstrain slices
|
- unconstrain slices
|
||||||
|
|
||||||
"""
|
"""
|
||||||
fixed = False # if this parameter is fixed
|
fixed = False # if this parameter is fixed
|
||||||
__array_priority__ = -1 # Only give back a param if slicing
|
__array_priority__ = -1. # Allways give back Param
|
||||||
|
def __new__(cls, name, input_array):
|
||||||
def __new__(cls, name, input_array, constraints=None, ties=None):
|
|
||||||
obj = numpy.atleast_1d(numpy.array(input_array)).view(cls)
|
obj = numpy.atleast_1d(numpy.array(input_array)).view(cls)
|
||||||
obj.name = name
|
obj.name = name
|
||||||
|
obj._parent = None
|
||||||
|
obj._parent_index = None
|
||||||
obj._current_slice = slice(None)
|
obj._current_slice = slice(None)
|
||||||
obj._realshape = obj.shape
|
obj._realshape = obj.shape
|
||||||
#obj.parameters = parameters
|
obj._realsize = obj.size
|
||||||
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
|
return obj
|
||||||
|
|
||||||
def __array_finalize__(self, obj):
|
def __array_finalize__(self, obj):
|
||||||
# see InfoArray.__array_finalize__ for comments
|
# see InfoArray.__array_finalize__ for comments
|
||||||
if obj is None: return
|
if obj is None: return
|
||||||
self.name = getattr(obj, 'name', None)
|
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)
|
self._current_slice = getattr(obj, '_current_slice', None)
|
||||||
|
self._parent = getattr(obj, '_parent', None)
|
||||||
@property
|
self._parent_index = getattr(obj, '_parent_index', None)
|
||||||
def value(self):
|
self._realshape = getattr(obj, '_realshape', None)
|
||||||
return self#self.base[self._current_slice]
|
self._realsize = getattr(obj, '_realsize', None)
|
||||||
|
#===========================================================================
|
||||||
@property
|
# get/set parameters
|
||||||
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()])
|
|
||||||
|
|
||||||
def _set_params(self, param):
|
def _set_params(self, param):
|
||||||
self.value.flat = param
|
self.flat = param
|
||||||
|
|
||||||
def _get_params(self):
|
def _get_params(self):
|
||||||
return self.value.flat
|
return self.flat
|
||||||
|
#===========================================================================
|
||||||
@property
|
# Fixing Parameters:
|
||||||
def num_params_transformed(self):
|
#===========================================================================
|
||||||
return self.size - self.ties.size()
|
def constrain_fixed(self):
|
||||||
|
self._parent._fix(self)
|
||||||
def _get_params_transformed(self):
|
def unconstrain_fixed(self):
|
||||||
params = self.base.flatten()
|
self._parent._unfix(self)
|
||||||
[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)))
|
# Constrain operations -> done
|
||||||
|
#===========================================================================
|
||||||
def _set_params_transformed(self, params):
|
def constrain(self, transform):
|
||||||
numpy.put(self.base, numpy.setdiff1d(numpy.r_[:self.size],reduce(numpy.union1d, self.ties.iterindices(), numpy.array([],dtype=self.dtype))), params)
|
self._parent._add_constrain(self, transform)
|
||||||
[numpy.put(self.base, indices, constraint.f(self.base.flat[indices])) for constraint, indices in self.constraints.iteritems()]
|
self[...] = transform.initialize(self)
|
||||||
|
def constrain_positive(self):
|
||||||
def _handle_ties(self):
|
self.constrain(Logexp())
|
||||||
# handle all tied values
|
def constrain_negative(self):
|
||||||
for tie, indices in self.ties.iteritems():
|
self.constrain(NegativeLogexp())
|
||||||
self.base.flat[indices] = tie.flat
|
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):
|
def tie_to(self, param):
|
||||||
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__)
|
||||||
try:
|
try:
|
||||||
rav_index = self.ties.create_raveled_indices(self._current_slice)
|
self[...] = param
|
||||||
self.base.flat[rav_index] = param.flat
|
self._parent._add_tie(self, param)
|
||||||
self.ties.add(param, self._current_slice)
|
|
||||||
self._handle_ties()
|
|
||||||
except ValueError:
|
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):
|
def untie(self, *params):
|
||||||
if len(params) == 0:
|
if len(params) == 0:
|
||||||
params = self.ties.properties()
|
params = self._parent._ties.properties()
|
||||||
for p in self.ties.properties():
|
for p in self._parent._ties.properties():
|
||||||
if any((p == x).all() for x in params):
|
if any((p == x).all() for x in params):
|
||||||
self.ties.remove(p, self._current_slice)
|
self._parent._remove_tie(self, params)
|
||||||
|
#===========================================================================
|
||||||
def constrain(self, transform):
|
# Array operations -> done
|
||||||
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())
|
|
||||||
|
|
||||||
def __getitem__(self, s, *args, **kwargs):
|
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)
|
new_arr = numpy.ndarray.__getitem__(self, s, *args, **kwargs)
|
||||||
try:
|
try: new_arr._current_slice = s
|
||||||
new_arr._current_slice = s
|
except AttributeError: pass# returning 0d array or float, double etc
|
||||||
except AttributeError:
|
|
||||||
# returning 0d array or float, double etc:
|
|
||||||
pass
|
|
||||||
return new_arr
|
return new_arr
|
||||||
|
def __getslice__(self, start, stop):
|
||||||
|
return self.__getitem__(slice(start, stop))
|
||||||
def __setitem__(self, *args, **kwargs):
|
def __setitem__(self, *args, **kwargs):
|
||||||
numpy.ndarray.__setitem__(self, *args, **kwargs)
|
numpy.ndarray.__setitem__(self, *args, **kwargs)
|
||||||
self._parent._handle_ties()
|
self._parent._handle_ties()
|
||||||
|
#===========================================================================
|
||||||
def __repr__(self, *args, **kwargs):
|
# Printing ->
|
||||||
return super(Param, self).__repr__(*args, **kwargs)
|
#===========================================================================
|
||||||
view = repr(self.value)
|
@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
|
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):
|
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[:] = ''
|
constr_matrix[:] = ''
|
||||||
for constr, indices in self.constraints.iteritems(): # put in all the constraints:
|
for constr, indices in self._parent._constraints_iter_items(self): # put in all the constraints:
|
||||||
constr_matrix[indices] = numpy.vectorize(lambda x:" ".join([x, str(constr)]) if x else str(constr))(constr_matrix[indices])
|
cstr = ""+str(constr)+""
|
||||||
return constr_matrix.astype(numpy.string_)[self._current_slice] # and get the slice we did before
|
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):
|
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[:] = ''
|
ties_matr[:] = ''
|
||||||
for tie, indices in self.ties.iteritems(): # go through all ties:
|
for tie, indices in self._parent._ties_iter_items(self): # go through all ties:
|
||||||
tie_cycle = itertools.cycle(tie._indices())
|
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])
|
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):
|
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):
|
def _max_len_names(self, constr_matrix, header):
|
||||||
return max(reduce(lambda a, b:max(a, len(b)), constr_matrix.flat, 0), len(header))
|
return max(reduce(lambda a, b:max(a, len(b)), constr_matrix.flat, 0), len(header))
|
||||||
def _max_len_values(self):
|
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):
|
def _max_len_index(self, ind):
|
||||||
return max(reduce(lambda a, b:max(a, len(str(b))), ind, 0), len(__index_name__))
|
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):
|
def __str__(self, constr_matrix=None, indices=None, ties=None, lc=None, lx=None, li=None, lt=None):
|
||||||
if format_spec is None:
|
try:
|
||||||
if indices is None:
|
if indices is None: indices = self._indices()
|
||||||
indices = self._indices()
|
if constr_matrix is None: constr_matrix = self._constr_matrix_str()
|
||||||
if constr_matrix is None:
|
if ties is None: ties = self._ties_matrix_str()
|
||||||
constr_matrix = self._constr_matrix_str()
|
if lc is None: lc = self._max_len_names(constr_matrix, __constraints_name__)
|
||||||
if ties is None:
|
if lx is None: lx = self._max_len_values()
|
||||||
ties = self._ties_matrix_str()
|
if li is None: li = self._max_len_index(indices)
|
||||||
if lc is None:
|
if lt is None: lt = self._max_len_names(ties, __tie_name__)
|
||||||
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
|
constr = constr_matrix.flat
|
||||||
ties = ties.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
|
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 "\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
|
||||||
return format_spec.format(self=self)
|
except: return super(Param, self).__str__()
|
||||||
|
|
||||||
class ParamConcatenation(object):
|
class ParamConcatenation(object):
|
||||||
def __init__(self, params):
|
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.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):
|
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):
|
def constrain(self, constraint):
|
||||||
[param.constrain(constraint) for param in self.params]
|
[param.constrain(constraint) for param in self.params]
|
||||||
def constrain_positive(self):
|
def constrain_positive(self):
|
||||||
[param.constrain_positive() for param in self.params]
|
[param.constrain_positive() for param in self.params]
|
||||||
|
def constrain_fixed(self):
|
||||||
|
[param.constrain_fixed() for param in self.params]
|
||||||
def constrain_negative(self):
|
def constrain_negative(self):
|
||||||
[param.constrain_negative() for param in self.params]
|
[param.constrain_negative() for param in self.params]
|
||||||
def unconstrain(self, constraints=None):
|
def unconstrain(self, constraints=None):
|
||||||
[param.unconstrain(constraints) for param in self.params]
|
[param.unconstrain(constraints) for param in self.params]
|
||||||
def unconstrain_negative(self):
|
def unconstrain_negative(self):
|
||||||
[param.unconstrain_negative() for param in self.params]
|
[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):
|
def __str__(self, *args, **kwargs):
|
||||||
constr_matrices = [p._constr_matrix_str() for p in self.params]
|
constr_matrices = [p._constr_matrix_str() for p in self.params]
|
||||||
ties_matrices = [p._ties_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])
|
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)])
|
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)])
|
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):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return "\n".join(map(repr,self.params))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
X = numpy.random.randn(100,8)
|
X = numpy.random.randn(3,3,2)
|
||||||
p = Param("X", X)
|
p = Param("q_mean", X)
|
||||||
p1 = Param("X_variance", numpy.random.rand(*X.shape))
|
p1 = Param("q_variance", numpy.random.rand(*p.shape))
|
||||||
p2 = Param("Y", numpy.random.randn(3,1))
|
p2 = Param("Y", numpy.random.randn(p.shape[0],1))
|
||||||
p3 = Param("rbf_variance", numpy.random.rand(1))
|
p3 = Param("rbf_variance", numpy.random.rand())
|
||||||
p4 = Param("rbf_lengthscale", numpy.random.rand(2))
|
p4 = Param("rbf_lengthscale", numpy.random.rand(2))
|
||||||
params = Parameterized([p,p1,p2,p3,p4])
|
m = Parameterized([p,p1,p2,p3,p4])
|
||||||
params.rbf.constrain_positive()
|
m[".*variance"].constrain_positive()
|
||||||
params[".*variance"].constrain_positive()
|
m.rbf.constrain_positive()
|
||||||
params.rbf_l.tie_to(params.rbf_va)
|
m.q_v.tie_to(m.rbf_v)
|
||||||
pt = numpy.array(params._get_params_transformed())
|
# m.rbf_l.tie_to(m.rbf_va)
|
||||||
ptr = numpy.random.randn(*pt.shape)
|
# pt = numpy.array(params._get_params_transformed())
|
||||||
|
# ptr = numpy.random.randn(*pt.shape)
|
||||||
# params.X.tie_to(params.rbf_v)
|
# params.X.tie_to(params.rbf_v)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue