From f71af385056aa1948f62a04a32c7791d6c20a1ab Mon Sep 17 00:00:00 2001 From: Max Zwiessele Date: Thu, 13 Feb 2014 15:06:05 +0000 Subject: [PATCH] fixing now works, removing parameters needs fixing --- GPy/__init__.py | 1 + GPy/core/parameterization/index_operations.py | 15 ++- GPy/core/parameterization/parameter_core.py | 91 ++++++++----- GPy/core/parameterization/parameterized.py | 127 +++--------------- GPy/core/parameterization/transformations.py | 9 ++ GPy/kern/parts/rbf.py | 2 +- GPy/plotting/matplot_dep/visualize.py | 2 +- 7 files changed, 100 insertions(+), 147 deletions(-) diff --git a/GPy/__init__.py b/GPy/__init__.py index 41369a50..94128dfa 100644 --- a/GPy/__init__.py +++ b/GPy/__init__.py @@ -4,6 +4,7 @@ import warnings warnings.filterwarnings("ignore", category=DeprecationWarning) import core +from core.parameterization import transformations import models import mappings import inference diff --git a/GPy/core/parameterization/index_operations.py b/GPy/core/parameterization/index_operations.py index d2549dad..091b6372 100644 --- a/GPy/core/parameterization/index_operations.py +++ b/GPy/core/parameterization/index_operations.py @@ -57,9 +57,11 @@ class ParameterIndexOperations(object): You can give an offset to set an offset for the given indices in the index array, for multi-param handling. ''' - def __init__(self): + def __init__(self, constraints=None): self._properties = IntArrayDict() - #self._reverse = collections.defaultdict(list) + if constraints is not None: + for t, i in constraints.iteritems(): + self.add(t, i) def __getstate__(self): return self._properties#, self._reverse @@ -191,7 +193,7 @@ class ParameterIndexOperationsView(object): def indices(self): - [ind for ind in self.iterindices()] + return [ind for ind in self.iterindices()] def properties_for(self, index): @@ -206,6 +208,8 @@ class ParameterIndexOperationsView(object): removed = self._param_index_ops.remove(prop, indices+self._offset) if removed.size > 0: return removed - self._size + 1 + if self[prop].size == 0: + del self[prop] return removed @@ -222,6 +226,9 @@ class ParameterIndexOperationsView(object): def update(self, parameter_index_view): for i, v in parameter_index_view.iteritems(): self.add(i, v) - + + + def copy(self): + return ParameterIndexOperations(dict(self.iteritems())) pass diff --git a/GPy/core/parameterization/parameter_core.py b/GPy/core/parameterization/parameter_core.py index 6afea470..cfee60bd 100644 --- a/GPy/core/parameterization/parameter_core.py +++ b/GPy/core/parameterization/parameter_core.py @@ -1,7 +1,7 @@ # Copyright (c) 2012, GPy authors (see AUTHORS.txt). # Licensed under the BSD 3-clause license (see LICENSE.txt) -from transformations import Transformation, Logexp, NegativeLogexp, Logistic +from transformations import Transformation, Logexp, NegativeLogexp, Logistic, __fixed__, FIXED, UNFIXED __updated__ = '2013-12-16' @@ -10,11 +10,6 @@ def adjust_name_for_printing(name): return name.replace(" ", "_").replace(".", "_").replace("-","").replace("+","").replace("!","").replace("*","").replace("/","") return '' -#=============================================================================== -# Printing: -__fixed__ = "fixed" -#=============================================================================== - class Observable(object): _observers_ = {} def add_observer(self, observer, callble): @@ -119,6 +114,14 @@ class Indexable(object): def _offset_for(self, param): raise NotImplementedError, "shouldnt happen, offset required from non parameterization object?" + + def _raveled_index_for(self, param): + """ + get the raveled index for a param + that is an int array, containing the indexes for the flattened + param inside this parameterized logic. + """ + raise NotImplementedError, "shouldnt happen, raveld index transformation required from non parameterization object?" class Constrainable(Nameable, Indexable, Parameterizable): def __init__(self, name, default_constraint=None): @@ -126,6 +129,9 @@ class Constrainable(Nameable, Indexable, Parameterizable): self._default_constraint_ = default_constraint from index_operations import ParameterIndexOperations self.constraints = ParameterIndexOperations() + if self._default_constraint_ is not None: + self.constrain(self._default_constraint_) + #=========================================================================== # Fixing Parameters: #=========================================================================== @@ -138,17 +144,40 @@ class Constrainable(Nameable, Indexable, Parameterizable): if value is not None: self[:] = value self.constrain(__fixed__, warning=warning) - self._highest_parent_._set_fixed(self._raveled_index()) + rav_i = self._highest_parent_._raveled_index_for(self) + self._highest_parent_._set_fixed(rav_i) fix = constrain_fixed + def unconstrain_fixed(self): """ This parameter will no longer be fixed. """ unconstrained = self.unconstrain(__fixed__) - import ipdb;ipdb.set_trace() - self._highest_parent_._set_unfixed(unconstrained) - + self._highest_parent_._set_unfixed(unconstrained) unfix = unconstrain_fixed + + def _set_fixed(self, index): + import numpy as np + if not self._has_fixes(): self._fixes_ = np.ones(self.size, dtype=bool) + self._fixes_[index] = FIXED + if np.all(self._fixes_): self._fixes_ = None # ==UNFIXED + + def _set_unfixed(self, index): + import numpy as np + if not self._has_fixes(): self._fixes_ = np.ones(self.size, dtype=bool) + #rav_i = self._raveled_index_for(param)[index] + self._fixes_[index] = UNFIXED + if np.all(self._fixes_): self._fixes_ = None # ==UNFIXED + + def _connect_fixes(self): + import numpy as np + fixed_indices = self.constraints[__fixed__] + if fixed_indices.size > 0: + self._fixes_ = np.ones(self.size, dtype=bool) * UNFIXED + self._fixes_[fixed_indices] = FIXED + else: + self._fixes_ = None + #=========================================================================== # Constrain operations -> done #=========================================================================== @@ -174,18 +203,29 @@ class Constrainable(Nameable, Indexable, Parameterizable): self._set_params(transform.initialize(self._get_params()), update=False) reconstrained = self.unconstrain() self.constraints.add(transform, self._raveled_index()) - if reconstrained.size > 0: + if warning and reconstrained.size > 0: print "WARNING: reconstraining parameters {}".format(self.parameter_names) if update: self._highest_parent_.parameters_changed() - # if self.has_parent(): - # self._highest_parent_._add_constrain(self, transform, warning) - # else: - # for p in self._parameters_: - # self._add_constrain(p, transform, warning) - # if update: - # self.parameters_changed() + def unconstrain(self, *transforms): + """ + :param transforms: The transformations to unconstrain from. + + remove all :py:class:`GPy.core.transformations.Transformation` + transformats of this parameter object. + """ + if len(transforms) == 0: + transforms = self.constraints.properties() + import numpy as np + removed = np.empty((0,),dtype=int) + for t in transforms: + unconstrained = self.constraints.remove(t, self._raveled_index()) + removed = np.union1d(removed, unconstrained) + if t is __fixed__: + self._highest_parent_._set_unfixed(unconstrained) + return removed + def constrain_positive(self, warning=True, update=True): """ :param warning: print a warning if re-constraining parameters. @@ -211,21 +251,6 @@ class Constrainable(Nameable, Indexable, Parameterizable): """ self.constrain(Logistic(lower, upper), warning=warning, update=update) - def unconstrain(self, *transforms): - """ - :param transforms: The transformations to unconstrain from. - - remove all :py:class:`GPy.core.transformations.Transformation` - transformats of this parameter object. - """ - if len(transforms) == 0: - transforms = self.constraints.properties() - import numpy as np - removed = np.empty((0,),dtype=int) - for t in transforms: - removed = np.union1d(removed, self.constraints.remove(t, self._raveled_index())) - return removed - def unconstrain_positive(self): """ Remove positive constraint of this parameter. diff --git a/GPy/core/parameterization/parameterized.py b/GPy/core/parameterization/parameterized.py index a30a1e3b..80bf2959 100644 --- a/GPy/core/parameterization/parameterized.py +++ b/GPy/core/parameterization/parameterized.py @@ -8,15 +8,10 @@ import cPickle import itertools from re import compile, _pattern_type from param import ParamConcatenation, Param -from parameter_core import Constrainable, Pickleable, Observable, adjust_name_for_printing, Gradcheckable, __fixed__ +from parameter_core import Constrainable, Pickleable, Observable, adjust_name_for_printing, Gradcheckable +from transformations import __fixed__, FIXED, UNFIXED from array_core import ParamList -#=============================================================================== -# constants -FIXED = False -UNFIXED = True -#=============================================================================== - class Parameterized(Constrainable, Pickleable, Observable, Gradcheckable): """ Parameterized class @@ -71,37 +66,6 @@ class Parameterized(Constrainable, Pickleable, Observable, Gradcheckable): self._added_names_ = set() del self._in_init_ - #=========================================================================== - # Parameter connection for model creation: - #=========================================================================== -# def set_as_parameter(self, name, array, gradient, index=None, gradient_parent=None): -# """ -# :param name: name of the param (in print and plots), can be callable without parameters -# :type name: str, callable -# :param array: array which the param consists of -# :type array: array-like -# :param gradient: gradient method of the param -# :type gradient: callable -# :param index: (optional) index of the param when printing -# -# (:param gradient_parent: connect these parameters to this class, but tell -# updates to highest_parent, this is needed when parameterized classes -# contain parameterized classes, but want to access the parameters -# of their children) -# -# -# Set array (e.g. self.X) as param with name and gradient. -# I.e: self.set_as_parameter('curvature', self.lengthscale, self.dK_dlengthscale) -# -# Note: the order in which parameters are added can be adjusted by -# giving an index, of where to put this param in printing -# """ -# if index is None: -# self._parameters_.append(Param(name, array, gradient)) -# else: -# self._parameters_.insert(index, Param(name, array, gradient)) -# self._connect_parameters(gradient_parent=gradient_parent) - def _has_fixes(self): return hasattr(self, "_fixes_") and self._fixes_ is not None @@ -118,53 +82,22 @@ class Parameterized(Constrainable, Pickleable, Observable, Gradcheckable): # if param.has_parent(): # raise AttributeError, "parameter {} already in another model, create new object (or copy) for adding".format(param._short()) if param in self._parameters_ and index is not None: - # make sure fixes and constraints are indexed right - if self._has_fixes(): - param_slice = slice(self._offset_for(param), self._offset_for(param) + param.size) - dest_index = sum((p.size for p in self._parameters_[:index])) - dest_slice = slice(dest_index, dest_index + param.size) - fixes_param = self._fixes_[param_slice].copy() - self._fixes_[param_slice] = self._fixes_[dest_slice] - self._fixes_[dest_slice] = fixes_param - - del self._parameters_[param._parent_index_] - self._parameters_.insert(index, param) + self.remove_parameter(param) + self.add_parameter(param, index) elif param not in self._parameters_: # make sure the size is set - if not hasattr(self, 'size'): - self.size = sum(p.size for p in self._parameters_) if index is None: self._parameters_.append(param) - - # make sure fixes and constraints are indexed right - if param._has_fixes(): fixes_param = param._fixes_.copy() - else: fixes_param = numpy.ones(param.size, dtype=bool) - if self._has_fixes(): self._fixes_ = np.r_[self._fixes_, fixes_param] - elif param._has_fixes(): self._fixes_ = np.r_[np.ones(self.size, dtype=bool), fixes_param] - else: start = sum(p.size for p in self._parameters_[:index]) self.constraints.shift(start, param.size) self._parameters_.insert(index, param) - - # make sure fixes and constraints are indexed right - if param._has_fixes(): fixes_param = param._fixes_.copy() - else: fixes_param = numpy.ones(param.size, dtype=bool) - ins = sum((p.size for p in self._parameters_[:index])) - if self._has_fixes(): self._fixes_ = np.r_[self._fixes_[:ins], fixes_param, self._fixes[ins:]] - elif not np.all(fixes_param): - self._fixes_ = np.ones(self.size + param.size, dtype=bool) - self._fixes_[ins:ins + param.size] = fixes_param self.size += param.size else: raise RuntimeError, """Parameter exists already added and no copy made""" self._connect_parameters() for p in self._parameters_: p._parent_changed(self) - if param._default_constraint_ is not None: - param.constrain(param._default_constraint_, False) - if self._has_fixes() and np.all(self._fixes_): # ==UNFIXED - self._fixes_ = None def add_parameters(self, *parameters): """ @@ -173,18 +106,20 @@ class Parameterized(Constrainable, Pickleable, Observable, Gradcheckable): """ [self.add_parameter(p) for p in parameters] - def remove_parameter(self, *names_params_indices): + def remove_parameter(self, param): """ - :param names_params_indices: mix of parameter_names, param objects, or indices - to remove from being a param of this parameterized object. - - note: if it is a string object it will not (!) be regexp-matched - automatically. + :param param: param object to remove from being a parameter of this parameterized object. """ - self._parameters_ = ParamList([p for p in self._parameters_ - if not (p._parent_index_ in names_params_indices - or p.name in names_params_indices - or p in names_params_indices)]) + if not param in self._parameters_: + raise RuntimeError, "Parameter {} does not belong to this object, remove parameters directly from their respective parents".format(param._short()) + del self._parameters_[param._parent_index_] + self.size -= param.size + constr = param.constraints.copy() + param.constraints.clear() + param.constraints = constr + param._direct_parent_ = None + param._parent_index_ = None + param._connect_fixes() self._connect_parameters() def _connect_parameters(self): @@ -214,6 +149,8 @@ class Parameterized(Constrainable, Pickleable, Observable, Gradcheckable): elif not (pname in not_unique): self.__dict__[pname] = p self._added_names_.add(pname) + self._connect_fixes() + #=========================================================================== # Pickling operations #=========================================================================== @@ -337,7 +274,7 @@ class Parameterized(Constrainable, Pickleable, Observable, Gradcheckable): self._added_names_.add(pname) self.__dict__[pname] = param #=========================================================================== - # Index Handling + # Indexable Handling #=========================================================================== def _backtranslate_index(self, param, ind): # translate an index in parameterized indexing into the index of param @@ -373,36 +310,10 @@ class Parameterized(Constrainable, Pickleable, Observable, Gradcheckable): #=========================================================================== # Fixing parameters: #=========================================================================== - def _set_fixed(self, param_or_index): - if not self._has_fixes(): self._fixes_ = numpy.ones(self.size, dtype=bool) - try: - param_or_index = self._raveled_index_for(param_or_index) - except AttributeError: - pass - self._fixes_[param_or_index] = FIXED - if numpy.all(self._fixes_): self._fixes_ = None # ==UNFIXED - def _set_unfixed(self, param_or_index): - if not self._has_fixes(): self._fixes_ = numpy.ones(self.size, dtype=bool) - try: - param_or_index = self._raveled_index_for(param_or_index) - except AttributeError: - pass - self._fixes_[param_or_index] = UNFIXED - for constr, ind in self.constraints.iteritems(): - if constr is __fixed__: - self._fixes_[ind] = FIXED - if numpy.all(self._fixes_): self._fixes_ = None # ==UNFIXED def _fixes_for(self, param): if self._has_fixes(): return self._fixes_[self._raveled_index_for(param)] return numpy.ones(self.size, dtype=bool)[self._raveled_index_for(param)] - # def _fix(self, param, warning=True): - # f = self._add_constrain(param, __fixed__, warning) - # self._set_fixed(f) - # def _unfix(self, param): - # if self._has_fixes(): - # f = self._remove_constrain(param, __fixed__) - # self._set_unfixed(f) #=========================================================================== # Convenience for fixed, tied checking of param: #=========================================================================== diff --git a/GPy/core/parameterization/transformations.py b/GPy/core/parameterization/transformations.py index e17da404..9582ad4a 100644 --- a/GPy/core/parameterization/transformations.py +++ b/GPy/core/parameterization/transformations.py @@ -6,8 +6,17 @@ import numpy as np from domains import _POSITIVE,_NEGATIVE, _BOUNDED import sys import weakref + _lim_val = -np.log(sys.float_info.epsilon) +#=============================================================================== +# Fixing constants +__fixed__ = "fixed" +FIXED = False +UNFIXED = True +#=============================================================================== + + class Transformation(object): domain = None _instance = None diff --git a/GPy/kern/parts/rbf.py b/GPy/kern/parts/rbf.py index 4b38bd0f..4a8ebd4d 100644 --- a/GPy/kern/parts/rbf.py +++ b/GPy/kern/parts/rbf.py @@ -53,7 +53,7 @@ class RBF(Kernpart): self.variance = Param('variance', variance, Logexp()) - self.lengthscale = Param('lengthscale', lengthscale) + self.lengthscale = Param('lengthscale', lengthscale, Logexp()) self.lengthscale.add_observer(self, self.update_lengthscale) self.update_lengthscale(self.lengthscale) diff --git a/GPy/plotting/matplot_dep/visualize.py b/GPy/plotting/matplot_dep/visualize.py index 99e8a0da..fb085f39 100644 --- a/GPy/plotting/matplot_dep/visualize.py +++ b/GPy/plotting/matplot_dep/visualize.py @@ -4,7 +4,6 @@ import GPy import numpy as np import matplotlib as mpl import time -import Image try: import visual visual_available = True @@ -324,6 +323,7 @@ class image_show(matplotlib_show): else: self.vals = 255*(self.vals - self.vals.min())/(self.vals.max() - self.vals.min()) if not self.palette == []: # applying using an image palette (e.g. if the image has been quantized) + from PIL import Image self.vals = Image.fromarray(self.vals.astype('uint8')) self.vals.putpalette(self.palette) # palette is a list, must be loaded before calling this function