diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..84dfe227 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,34 @@ +# .coveragerc to control coverage.py +[run] +branch = True +source = GPy +omit = ./GPy/testing/*.py, travis_tests.py, setup.py, ./GPy/__version__.py + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + + # Don't complain about missing debug-only code: + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + raise NotImplemented + except NotImplementedError + except NotImplemented + except AssertionError + except ImportError + pass + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: + + # Don't fail on python3 catch clauses: + python3 + +ignore_errors = True \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 6ce0741c..98806750 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,6 +60,11 @@ before_deploy: - sphinx-apidoc -o source/ ../GPy - make html - cd ../ + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; + then export DIST='sdist'; + elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; + then export DIST='bdist_wheel'; + fi; deploy: provider: pypi @@ -67,8 +72,8 @@ deploy: password: secure: "vMEOlP7DQhFJ7hQAKtKC5hrJXFl5BkUt4nXdosWWiw//Kg8E+PPLg88XPI2gqIosir9wwgtbSBBbbwCxkM6uxRNMpoNR8Ixyv9fmSXp4rLl7bbBY768W7IRXKIBjpuEy2brQjoT+CwDDSzUkckHvuUjJDNRvUv8ab4P/qYO1LG4=" on: - tags: true - branch: master - #server: https://testpypi.python.org/pypi - distributions: "bdist_wheel sdist" + tags: false + branch: devel + server: https://testpypi.python.org/pypi + distributions: $DIST skip_cleanup: true diff --git a/GPy/__init__.py b/GPy/__init__.py index d044b2c0..f27ce81d 100644 --- a/GPy/__init__.py +++ b/GPy/__init__.py @@ -4,8 +4,6 @@ import warnings warnings.filterwarnings("ignore", category=DeprecationWarning) from . import core -from .core.parameterization import transformations, priors -constraints = transformations from . import models from . import mappings from . import inference @@ -13,16 +11,23 @@ from . import util from . import examples from . import likelihoods from . import testing -from numpy.testing import Tester from . import kern from . import plotting +# backwards compatibility +import sys +backwards_compatibility = ['lists_and_dicts', 'observable_array', 'ties_and_remappings', 'index_operations'] +for bc in backwards_compatibility: + sys.modules['GPy.core.parameterization.{!s}'.format(bc)] = getattr(core.parameterization, bc) + # Direct imports for convenience: from .core import Model -from .core.parameterization import Param, Parameterized, ObsAr +from .core.parameterization import priors +from .core.parameterization import Param, Parameterized, ObsAr, transformations as constraints from .__version__ import __version__ +from numpy.testing import Tester #@nottest try: #Get rid of nose dependency by only ignoring if you have nose installed @@ -41,27 +46,12 @@ def load(file_or_path): :param file_name: path/to/file.pickle """ # This is the pickling pain when changing _src -> src - try: - try: - import cPickle as pickle - if isinstance(file_or_path, basestring): - with open(file_or_path, 'rb') as f: - m = pickle.load(f) - else: - m = pickle.load(file_or_path) - except: - import pickle - if isinstance(file_or_path, str): - with open(file_or_path, 'rb') as f: - m = pickle.load(f) - else: - m = pickle.load(file_or_path) - except ImportError: - import sys - import inspect - sys.modules['GPy.kern._src'] = kern.src - for name, module in inspect.getmembers(kern.src): - if not name.startswith('_'): - sys.modules['GPy.kern._src.{}'.format(name)] = module - m = load(file_or_path) - return m + import sys + import inspect + sys.modules['GPy.kern._src'] = kern.src + for name, module in inspect.getmembers(kern.src): + if not name.startswith('_'): + sys.modules['GPy.kern._src.{}'.format(name)] = module + sys.modules['GPy.inference.optimization'] = inference.optimization + import paramz + return paramz.load(file_or_path) diff --git a/GPy/__version__.py b/GPy/__version__.py index 8cce3f28..e94731c0 100644 --- a/GPy/__version__.py +++ b/GPy/__version__.py @@ -1 +1 @@ -__version__ = "0.8.8dev5" +__version__ = "0.9.4" diff --git a/GPy/core/__init__.py b/GPy/core/__init__.py index 142eccbf..b3a29859 100644 --- a/GPy/core/__init__.py +++ b/GPy/core/__init__.py @@ -1,12 +1,46 @@ # Copyright (c) 2012-2014, GPy authors (see AUTHORS.txt). # Licensed under the BSD 3-clause license (see LICENSE.txt) -from .model import * -from .parameterization.parameterized import adjust_name_for_printing, Parameterizable -from .parameterization.param import Param, ParamConcatenation -from .parameterization.observable_array import ObsAr +from GPy.core.model import Model +from .parameterization import Param, Parameterized +from . import parameterization from .gp import GP from .svgp import SVGP from .sparse_gp import SparseGP from .mapping import * + + +#=========================================================================== +# Handle priors, this needs to be +# cleaned up at some point +#=========================================================================== +def randomize(self, rand_gen=None, *args, **kwargs): + """ + Randomize the model. + Make this draw from the prior if one exists, else draw from given random generator + + :param rand_gen: np random number generator which takes args and kwargs + :param flaot loc: loc parameter for random number generator + :param float scale: scale parameter for random number generator + :param args, kwargs: will be passed through to random number generator + """ + if rand_gen is None: + rand_gen = np.random.normal + # first take care of all parameters (from N(0,1)) + x = rand_gen(size=self._size_transformed(), *args, **kwargs) + updates = self.update_model() + self.update_model(False) # Switch off the updates + self.optimizer_array = x # makes sure all of the tied parameters get the same init (since there's only one prior object...) + # now draw from prior where possible + x = self.param_array.copy() + [np.put(x, ind, p.rvs(ind.size)) for p, ind in self.priors.items() if not p is None] + unfixlist = np.ones((self.size,),dtype=np.bool) + from paramz.transformations import __fixed__ + unfixlist[self.constraints[__fixed__]] = False + self.param_array.flat[unfixlist] = x.view(np.ndarray).ravel()[unfixlist] + self.update_model(updates) + +Model.randomize = randomize +Param.randomize = randomize +Parameterized.randomize = randomize \ No newline at end of file diff --git a/GPy/core/gp.py b/GPy/core/gp.py index c08e7906..ae710355 100644 --- a/GPy/core/gp.py +++ b/GPy/core/gp.py @@ -2,14 +2,13 @@ # Licensed under the BSD 3-clause license (see LICENSE.txt) import numpy as np -import sys from .. import kern -from .model import Model -from .parameterization import ObsAr +from GPy.core.model import Model +from paramz import ObsAr from .mapping import Mapping from .. import likelihoods from ..inference.latent_function_inference import exact_gaussian_inference, expectation_propagation -from .parameterization.variational import VariationalPosterior +from GPy.core.parameterization.variational import VariationalPosterior import logging import warnings diff --git a/GPy/core/model.py b/GPy/core/model.py index c79c5465..ad09c917 100644 --- a/GPy/core/model.py +++ b/GPy/core/model.py @@ -1,126 +1,18 @@ # Copyright (c) 2012-2014, GPy authors (see AUTHORS.txt). # Licensed under the BSD 3-clause license (see LICENSE.txt) +from .parameterization.priorizable import Priorizable +from paramz import Model as ParamzModel - -from .. import likelihoods -from ..inference import optimization -from ..util.misc import opt_wrapper -from .parameterization import Parameterized -import multiprocessing as mp -import numpy as np -from numpy.linalg.linalg import LinAlgError -import itertools -import sys -from .verbose_optimization import VerboseOptimization -# import numdifftools as ndt -from functools import reduce - -class Model(Parameterized): - _fail_count = 0 # Count of failed optimization steps (see objective) - _allowed_failures = 10 # number of allowed failures +class Model(ParamzModel, Priorizable): def __init__(self, name): super(Model, self).__init__(name) # Parameterized.__init__(self) - self.optimization_runs = [] - self.sampling_runs = [] - self.preferred_optimizer = 'lbfgsb' - from .parameterization.ties_and_remappings import Tie - self.tie = Tie() - self.link_parameter(self.tie, -1) - self.obj_grads = None - self.add_observer(self.tie, self.tie._parameters_changed_notification, priority=-500) def log_likelihood(self): raise NotImplementedError("this needs to be implemented to use the model class") + def _log_likelihood_gradients(self): - return self.gradient.copy() - - def optimize_restarts(self, num_restarts=10, robust=False, verbose=True, parallel=False, num_processes=None, **kwargs): - """ - Perform random restarts of the model, and set the model to the best - seen solution. - - If the robust flag is set, exceptions raised during optimizations will - be handled silently. If _all_ runs fail, the model is reset to the - existing parameter values. - - **Notes** - - :param num_restarts: number of restarts to use (default 10) - :type num_restarts: int - :param robust: whether to handle exceptions silently or not (default False) - :type robust: bool - :param parallel: whether to run each restart as a separate process. It relies on the multiprocessing module. - :type parallel: bool - :param num_processes: number of workers in the multiprocessing pool - :type numprocesses: int - - \*\*kwargs are passed to the optimizer. They can be: - - :param max_f_eval: maximum number of function evaluations - :type max_f_eval: int - :param max_iters: maximum number of iterations - :type max_iters: int - :param messages: whether to display during optimisation - :type messages: bool - - .. note:: If num_processes is None, the number of workes in the - multiprocessing pool is automatically set to the number of processors - on the current machine. - - """ - initial_parameters = self.optimizer_array.copy() - - if parallel: - try: - jobs = [] - pool = mp.Pool(processes=num_processes) - for i in range(num_restarts): - if i>0: self.randomize() - job = pool.apply_async(opt_wrapper, args=(self,), kwds=kwargs) - jobs.append(job) - - pool.close() # signal that no more data coming in - pool.join() # wait for all the tasks to complete - except KeyboardInterrupt: - print("Ctrl+c received, terminating and joining pool.") - pool.terminate() - pool.join() - - for i in range(num_restarts): - try: - if not parallel: - if i>0: self.randomize() - self.optimize(**kwargs) - else: - self.optimization_runs.append(jobs[i].get()) - - if verbose: - print(("Optimization restart {0}/{1}, f = {2}".format(i + 1, num_restarts, self.optimization_runs[-1].f_opt))) - except Exception as e: - if robust: - print(("Warning - optimization restart {0}/{1} failed".format(i + 1, num_restarts))) - else: - raise e - - if len(self.optimization_runs): - i = np.nanargmin([o.f_opt for o in self.optimization_runs]) - self.optimizer_array = self.optimization_runs[i].x_opt - else: - self.optimizer_array = initial_parameters - - def ensure_default_constraints(self, warning=True): - """ - Ensure that any variables which should clearly be positive - have been constrained somehow. The method performs a regular - expression search on parameter names looking for the terms - 'variance', 'lengthscale', 'precision' and 'kappa'. If any of - these terms are present in the name the parameter is - constrained positive. - - DEPRECATED. - """ - raise DeprecationWarning('parameters now have default constraints') + return self.gradient#.copy() def objective_function(self): """ @@ -153,285 +45,4 @@ class Model(Parameterized): (including the MAP prior), so we return it here. If your model is not probabilistic, just return your *negative* gradient here! """ - return -(self._log_likelihood_gradients() + self._log_prior_gradients()) - - def _grads(self, x): - """ - Gets the gradients from the likelihood and the priors. - - Failures are handled robustly. The algorithm will try several times to - return the gradients, and will raise the original exception if - the objective cannot be computed. - - :param x: the parameters of the model. - :type x: np.array - """ - try: - # self._set_params_transformed(x) - self.optimizer_array = x - self.obj_grads = self._transform_gradients(self.objective_function_gradients()) - self._fail_count = 0 - except (LinAlgError, ZeroDivisionError, ValueError): - if self._fail_count >= self._allowed_failures: - raise - self._fail_count += 1 - self.obj_grads = np.clip(self._transform_gradients(self.objective_function_gradients()), -1e100, 1e100) - return self.obj_grads - - def _objective(self, x): - """ - The objective function passed to the optimizer. It combines - the likelihood and the priors. - - Failures are handled robustly. The algorithm will try several times to - return the objective, and will raise the original exception if - the objective cannot be computed. - - :param x: the parameters of the model. - :parameter type: np.array - """ - try: - self.optimizer_array = x - obj = self.objective_function() - self._fail_count = 0 - except (LinAlgError, ZeroDivisionError, ValueError): - if self._fail_count >= self._allowed_failures: - raise - self._fail_count += 1 - return np.inf - return obj - - def _objective_grads(self, x): - try: - self.optimizer_array = x - obj_f, self.obj_grads = self.objective_function(), self._transform_gradients(self.objective_function_gradients()) - self._fail_count = 0 - except (LinAlgError, ZeroDivisionError, ValueError): - if self._fail_count >= self._allowed_failures: - raise - self._fail_count += 1 - obj_f = np.inf - self.obj_grads = np.clip(self._transform_gradients(self.objective_function_gradients()), -1e10, 1e10) - return obj_f, self.obj_grads - - def optimize(self, optimizer=None, start=None, messages=False, max_iters=1000, ipython_notebook=True, clear_after_finish=False, **kwargs): - """ - Optimize the model using self.log_likelihood and self.log_likelihood_gradient, as well as self.priors. - - kwargs are passed to the optimizer. They can be: - - :param max_iters: maximum number of function evaluations - :type max_iters: int - :messages: True: Display messages during optimisation, "ipython_notebook": - :type messages: bool"string - :param optimizer: which optimizer to use (defaults to self.preferred optimizer) - :type optimizer: string - - Valid optimizers are: - - 'scg': scaled conjugate gradient method, recommended for stability. - See also GPy.inference.optimization.scg - - 'fmin_tnc': truncated Newton method (see scipy.optimize.fmin_tnc) - - 'simplex': the Nelder-Mead simplex method (see scipy.optimize.fmin), - - 'lbfgsb': the l-bfgs-b method (see scipy.optimize.fmin_l_bfgs_b), - - 'sgd': stochastic gradient decsent (see scipy.optimize.sgd). For experts only! - - - """ - if self.is_fixed or self.size == 0: - print('nothing to optimize') - - if not self.update_model(): - print("updates were off, setting updates on again") - self.update_model(True) - - if start == None: - start = self.optimizer_array - - if optimizer is None: - optimizer = self.preferred_optimizer - - if isinstance(optimizer, optimization.Optimizer): - opt = optimizer - opt.model = self - else: - optimizer = optimization.get_optimizer(optimizer) - opt = optimizer(x_init=start, model=self, max_iters=max_iters, **kwargs) - - with VerboseOptimization(self, opt, maxiters=max_iters, verbose=messages, ipython_notebook=ipython_notebook, clear_after_finish=clear_after_finish) as vo: - opt.run(f_fp=self._objective_grads, f=self._objective, fp=self._grads) - vo.finish(opt) - - self.optimization_runs.append(opt) - - self.optimizer_array = opt.x_opt - - def optimize_SGD(self, momentum=0.1, learning_rate=0.01, iterations=20, **kwargs): - # assert self.Y.shape[1] > 1, "SGD only works with D > 1" - sgd = SGD.StochasticGD(self, iterations, learning_rate, momentum, **kwargs) # @UndefinedVariable - sgd.run() - self.optimization_runs.append(sgd) - - def _checkgrad(self, target_param=None, verbose=False, step=1e-6, tolerance=1e-3, df_tolerance=1e-12): - """ - Check the gradient of the ,odel by comparing to a numerical - estimate. If the verbose flag is passed, individual - components are tested (and printed) - - :param verbose: If True, print a "full" checking of each parameter - :type verbose: bool - :param step: The size of the step around which to linearise the objective - :type step: float (default 1e-6) - :param tolerance: the tolerance allowed (see note) - :type tolerance: float (default 1e-3) - - Note:- - The gradient is considered correct if the ratio of the analytical - and numerical gradients is within of unity. - - The *dF_ratio* indicates the limit of numerical accuracy of numerical gradients. - If it is too small, e.g., smaller than 1e-12, the numerical gradients are usually - not accurate enough for the tests (shown with blue). - """ - x = self.optimizer_array.copy() - - if not verbose: - # make sure only to test the selected parameters - if target_param is None: - transformed_index = range(len(x)) - else: - transformed_index = self._raveled_index_for(target_param) - if self._has_fixes(): - indices = np.r_[:self.size] - which = (transformed_index[:, None] == indices[self._fixes_][None, :]).nonzero() - transformed_index = (indices - (~self._fixes_).cumsum())[transformed_index[which[0]]] - - if transformed_index.size == 0: - print("No free parameters to check") - return - - # just check the global ratio - dx = np.zeros(x.shape) - dx[transformed_index] = step * (np.sign(np.random.uniform(-1, 1, transformed_index.size)) if transformed_index.size != 2 else 1.) - - # evaulate around the point x - f1 = self._objective(x + dx) - f2 = self._objective(x - dx) - gradient = self._grads(x) - - dx = dx[transformed_index] - gradient = gradient[transformed_index] - - denominator = (2 * np.dot(dx, gradient)) - global_ratio = (f1 - f2) / np.where(denominator == 0., 1e-32, denominator) - global_diff = np.abs(f1 - f2) < tolerance and np.allclose(gradient, 0, atol=tolerance) - if global_ratio is np.nan: - global_ratio = 0 - return np.abs(1. - global_ratio) < tolerance or global_diff - else: - # check the gradient of each parameter individually, and do some pretty printing - try: - names = self._get_param_names() - except NotImplementedError: - names = ['Variable %i' % i for i in range(len(x))] - # Prepare for pretty-printing - header = ['Name', 'Ratio', 'Difference', 'Analytical', 'Numerical', 'dF_ratio'] - max_names = max([len(names[i]) for i in range(len(names))] + [len(header[0])]) - float_len = 10 - cols = [max_names] - cols.extend([max(float_len, len(header[i])) for i in range(1, len(header))]) - cols = np.array(cols) + 5 - header_string = ["{h:^{col}}".format(h=header[i], col=cols[i]) for i in range(len(cols))] - header_string = list(map(lambda x: '|'.join(x), [header_string])) - separator = '-' * len(header_string[0]) - print('\n'.join([header_string[0], separator])) - if target_param is None: - param_index = range(len(x)) - transformed_index = param_index - else: - param_index = self._raveled_index_for(target_param) - if self._has_fixes(): - indices = np.r_[:self.size] - which = (param_index[:, None] == indices[self._fixes_][None, :]).nonzero() - param_index = param_index[which[0]] - transformed_index = (indices - (~self._fixes_).cumsum())[param_index] - # print param_index, transformed_index - else: - transformed_index = param_index - - if param_index.size == 0: - print("No free parameters to check") - return - - gradient = self._grads(x).copy() - np.where(gradient == 0, 1e-312, gradient) - ret = True - for nind, xind in zip(param_index, transformed_index): - xx = x.copy() - xx[xind] += step - f1 = float(self._objective(xx)) - xx[xind] -= 2.*step - f2 = float(self._objective(xx)) - #Avoid divide by zero, if any of the values are above 1e-15, otherwise both values are essentiall - #the same - if f1 > 1e-15 or f1 < -1e-15 or f2 > 1e-15 or f2 < -1e-15: - df_ratio = np.abs((f1 - f2) / min(f1, f2)) - else: - df_ratio = 1.0 - df_unstable = df_ratio < df_tolerance - numerical_gradient = (f1 - f2) / (2. * step) - if np.all(gradient[xind] == 0): ratio = (f1 - f2) == gradient[xind] - else: ratio = (f1 - f2) / (2. * step * gradient[xind]) - difference = np.abs(numerical_gradient - gradient[xind]) - - if (np.abs(1. - ratio) < tolerance) or np.abs(difference) < tolerance: - formatted_name = "\033[92m {0} \033[0m".format(names[nind]) - ret &= True - else: - formatted_name = "\033[91m {0} \033[0m".format(names[nind]) - ret &= False - if df_unstable: - formatted_name = "\033[94m {0} \033[0m".format(names[nind]) - - r = '%.6f' % float(ratio) - d = '%.6f' % float(difference) - g = '%.6f' % gradient[xind] - ng = '%.6f' % float(numerical_gradient) - df = '%1.e' % float(df_ratio) - grad_string = "{0:<{c0}}|{1:^{c1}}|{2:^{c2}}|{3:^{c3}}|{4:^{c4}}|{5:^{c5}}".format(formatted_name, r, d, g, ng, df, c0=cols[0] + 9, c1=cols[1], c2=cols[2], c3=cols[3], c4=cols[4], c5=cols[5]) - print(grad_string) - - self.optimizer_array = x - return ret - - def _repr_html_(self): - """Representation of the model in html for notebook display.""" - model_details = [['Model', self.name + '
'], - ['Log-likelihood', '{}
'.format(float(self.log_likelihood()))], - ["Number of Parameters", '{}
'.format(self.size)], - ["Number of Optimization Parameters", '{}
'.format(self._size_transformed())], - ["Updates", '{}
'.format(self._update_on)], - ] - from operator import itemgetter - to_print = ["""\n"""] + ["

"] + ["{}: {}".format(name, detail) for name, detail in model_details] + ["

"] - to_print.append(super(Model, self)._repr_html_()) - return "\n".join(to_print) - - def __str__(self, VT100=True): - model_details = [['Name', self.name], - ['Log-likelihood', '{}'.format(float(self.log_likelihood()))], - ["Number of Parameters", '{}'.format(self.size)], - ["Number of Optimization Parameters", '{}'.format(self._size_transformed())], - ["Updates", '{}'.format(self._update_on)], - ] - from operator import itemgetter - max_len = reduce(lambda a, b: max(len(b[0]), a), model_details, 0) - to_print = [""] + ["{0:{l}} : {1}".format(name, detail, l=max_len) for name, detail in model_details] + ["Parameters:"] - to_print.append(super(Model, self).__str__(VT100=VT100)) - return "\n".join(to_print) - + return -(self._log_likelihood_gradients() + self._log_prior_gradients()) \ No newline at end of file diff --git a/GPy/core/parameterization/__init__.py b/GPy/core/parameterization/__init__.py index de736671..11b75730 100644 --- a/GPy/core/parameterization/__init__.py +++ b/GPy/core/parameterization/__init__.py @@ -1,5 +1,9 @@ -# Copyright (c) 2012, GPy authors (see AUTHORS.txt). +# Copyright (c) 2012-2014, GPy authors (see AUTHORS.txt). # Licensed under the BSD 3-clause license (see LICENSE.txt) -from .param import Param, ObsAr +from .param import Param from .parameterized import Parameterized +from paramz import transformations + +from paramz.core import lists_and_dicts, index_operations, observable_array, observable +from paramz import ties_and_remappings, ObsAr \ No newline at end of file diff --git a/GPy/core/parameterization/domains.py b/GPy/core/parameterization/domains.py deleted file mode 100644 index c04b414f..00000000 --- a/GPy/core/parameterization/domains.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2012, GPy authors (see AUTHORS.txt). -# Licensed under the BSD 3-clause license (see LICENSE.txt) - -""" -(Hyper-)Parameter domains defined for :py:mod:`~GPy.core.priors` and :py:mod:`~GPy.kern`. -These domains specify the legitimate realm of the parameters to live in. - -:const:`~GPy.core.domains._REAL` : - real domain, all values in the real numbers are allowed - -:const:`~GPy.core.domains._POSITIVE`: - positive domain, only positive real values are allowed - -:const:`~GPy.core.domains._NEGATIVE`: - same as :const:`~GPy.core.domains._POSITIVE`, but only negative values are allowed - -:const:`~GPy.core.domains._BOUNDED`: - only values within the bounded range are allowed, - the bounds are specified withing the object with the bounded range -""" - -_REAL = 'real' -_POSITIVE = "positive" -_NEGATIVE = 'negative' -_BOUNDED = 'bounded' diff --git a/GPy/core/parameterization/index_operations.py b/GPy/core/parameterization/index_operations.py deleted file mode 100644 index 76b5f79e..00000000 --- a/GPy/core/parameterization/index_operations.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright (c) 2014, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) - -import numpy -from numpy.lib.function_base import vectorize -from .lists_and_dicts import IntArrayDict -from functools import reduce -from .transformations import Transformation - -def extract_properties_to_index(index, props): - prop_index = dict() - for i, cl in enumerate(props): - for c in cl: - ind = prop_index.get(c, list()) - ind.append(index[i]) - prop_index[c] = ind - - for c, i in prop_index.items(): - prop_index[c] = numpy.array(i, dtype=int) - - return prop_index - - -class ParameterIndexOperations(object): - """ - This object wraps a dictionary, whos keys are _operations_ that we'd like - to apply to a parameter array, and whose values are np integer arrays which - index the parameter array appropriately. - - A model instance will contain one instance of this class for each thing - that needs indexing (i.e. constraints, ties and priors). Parameters within - the model constain instances of the ParameterIndexOperationsView class, - which can map from a 'local' index (starting 0) to this global index. - - Here's an illustration: - - #======================================================================= - model : 0 1 2 3 4 5 6 7 8 9 - key1: 4 5 - key2: 7 8 - - param1: 0 1 2 3 4 5 - key1: 2 3 - key2: 5 - - param2: 0 1 2 3 4 - key1: 0 - key2: 2 3 - #======================================================================= - - The views of this global index have a subset of the keys in this global - (model) index. - - Adding a new key (e.g. a constraint) to a view will cause the view to pass - the new key to the global index, along with the local index and an offset. - This global index then stores the key and the appropriate global index - (which can be seen by the view). - - See also: - ParameterIndexOperationsView - - """ - _offset = 0 - def __init__(self, constraints=None): - self._properties = IntArrayDict() - if constraints is not None: - #python 3 fix - #for t, i in constraints.iteritems(): - for t, i in constraints.items(): - self.add(t, i) - - #iteritems has gone in python 3 - #def iteritems(self): - # return self._properties.iteritems() - - def items(self): - return self._properties.items() - - def properties(self): - return self._properties.keys() - - def iterproperties(self): - return iter(self._properties) - - def shift_right(self, start, size): - for ind in self.iterindices(): - toshift = ind>=start - ind[toshift] += size - - def shift_left(self, start, size): - for v, ind in list(self.items()): - todelete = (ind>=start) * (ind=start - if toshift.size != 0: - ind[toshift] -= size - if ind.size != 0: self._properties[v] = ind - else: del self._properties[v] - - def clear(self): - self._properties.clear() - - @property - def size(self): - return reduce(lambda a,b: a+b.size, self.iterindices(), 0) - - def iterindices(self): - try: - return self._properties.itervalues() - except AttributeError: - #Changed this from itervalues to values for Py3 compatibility. It didn't break the test suite. - return self._properties.values() - - def indices(self): - return self._properties.values() - - def properties_for(self, index): - """ - Returns a list of properties, such that each entry in the list corresponds - to the element of the index given. - - Example: - let properties: 'one':[1,2,3,4], 'two':[3,5,6] - - >>> properties_for([2,3,5]) - [['one'], ['one', 'two'], ['two']] - """ - return vectorize(lambda i: [prop for prop in self.iterproperties() if i in self[prop]], otypes=[list])(index) - - def properties_to_index_dict(self, index): - """ - Return a dictionary, containing properties as keys and indices as index - Thus, the indices for each constraint, which is contained will be collected as - one dictionary - - Example: - let properties: 'one':[1,2,3,4], 'two':[3,5,6] - - >>> properties_to_index_dict([2,3,5]) - {'one':[2,3], 'two':[3,5]} - """ - props = self.properties_for(index) - prop_index = extract_properties_to_index(index, props) - return prop_index - - def add(self, prop, indices): - self._properties[prop] = combine_indices(self._properties[prop], indices) - - def remove(self, prop, indices): - if prop in self._properties: - diff = remove_indices(self[prop], indices) - removed = numpy.intersect1d(self[prop], indices, True) - if not index_empty(diff): - self._properties[prop] = diff - else: - del self._properties[prop] - return removed.astype(int) - return numpy.array([]).astype(int) - - def update(self, parameter_index_view, offset=0): - #py3 fix - #for i, v in parameter_index_view.iteritems(): - for i, v in parameter_index_view.items(): - self.add(i, v+offset) - - def copy(self): - return self.__deepcopy__(None) - - def __deepcopy__(self, memo): - #py3 fix - #return ParameterIndexOperations(dict(self.iteritems())) - return ParameterIndexOperations(dict(self.items())) - - def __getitem__(self, prop): - return self._properties[prop] - - def __delitem__(self, prop): - del self._properties[prop] - - def __str__(self, *args, **kwargs): - import pprint - return pprint.pformat(dict(self._properties)) - -def combine_indices(arr1, arr2): - return numpy.union1d(arr1, arr2) - -def remove_indices(arr, to_remove): - return numpy.setdiff1d(arr, to_remove, True) - -def index_empty(index): - return numpy.size(index) == 0 - -class ParameterIndexOperationsView(object): - def __init__(self, param_index_operations, offset, size): - self._param_index_ops = param_index_operations - self._offset = offset - self._size = size - - def __getstate__(self): - return [self._param_index_ops, self._offset, self._size] - - def __setstate__(self, state): - self._param_index_ops = state[0] - self._offset = state[1] - self._size = state[2] - - def _filter_index(self, ind): - return ind[(ind >= self._offset) * (ind < (self._offset + self._size))] - self._offset - - #iteritems has gone in python 3. It has been renamed items() - def items(self): - _items_list = list(self._param_index_ops.items()) - for i, ind in _items_list: - ind2 = self._filter_index(ind) - if ind2.size > 0: - yield i, ind2 - - #Python 3 items() is now implemented as per py2 iteritems - #def items(self): - # return [[i,v] for i,v in self.iteritems()] - - def properties(self): - return [i for i in self.iterproperties()] - - - def iterproperties(self): - #py3 fix - #for i, _ in self.iteritems(): - for i, _ in self.items(): - yield i - - - def shift_right(self, start, size): - self._param_index_ops.shift_right(start+self._offset, size) - - def shift_left(self, start, size): - self._param_index_ops.shift_left(start+self._offset, size) - - def clear(self): - for i, ind in self.items(): - self._param_index_ops.remove(i, ind+self._offset) - - @property - def size(self): - return reduce(lambda a,b: a+b.size, self.iterindices(), 0) - - - def iterindices(self): - #py3 fix - #for _, ind in self.iteritems(): - for _, ind in self.items(): - yield ind - - - def indices(self): - return [ind for ind in self.iterindices()] - - - def properties_for(self, index): - """ - Returns a list of properties, such that each entry in the list corresponds - to the element of the index given. - - Example: - let properties: 'one':[1,2,3,4], 'two':[3,5,6] - - >>> properties_for([2,3,5]) - [['one'], ['one', 'two'], ['two']] - """ - return vectorize(lambda i: [prop for prop in self.iterproperties() if i in self[prop]], otypes=[list])(index) - - def properties_to_index_dict(self, index): - """ - Return a dictionary, containing properties as keys and indices as index - Thus, the indices for each constraint, which is contained will be collected as - one dictionary - - Example: - let properties: 'one':[1,2,3,4], 'two':[3,5,6] - - >>> properties_to_index_dict([2,3,5]) - {'one':[2,3], 'two':[3,5]} - """ - return extract_properties_to_index(index, self.properties_for(index)) - - - def add(self, prop, indices): - self._param_index_ops.add(prop, indices+self._offset) - - - def remove(self, prop, indices): - removed = self._param_index_ops.remove(prop, numpy.array(indices)+self._offset) - if removed.size > 0: - return removed-self._offset - return removed - - - def __getitem__(self, prop): - ind = self._filter_index(self._param_index_ops[prop]) - return ind - - def __delitem__(self, prop): - self.remove(prop, self[prop]) - - def __str__(self, *args, **kwargs): - import pprint - #py3 fixes - #return pprint.pformat(dict(self.iteritems())) - return pprint.pformat(dict(self.items())) - - def update(self, parameter_index_view, offset=0): - #py3 fixes - #for i, v in parameter_index_view.iteritems(): - for i, v in parameter_index_view.items(): - self.add(i, v+offset) - - - def copy(self): - return self.__deepcopy__(None) - - def __deepcopy__(self, memo): - #py3 fix - #return ParameterIndexOperations(dict(self.iteritems())) - return ParameterIndexOperations(dict(self.items())) - pass - diff --git a/GPy/core/parameterization/lists_and_dicts.py b/GPy/core/parameterization/lists_and_dicts.py deleted file mode 100644 index 2d774a76..00000000 --- a/GPy/core/parameterization/lists_and_dicts.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) 2014, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) - -from collections import defaultdict -import weakref - -def intarray_default_factory(): - import numpy as np - return np.int_([]) - -class IntArrayDict(defaultdict): - def __init__(self, default_factory=None): - """ - Default will be self._default, if not set otherwise - """ - defaultdict.__init__(self, intarray_default_factory) - -class ArrayList(list): - """ - List to store ndarray-likes in. - It will look for 'is' instead of calling __eq__ on each element. - """ - def __contains__(self, other): - for el in self: - if el is other: - return True - return False - - def index(self, item): - index = 0 - for el in self: - if el is item: - return index - index += 1 - raise ValueError("{} is not in list".format(item)) - pass - -class ObserverList(object): - """ - A list which containts the observables. - It only holds weak references to observers, such that unbound - observers dont dangle in memory. - """ - def __init__(self): - self._poc = [] - - def __getitem__(self, ind): - p,o,c = self._poc[ind] - return p, o(), c - - def remove(self, priority, observer, callble): - """ - Remove one observer, which had priority and callble. - """ - self.flush() - for i in range(len(self) - 1, -1, -1): - p,o,c = self[i] - if priority==p and observer==o and callble==c: - del self._poc[i] - - def __repr__(self): - return self._poc.__repr__() - - def add(self, priority, observer, callble): - """ - Add an observer with priority and callble - """ - if observer is not None: - ins = 0 - for pr, _, _ in self: - if priority > pr: - break - ins += 1 - self._poc.insert(ins, (priority, weakref.ref(observer), callble)) - - def __str__(self): - from . import ObsAr, Param - from .parameter_core import Parameterizable - ret = [] - curr_p = None - - def frmt(o): - if isinstance(o, ObsAr): - return 'ObsArr <{}>'.format(hex(id(o))) - elif isinstance(o, (Param,Parameterizable)): - return '{}'.format(o.hierarchy_name()) - else: - return repr(o) - for p, o, c in self: - curr = '' - if curr_p != p: - pre = "{!s}: ".format(p) - curr_pre = pre - else: curr_pre = " "*len(pre) - curr_p = p - curr += curr_pre - - ret.append(curr + ", ".join([frmt(o), str(c)])) - return '\n'.join(ret) - - def flush(self): - """ - Make sure all weak references, which point to nothing are flushed (deleted) - """ - self._poc = [(p,o,c) for p,o,c in self._poc if o() is not None] - - def __iter__(self): - self.flush() - for p, o, c in self._poc: - yield p, o(), c - - def __len__(self): - self.flush() - return self._poc.__len__() - - def __deepcopy__(self, memo): - s = ObserverList() - for p,o,c in self: - import copy - s.add(p, copy.deepcopy(o, memo), copy.deepcopy(c, memo)) - s.flush() - return s - - def __getstate__(self): - self.flush() - from ...util.caching import Cacher - obs = [] - for p, o, c in self: - if (getattr(o, c.__name__, None) is not None - and not isinstance(o, Cacher)): - obs.append((p,o,c.__name__)) - return obs - - def __setstate__(self, state): - self._poc = [] - for p, o, c in state: - self.add(p,o,getattr(o, c)) - - pass diff --git a/GPy/core/parameterization/observable.py b/GPy/core/parameterization/observable.py deleted file mode 100644 index 0836b5d6..00000000 --- a/GPy/core/parameterization/observable.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2014, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) - - -class Observable(object): - """ - Observable pattern for parameterization. - - This Object allows for observers to register with self and a (bound!) function - as an observer. Every time the observable changes, it sends a notification with - self as only argument to all its observers. - """ - def __init__(self, *args, **kwargs): - super(Observable, self).__init__() - from .lists_and_dicts import ObserverList - self.observers = ObserverList() - self._update_on = True - - def set_updates(self, on=True): - self._update_on = on - - def add_observer(self, observer, callble, priority=0): - """ - Add an observer `observer` with the callback `callble` - and priority `priority` to this observers list. - """ - self.observers.add(priority, observer, callble) - - def remove_observer(self, observer, callble=None): - """ - Either (if callble is None) remove all callables, - which were added alongside observer, - or remove callable `callble` which was added alongside - the observer `observer`. - """ - to_remove = [] - for poc in self.observers: - _, obs, clble = poc - if callble is not None: - if (obs is observer) and (callble == clble): - to_remove.append(poc) - else: - if obs is observer: - to_remove.append(poc) - for r in to_remove: - self.observers.remove(*r) - - def notify_observers(self, which=None, min_priority=None): - """ - Notifies all observers. Which is the element, which kicked off this - notification loop. The first argument will be self, the second `which`. - - NOTE: notifies only observers with priority p > min_priority! - ^^^^^^^^^^^^^^^^ - :param min_priority: only notify observers with priority > min_priority - if min_priority is None, notify all observers in order - """ - if self._update_on: - if which is None: - which = self - if min_priority is None: - [callble(self, which=which) for _, _, callble in self.observers] - else: - for p, _, callble in self.observers: - if p <= min_priority: - break - callble(self, which=which) - - def change_priority(self, observer, callble, priority): - self.remove_observer(observer, callble) - self.add_observer(observer, callble, priority) diff --git a/GPy/core/parameterization/observable_array.py b/GPy/core/parameterization/observable_array.py deleted file mode 100644 index c6fea497..00000000 --- a/GPy/core/parameterization/observable_array.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright (c) 2014, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) - - -import numpy as np -from .parameter_core import Pickleable -from .observable import Observable - -class ObsAr(np.ndarray, Pickleable, Observable): - """ - An ndarray which reports changes to its observers. - The observers can add themselves with a callable, which - will be called every time this array changes. The callable - takes exactly one argument, which is this array itself. - """ - __array_priority__ = -1 # Never give back ObsAr - def __new__(cls, input_array, *a, **kw): - # allways make a copy of input paramters, as we need it to be in C order: - if not isinstance(input_array, ObsAr): - obj = np.atleast_1d(np.require(input_array, dtype=np.float64, requirements=['W', 'C'])).view(cls) - else: obj = input_array - super(ObsAr, obj).__init__(*a, **kw) - return obj - - def __array_finalize__(self, obj): - # see InfoArray.__array_finalize__ for comments - if obj is None: return - self.observers = getattr(obj, 'observers', None) - - def __array_wrap__(self, out_arr, context=None): - return out_arr.view(np.ndarray) - - def _setup_observers(self): - # do not setup anything, as observable arrays do not have default observers - pass - - @property - def values(self): - return self.view(np.ndarray) - - def copy(self): - from .lists_and_dicts import ObserverList - memo = {} - memo[id(self)] = self - memo[id(self.observers)] = ObserverList() - return self.__deepcopy__(memo) - - def __deepcopy__(self, memo): - s = self.__new__(self.__class__, input_array=self.view(np.ndarray).copy()) - memo[id(self)] = s - import copy - Pickleable.__setstate__(s, copy.deepcopy(self.__getstate__(), memo)) - return s - - def __reduce__(self): - func, args, state = super(ObsAr, self).__reduce__() - return func, args, (state, Pickleable.__getstate__(self)) - - def __setstate__(self, state): - np.ndarray.__setstate__(self, state[0]) - Pickleable.__setstate__(self, state[1]) - - def __setitem__(self, s, val): - super(ObsAr, self).__setitem__(s, val) - self.notify_observers() - - def __getslice__(self, start, stop): - return self.__getitem__(slice(start, stop)) - - def __setslice__(self, start, stop, val): - return self.__setitem__(slice(start, stop), val) - - def __ilshift__(self, *args, **kwargs): - r = np.ndarray.__ilshift__(self, *args, **kwargs) - self.notify_observers() - return r - - def __irshift__(self, *args, **kwargs): - r = np.ndarray.__irshift__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __ixor__(self, *args, **kwargs): - r = np.ndarray.__ixor__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __ipow__(self, *args, **kwargs): - r = np.ndarray.__ipow__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __ifloordiv__(self, *args, **kwargs): - r = np.ndarray.__ifloordiv__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __isub__(self, *args, **kwargs): - r = np.ndarray.__isub__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __ior__(self, *args, **kwargs): - r = np.ndarray.__ior__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __itruediv__(self, *args, **kwargs): - r = np.ndarray.__itruediv__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __idiv__(self, *args, **kwargs): - r = np.ndarray.__idiv__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __iand__(self, *args, **kwargs): - r = np.ndarray.__iand__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __imod__(self, *args, **kwargs): - r = np.ndarray.__imod__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __iadd__(self, *args, **kwargs): - r = np.ndarray.__iadd__(self, *args, **kwargs) - self.notify_observers() - return r - - - def __imul__(self, *args, **kwargs): - r = np.ndarray.__imul__(self, *args, **kwargs) - self.notify_observers() - return r diff --git a/GPy/core/parameterization/param.py b/GPy/core/parameterization/param.py index 8fdd744e..69b93548 100644 --- a/GPy/core/parameterization/param.py +++ b/GPy/core/parameterization/param.py @@ -1,496 +1,10 @@ # Copyright (c) 2014, Max Zwiessele # Licensed under the BSD 3-clause license (see LICENSE.txt) -import itertools -import numpy -np = numpy -from .parameter_core import Parameterizable, adjust_name_for_printing, Pickleable -from .observable_array import ObsAr -from functools import reduce +from paramz import Param +from .priorizable import Priorizable +from paramz.transformations import __fixed__ +import logging, numpy as np -###### printing -__constraints_name__ = "Constraint" -__index_name__ = "Index" -__tie_name__ = "Tied to" -__priors_name__ = "Prior" -__precision__ = numpy.get_printoptions()['precision'] # numpy printing precision used, sublassing numpy ndarray after all -__print_threshold__ = 5 -###### - -class Param(Parameterizable, ObsAr): - """ - Parameter object for GPy models. - - :param str name: name of the parameter to be printed - :param input_array: array which this parameter handles - :type input_array: numpy.ndarray - :param default_constraint: The default constraint for this parameter - :type default_constraint: - - You can add/remove constraints by calling constrain on the parameter itself, e.g: - - - self[:,1].constrain_positive() - - self[0].tie_to(other) - - self.untie() - - self[:3,:].unconstrain() - - self[1].fix() - - Fixing parameters will fix them to the value they are right now. If you change - the fixed value, it will be fixed to the new value! - - Important Note: - Multilevel indexing (e.g. self[:2][1:]) is not supported and might lead to unexpected behaviour. - Try to index in one go, using boolean indexing or the numpy builtin - np.index function. - - See :py:class:`GPy.core.parameterized.Parameterized` for more details on constraining etc. - - """ - __array_priority__ = -1 # Never give back Param - _fixes_ = None - parameters = [] - def __new__(cls, name, input_array, default_constraint=None): - obj = numpy.atleast_1d(super(Param, cls).__new__(cls, input_array=input_array)) - obj._current_slice_ = (slice(obj.shape[0]),) - obj._realshape_ = obj.shape - obj._realsize_ = obj.size - obj._realndim_ = obj.ndim - obj._original_ = obj - return obj - - def __init__(self, name, input_array, default_constraint=None, *a, **kw): - self._in_init_ = True - super(Param, self).__init__(name=name, default_constraint=default_constraint, *a, **kw) - self._in_init_ = False - - def build_pydot(self,G): - import pydot - node = pydot.Node(id(self), shape='trapezium', label=self.name)#, fontcolor='white', color='white') - G.add_node(node) - for _, o, _ in self.observers: - label = o.name if hasattr(o, 'name') else str(o) - observed_node = pydot.Node(id(o), label=label) - G.add_node(observed_node) - edge = pydot.Edge(str(id(self)), str(id(o)), color='darkorange2', arrowhead='vee') - G.add_edge(edge) - - return node - - def __array_finalize__(self, obj): - # see InfoArray.__array_finalize__ for comments - if obj is None: return - super(Param, self).__array_finalize__(obj) - self._parent_ = getattr(obj, '_parent_', None) - self._parent_index_ = getattr(obj, '_parent_index_', None) - self._default_constraint_ = getattr(obj, '_default_constraint_', None) - self._current_slice_ = getattr(obj, '_current_slice_', 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) - self._name = getattr(obj, '_name', None) - self._gradient_array_ = getattr(obj, '_gradient_array_', None) - self._update_on = getattr(obj, '_update_on', None) - self.constraints = getattr(obj, 'constraints', None) - self.priors = getattr(obj, 'priors', None) - - @property - def param_array(self): - """ - As we are a leaf, this just returns self - """ - return self - - @property - def values(self): - """ - Return self as numpy array view - """ - return self.view(np.ndarray) - - @property - def gradient(self): - """ - Return a view on the gradient, which is in the same shape as this parameter is. - Note: this is not the real gradient array, it is just a view on it. - - To work on the real gradient array use: self.full_gradient - """ - if getattr(self, '_gradient_array_', None) is None: - self._gradient_array_ = numpy.empty(self._realshape_, dtype=numpy.float64) - return self._gradient_array_#[self._current_slice_] - - @gradient.setter - def gradient(self, val): - self._gradient_array_[:] = val - - #=========================================================================== - # Array operations -> done - #=========================================================================== - def __getitem__(self, s, *args, **kwargs): - if not isinstance(s, tuple): - s = (s,) - #if not reduce(lambda a, b: a or numpy.any(b is Ellipsis), s, False) and len(s) <= self.ndim: - # s += (Ellipsis,) - new_arr = super(Param, self).__getitem__(s, *args, **kwargs) - try: - new_arr._current_slice_ = s - new_arr._gradient_array_ = self.gradient[s] - new_arr._original_ = self._original_ - except AttributeError: pass # returning 0d array or float, double etc - return new_arr - - 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(slice_index) - if ind.ndim < 2: ind = ind[:, None] - return numpy.asarray(numpy.apply_along_axis(lambda x: numpy.sum(extended_realshape * x), 1, ind), dtype=int) - - def _raveled_index_for(self, obj): - return self._raveled_index() - - #=========================================================================== - # Constrainable - #=========================================================================== - def _ensure_fixes(self): - if not self._has_fixes(): self._fixes_ = numpy.ones(self._realsize_, dtype=bool) - - #=========================================================================== - # Convenience - #=========================================================================== - @property - def is_fixed(self): - from .transformations import __fixed__ - return self.constraints[__fixed__].size == self.size - - def _get_original(self, param): - return self._original_ - - #=========================================================================== - # Pickling and copying - #=========================================================================== - def copy(self): - return Parameterizable.copy(self, which=self) - - def __deepcopy__(self, memo): - s = self.__new__(self.__class__, name=self.name, input_array=self.view(numpy.ndarray).copy()) - memo[id(self)] = s - import copy - Pickleable.__setstate__(s, copy.deepcopy(self.__getstate__(), memo)) - return s - - def _setup_observers(self): - """ - Setup the default observers - - 1: pass through to parent, if present - """ - if self.has_parent(): - self.add_observer(self._parent_, self._parent_._pass_through_notify_observers, -np.inf) - - #=========================================================================== - # Printing -> done - #=========================================================================== - @property - def _description_str(self): - if self.size <= 1: - return [str(self.view(numpy.ndarray)[0])] - else: return [str(self.shape)] - def parameter_names(self, add_self=False, adjust_for_printing=False, recursive=True): - # this is just overwrighting the parameterized calls to parameter names, in order to maintain OOP - if adjust_for_printing: - return [adjust_name_for_printing(self.name)] - return [self.name] - @property - def flattened_parameters(self): - return [self] - @property - def parameter_shapes(self): - return [self.shape] - @property - def num_params(self): - return 0 - @property - def _constraints_str(self): - #py3 fix - #return [' '.join(map(lambda c: str(c[0]) if c[1].size == self._realsize_ else "{" + str(c[0]) + "}", self.constraints.iteritems()))] - return [' '.join(map(lambda c: str(c[0]) if c[1].size == self._realsize_ else "{" + str(c[0]) + "}", self.constraints.items()))] - @property - def _priors_str(self): - #py3 fix - #return [' '.join(map(lambda c: str(c[0]) if c[1].size == self._realsize_ else "{" + str(c[0]) + "}", self.priors.iteritems()))] - return [' '.join(map(lambda c: str(c[0]) if c[1].size == self._realsize_ else "{" + str(c[0]) + "}", self.priors.items()))] - @property - def _ties_str(self): - return [''] - def _ties_for(self, ravi): - return [['N/A']]*ravi.size - def __repr__(self, *args, **kwargs): - name = "\033[1m{x:s}\033[0;0m:\n".format( - x=self.hierarchy_name()) - return name + super(Param, self).__repr__(*args, **kwargs) - def _indices(self, slice_index=None): - # get a int-array containing all indices in the first axis. - if slice_index is None: - slice_index = self._current_slice_ - try: - indices = np.indices(self._realshape_, dtype=int) - indices = indices[(slice(None),)+slice_index] - indices = np.rollaxis(indices, 0, indices.ndim).reshape(-1,self._realndim_) - #print indices_ - #if not np.all(indices==indices__): - # import ipdb; ipdb.set_trace() - except: - indices = np.indices(self._realshape_, dtype=int) - indices = indices[(slice(None),)+slice_index] - indices = np.rollaxis(indices, 0, indices.ndim) - return indices - def _max_len_names(self, gen, header): - gen = map(lambda x: " ".join(map(str, x)), gen) - return reduce(lambda a, b:max(a, len(b)), gen, len(header)) - def _max_len_values(self): - return reduce(lambda a, b:max(a, len("{x:=.{0}g}".format(__precision__, x=b))), self.flat, len(self.hierarchy_name())) - def _max_len_index(self, ind): - return reduce(lambda a, b:max(a, len(str(b))), ind, len(__index_name__)) - def _short(self): - # short string to print - name = self.hierarchy_name() - if self._realsize_ < 2: - return name - ind = self._indices() - if ind.size > 4: indstr = ','.join(map(str, ind[:2])) + "..." + ','.join(map(str, ind[-2:])) - else: indstr = ','.join(map(str, ind)) - return name + '[' + indstr + ']' - - def _repr_html_(self, constr_matrix=None, indices=None, prirs=None, ties=None): - """Representation of the parameter in html for notebook display.""" - filter_ = self._current_slice_ - vals = self.flat - if indices is None: indices = self._indices(filter_) - ravi = self._raveled_index(filter_) - if constr_matrix is None: constr_matrix = self.constraints.properties_for(ravi) - if prirs is None: prirs = self.priors.properties_for(ravi) - if ties is None: ties = self._ties_for(ravi) - ties = [' '.join(map(lambda x: x, t)) for t in ties] - header_format = """ - - {i} - {x} - {c} - {p} - {t} -""" - header = header_format.format(x=self.hierarchy_name(), c=__constraints_name__, i=__index_name__, t=__tie_name__, p=__priors_name__) # nice header for printing - if not ties: ties = itertools.cycle(['']) - return "\n".join([""""""] + [''] + [header] + ["".format(x=x, c=" ".join(map(str, c)), p=" ".join(map(str, p)), t=(t or ''), i=i) for i, x, c, t, p in zip(indices, vals, constr_matrix, ties, prirs)] + ["
{i}{x}{c}{p}{t}
"]) - - def __str__(self, constr_matrix=None, indices=None, prirs=None, ties=None, lc=None, lx=None, li=None, lp=None, lt=None, only_name=False): - filter_ = self._current_slice_ - vals = self.flat - if indices is None: indices = self._indices(filter_) - ravi = self._raveled_index(filter_) - if constr_matrix is None: constr_matrix = self.constraints.properties_for(ravi) - if prirs is None: prirs = self.priors.properties_for(ravi) - if ties is None: ties = self._ties_for(ravi) - ties = [' '.join(map(lambda x: x, t)) for t in ties] - 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__) - if lp is None: lp = self._max_len_names(prirs, __tie_name__) - sep = '-' - header_format = " {i:{5}^{2}s} | \033[1m{x:{5}^{1}s}\033[0;0m | {c:{5}^{0}s} | {p:{5}^{4}s} | {t:{5}^{3}s}" - if only_name: header = header_format.format(lc, lx, li, lt, lp, ' ', x=self.hierarchy_name(), c=sep*lc, i=sep*li, t=sep*lt, p=sep*lp) # nice header for printing - else: header = header_format.format(lc, lx, li, lt, lp, ' ', x=self.hierarchy_name(), c=__constraints_name__, i=__index_name__, t=__tie_name__, p=__priors_name__) # nice header for printing - if not ties: ties = itertools.cycle(['']) - return "\n".join([header] + [" {i!s:^{3}s} | {x: >{1}.{2}g} | {c:^{0}s} | {p:^{5}s} | {t:^{4}s} ".format(lc, lx, __precision__, li, lt, lp, x=x, c=" ".join(map(str, c)), p=" ".join(map(str, p)), t=(t or ''), i=i) for i, x, c, t, p in zip(indices, vals, constr_matrix, ties, prirs)]) # return all the constraints with right indices - # except: return super(Param, self).__str__() - -class ParamConcatenation(object): - def __init__(self, params): - """ - Parameter concatenation for convenience 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 (Broadcasting enabled). - - See :py:class:`GPy.core.parameter.Param` for more details on constraining. - """ - # self.params = params - from .lists_and_dicts import ArrayList - self.params = ArrayList([]) - for p in params: - for p in p.flattened_parameters: - if p not in self.params: - self.params.append(p) - 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:])] - - parents = dict() - for p in self.params: - if p.has_parent(): - parent = p._parent_ - level = 0 - while parent is not None: - if parent in parents: - parents[parent] = max(level, parents[parent]) - else: - parents[parent] = level - level += 1 - parent = parent._parent_ - import operator - #py3 fix - #self.parents = map(lambda x: x[0], sorted(parents.iteritems(), key=operator.itemgetter(1))) - self.parents = map(lambda x: x[0], sorted(parents.items(), key=operator.itemgetter(1))) - #=========================================================================== - # Get/set items, enable broadcasting - #=========================================================================== - def __getitem__(self, s): - ind = numpy.zeros(sum(self._param_sizes), dtype=bool); ind[s] = True; - params = [p.param_array.flat[ind[ps]] for p,ps in zip(self.params, self._param_slices_) if numpy.any(p.param_array.flat[ind[ps]])] - if len(params)==1: return params[0] - return ParamConcatenation(params) - def __setitem__(self, s, val, update=True): - if isinstance(val, ParamConcatenation): - val = val.values() - ind = numpy.zeros(sum(self._param_sizes), dtype=bool); ind[s] = True; - vals = self.values(); vals[s] = val - for p, ps in zip(self.params, self._param_slices_): - p.flat[ind[ps]] = vals[ps] - if update: - self.update_all_params() - def values(self): - return numpy.hstack([p.param_array.flat for p in self.params]) - #=========================================================================== - # parameter operations: - #=========================================================================== - def update_all_params(self): - for par in self.parents: - par.trigger_update(trigger_parent=False) - - def constrain(self, constraint, warning=True): - [param.constrain(constraint, trigger_parent=False) for param in self.params] - self.update_all_params() - constrain.__doc__ = Param.constrain.__doc__ - - def constrain_positive(self, warning=True): - [param.constrain_positive(warning, trigger_parent=False) for param in self.params] - self.update_all_params() - constrain_positive.__doc__ = Param.constrain_positive.__doc__ - - def constrain_fixed(self, value=None, warning=True, trigger_parent=True): - [param.constrain_fixed(value, warning, trigger_parent) for param in self.params] - constrain_fixed.__doc__ = Param.constrain_fixed.__doc__ - fix = constrain_fixed - - def constrain_negative(self, warning=True): - [param.constrain_negative(warning, trigger_parent=False) for param in self.params] - self.update_all_params() - constrain_negative.__doc__ = Param.constrain_negative.__doc__ - - def constrain_bounded(self, lower, upper, warning=True): - [param.constrain_bounded(lower, upper, warning, trigger_parent=False) for param in self.params] - self.update_all_params() - constrain_bounded.__doc__ = Param.constrain_bounded.__doc__ - - def unconstrain(self, *constraints): - [param.unconstrain(*constraints) for param in self.params] - unconstrain.__doc__ = Param.unconstrain.__doc__ - - def unconstrain_negative(self): - [param.unconstrain_negative() for param in self.params] - unconstrain_negative.__doc__ = Param.unconstrain_negative.__doc__ - - def unconstrain_positive(self): - [param.unconstrain_positive() for param in self.params] - unconstrain_positive.__doc__ = Param.unconstrain_positive.__doc__ - - def unconstrain_fixed(self): - [param.unconstrain_fixed() for param in self.params] - unconstrain_fixed.__doc__ = Param.unconstrain_fixed.__doc__ - unfix = unconstrain_fixed - - def unconstrain_bounded(self, lower, upper): - [param.unconstrain_bounded(lower, upper) for param in self.params] - unconstrain_bounded.__doc__ = Param.unconstrain_bounded.__doc__ - - def untie(self, *ties): - [param.untie(*ties) for param in self.params] - - def checkgrad(self, verbose=0, step=1e-6, tolerance=1e-3): - return self.params[0]._highest_parent_._checkgrad(self, verbose, step, tolerance) - #checkgrad.__doc__ = Gradcheckable.checkgrad.__doc__ - - __lt__ = lambda self, val: self.values() < val - __le__ = lambda self, val: self.values() <= val - __eq__ = lambda self, val: self.values() == val - __ne__ = lambda self, val: self.values() != val - __gt__ = lambda self, val: self.values() > val - __ge__ = lambda self, val: self.values() >= val - def __str__(self, *args, **kwargs): - def f(p): - ind = p._raveled_index() - return p.constraints.properties_for(ind), p._ties_for(ind), p.priors.properties_for(ind) - params = self.params - constr_matrices, ties_matrices, prior_matrices = zip(*map(f, params)) - indices = [p._indices() for p in params] - lc = max([p._max_len_names(cm, __constraints_name__) for p, cm in zip(params, constr_matrices)]) - lx = max([p._max_len_values() for p in params]) - li = max([p._max_len_index(i) for p, i in zip(params, indices)]) - lt = max([p._max_len_names(tm, __tie_name__) for p, tm in zip(params, ties_matrices)]) - lp = max([p._max_len_names(pm, __constraints_name__) for p, pm in zip(params, prior_matrices)]) - strings = [] - start = True - for p, cm, i, tm, pm in zip(params,constr_matrices,indices,ties_matrices,prior_matrices): - strings.append(p.__str__(constr_matrix=cm, indices=i, prirs=pm, ties=tm, lc=lc, lx=lx, li=li, lp=lp, lt=lt, only_name=(1-start))) - start = False - return "\n".join(strings) - def __repr__(self): - return "\n".join(map(repr,self.params)) - - def __ilshift__(self, *args, **kwargs): - self[:] = np.ndarray.__ilshift__(self.values(), *args, **kwargs) - - def __irshift__(self, *args, **kwargs): - self[:] = np.ndarray.__irshift__(self.values(), *args, **kwargs) - - def __ixor__(self, *args, **kwargs): - self[:] = np.ndarray.__ixor__(self.values(), *args, **kwargs) - - def __ipow__(self, *args, **kwargs): - self[:] = np.ndarray.__ipow__(self.values(), *args, **kwargs) - - def __ifloordiv__(self, *args, **kwargs): - self[:] = np.ndarray.__ifloordiv__(self.values(), *args, **kwargs) - - def __isub__(self, *args, **kwargs): - self[:] = np.ndarray.__isub__(self.values(), *args, **kwargs) - - def __ior__(self, *args, **kwargs): - self[:] = np.ndarray.__ior__(self.values(), *args, **kwargs) - - def __itruediv__(self, *args, **kwargs): - self[:] = np.ndarray.__itruediv__(self.values(), *args, **kwargs) - - def __idiv__(self, *args, **kwargs): - self[:] = np.ndarray.__idiv__(self.values(), *args, **kwargs) - - def __iand__(self, *args, **kwargs): - self[:] = np.ndarray.__iand__(self.values(), *args, **kwargs) - - def __imod__(self, *args, **kwargs): - self[:] = np.ndarray.__imod__(self.values(), *args, **kwargs) - - def __iadd__(self, *args, **kwargs): - self[:] = np.ndarray.__iadd__(self.values(), *args, **kwargs) - - def __imul__(self, *args, **kwargs): - self[:] = np.ndarray.__imul__(self.values(), *args, **kwargs) +class Param(Param, Priorizable): + pass \ No newline at end of file diff --git a/GPy/core/parameterization/parameter_core.py b/GPy/core/parameterization/parameter_core.py deleted file mode 100644 index b89f6f4e..00000000 --- a/GPy/core/parameterization/parameter_core.py +++ /dev/null @@ -1,1102 +0,0 @@ -# Copyright (c) 2012, GPy authors (see AUTHORS.txt). -# Licensed under the BSD 3-clause license (see LICENSE.txt) -""" -Core module for parameterization. -This module implements all parameterization techniques, split up in modular bits. - -HierarchyError: -raised when an error with the hierarchy occurs (circles etc.) - -Observable: -Observable Pattern for patameterization - - -""" - -from .transformations import Transformation,Logexp, NegativeLogexp, Logistic, __fixed__, FIXED, UNFIXED -import numpy as np -import re -import logging -from .updateable import Updateable -from functools import reduce - -class HierarchyError(Exception): - """ - Gets thrown when something is wrong with the parameter hierarchy. - """ - -def adjust_name_for_printing(name): - """ - Make sure a name can be printed, alongside used as a variable name. - """ - if name is not None: - name2 = name - name = name.replace(" ", "_").replace(".", "_").replace("-", "_m_") - name = name.replace("+", "_p_").replace("!", "_I_") - name = name.replace("**", "_xx_").replace("*", "_x_") - name = name.replace("/", "_l_").replace("@", '_at_') - name = name.replace("(", "_of_").replace(")", "") - if re.match(r'^[a-zA-Z_][a-zA-Z0-9-_]*$', name) is None: - raise NameError("name {} converted to {} cannot be further converted to valid python variable name!".format(name2, name)) - return name - return '' - - - -class Parentable(object): - """ - Enable an Object to have a parent. - - Additionally this adds the parent_index, which is the index for the parent - to look for in its parameter list. - """ - _parent_ = None - _parent_index_ = None - def __init__(self, *args, **kwargs): - super(Parentable, self).__init__() - - def has_parent(self): - """ - Return whether this parentable object currently has a parent. - """ - return self._parent_ is not None - - def _parent_changed(self): - """ - Gets called, when the parent changed, so we can adjust our - inner attributes according to the new parent. - """ - raise NotImplementedError("shouldnt happen, Parentable objects need to be able to change their parent") - - def _disconnect_parent(self, *args, **kw): - """ - Disconnect this object from its parent - """ - raise NotImplementedError("Abstract superclass") - - @property - def _highest_parent_(self): - """ - Gets the highest parent by traversing up to the root node of the hierarchy. - """ - if self._parent_ is None: - return self - return self._parent_._highest_parent_ - - def _notify_parent_change(self): - """ - Dont do anything if in leaf node - """ - pass - -class Pickleable(object): - """ - Make an object pickleable (See python doc 'pickling'). - - This class allows for pickling support by Memento pattern. - _getstate returns a memento of the class, which gets pickled. - _setstate() (re-)sets the state of the class to the memento - """ - def __init__(self, *a, **kw): - super(Pickleable, self).__init__() - - #=========================================================================== - # Pickling operations - #=========================================================================== - def pickle(self, f, protocol=-1): - """ - Pickle a model to file to reload from disk. - - .. warning:: - - Pickling is a local method, to pickle a model to disk and reload - it on the same machine in this instance. - Using pickling as saving states of models could be not supported - across versions and need more work to reload a model after a version - change. If you want to save a model consistently, save the script to - create the model and the `param_array` (e.g. using numpy.save) of - the model you want to save. Then load the model using the script and - push the parameters from the saved `param_array` into the newly - created model. - - :param f: either filename or open file object to write to. - if it is an open buffer, you have to make sure to close - it properly. - :param protocol: pickling protocol to use, python-pickle for details. - """ - try: #Py2 - import cPickle as pickle - except ImportError: #Py3 - import pickle - if isinstance(f, str): - with open(f, 'wb') as f: - pickle.dump(self, f, protocol) - else: - pickle.dump(self, f, protocol) - - #=========================================================================== - # copy and pickling - #=========================================================================== - def copy(self, memo=None, which=None): - """ - Returns a (deep) copy of the current parameter handle. - - All connections to parents of the copy will be cut. - - :param dict memo: memo for deepcopy - :param Parameterized which: parameterized object which started the copy process [default: self] - """ - #raise NotImplementedError, "Copy is not yet implemented, TODO: Observable hierarchy" - if memo is None: - memo = {} - import copy - # the next part makes sure that we do not include parents in any form: - parents = [] - if which is None: - which = self - which.traverse_parents(parents.append) # collect parents - for p in parents: - if not id(p) in memo :memo[id(p)] = None # set all parents to be None, so they will not be copied - if not id(self.gradient) in memo:memo[id(self.gradient)] = None # reset the gradient - if not id(self._fixes_) in memo :memo[id(self._fixes_)] = None # fixes have to be reset, as this is now highest parent - copy = copy.deepcopy(self, memo) # and start the copy - copy._parent_index_ = None - copy._trigger_params_changed() - return copy - - def __deepcopy__(self, memo): - s = self.__new__(self.__class__) # fresh instance - memo[id(self)] = s # be sure to break all cycles --> self is already done - import copy - s.__setstate__(copy.deepcopy(self.__getstate__(), memo)) # standard copy - return s - - def __getstate__(self): - ignore_list = ['_param_array_', # parameters get set from bottom to top - '_gradient_array_', # as well as gradients - '_optimizer_copy_', - 'logger', - 'observers', - '_fixes_', # and fixes - '_Cacher_wrap__cachers', # never pickle cachers - ] - dc = dict() - #py3 fix - #for k,v in self.__dict__.iteritems(): - for k,v in self.__dict__.items(): - if k not in ignore_list: - dc[k] = v - return dc - - def __setstate__(self, state): - self.__dict__.update(state) - from .lists_and_dicts import ObserverList - self.observers = ObserverList() - self._setup_observers() - self._optimizer_copy_transformed = False - - -class Gradcheckable(Pickleable, Parentable): - """ - Adds the functionality for an object to be gradcheckable. - It is just a thin wrapper of a call to the highest parent for now. - TODO: Can be done better, by only changing parameters of the current parameter handle, - such that object hierarchy only has to change for those. - """ - def __init__(self, *a, **kw): - super(Gradcheckable, self).__init__(*a, **kw) - - def checkgrad(self, verbose=0, step=1e-6, tolerance=1e-3, df_tolerance=1e-12): - """ - Check the gradient of this parameter with respect to the highest parent's - objective function. - This is a three point estimate of the gradient, wiggling at the parameters - with a stepsize step. - The check passes if either the ratio or the difference between numerical and - analytical gradient is smaller then tolerance. - - :param bool verbose: whether each parameter shall be checked individually. - :param float step: the stepsize for the numerical three point gradient estimate. - :param float tolerance: the tolerance for the gradient ratio or difference. - :param float df_tolerance: the tolerance for df_tolerance - - Note:- - The *dF_ratio* indicates the limit of accuracy of numerical gradients. - If it is too small, e.g., smaller than 1e-12, the numerical gradients - are usually not accurate enough for the tests (shown with blue). - """ - if self.has_parent(): - return self._highest_parent_._checkgrad(self, verbose=verbose, step=step, tolerance=tolerance, df_tolerance=df_tolerance) - return self._checkgrad(self, verbose=verbose, step=step, tolerance=tolerance, df_tolerance=df_tolerance) - - def _checkgrad(self, param, verbose=0, step=1e-6, tolerance=1e-3): - """ - Perform the checkgrad on the model. - TODO: this can be done more efficiently, when doing it inside here - """ - raise HierarchyError("This parameter is not in a model with a likelihood, and, therefore, cannot be gradient checked!") - -class Nameable(Gradcheckable): - """ - Make an object nameable inside the hierarchy. - """ - def __init__(self, name, *a, **kw): - self._name = name or self.__class__.__name__ - super(Nameable, self).__init__(*a, **kw) - - @property - def name(self): - """ - The name of this object - """ - return self._name - @name.setter - def name(self, name): - """ - Set the name of this object. - Tell the parent if the name has changed. - """ - from_name = self.name - assert isinstance(name, str) - self._name = name - if self.has_parent(): - self._parent_._name_changed(self, from_name) - def hierarchy_name(self, adjust_for_printing=True): - """ - return the name for this object with the parents names attached by dots. - - :param bool adjust_for_printing: whether to call :func:`~adjust_for_printing()` - on the names, recursively - """ - if adjust_for_printing: adjust = lambda x: adjust_name_for_printing(x) - else: adjust = lambda x: x - if self.has_parent(): - return self._parent_.hierarchy_name() + "." + adjust(self.name) - return adjust(self.name) - - -class Indexable(Nameable, Updateable): - """ - Make an object constrainable with Priors and Transformations. - TODO: Mappings!! - Adding a constraint to a Parameter means to tell the highest parent that - the constraint was added and making sure that all parameters covered - by this object are indeed conforming to the constraint. - - :func:`constrain()` and :func:`unconstrain()` are main methods here - """ - def __init__(self, name, default_constraint=None, *a, **kw): - super(Indexable, self).__init__(name=name, *a, **kw) - self._default_constraint_ = default_constraint - from .index_operations import ParameterIndexOperations - self.constraints = ParameterIndexOperations() - self.priors = ParameterIndexOperations() - if self._default_constraint_ is not None: - self.constrain(self._default_constraint_) - - def _disconnect_parent(self, constr=None, *args, **kw): - """ - From Parentable: - disconnect the parent and set the new constraints to constr - """ - if constr is None: - constr = self.constraints.copy() - self.constraints.clear() - self.constraints = constr - self._parent_ = None - self._parent_index_ = None - self._connect_fixes() - self._notify_parent_change() - - #=========================================================================== - # Indexable - #=========================================================================== - def _offset_for(self, param): - """ - Return the offset of the param inside this parameterized object. - This does not need to account for shaped parameters, as it - basically just sums up the parameter sizes which come before param. - """ - if param.has_parent(): - p = param._parent_._get_original(param) - if p in self.parameters: - return reduce(lambda a,b: a + b.size, self.parameters[:p._parent_index_], 0) - return self._offset_for(param._parent_) + param._parent_._offset_for(param) - return 0 - - 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. - """ - from .param import ParamConcatenation - if isinstance(param, ParamConcatenation): - return np.hstack((self._raveled_index_for(p) for p in param.params)) - return param._raveled_index() + self._offset_for(param) - - def _raveled_index(self): - """ - Flattened array of ints, specifying the index of this object. - This has to account for shaped parameters! - """ - return np.r_[:self.size] - - #=========================================================================== - # Fixing Parameters: - #=========================================================================== - def constrain_fixed(self, value=None, warning=True, trigger_parent=True): - """ - Constrain this parameter to be fixed to the current value it carries. - - :param warning: print a warning for overwriting constraints. - """ - if value is not None: - self[:] = value - - index = self.unconstrain() - index = self._add_to_index_operations(self.constraints, index, __fixed__, warning) - self._highest_parent_._set_fixed(self, index) - self.notify_observers(self, None if trigger_parent else -np.inf) - return index - fix = constrain_fixed - - def unconstrain_fixed(self): - """ - This parameter will no longer be fixed. - """ - unconstrained = self.unconstrain(__fixed__) - self._highest_parent_._set_unfixed(self, unconstrained) - return unconstrained - unfix = unconstrain_fixed - - def _ensure_fixes(self): - # Ensure that the fixes array is set: - # Parameterized: ones(self.size) - # Param: ones(self._realsize_ - if not self._has_fixes(): self._fixes_ = np.ones(self.size, dtype=bool) - - def _set_fixed(self, param, index): - self._ensure_fixes() - offset = self._offset_for(param) - self._fixes_[index+offset] = FIXED - if np.all(self._fixes_): self._fixes_ = None # ==UNFIXED - - def _set_unfixed(self, param, index): - self._ensure_fixes() - offset = self._offset_for(param) - self._fixes_[index+offset] = UNFIXED - if np.all(self._fixes_): self._fixes_ = None # ==UNFIXED - - def _connect_fixes(self): - fixed_indices = self.constraints[__fixed__] - if fixed_indices.size > 0: - self._ensure_fixes() - self._fixes_[fixed_indices] = FIXED - else: - self._fixes_ = None - del self.constraints[__fixed__] - - #=========================================================================== - # Convenience for fixed - #=========================================================================== - def _has_fixes(self): - return hasattr(self, "_fixes_") and self._fixes_ is not None and self._fixes_.size == self.size - - @property - def is_fixed(self): - for p in self.parameters: - if not p.is_fixed: return False - return True - - def _get_original(self, param): - # if advanced indexing is activated it happens that the array is a copy - # you can retrieve the original param through this method, by passing - # the copy here - return self.parameters[param._parent_index_] - - #=========================================================================== - # Prior Operations - #=========================================================================== - def set_prior(self, prior, warning=True): - """ - Set the prior for this object to prior. - :param :class:`~GPy.priors.Prior` prior: a prior to set for this parameter - :param bool warning: whether to warn if another prior was set for this parameter - """ - repriorized = self.unset_priors() - self._add_to_index_operations(self.priors, repriorized, prior, warning) - - from .domains import _REAL, _POSITIVE, _NEGATIVE - if prior.domain is _POSITIVE: - self.constrain_positive(warning) - elif prior.domain is _NEGATIVE: - self.constrain_negative(warning) - elif prior.domain is _REAL: - rav_i = self._raveled_index() - assert all(all(False if c is __fixed__ else c.domain is _REAL for c in con) for con in self.constraints.properties_for(rav_i)), 'Domain of prior and constraint have to match, please unconstrain if you REALLY wish to use this prior' - - def unset_priors(self, *priors): - """ - Un-set all priors given (in *priors) from this parameter handle. - """ - return self._remove_from_index_operations(self.priors, priors) - - def log_prior(self): - """evaluate the prior""" - if self.priors.size == 0: - return 0. - x = self.param_array - #evaluate the prior log densities - log_p = reduce(lambda a, b: a + b, (p.lnpdf(x[ind]).sum() for p, ind in self.priors.items()), 0) - - #account for the transformation by evaluating the log Jacobian (where things are transformed) - log_j = 0. - priored_indexes = np.hstack([i for p, i in self.priors.items()]) - for c,j in self.constraints.items(): - if not isinstance(c, Transformation):continue - for jj in j: - if jj in priored_indexes: - log_j += c.log_jacobian(x[jj]) - return log_p + log_j - - def _log_prior_gradients(self): - """evaluate the gradients of the priors""" - if self.priors.size == 0: - return 0. - x = self.param_array - ret = np.zeros(x.size) - #compute derivate of prior density - [np.put(ret, ind, p.lnpdf_grad(x[ind])) for p, ind in self.priors.items()] - #add in jacobian derivatives if transformed - priored_indexes = np.hstack([i for p, i in self.priors.items()]) - for c,j in self.constraints.items(): - if not isinstance(c, Transformation):continue - for jj in j: - if jj in priored_indexes: - ret[jj] += c.log_jacobian_grad(x[jj]) - return ret - - #=========================================================================== - # Tie parameters together - #=========================================================================== - - def _has_ties(self): - if self._highest_parent_.tie.tied_param is None: - return False - if self.has_parent(): - return self._highest_parent_.tie.label_buf[self._highest_parent_._raveled_index_for(self)].sum()>0 - return True - - def tie_together(self): - self._highest_parent_.tie.add_tied_parameter(self) - self._highest_parent_._set_fixed(self,self._raveled_index()) - self._trigger_params_changed() - - #=========================================================================== - # Constrain operations -> done - #=========================================================================== - - def constrain(self, transform, warning=True, trigger_parent=True): - """ - :param transform: the :py:class:`GPy.core.transformations.Transformation` - to constrain the this parameter to. - :param warning: print a warning if re-constraining parameters. - - Constrain the parameter to the given - :py:class:`GPy.core.transformations.Transformation`. - """ - if isinstance(transform, Transformation): - self.param_array[...] = transform.initialize(self.param_array) - reconstrained = self.unconstrain() - added = self._add_to_index_operations(self.constraints, reconstrained, transform, warning) - self.trigger_update(trigger_parent) - return added - - def unconstrain(self, *transforms): - """ - :param transforms: The transformations to unconstrain from. - - remove all :py:class:`GPy.core.transformations.Transformation` - transformats of this parameter object. - """ - return self._remove_from_index_operations(self.constraints, transforms) - - def constrain_positive(self, warning=True, trigger_parent=True): - """ - :param warning: print a warning if re-constraining parameters. - - Constrain this parameter to the default positive constraint. - """ - self.constrain(Logexp(), warning=warning, trigger_parent=trigger_parent) - - def constrain_negative(self, warning=True, trigger_parent=True): - """ - :param warning: print a warning if re-constraining parameters. - - Constrain this parameter to the default negative constraint. - """ - self.constrain(NegativeLogexp(), warning=warning, trigger_parent=trigger_parent) - - def constrain_bounded(self, lower, upper, warning=True, trigger_parent=True): - """ - :param lower, upper: the limits to bound this parameter to - :param warning: print a warning if re-constraining parameters. - - Constrain this parameter to lie within the given range. - """ - self.constrain(Logistic(lower, upper), warning=warning, trigger_parent=trigger_parent) - - def unconstrain_positive(self): - """ - Remove positive constraint of this parameter. - """ - self.unconstrain(Logexp()) - - def unconstrain_negative(self): - """ - Remove negative constraint of this parameter. - """ - self.unconstrain(NegativeLogexp()) - - def unconstrain_bounded(self, lower, upper): - """ - :param lower, upper: the limits to unbound this parameter from - - Remove (lower, upper) bounded constrain from this parameter/ - """ - self.unconstrain(Logistic(lower, upper)) - - def _parent_changed(self, parent): - """ - From Parentable: - Called when the parent changed - - update the constraints and priors view, so that - constraining is automized for the parent. - """ - from .index_operations import ParameterIndexOperationsView - #if getattr(self, "_in_init_"): - #import ipdb;ipdb.set_trace() - #self.constraints.update(param.constraints, start) - #self.priors.update(param.priors, start) - offset = parent._offset_for(self) - self.constraints = ParameterIndexOperationsView(parent.constraints, offset, self.size) - self.priors = ParameterIndexOperationsView(parent.priors, offset, self.size) - self._fixes_ = None - for p in self.parameters: - p._parent_changed(parent) - - def _add_to_index_operations(self, which, reconstrained, what, warning): - """ - Helper preventing copy code. - This adds the given what (transformation, prior etc) to parameter index operations which. - reconstrained are reconstrained indices. - warn when reconstraining parameters if warning is True. - TODO: find out which parameters have changed specifically - """ - if warning and reconstrained.size > 0: - # TODO: figure out which parameters have changed and only print those - print("WARNING: reconstraining parameters {}".format(self.hierarchy_name() or self.name)) - index = self._raveled_index() - which.add(what, index) - return index - - def _remove_from_index_operations(self, which, transforms): - """ - Helper preventing copy code. - Remove given what (transform prior etc) from which param index ops. - """ - if len(transforms) == 0: - transforms = which.properties() - removed = np.empty((0,), dtype=int) - for t in list(transforms): - unconstrained = which.remove(t, self._raveled_index()) - removed = np.union1d(removed, unconstrained) - if t is __fixed__: - self._highest_parent_._set_unfixed(self, unconstrained) - - return removed - -class OptimizationHandlable(Indexable): - """ - This enables optimization handles on an Object as done in GPy 0.4. - - `..._optimizer_copy_transformed`: make sure the transformations and constraints etc are handled - """ - def __init__(self, name, default_constraint=None, *a, **kw): - super(OptimizationHandlable, self).__init__(name, default_constraint=default_constraint, *a, **kw) - self._optimizer_copy_ = None - self._optimizer_copy_transformed = False - - #=========================================================================== - # Optimizer copy - #=========================================================================== - @property - def optimizer_array(self): - """ - Array for the optimizer to work on. - This array always lives in the space for the optimizer. - Thus, it is untransformed, going from Transformations. - - Setting this array, will make sure the transformed parameters for this model - will be set accordingly. It has to be set with an array, retrieved from - this method, as e.g. fixing will resize the array. - - The optimizer should only interfere with this array, such that transformations - are secured. - """ - if self.__dict__.get('_optimizer_copy_', None) is None or self.size != self._optimizer_copy_.size: - self._optimizer_copy_ = np.empty(self.size) - - if not self._optimizer_copy_transformed: - self._optimizer_copy_.flat = self.param_array.flat - #py3 fix - #[np.put(self._optimizer_copy_, ind, c.finv(self.param_array[ind])) for c, ind in self.constraints.iteritems() if c != __fixed__] - [np.put(self._optimizer_copy_, ind, c.finv(self.param_array[ind])) for c, ind in self.constraints.items() if c != __fixed__] - if self.has_parent() and (self.constraints[__fixed__].size != 0 or self._has_ties()): - fixes = np.ones(self.size).astype(bool) - fixes[self.constraints[__fixed__]] = FIXED - return self._optimizer_copy_[np.logical_and(fixes, self._highest_parent_.tie.getTieFlag(self))] - elif self._has_fixes(): - return self._optimizer_copy_[self._fixes_] - - self._optimizer_copy_transformed = True - - return self._optimizer_copy_ - - @optimizer_array.setter - def optimizer_array(self, p): - """ - Make sure the optimizer copy does not get touched, thus, we only want to - set the values *inside* not the array itself. - - Also we want to update param_array in here. - """ - f = None - if self.has_parent() and self.constraints[__fixed__].size != 0: - f = np.ones(self.size).astype(bool) - f[self.constraints[__fixed__]] = FIXED - elif self._has_fixes(): - f = self._fixes_ - if f is None: - self.param_array.flat = p - [np.put(self.param_array, ind, c.f(self.param_array.flat[ind])) - #py3 fix - #for c, ind in self.constraints.iteritems() if c != __fixed__] - for c, ind in self.constraints.items() if c != __fixed__] - else: - self.param_array.flat[f] = p - [np.put(self.param_array, ind[f[ind]], c.f(self.param_array.flat[ind[f[ind]]])) - #py3 fix - #for c, ind in self.constraints.iteritems() if c != __fixed__] - for c, ind in self.constraints.items() if c != __fixed__] - #self._highest_parent_.tie.propagate_val() - - self._optimizer_copy_transformed = False - self.trigger_update() - - def _get_params_transformed(self): - raise DeprecationWarning("_get|set_params{_optimizer_copy_transformed} is deprecated, use self.optimizer array insetad!") -# - def _set_params_transformed(self, p): - raise DeprecationWarning("_get|set_params{_optimizer_copy_transformed} is deprecated, use self.optimizer array insetad!") - - def _trigger_params_changed(self, trigger_parent=True): - """ - First tell all children to update, - then update yourself. - - If trigger_parent is True, we will tell the parent, otherwise not. - """ - [p._trigger_params_changed(trigger_parent=False) for p in self.parameters if not p.is_fixed] - self.notify_observers(None, None if trigger_parent else -np.inf) - - def _size_transformed(self): - """ - As fixes are not passed to the optimiser, the size of the model for the optimiser - is the size of all parameters minus the size of the fixes. - """ - return self.size - self.constraints[__fixed__].size - - def _transform_gradients(self, g): - """ - Transform the gradients by multiplying the gradient factor for each - constraint to it. - """ - self._highest_parent_.tie.collate_gradient() - #py3 fix - #[np.put(g, i, c.gradfactor(self.param_array[i], g[i])) for c, i in self.constraints.iteritems() if c != __fixed__] - [np.put(g, i, c.gradfactor(self.param_array[i], g[i])) for c, i in self.constraints.items() if c != __fixed__] - if self._has_fixes(): return g[self._fixes_] - return g - - def _transform_gradients_non_natural(self, g): - """ - Transform the gradients by multiplying the gradient factor for each - constraint to it. - """ - self._highest_parent_.tie.collate_gradient() - #py3 fix - #[np.put(g, i, c.gradfactor_non_natural(self.param_array[i], g[i])) for c, i in self.constraints.iteritems() if c != __fixed__] - [np.put(g, i, c.gradfactor_non_natural(self.param_array[i], g[i])) for c, i in self.constraints.items() if c != __fixed__] - if self._has_fixes(): return g[self._fixes_] - return g - - - @property - def num_params(self): - """ - Return the number of parameters of this parameter_handle. - Param objects will always return 0. - """ - raise NotImplemented("Abstract, please implement in respective classes") - - def parameter_names(self, add_self=False, adjust_for_printing=False, recursive=True): - """ - Get the names of all parameters of this model. - - :param bool add_self: whether to add the own name in front of names - :param bool adjust_for_printing: whether to call `adjust_name_for_printing` on names - :param bool recursive: whether to traverse through hierarchy and append leaf node names - """ - if adjust_for_printing: adjust = lambda x: adjust_name_for_printing(x) - else: adjust = lambda x: x - if recursive: names = [xi for x in self.parameters for xi in x.parameter_names(add_self=True, adjust_for_printing=adjust_for_printing)] - else: names = [adjust(x.name) for x in self.parameters] - if add_self: names = map(lambda x: adjust(self.name) + "." + x, names) - return names - - def _get_param_names(self): - n = np.array([p.hierarchy_name() + '[' + str(i) + ']' for p in self.flattened_parameters for i in p._indices()]) - return n - - def _get_param_names_transformed(self): - n = self._get_param_names() - if self._has_fixes(): - return n[self._fixes_] - return n - - #=========================================================================== - # Randomizeable - #=========================================================================== - def randomize(self, rand_gen=None, *args, **kwargs): - """ - Randomize the model. - Make this draw from the prior if one exists, else draw from given random generator - - :param rand_gen: np random number generator which takes args and kwargs - :param flaot loc: loc parameter for random number generator - :param float scale: scale parameter for random number generator - :param args, kwargs: will be passed through to random number generator - """ - if rand_gen is None: - rand_gen = np.random.normal - # first take care of all parameters (from N(0,1)) - x = rand_gen(size=self._size_transformed(), *args, **kwargs) - updates = self.update_model() - self.update_model(False) # Switch off the updates - self.optimizer_array = x # makes sure all of the tied parameters get the same init (since there's only one prior object...) - # now draw from prior where possible - x = self.param_array.copy() - #Py3 fix - #[np.put(x, ind, p.rvs(ind.size)) for p, ind in self.priors.iteritems() if not p is None] - [np.put(x, ind, p.rvs(ind.size)) for p, ind in self.priors.items() if not p is None] - unfixlist = np.ones((self.size,),dtype=np.bool) - unfixlist[self.constraints[__fixed__]] = False - self.param_array.flat[unfixlist] = x.view(np.ndarray).ravel()[unfixlist] - self.update_model(updates) - - #=========================================================================== - # For shared memory arrays. This does nothing in Param, but sets the memory - # for all parameterized objects - #=========================================================================== - @property - def gradient_full(self): - """ - Note to users: - This does not return the gradient in the right shape! Use self.gradient - for the right gradient array. - - To work on the gradient array, use this as the gradient handle. - This method exists for in memory use of parameters. - When trying to access the true gradient array, use this. - """ - self.gradient # <<< ensure _gradient_array_ - return self._gradient_array_ - - def _propagate_param_grad(self, parray, garray): - """ - For propagating the param_array and gradient_array. - This ensures the in memory view of each subsequent array. - - 1.) connect param_array of children to self.param_array - 2.) tell all children to propagate further - """ - if self.param_array.size != self.size: - self._param_array_ = np.empty(self.size, dtype=np.float64) - if self.gradient.size != self.size: - self._gradient_array_ = np.empty(self.size, dtype=np.float64) - - pi_old_size = 0 - for pi in self.parameters: - pislice = slice(pi_old_size, pi_old_size + pi.size) - - self.param_array[pislice] = pi.param_array.flat # , requirements=['C', 'W']).flat - self.gradient_full[pislice] = pi.gradient_full.flat # , requirements=['C', 'W']).flat - - pi.param_array.data = parray[pislice].data - pi.gradient_full.data = garray[pislice].data - - pi._propagate_param_grad(parray[pislice], garray[pislice]) - pi_old_size += pi.size - - def _connect_parameters(self): - pass - -_name_digit = re.compile("(?P.*)_(?P\d+)$") -class Parameterizable(OptimizationHandlable): - """ - A parameterisable class. - - This class provides the parameters list (ArrayList) and standard parameter handling, - such as {link|unlink}_parameter(), traverse hierarchy and param_array, gradient_array - and the empty parameters_changed(). - - This class is abstract and should not be instantiated. - Use GPy.core.Parameterized() as node (or leaf) in the parameterized hierarchy. - Use GPy.core.Param() for a leaf in the parameterized hierarchy. - """ - def __init__(self, *args, **kwargs): - super(Parameterizable, self).__init__(*args, **kwargs) - from GPy.core.parameterization.lists_and_dicts import ArrayList - self.parameters = ArrayList() - self._param_array_ = None - self._added_names_ = set() - self.logger = logging.getLogger(self.__class__.__name__) - self.__visited = False # for traversing in reverse order we need to know if we were here already - - @property - def param_array(self): - """ - Array representing the parameters of this class. - There is only one copy of all parameters in memory, two during optimization. - - !WARNING!: setting the parameter array MUST always be done in memory: - m.param_array[:] = m_copy.param_array - """ - if (self.__dict__.get('_param_array_', None) is None) or (self._param_array_.size != self.size): - self._param_array_ = np.empty(self.size, dtype=np.float64) - return self._param_array_ - - @property - def unfixed_param_array(self): - """ - Array representing the parameters of this class. - There is only one copy of all parameters in memory, two during optimization. - - !WARNING!: setting the parameter array MUST always be done in memory: - m.param_array[:] = m_copy.param_array - """ - if self.__dict__.get('_param_array_', None) is None: - self._param_array_ = np.empty(self.size, dtype=np.float64) - - if self.constraints[__fixed__].size !=0: - fixes = np.ones(self.size).astype(bool) - fixes[self.constraints[__fixed__]] = FIXED - return self._param_array_[fixes] - else: - return self._param_array_ - - @param_array.setter - def param_array(self, arr): - self._param_array_ = arr - - def traverse(self, visit, *args, **kwargs): - """ - Traverse the hierarchy performing visit(self, *args, **kwargs) - at every node passed by downwards. This function includes self! - - See "visitor pattern" in literature. This is implemented in pre-order fashion. - - Example: - Collect all children: - - children = [] - self.traverse(children.append) - print children - """ - if not self.__visited: - visit(self, *args, **kwargs) - self.__visited = True - for c in self.parameters: - c.traverse(visit, *args, **kwargs) - self.__visited = False - - def traverse_parents(self, visit, *args, **kwargs): - """ - Traverse the hierarchy upwards, visiting all parents and their children except self. - See "visitor pattern" in literature. This is implemented in pre-order fashion. - - Example: - - parents = [] - self.traverse_parents(parents.append) - print parents - """ - if self.has_parent(): - self.__visited = True - self._parent_._traverse_parents(visit, *args, **kwargs) - self.__visited = False - - def _traverse_parents(self, visit, *args, **kwargs): - if not self.__visited: - self.__visited = True - visit(self, *args, **kwargs) - if self.has_parent(): - self._parent_._traverse_parents(visit, *args, **kwargs) - self._parent_.traverse(visit, *args, **kwargs) - self.__visited = False - - #========================================================================= - # Gradient handling - #========================================================================= - @property - def gradient(self): - if (self.__dict__.get('_gradient_array_', None) is None) or self._gradient_array_.size != self.size: - self._gradient_array_ = np.empty(self.size, dtype=np.float64) - return self._gradient_array_ - - @gradient.setter - def gradient(self, val): - self._gradient_array_[:] = val - - @property - def num_params(self): - return len(self.parameters) - - def _add_parameter_name(self, param, ignore_added_names=False): - pname = adjust_name_for_printing(param.name) - if ignore_added_names: - self.__dict__[pname] = param - return - - def warn_and_retry(param, match=None): - #=================================================================== - # print """ - # WARNING: added a parameter with formatted name {}, - # which is already assigned to {}. - # Trying to change the parameter name to - # - # {}.{} - # """.format(pname, self.hierarchy_name(), self.hierarchy_name(), param.name + "_") - #=================================================================== - if match is None: - param.name += "_1" - else: - param.name = match.group('name') + "_" + str(int(match.group('digit'))+1) - self._add_parameter_name(param, ignore_added_names) - # and makes sure to not delete programmatically added parameters - for other in self.parameters[::-1]: - if other is not param and other.name == param.name: - warn_and_retry(param, _name_digit.match(other.name)) - return - if pname not in dir(self): - self.__dict__[pname] = param - self._added_names_.add(pname) - elif pname in self.__dict__: - if pname in self._added_names_: - other = self.__dict__[pname] - if not (param is other): - del self.__dict__[pname] - self._added_names_.remove(pname) - warn_and_retry(other) - warn_and_retry(param, _name_digit.match(other.name)) - return - - def _remove_parameter_name(self, param=None, pname=None): - assert param is None or pname is None, "can only delete either param by name, or the name of a param" - pname = adjust_name_for_printing(pname) or adjust_name_for_printing(param.name) - if pname in self._added_names_: - del self.__dict__[pname] - self._added_names_.remove(pname) - self._connect_parameters() - - def _name_changed(self, param, old_name): - self._remove_parameter_name(None, old_name) - self._add_parameter_name(param) - - def __setstate__(self, state): - super(Parameterizable, self).__setstate__(state) - self.logger = logging.getLogger(self.__class__.__name__) - return self - - #=========================================================================== - # notification system - #=========================================================================== - def _parameters_changed_notification(self, me, which=None): - """ - In parameterizable we just need to make sure, that the next call to optimizer_array - will update the optimizer_array to the latest parameters - """ - self._optimizer_copy_transformed = False # tells the optimizer array to update on next request - self.parameters_changed() - def _pass_through_notify_observers(self, me, which=None): - self.notify_observers(which=which) - def _setup_observers(self): - """ - Setup the default observers - - 1: parameters_changed_notify - 2: pass through to parent, if present - """ - self.add_observer(self, self._parameters_changed_notification, -100) - if self.has_parent(): - self.add_observer(self._parent_, self._parent_._pass_through_notify_observers, -np.inf) - #=========================================================================== - # From being parentable, we have to define the parent_change notification - #=========================================================================== - def _notify_parent_change(self): - """ - Notify all parameters that the parent has changed - """ - for p in self.parameters: - p._parent_changed(self) - - def parameters_changed(self): - """ - This method gets called when parameters have changed. - Another way of listening to param changes is to - add self as a listener to the param, such that - updates get passed through. See :py:function:``GPy.core.param.Observable.add_observer`` - """ - pass - - def save(self, filename, ftype='HDF5'): - """ - Save all the model parameters into a file (HDF5 by default). - """ - from . import Param - from ...util.misc import param_to_array - def gather_params(self, plist): - if isinstance(self,Param): - plist.append(self) - plist = [] - self.traverse(gather_params, plist) - names = self.parameter_names(adjust_for_printing=True) - if ftype=='HDF5': - try: - import h5py - f = h5py.File(filename,'w') - for p,n in zip(plist,names): - n = n.replace('.','_') - p = param_to_array(p) - d = f.create_dataset(n,p.shape,dtype=p.dtype) - d[:] = p - if hasattr(self, 'param_array'): - d = f.create_dataset('param_array',self.param_array.shape, dtype=self.param_array.dtype) - d[:] = self.param_array - f.close() - except: - raise 'Fails to write the parameters into a HDF5 file!' - diff --git a/GPy/core/parameterization/parameterized.py b/GPy/core/parameterization/parameterized.py index 8378b4ce..9e71ddcf 100644 --- a/GPy/core/parameterization/parameterized.py +++ b/GPy/core/parameterization/parameterized.py @@ -1,34 +1,13 @@ # Copyright (c) 2014, Max Zwiessele, James Hensman # Licensed under the BSD 3-clause license (see LICENSE.txt) -import six # For metaclass support in Python 2 and 3 simultaneously -import numpy; np = numpy -import itertools -from re import compile, _pattern_type -from .param import ParamConcatenation -from .parameter_core import HierarchyError, Parameterizable, adjust_name_for_printing +from paramz import Parameterized +from .priorizable import Priorizable import logging -from .index_operations import ParameterIndexOperationsView logger = logging.getLogger("parameters changed meta") -class ParametersChangedMeta(type): - def __call__(self, *args, **kw): - self._in_init_ = True - #import ipdb;ipdb.set_trace() - self = super(ParametersChangedMeta, self).__call__(*args, **kw) - logger.debug("finished init") - self._in_init_ = False - logger.debug("connecting parameters") - self._highest_parent_._connect_parameters() - #self._highest_parent_._notify_parent_change() - self._highest_parent_._connect_fixes() - logger.debug("calling parameters changed") - self.parameters_changed() - return self - -@six.add_metaclass(ParametersChangedMeta) -class Parameterized(Parameterizable): +class Parameterized(Parameterized, Priorizable): """ Parameterized class @@ -69,365 +48,5 @@ class Parameterized(Parameterizable): If you want to operate on all parameters use m[''] to wildcard select all paramters and concatenate them. Printing m[''] will result in printing of all parameters in detail. - """ - #=========================================================================== - # Metaclass for parameters changed after init. - # This makes sure, that parameters changed will always be called after __init__ - # **Never** call parameters_changed() yourself - #This is ignored in Python 3 -- you need to put the meta class in the function definition. - #__metaclass__ = ParametersChangedMeta - #The six module is used to support both Python 2 and 3 simultaneously - #=========================================================================== - def __init__(self, name=None, parameters=[], *a, **kw): - super(Parameterized, self).__init__(name=name, *a, **kw) - self.size = sum(p.size for p in self.parameters) - self.add_observer(self, self._parameters_changed_notification, -100) - if not self._has_fixes(): - self._fixes_ = None - self._param_slices_ = [] - #self._connect_parameters() - self.link_parameters(*parameters) - - def build_pydot(self, G=None): - import pydot # @UnresolvedImport - iamroot = False - if G is None: - G = pydot.Dot(graph_type='digraph', bgcolor=None) - iamroot=True - node = pydot.Node(id(self), shape='box', label=self.name)#, color='white') - G.add_node(node) - for child in self.parameters: - child_node = child.build_pydot(G) - G.add_edge(pydot.Edge(node, child_node))#, color='white')) - - for _, o, _ in self.observers: - label = o.name if hasattr(o, 'name') else str(o) - observed_node = pydot.Node(id(o), label=label) - G.add_node(observed_node) - edge = pydot.Edge(str(id(self)), str(id(o)), color='darkorange2', arrowhead='vee') - G.add_edge(edge) - - if iamroot: - return G - return node - - #=========================================================================== - # Add remove parameters: - #=========================================================================== - def link_parameter(self, param, index=None, _ignore_added_names=False): - """ - :param parameters: the parameters to add - :type parameters: list of or one :py:class:`GPy.core.param.Param` - :param [index]: index of where to put parameters - - :param bool _ignore_added_names: whether the name of the parameter overrides a possibly existing field - - Add all parameters to this param class, you can insert parameters - at any given index using the :func:`list.insert` syntax - """ - if param in self.parameters and index is not None: - self.unlink_parameter(param) - self.link_parameter(param, index) - # elif param.has_parent(): - # raise HierarchyError, "parameter {} already in another model ({}), create new object (or copy) for adding".format(param._short(), param._highest_parent_._short()) - elif param not in self.parameters: - if param.has_parent(): - def visit(parent, self): - if parent is self: - raise HierarchyError("You cannot add a parameter twice into the hierarchy") - param.traverse_parents(visit, self) - param._parent_.unlink_parameter(param) - # make sure the size is set - if index is None: - start = sum(p.size for p in self.parameters) - self.constraints.shift_right(start, param.size) - self.priors.shift_right(start, param.size) - self.constraints.update(param.constraints, self.size) - self.priors.update(param.priors, self.size) - param._parent_ = self - param._parent_index_ = len(self.parameters) - self.parameters.append(param) - else: - start = sum(p.size for p in self.parameters[:index]) - self.constraints.shift_right(start, param.size) - self.priors.shift_right(start, param.size) - self.constraints.update(param.constraints, start) - self.priors.update(param.priors, start) - param._parent_ = self - param._parent_index_ = index if index>=0 else len(self.parameters[:index]) - for p in self.parameters[index:]: - p._parent_index_ += 1 - self.parameters.insert(index, param) - - param.add_observer(self, self._pass_through_notify_observers, -np.inf) - - parent = self - while parent is not None: - parent.size += param.size - parent = parent._parent_ - self._notify_parent_change() - - if not self._in_init_: - #self._connect_parameters() - #self._notify_parent_change() - - self._highest_parent_._connect_parameters(ignore_added_names=_ignore_added_names) - self._highest_parent_._notify_parent_change() - self._highest_parent_._connect_fixes() - - else: - raise HierarchyError("""Parameter exists already, try making a copy""") - - - def link_parameters(self, *parameters): - """ - convenience method for adding several - parameters without gradient specification - """ - [self.link_parameter(p) for p in parameters] - - def unlink_parameter(self, param): - """ - :param param: param object to remove from being a parameter of this parameterized object. - """ - if not param in self.parameters: - try: - raise RuntimeError("{} does not belong to this object {}, remove parameters directly from their respective parents".format(param._short(), self.name)) - except AttributeError: - raise RuntimeError("{} does not seem to be a parameter, remove parameters directly from their respective parents".format(str(param))) - - start = sum([p.size for p in self.parameters[:param._parent_index_]]) - self.size -= param.size - del self.parameters[param._parent_index_] - self._remove_parameter_name(param) - - - param._disconnect_parent() - param.remove_observer(self, self._pass_through_notify_observers) - self.constraints.shift_left(start, param.size) - - self._connect_parameters() - self._notify_parent_change() - - parent = self._parent_ - while parent is not None: - parent.size -= param.size - parent = parent._parent_ - - self._highest_parent_._connect_parameters() - self._highest_parent_._connect_fixes() - self._highest_parent_._notify_parent_change() - - def add_parameter(self, *args, **kwargs): - raise DeprecationWarning("add_parameter was renamed to link_parameter to avoid confusion of setting variables, use link_parameter instead") - def remove_parameter(self, *args, **kwargs): - raise DeprecationWarning("remove_parameter was renamed to unlink_parameter to avoid confusion of setting variables, use unlink_parameter instead") - - def _connect_parameters(self, ignore_added_names=False): - # connect parameterlist to this parameterized object - # This just sets up the right connection for the params objects - # to be used as parameters - # it also sets the constraints for each parameter to the constraints - # of their respective parents - if not hasattr(self, "parameters") or len(self.parameters) < 1: - # no parameters for this class - return - if self.param_array.size != self.size: - self._param_array_ = np.empty(self.size, dtype=np.float64) - if self.gradient.size != self.size: - self._gradient_array_ = np.empty(self.size, dtype=np.float64) - - old_size = 0 - self._param_slices_ = [] - for i, p in enumerate(self.parameters): - if not p.param_array.flags['C_CONTIGUOUS']: - raise ValueError("This should not happen! Please write an email to the developers with the code, which reproduces this error. All parameter arrays must be C_CONTIGUOUS") - - p._parent_ = self - p._parent_index_ = i - - pslice = slice(old_size, old_size + p.size) - - # first connect all children - p._propagate_param_grad(self.param_array[pslice], self.gradient_full[pslice]) - - # then connect children to self - self.param_array[pslice] = p.param_array.flat # , requirements=['C', 'W']).ravel(order='C') - self.gradient_full[pslice] = p.gradient_full.flat # , requirements=['C', 'W']).ravel(order='C') - - p.param_array.data = self.param_array[pslice].data - p.gradient_full.data = self.gradient_full[pslice].data - - self._param_slices_.append(pslice) - - self._add_parameter_name(p, ignore_added_names=ignore_added_names) - old_size += p.size - - #=========================================================================== - # Get/set parameters: - #=========================================================================== - def grep_param_names(self, regexp): - """ - create a list of parameters, matching regular expression regexp - """ - if not isinstance(regexp, _pattern_type): regexp = compile(regexp) - found_params = [] - for n, p in zip(self.parameter_names(False, False, True), self.flattened_parameters): - if regexp.match(n) is not None: - found_params.append(p) - return found_params - - def __getitem__(self, name, paramlist=None): - if isinstance(name, (int, slice, tuple, np.ndarray)): - return self.param_array[name] - else: - if paramlist is None: - paramlist = self.grep_param_names(name) - if len(paramlist) < 1: raise AttributeError(name) - if len(paramlist) == 1: - if isinstance(paramlist[-1], Parameterized): - paramlist = paramlist[-1].flattened_parameters - if len(paramlist) != 1: - return ParamConcatenation(paramlist) - return paramlist[-1] - return ParamConcatenation(paramlist) - - def __setitem__(self, name, value, paramlist=None): - if value is None: - return # nothing to do here - if isinstance(name, (slice, tuple, np.ndarray)): - try: - self.param_array[name] = value - except: - raise ValueError("Setting by slice or index only allowed with array-like") - self.trigger_update() - else: - try: param = self.__getitem__(name, paramlist) - except: raise - param[:] = value - - def __setattr__(self, name, val): - # override the default behaviour, if setting a param, so broadcasting can by used - if hasattr(self, "parameters"): - try: - pnames = self.parameter_names(False, adjust_for_printing=True, recursive=False) - if name in pnames: - param = self.parameters[pnames.index(name)] - param[:] = val; return - except AttributeError as a: - raise - return object.__setattr__(self, name, val); - - #=========================================================================== - # Pickling - #=========================================================================== - def __setstate__(self, state): - super(Parameterized, self).__setstate__(state) - try: - self._connect_parameters() - self._connect_fixes() - self._notify_parent_change() - self.parameters_changed() - except Exception as e: - print("WARNING: caught exception {!s}, trying to continue".format(e)) - - def copy(self, memo=None): - if memo is None: - memo = {} - memo[id(self.optimizer_array)] = None # and param_array - memo[id(self.param_array)] = None # and param_array - copy = super(Parameterized, self).copy(memo) - copy._connect_parameters() - copy._connect_fixes() - copy._notify_parent_change() - return copy - - #=========================================================================== - # Printing: - #=========================================================================== - def _short(self): - return self.hierarchy_name() - @property - def flattened_parameters(self): - return [xi for x in self.parameters for xi in x.flattened_parameters] - @property - def _parameter_sizes_(self): - return [x.size for x in self.parameters] - @property - def parameter_shapes(self): - return [xi for x in self.parameters for xi in x.parameter_shapes] - @property - def _constraints_str(self): - return [cs for p in self.parameters for cs in p._constraints_str] - @property - def _priors_str(self): - return [cs for p in self.parameters for cs in p._priors_str] - @property - def _description_str(self): - return [xi for x in self.parameters for xi in x._description_str] - @property - def _ties_str(self): - return [','.join(x._ties_str) for x in self.flattened_parameters] - - def _repr_html_(self, header=True): - """Representation of the parameters in html for notebook display.""" - name = adjust_name_for_printing(self.name) + "." - constrs = self._constraints_str; - ts = self._ties_str - prirs = self._priors_str - desc = self._description_str; names = self.parameter_names() - nl = max([len(str(x)) for x in names + [name]]) - sl = max([len(str(x)) for x in desc + ["Value"]]) - cl = max([len(str(x)) if x else 0 for x in constrs + ["Constraint"]]) - tl = max([len(str(x)) if x else 0 for x in ts + ["Tied to"]]) - pl = max([len(str(x)) if x else 0 for x in prirs + ["Prior"]]) - format_spec = "{{name:<{0}s}}{{desc:>{1}s}}{{const:^{2}s}}{{pri:^{3}s}}{{t:^{4}s}}".format(nl, sl, cl, pl, tl) - to_print = [] - for n, d, c, t, p in zip(names, desc, constrs, ts, prirs): - to_print.append(format_spec.format(name=n, desc=d, const=c, t=t, pri=p)) - sep = '-' * (nl + sl + cl + + pl + tl + 8 * 2 + 3) - if header: - header = """ - - {name} - Value - Constraint - Prior - Tied to -""".format(name=name) - to_print.insert(0, header) - style = """""" - return style + '\n' + '' + '\n'.format(sep).join(to_print) + '\n
' - - def __str__(self, header=True, VT100=True): - name = adjust_name_for_printing(self.name) + "." - constrs = self._constraints_str; - ts = self._ties_str - prirs = self._priors_str - desc = self._description_str; names = self.parameter_names() - nl = max([len(str(x)) for x in names + [name]]) - sl = max([len(str(x)) for x in desc + ["Value"]]) - cl = max([len(str(x)) if x else 0 for x in constrs + ["Constraint"]]) - tl = max([len(str(x)) if x else 0 for x in ts + ["Tied to"]]) - pl = max([len(str(x)) if x else 0 for x in prirs + ["Prior"]]) - if VT100: - format_spec = " \033[1m{{name:<{0}s}}\033[0;0m | {{desc:>{1}s}} | {{const:^{2}s}} | {{pri:^{3}s}} | {{t:^{4}s}}".format(nl, sl, cl, pl, tl) - else: - format_spec = " {{name:<{0}s}} | {{desc:>{1}s}} | {{const:^{2}s}} | {{pri:^{3}s}} | {{t:^{4}s}}".format(nl, sl, cl, pl, tl) - to_print = [] - for n, d, c, t, p in zip(names, desc, constrs, ts, prirs): - to_print.append(format_spec.format(name=n, desc=d, const=c, t=t, pri=p)) - sep = '-' * (nl + sl + cl + + pl + tl + 8 * 2 + 3) - if header: - header = " {{0:<{0}s}} | {{1:^{1}s}} | {{2:^{2}s}} | {{3:^{3}s}} | {{4:^{4}s}}".format(nl, sl, cl, pl, tl).format(name, "Value", "Constraint", "Prior", "Tied to") - to_print.insert(0, header) - return '\n'.format(sep).join(to_print) - pass - - + """ + pass \ No newline at end of file diff --git a/GPy/core/parameterization/priorizable.py b/GPy/core/parameterization/priorizable.py new file mode 100644 index 00000000..1d1153c7 --- /dev/null +++ b/GPy/core/parameterization/priorizable.py @@ -0,0 +1,82 @@ +# Copyright (c) 2012-2014, GPy authors (see AUTHORS.txt). +# Licensed under the BSD 3-clause license (see LICENSE.txt) +import numpy as np +from paramz.transformations import Transformation, __fixed__ +from paramz.core.parameter_core import Parameterizable +from functools import reduce + +class Priorizable(Parameterizable): + def __init__(self, name, default_prior=None, *a, **kw): + super(Priorizable, self).__init__(name=name, *a, **kw) + self._default_prior_ = default_prior + from paramz.core.index_operations import ParameterIndexOperations + self.add_index_operation('priors', ParameterIndexOperations()) + if self._default_prior_ is not None: + self.set_prior(self._default_prior_) + + def __setstate__(self, state): + super(Priorizable, self).__setstate__(state) + self._index_operations['priors'] = self.priors + + + #=========================================================================== + # Prior Operations + #=========================================================================== + def set_prior(self, prior, warning=True): + """ + Set the prior for this object to prior. + :param :class:`~GPy.priors.Prior` prior: a prior to set for this parameter + :param bool warning: whether to warn if another prior was set for this parameter + """ + repriorized = self.unset_priors() + self._add_to_index_operations(self.priors, repriorized, prior, warning) + + from paramz.domains import _REAL, _POSITIVE, _NEGATIVE + if prior.domain is _POSITIVE: + self.constrain_positive(warning) + elif prior.domain is _NEGATIVE: + self.constrain_negative(warning) + elif prior.domain is _REAL: + rav_i = self._raveled_index() + assert all(all(False if c is __fixed__ else c.domain is _REAL for c in con) for con in self.constraints.properties_for(rav_i)), 'Domain of prior and constraint have to match, please unconstrain if you REALLY wish to use this prior' + + def unset_priors(self, *priors): + """ + Un-set all priors given (in *priors) from this parameter handle. + """ + return self._remove_from_index_operations(self.priors, priors) + + def log_prior(self): + """evaluate the prior""" + if self.priors.size == 0: + return 0. + x = self.param_array + #evaluate the prior log densities + log_p = reduce(lambda a, b: a + b, (p.lnpdf(x[ind]).sum() for p, ind in self.priors.items()), 0) + + #account for the transformation by evaluating the log Jacobian (where things are transformed) + log_j = 0. + priored_indexes = np.hstack([i for p, i in self.priors.items()]) + for c,j in self.constraints.items(): + if not isinstance(c, Transformation):continue + for jj in j: + if jj in priored_indexes: + log_j += c.log_jacobian(x[jj]) + return log_p + log_j + + def _log_prior_gradients(self): + """evaluate the gradients of the priors""" + if self.priors.size == 0: + return 0. + x = self.param_array + ret = np.zeros(x.size) + #compute derivate of prior density + [np.put(ret, ind, p.lnpdf_grad(x[ind])) for p, ind in self.priors.items()] + #add in jacobian derivatives if transformed + priored_indexes = np.hstack([i for p, i in self.priors.items()]) + for c,j in self.constraints.items(): + if not isinstance(c, Transformation):continue + for jj in j: + if jj in priored_indexes: + ret[jj] += c.log_jacobian_grad(x[jj]) + return ret diff --git a/GPy/core/parameterization/priors.py b/GPy/core/parameterization/priors.py index 224394ca..cb7699eb 100644 --- a/GPy/core/parameterization/priors.py +++ b/GPy/core/parameterization/priors.py @@ -5,7 +5,7 @@ import numpy as np from scipy.special import gammaln, digamma from ...util.linalg import pdinv -from .domains import _REAL, _POSITIVE +from paramz.domains import _REAL, _POSITIVE import warnings import weakref @@ -725,8 +725,9 @@ class DGPLVM(Prior): # ****************************************** -from .. import Parameterized -from .. import Param +from . import Parameterized +from . import Param + class DGPLVM_Lamda(Prior, Parameterized): """ Implementation of the Discriminative Gaussian Process Latent Variable model paper, by Raquel. diff --git a/GPy/core/parameterization/ties_and_remappings.py b/GPy/core/parameterization/ties_and_remappings.py deleted file mode 100644 index 527bc47c..00000000 --- a/GPy/core/parameterization/ties_and_remappings.py +++ /dev/null @@ -1,225 +0,0 @@ -# Copyright (c) 2014, James Hensman, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) - -import numpy as np -from .parameterized import Parameterized -from .param import Param - -class Remapping(Parameterized): - def mapping(self): - """ - The return value of this function gives the values which the re-mapped - parameters should take. Implement in sub-classes. - """ - raise NotImplementedError - - def callback(self): - raise NotImplementedError - - def __str__(self): - return self.name - - def parameters_changed(self): - #ensure all out parameters have the correct value, as specified by our mapping - index = self._highest_parent_.constraints[self] - self._highest_parent_.param_array[index] = self.mapping() - [p.notify_observers(which=self) for p in self.tied_parameters] - -class Fix(Remapping): - pass - - - - -class Tie(Parameterized): - """ - The new parameter tie framework. (under development) - - All the parameters tied together get a new parameter inside the *Tie* object. - Its value should always be equal to all the tied parameters, and its gradient - is the sum of all the tied parameters. - - =====Implementation Details===== - The *Tie* object should only exist on the top of param tree (the highest parent). - - self.label_buf: - It uses a label buffer that has the same length as all the parameters (self._highest_parent_.param_array). - The buffer keeps track of all the tied parameters. All the tied parameters have a label (an interger) higher - than 0, and the parameters that have the same label are tied together. - - self.buf_index: - An auxiliary index list for the global index of the tie parameter inside the *Tie* object. - - ================================ - - TODO: - * EVERYTHING - - """ - def __init__(self, name='tie'): - super(Tie, self).__init__(name) - self.tied_param = None - # The buffer keeps track of tie status - self.label_buf = None - # The global indices of the 'tied' param - self.buf_idx = None - # A boolean array indicating non-tied parameters - self._tie_ = None - - def getTieFlag(self, p=None): - if self.tied_param is None: - if self._tie_ is None or self._tie_.size != self._highest_parent_.param_array.size: - self._tie_ = np.ones((self._highest_parent_.param_array.size,),dtype=np.bool) - if p is not None: - return self._tie_[p._highest_parent_._raveled_index_for(p)] - return self._tie_ - - def _init_labelBuf(self): - if self.label_buf is None: - self.label_buf = np.zeros(self._highest_parent_.param_array.shape, dtype=np.int) - if self._tie_ is None or self._tie_.size != self._highest_parent_.param_array.size: - self._tie_ = np.ones((self._highest_parent_.param_array.size,),dtype=np.bool) - - def _updateTieFlag(self): - if self._tie_.size != self.label_buf.size: - self._tie_ = np.ones((self._highest_parent_.param_array.size,),dtype=np.bool) - self._tie_[self.label_buf>0] = False - self._tie_[self.buf_idx] = True - - def add_tied_parameter(self, p, p2=None): - """ - Tie the list of parameters p together (p2==None) or - Tie the list of parameters p with the list of parameters p2 (p2!=None) - """ - self._init_labelBuf() - if p2 is None: - idx = self._highest_parent_._raveled_index_for(p) - val = self._sync_val_group(idx) - if np.all(self.label_buf[idx]==0): - # None of p has been tied before. - tie_idx = self._expandTieParam(1) - print(tie_idx) - tie_id = self.label_buf.max()+1 - self.label_buf[tie_idx] = tie_id - else: - b = self.label_buf[idx] - ids = np.unique(b[b>0]) - tie_id, tie_idx = self._merge_tie_param(ids) - self._highest_parent_.param_array[tie_idx] = val - idx = self._highest_parent_._raveled_index_for(p) - self.label_buf[idx] = tie_id - else: - pass - self._updateTieFlag() - - def _merge_tie_param(self, ids): - """Merge the tie parameters with ids in the list.""" - if len(ids)==1: - id_final_idx = self.buf_idx[self.label_buf[self.buf_idx]==ids[0]][0] - return ids[0],id_final_idx - id_final = ids[0] - ids_rm = ids[1:] - label_buf_param = self.label_buf[self.buf_idx] - idx_param = [np.where(label_buf_param==i)[0][0] for i in ids_rm] - self._removeTieParam(idx_param) - [np.put(self.label_buf, np.where(self.label_buf==i), id_final) for i in ids_rm] - id_final_idx = self.buf_idx[self.label_buf[self.buf_idx]==id_final][0] - return id_final, id_final_idx - - def _sync_val_group(self, idx): - self._highest_parent_.param_array[idx] = self._highest_parent_.param_array[idx].mean() - return self._highest_parent_.param_array[idx][0] - - def _expandTieParam(self, num): - """Expand the tie param with the number of *num* parameters""" - if self.tied_param is None: - new_buf = np.empty((num,)) - else: - new_buf = np.empty((self.tied_param.size+num,)) - new_buf[:self.tied_param.size] = self.tied_param.param_array.copy() - self.remove_parameter(self.tied_param) - self.tied_param = Param('tied',new_buf) - self.add_parameter(self.tied_param) - buf_idx_new = self._highest_parent_._raveled_index_for(self.tied_param) - self._expand_label_buf(self.buf_idx, buf_idx_new) - self.buf_idx = buf_idx_new - return self.buf_idx[-num:] - - def _removeTieParam(self, idx): - """idx within tied_param""" - new_buf = np.empty((self.tied_param.size-len(idx),)) - bool_list = np.ones((self.tied_param.size,),dtype=np.bool) - bool_list[idx] = False - new_buf[:] = self.tied_param.param_array[bool_list] - self.remove_parameter(self.tied_param) - self.tied_param = Param('tied',new_buf) - self.add_parameter(self.tied_param) - buf_idx_new = self._highest_parent_._raveled_index_for(self.tied_param) - self._shrink_label_buf(self.buf_idx, buf_idx_new, bool_list) - self.buf_idx = buf_idx_new - - def _expand_label_buf(self, idx_old, idx_new): - """Expand label buffer accordingly""" - if idx_old is None: - self.label_buf = np.zeros(self._highest_parent_.param_array.shape, dtype=np.int) - else: - bool_old = np.zeros((self.label_buf.size,),dtype=np.bool) - bool_old[idx_old] = True - bool_new = np.zeros((self._highest_parent_.param_array.size,),dtype=np.bool) - bool_new[idx_new] = True - label_buf_new = np.zeros(self._highest_parent_.param_array.shape, dtype=np.int) - label_buf_new[np.logical_not(bool_new)] = self.label_buf[np.logical_not(bool_old)] - label_buf_new[idx_new[:len(idx_old)]] = self.label_buf[idx_old] - self.label_buf = label_buf_new - - def _shrink_label_buf(self, idx_old, idx_new, bool_list): - bool_old = np.zeros((self.label_buf.size,),dtype=np.bool) - bool_old[idx_old] = True - bool_new = np.zeros((self._highest_parent_.param_array.size,),dtype=np.bool) - bool_new[idx_new] = True - label_buf_new = np.empty(self._highest_parent_.param_array.shape, dtype=np.int) - label_buf_new[np.logical_not(bool_new)] = self.label_buf[np.logical_not(bool_old)] - label_buf_new[idx_new] = self.label_buf[idx_old[bool_list]] - self.label_buf = label_buf_new - - def _check_change(self): - changed = False - if self.tied_param is not None: - for i in range(self.tied_param.size): - b0 = self.label_buf==self.label_buf[self.buf_idx[i]] - b = self._highest_parent_.param_array[b0]!=self.tied_param[i] - if b.sum()==0: - print('XXX') - continue - elif b.sum()==1: - print('!!!') - val = self._highest_parent_.param_array[b0][b][0] - self._highest_parent_.param_array[b0] = val - else: - print('@@@') - self._highest_parent_.param_array[b0] = self.tied_param[i] - changed = True - return changed - - def parameters_changed(self): - #ensure all out parameters have the correct value, as specified by our mapping - changed = self._check_change() - if changed: - self._highest_parent_._trigger_params_changed() - self.collate_gradient() - - def collate_gradient(self): - if self.tied_param is not None: - self.tied_param.gradient = 0. - [np.put(self.tied_param.gradient, i, self._highest_parent_.gradient[self.label_buf==self.label_buf[self.buf_idx[i]]].sum()) - for i in range(self.tied_param.size)] - - def propagate_val(self): - if self.tied_param is not None: - for i in range(self.tied_param.size): - self._highest_parent_.param_array[self.label_buf==self.label_buf[self.buf_idx[i]]] = self.tied_param[i] - - - - - diff --git a/GPy/core/parameterization/transformations.py b/GPy/core/parameterization/transformations.py index 830809d6..1799a06d 100644 --- a/GPy/core/parameterization/transformations.py +++ b/GPy/core/parameterization/transformations.py @@ -1,518 +1,4 @@ -# Copyright (c) 2012, GPy authors (see AUTHORS.txt). +# Copyright (c) 2014, Max Zwiessele, James Hensman # Licensed under the BSD 3-clause license (see LICENSE.txt) - -import numpy as np -from .domains import _POSITIVE,_NEGATIVE, _BOUNDED -import weakref - -import sys - -_exp_lim_val = np.finfo(np.float64).max -_lim_val = 36.0 -epsilon = np.finfo(np.float64).resolution - -#=============================================================================== -# Fixing constants -__fixed__ = "fixed" -FIXED = False -UNFIXED = True -#=============================================================================== - - -class Transformation(object): - domain = None - _instance = None - def __new__(cls, *args, **kwargs): - if not cls._instance or cls._instance.__class__ is not cls: - cls._instance = super(Transformation, cls).__new__(cls, *args, **kwargs) - return cls._instance - def f(self, opt_param): - raise NotImplementedError - def finv(self, model_param): - raise NotImplementedError - def log_jacobian(self, model_param): - """ - compute the log of the jacobian of f, evaluated at f(x)= model_param - """ - raise NotImplementedError - def log_jacobian_grad(self, model_param): - """ - compute the drivative of the log of the jacobian of f, evaluated at f(x)= model_param - """ - raise NotImplementedError - def gradfactor(self, model_param, dL_dmodel_param): - """ df(opt_param)_dopt_param evaluated at self.f(opt_param)=model_param, times the gradient dL_dmodel_param, - - i.e.: - define - - .. math:: - - \frac{\frac{\partial L}{\partial f}\left(\left.\partial f(x)}{\partial x}\right|_{x=f^{-1}(f)\right)} - """ - raise NotImplementedError - def gradfactor_non_natural(self, model_param, dL_dmodel_param): - return self.gradfactor(model_param, dL_dmodel_param) - def initialize(self, f): - """ produce a sensible initial value for f(x)""" - raise NotImplementedError - def plot(self, xlabel=r'transformed $\theta$', ylabel=r'$\theta$', axes=None, *args,**kw): - assert "matplotlib" in sys.modules, "matplotlib package has not been imported." - import matplotlib.pyplot as plt - from ...plotting.matplot_dep import base_plots - x = np.linspace(-8,8) - base_plots.meanplot(x, self.f(x), *args, ax=axes, **kw) - axes = plt.gca() - axes.set_xlabel(xlabel) - axes.set_ylabel(ylabel) - def __str__(self): - raise NotImplementedError - def __repr__(self): - return self.__class__.__name__ - -class Logexp(Transformation): - domain = _POSITIVE - def f(self, x): - return np.where(x>_lim_val, x, np.log1p(np.exp(np.clip(x, -_lim_val, _lim_val)))) + epsilon - #raises overflow warning: return np.where(x>_lim_val, x, np.log(1. + np.exp(x))) - def finv(self, f): - return np.where(f>_lim_val, f, np.log(np.exp(f+1e-20) - 1.)) - def gradfactor(self, f, df): - return np.einsum('i,i->i', df, np.where(f>_lim_val, 1., 1. - np.exp(-f))) - def initialize(self, f): - if np.any(f < 0.): - print("Warning: changing parameters to satisfy constraints") - return np.abs(f) - def log_jacobian(self, model_param): - return np.where(model_param>_lim_val, model_param, np.log(np.exp(model_param+1e-20) - 1.)) - model_param - def log_jacobian_grad(self, model_param): - return 1./(np.exp(model_param)-1.) - def __str__(self): - return '+ve' - -class Exponent(Transformation): - domain = _POSITIVE - def f(self, x): - return np.where(x<_lim_val, np.where(x>-_lim_val, np.exp(x), np.exp(-_lim_val)), np.exp(_lim_val)) - def finv(self, x): - return np.log(x) - def gradfactor(self, f, df): - return np.einsum('i,i->i', df, f) - def initialize(self, f): - if np.any(f < 0.): - print("Warning: changing parameters to satisfy constraints") - return np.abs(f) - def log_jacobian(self, model_param): - return np.log(model_param) - def log_jacobian_grad(self, model_param): - return 1./model_param - def __str__(self): - return '+ve' - - - -class NormalTheta(Transformation): - "Do not use, not officially supported!" - _instances = [] - def __new__(cls, mu_indices=None, var_indices=None): - "Do not use, not officially supported!" - if cls._instances: - cls._instances[:] = [instance for instance in cls._instances if instance()] - for instance in cls._instances: - if np.all(instance().mu_indices==mu_indices, keepdims=False) and np.all(instance().var_indices==var_indices, keepdims=False): - return instance() - o = super(Transformation, cls).__new__(cls, mu_indices, var_indices) - cls._instances.append(weakref.ref(o)) - return cls._instances[-1]() - - def __init__(self, mu_indices, var_indices): - self.mu_indices = mu_indices - self.var_indices = var_indices - - def f(self, theta): - # In here abs is only a trick to make sure the numerics are ok. - # The variance will never go below zero, but at initialization we need to make sure - # that the values are ok - # Before: - theta[self.var_indices] = np.abs(-.5/theta[self.var_indices]) - #theta[self.var_indices] = np.exp(-.5/theta[self.var_indices]) - theta[self.mu_indices] *= theta[self.var_indices] - return theta # which is now {mu, var} - - def finv(self, muvar): - # before: - varp = muvar[self.var_indices] - muvar[self.mu_indices] /= varp - muvar[self.var_indices] = -.5/varp - #muvar[self.var_indices] = -.5/np.log(varp) - - return muvar # which is now {theta1, theta2} - - def gradfactor(self, muvar, dmuvar): - mu = muvar[self.mu_indices] - var = muvar[self.var_indices] - #======================================================================= - # theta gradients - # This works and the gradient checks! - dmuvar[self.mu_indices] *= var - dmuvar[self.var_indices] *= 2*(var)**2 - dmuvar[self.var_indices] += 2*dmuvar[self.mu_indices]*mu - #======================================================================= - - return dmuvar # which is now the gradient multiplicator for {theta1, theta2} - - def initialize(self, f): - if np.any(f[self.var_indices] < 0.): - print("Warning: changing parameters to satisfy constraints") - f[self.var_indices] = np.abs(f[self.var_indices]) - return f - - def __str__(self): - return "theta" - - def __getstate__(self): - return [self.mu_indices, self.var_indices] - - def __setstate__(self, state): - self.mu_indices = state[0] - self.var_indices = state[1] - -class NormalNaturalAntti(NormalTheta): - "Do not use, not officially supported!" - _instances = [] - def __new__(cls, mu_indices=None, var_indices=None): - "Do not use, not officially supported!" - if cls._instances: - cls._instances[:] = [instance for instance in cls._instances if instance()] - for instance in cls._instances: - if np.all(instance().mu_indices==mu_indices, keepdims=False) and np.all(instance().var_indices==var_indices, keepdims=False): - return instance() - o = super(Transformation, cls).__new__(cls, mu_indices, var_indices) - cls._instances.append(weakref.ref(o)) - return cls._instances[-1]() - - def __init__(self, mu_indices, var_indices): - self.mu_indices = mu_indices - self.var_indices = var_indices - - def gradfactor(self, muvar, dmuvar): - mu = muvar[self.mu_indices] - var = muvar[self.var_indices] - - #======================================================================= - # theta gradients - # This works and the gradient checks! - dmuvar[self.mu_indices] *= var - dmuvar[self.var_indices] *= 2*var**2#np.einsum('i,i,i,i->i', dmuvar[self.var_indices], [2], var, var) - #======================================================================= - - return dmuvar # which is now the gradient multiplicator - - def initialize(self, f): - if np.any(f[self.var_indices] < 0.): - print("Warning: changing parameters to satisfy constraints") - f[self.var_indices] = np.abs(f[self.var_indices]) - return f - - def __str__(self): - return "natantti" - -class NormalEta(Transformation): - "Do not use, not officially supported!" - _instances = [] - def __new__(cls, mu_indices=None, var_indices=None): - "Do not use, not officially supported!" - if cls._instances: - cls._instances[:] = [instance for instance in cls._instances if instance()] - for instance in cls._instances: - if np.all(instance().mu_indices==mu_indices, keepdims=False) and np.all(instance().var_indices==var_indices, keepdims=False): - return instance() - o = super(Transformation, cls).__new__(cls, mu_indices, var_indices) - cls._instances.append(weakref.ref(o)) - return cls._instances[-1]() - - def __init__(self, mu_indices, var_indices): - self.mu_indices = mu_indices - self.var_indices = var_indices - - def f(self, theta): - theta[self.var_indices] = np.abs(theta[self.var_indices] - theta[self.mu_indices]**2) - return theta # which is now {mu, var} - - def finv(self, muvar): - muvar[self.var_indices] += muvar[self.mu_indices]**2 - return muvar # which is now {eta1, eta2} - - def gradfactor(self, muvar, dmuvar): - mu = muvar[self.mu_indices] - #======================================================================= - # Lets try natural gradients instead: Not working with bfgs... try stochastic! - dmuvar[self.mu_indices] -= 2*mu*dmuvar[self.var_indices] - #======================================================================= - return dmuvar # which is now the gradient multiplicator - - def initialize(self, f): - if np.any(f[self.var_indices] < 0.): - print("Warning: changing parameters to satisfy constraints") - f[self.var_indices] = np.abs(f[self.var_indices]) - return f - - def __str__(self): - return "eta" - -class NormalNaturalThroughTheta(NormalTheta): - "Do not use, not officially supported!" - _instances = [] - def __new__(cls, mu_indices=None, var_indices=None): - "Do not use, not officially supported!" - if cls._instances: - cls._instances[:] = [instance for instance in cls._instances if instance()] - for instance in cls._instances: - if np.all(instance().mu_indices==mu_indices, keepdims=False) and np.all(instance().var_indices==var_indices, keepdims=False): - return instance() - o = super(Transformation, cls).__new__(cls, mu_indices, var_indices) - cls._instances.append(weakref.ref(o)) - return cls._instances[-1]() - - def __init__(self, mu_indices, var_indices): - self.mu_indices = mu_indices - self.var_indices = var_indices - - def gradfactor(self, muvar, dmuvar): - mu = muvar[self.mu_indices] - var = muvar[self.var_indices] - - #======================================================================= - # This is just eta direction: - dmuvar[self.mu_indices] -= 2*mu*dmuvar[self.var_indices] - #======================================================================= - - #======================================================================= - # This is by going through theta fully and then going into eta direction: - #dmu = dmuvar[self.mu_indices] - #dmuvar[self.var_indices] += dmu*mu*(var + 4/var) - #======================================================================= - return dmuvar # which is now the gradient multiplicator - - def gradfactor_non_natural(self, muvar, dmuvar): - mu = muvar[self.mu_indices] - var = muvar[self.var_indices] - #======================================================================= - # theta gradients - # This works and the gradient checks! - dmuvar[self.mu_indices] *= var - dmuvar[self.var_indices] *= 2*(var)**2 - dmuvar[self.var_indices] += 2*dmuvar[self.mu_indices]*mu - #======================================================================= - - return dmuvar # which is now the gradient multiplicator for {theta1, theta2} - - def __str__(self): - return "natgrad" - - -class NormalNaturalWhooot(NormalTheta): - "Do not use, not officially supported!" - _instances = [] - def __new__(cls, mu_indices=None, var_indices=None): - "Do not use, not officially supported!" - if cls._instances: - cls._instances[:] = [instance for instance in cls._instances if instance()] - for instance in cls._instances: - if np.all(instance().mu_indices==mu_indices, keepdims=False) and np.all(instance().var_indices==var_indices, keepdims=False): - return instance() - o = super(Transformation, cls).__new__(cls, mu_indices, var_indices) - cls._instances.append(weakref.ref(o)) - return cls._instances[-1]() - - def __init__(self, mu_indices, var_indices): - self.mu_indices = mu_indices - self.var_indices = var_indices - - def gradfactor(self, muvar, dmuvar): - #mu = muvar[self.mu_indices] - #var = muvar[self.var_indices] - - #======================================================================= - # This is just eta direction: - #dmuvar[self.mu_indices] -= 2*mu*dmuvar[self.var_indices] - #======================================================================= - - #======================================================================= - # This is by going through theta fully and then going into eta direction: - #dmu = dmuvar[self.mu_indices] - #dmuvar[self.var_indices] += dmu*mu*(var + 4/var) - #======================================================================= - return dmuvar # which is now the gradient multiplicator - - def __str__(self): - return "natgrad" - -class NormalNaturalThroughEta(NormalEta): - "Do not use, not officially supported!" - _instances = [] - def __new__(cls, mu_indices=None, var_indices=None): - "Do not use, not officially supported!" - if cls._instances: - cls._instances[:] = [instance for instance in cls._instances if instance()] - for instance in cls._instances: - if np.all(instance().mu_indices==mu_indices, keepdims=False) and np.all(instance().var_indices==var_indices, keepdims=False): - return instance() - o = super(Transformation, cls).__new__(cls, mu_indices, var_indices) - cls._instances.append(weakref.ref(o)) - return cls._instances[-1]() - - def __init__(self, mu_indices, var_indices): - self.mu_indices = mu_indices - self.var_indices = var_indices - - def gradfactor(self, muvar, dmuvar): - mu = muvar[self.mu_indices] - var = muvar[self.var_indices] - #======================================================================= - # theta gradients - # This works and the gradient checks! - dmuvar[self.mu_indices] *= var - dmuvar[self.var_indices] *= 2*(var)**2 - dmuvar[self.var_indices] += 2*dmuvar[self.mu_indices]*mu - #======================================================================= - return dmuvar - - def __str__(self): - return "natgrad" - - -class LogexpNeg(Transformation): - domain = _POSITIVE - def f(self, x): - return np.where(x>_lim_val, -x, -np.log(1. + np.exp(np.clip(x, -np.inf, _lim_val)))) - #raises overflow warning: return np.where(x>_lim_val, x, np.log(1. + np.exp(x))) - def finv(self, f): - return np.where(f>_lim_val, 0, np.log(np.exp(-f) - 1.)) - def gradfactor(self, f, df): - return np.einsum('i,i->i', df, np.where(f>_lim_val, -1, -1 + np.exp(-f))) - def initialize(self, f): - if np.any(f < 0.): - print("Warning: changing parameters to satisfy constraints") - return np.abs(f) - def __str__(self): - return '+ve' - - -class NegativeLogexp(Transformation): - domain = _NEGATIVE - logexp = Logexp() - def f(self, x): - return -self.logexp.f(x) # np.log(1. + np.exp(x)) - def finv(self, f): - return self.logexp.finv(-f) # np.log(np.exp(-f) - 1.) - def gradfactor(self, f, df): - return np.einsum('i,i->i', df, -self.logexp.gradfactor(-f)) - def initialize(self, f): - return -self.logexp.initialize(f) # np.abs(f) - def __str__(self): - return '-ve' - -class LogexpClipped(Logexp): - max_bound = 1e100 - min_bound = 1e-10 - log_max_bound = np.log(max_bound) - log_min_bound = np.log(min_bound) - domain = _POSITIVE - _instances = [] - def __new__(cls, lower=1e-6, *args, **kwargs): - if cls._instances: - cls._instances[:] = [instance for instance in cls._instances if instance()] - for instance in cls._instances: - if instance().lower == lower: - return instance() - o = super(Transformation, cls).__new__(cls, lower, *args, **kwargs) - cls._instances.append(weakref.ref(o)) - return cls._instances[-1]() - def __init__(self, lower=1e-6): - self.lower = lower - def f(self, x): - exp = np.exp(np.clip(x, self.log_min_bound, self.log_max_bound)) - f = np.log(1. + exp) -# if np.isnan(f).any(): -# import ipdb;ipdb.set_trace() - return np.clip(f, self.min_bound, self.max_bound) - def finv(self, f): - return np.log(np.exp(f - 1.)) - def gradfactor(self, f, df): - ef = np.exp(f) # np.clip(f, self.min_bound, self.max_bound)) - gf = (ef - 1.) / ef - return np.einsum('i,i->i', df, gf) # np.where(f < self.lower, 0, gf) - def initialize(self, f): - if np.any(f < 0.): - print("Warning: changing parameters to satisfy constraints") - return np.abs(f) - def __str__(self): - return '+ve_c' - -class NegativeExponent(Exponent): - domain = _NEGATIVE - def f(self, x): - return -Exponent.f(x) - def finv(self, f): - return Exponent.finv(-f) - def gradfactor(self, f, df): - return np.einsum('i,i->i', df, f) - def initialize(self, f): - return -Exponent.initialize(f) #np.abs(f) - def __str__(self): - return '-ve' - -class Square(Transformation): - domain = _POSITIVE - def f(self, x): - return x ** 2 - def finv(self, x): - return np.sqrt(x) - def gradfactor(self, f, df): - return np.einsum('i,i->i', df, 2 * np.sqrt(f)) - def initialize(self, f): - return np.abs(f) - def __str__(self): - return '+sq' - -class Logistic(Transformation): - domain = _BOUNDED - _instances = [] - def __new__(cls, lower=1e-6, upper=1e-6, *args, **kwargs): - if cls._instances: - cls._instances[:] = [instance for instance in cls._instances if instance()] - for instance in cls._instances: - if instance().lower == lower and instance().upper == upper: - return instance() - newfunc = super(Transformation, cls).__new__ - if newfunc is object.__new__: - o = newfunc(cls) - else: - o = newfunc(cls, lower, upper, *args, **kwargs) - cls._instances.append(weakref.ref(o)) - return cls._instances[-1]() - def __init__(self, lower, upper): - assert lower < upper - self.lower, self.upper = float(lower), float(upper) - self.difference = self.upper - self.lower - def f(self, x): - if (x<-300.).any(): - x = x.copy() - x[x<-300.] = -300. - return self.lower + self.difference / (1. + np.exp(-x)) - def finv(self, f): - return np.log(np.clip(f - self.lower, 1e-10, np.inf) / np.clip(self.upper - f, 1e-10, np.inf)) - def gradfactor(self, f, df): - return np.einsum('i,i->i', df, (f - self.lower) * (self.upper - f) / self.difference) - def initialize(self, f): - if np.any(np.logical_or(f < self.lower, f > self.upper)): - print("Warning: changing parameters to satisfy constraints") - #return np.where(np.logical_or(f < self.lower, f > self.upper), self.f(f * 0.), f) - #FIXME: Max, zeros_like right? - return np.where(np.logical_or(f < self.lower, f > self.upper), self.f(np.zeros_like(f)), f) - def __str__(self): - return '{},{}'.format(self.lower, self.upper) - - +from paramz.transformations import * \ No newline at end of file diff --git a/GPy/core/parameterization/updateable.py b/GPy/core/parameterization/updateable.py deleted file mode 100644 index 07083ce0..00000000 --- a/GPy/core/parameterization/updateable.py +++ /dev/null @@ -1,54 +0,0 @@ -''' -Created on 11 Nov 2014 - -@author: maxz -''' -from .observable import Observable - - -class Updateable(Observable): - """ - A model can be updated or not. - Make sure updates can be switched on and off. - """ - def __init__(self, *args, **kwargs): - super(Updateable, self).__init__(*args, **kwargs) - - def update_model(self, updates=None): - """ - Get or set, whether automatic updates are performed. When updates are - off, the model might be in a non-working state. To make the model work - turn updates on again. - - :param bool|None updates: - - bool: whether to do updates - None: get the current update state - """ - if updates is None: - return self._update_on - assert isinstance(updates, bool), "updates are either on (True) or off (False)" - p = getattr(self, '_highest_parent_', None) - def turn_updates(s): - s._update_on = updates - p.traverse(turn_updates) - self.trigger_update() - - def toggle_update(self): - print("deprecated: toggle_update was renamed to update_toggle for easier access") - self.update_toggle() - def update_toggle(self): - self.update_model(not self.update_model()) - - def trigger_update(self, trigger_parent=True): - """ - Update the model from the current state. - Make sure that updates are on, otherwise this - method will do nothing - - :param bool trigger_parent: Whether to trigger the parent, after self has updated - """ - if not self.update_model() or (hasattr(self, "_in_init_") and self._in_init_): - #print "Warning: updates are off, updating the model will do nothing" - return - self._trigger_params_changed(trigger_parent) diff --git a/GPy/core/parameterization/variational.py b/GPy/core/parameterization/variational.py index a9585c86..84a0b739 100644 --- a/GPy/core/parameterization/variational.py +++ b/GPy/core/parameterization/variational.py @@ -7,9 +7,7 @@ Created on 6 Nov 2013 import numpy as np from .parameterized import Parameterized from .param import Param -from .transformations import Logexp, Logistic,__fixed__ -from GPy.util.misc import param_to_array -from GPy.util.caching import Cache_this +from paramz.transformations import Logexp, Logistic,__fixed__ class VariationalPrior(Parameterized): def __init__(self, name='latent space', **kw): diff --git a/GPy/core/sparse_gp.py b/GPy/core/sparse_gp.py index 6b364ab0..d71eecc3 100644 --- a/GPy/core/sparse_gp.py +++ b/GPy/core/sparse_gp.py @@ -6,11 +6,9 @@ from .gp import GP from .parameterization.param import Param from ..inference.latent_function_inference import var_dtc from .. import likelihoods -from .parameterization.variational import VariationalPosterior, NormalPosterior -from ..util.linalg import mdot +from GPy.core.parameterization.variational import VariationalPosterior import logging -import itertools logger = logging.getLogger("sparse gp") class SparseGP(GP): diff --git a/GPy/core/svgp.py b/GPy/core/svgp.py index b87fd493..a678a1fd 100644 --- a/GPy/core/svgp.py +++ b/GPy/core/svgp.py @@ -38,7 +38,7 @@ class SVGP(SparseGP): #create the SVI inference method inf_method = svgp_inf() - SparseGP.__init__(self, X_batch, Y_batch, Z, kernel, likelihood, mean_function=mean_function, inference_method=inf_method, + super(SVGP, self).__init__(X_batch, Y_batch, Z, kernel, likelihood, mean_function=mean_function, inference_method=inf_method, name=name, Y_metadata=Y_metadata, normalizer=False) #assume the number of latent functions is one per col of Y unless specified diff --git a/GPy/core/verbose_optimization.py b/GPy/core/verbose_optimization.py deleted file mode 100644 index c4539736..00000000 --- a/GPy/core/verbose_optimization.py +++ /dev/null @@ -1,185 +0,0 @@ -# Copyright (c) 2012-2014, Max Zwiessele. -# Licensed under the BSD 3-clause license (see LICENSE.txt) - -from __future__ import print_function -import numpy as np -import sys -import time -import datetime - -def exponents(fnow, current_grad): - exps = [np.abs(np.float(fnow)), 1 if current_grad is np.nan else current_grad] - return np.sign(exps) * np.log10(exps).astype(int) - -class VerboseOptimization(object): - def __init__(self, model, opt, maxiters, verbose=False, current_iteration=0, ipython_notebook=True, clear_after_finish=False): - self.verbose = verbose - if self.verbose: - self.model = model - self.iteration = current_iteration - self.p_iter = self.iteration - self.maxiters = maxiters - self.len_maxiters = len(str(maxiters)) - self.opt_name = opt.opt_name - self.model.add_observer(self, self.print_status) - self.status = 'running' - self.clear = clear_after_finish - - self.update() - - try: - from IPython.display import display - from IPython.html.widgets import IntProgress, HTML, Box, VBox, HBox, FlexBox - self.text = HTML(width='100%') - self.progress = IntProgress(min=0, max=maxiters) - #self.progresstext = Text(width='100%', disabled=True, value='0/{}'.format(maxiters)) - self.model_show = HTML() - self.ipython_notebook = ipython_notebook - except: - # Not in Ipython notebook - self.ipython_notebook = False - - if self.ipython_notebook: - left_col = VBox(children=[self.progress, self.text], padding=2, width='40%') - right_col = Box(children=[self.model_show], padding=2, width='60%') - self.hor_align = FlexBox(children = [left_col, right_col], width='100%', orientation='horizontal') - - display(self.hor_align) - - try: - self.text.set_css('width', '100%') - left_col.set_css({ - 'padding': '2px', - 'width': "100%", - }) - - right_col.set_css({ - 'padding': '2px', - }) - - self.hor_align.set_css({ - 'width': "100%", - }) - - self.hor_align.remove_class('vbox') - self.hor_align.add_class('hbox') - - left_col.add_class("box-flex1") - right_col.add_class('box-flex0') - - except: - pass - - #self.text.add_class('box-flex2') - #self.progress.add_class('box-flex1') - else: - self.exps = exponents(self.fnow, self.current_gradient) - print('Running {} Code:'.format(self.opt_name)) - print(' {3:7s} {0:{mi}s} {1:11s} {2:11s}'.format("i", "f", "|g|", "runtime", mi=self.len_maxiters)) - - def __enter__(self): - self.start = time.time() - self._time = self.start - return self - - def print_out(self, seconds): - if seconds<60: - ms = (seconds%1)*100 - self.timestring = "{s:0>2d}s{ms:0>2d}".format(s=int(seconds), ms=int(ms)) - else: - m, s = divmod(seconds, 60) - if m>59: - h, m = divmod(m, 60) - if h>23: - d, h = divmod(h, 24) - self.timestring = '{d:0>2d}d{h:0>2d}h{m:0>2d}'.format(m=int(m), h=int(h), d=int(d)) - else: - self.timestring = '{h:0>2d}h{m:0>2d}m{s:0>2d}'.format(m=int(m), s=int(s), h=int(h)) - else: - ms = (seconds%1)*100 - self.timestring = '{m:0>2d}m{s:0>2d}s{ms:0>2d}'.format(m=int(m), s=int(s), ms=int(ms)) - if self.ipython_notebook: - names_vals = [['optimizer', "{:s}".format(self.opt_name)], - ['runtime', "{:>s}".format(self.timestring)], - ['evaluation', "{:>0{l}}".format(self.iteration, l=self.len_maxiters)], - ['objective', "{: > 12.3E}".format(self.fnow)], - ['||gradient||', "{: >+12.3E}".format(float(self.current_gradient))], - ['status', "{:s}".format(self.status)], - ] - #message = "Lik:{:5.3E} Grad:{:5.3E} Lik:{:5.3E} Len:{!s}".format(float(m.log_likelihood()), np.einsum('i,i->', grads, grads), float(m.likelihood.variance), " ".join(["{:3.2E}".format(l) for l in m.kern.lengthscale.values])) - html_begin = """ - """ - html_end = "
" - html_body = "" - for name, val in names_vals: - html_body += "" - html_body += "{}".format(name) - html_body += "{}".format(val) - html_body += "" - self.text.value = html_begin + html_body + html_end - self.progress.value = (self.iteration+1) - #self.progresstext.value = '0/{}'.format((self.iteration+1)) - self.model_show.value = self.model._repr_html_() - else: - n_exps = exponents(self.fnow, self.current_gradient) - if self.iteration - self.p_iter >= 20 * np.random.rand(): - a = self.iteration >= self.p_iter * 2.78 - b = np.any(n_exps < self.exps) - if a or b: - self.p_iter = self.iteration - print('') - if b: - self.exps = n_exps - print('\r', end=' ') - print('{3:} {0:>0{mi}g} {1:> 12e} {2:> 12e}'.format(self.iteration, float(self.fnow), float(self.current_gradient), "{:>8s}".format(self.timestring), mi=self.len_maxiters), end=' ') # print 'Iteration:', iteration, ' Objective:', fnow, ' Scale:', beta, '\r', - sys.stdout.flush() - - def print_status(self, me, which=None): - self.update() - - t = time.time() - seconds = t-self.start - #sys.stdout.write(" "*len(self.message)) - if t-self._time > .3 or seconds < .3: - self.print_out(seconds) - self._time = t - - self.iteration += 1 - - def update(self): - self.fnow = self.model.objective_function() - if self.model.obj_grads is not None: - grad = self.model.obj_grads - self.current_gradient = np.dot(grad, grad) - else: - self.current_gradient = np.nan - - def finish(self, opt): - self.status = opt.status - if self.verbose and self.ipython_notebook: - if 'conv' in self.status.lower(): - self.progress.bar_style = 'success' - elif self.iteration >= self.maxiters: - self.progress.bar_style = 'warning' - else: - self.progress.bar_style = 'danger' - - def __exit__(self, type, value, traceback): - if self.verbose: - self.stop = time.time() - self.model.remove_observer(self) - self.print_out(self.stop - self.start) - - if not self.ipython_notebook: - print() - print('Runtime: {}'.format("{:>9s}".format(self.timestring))) - print('Optimization status: {0}'.format(self.status)) - print() - elif self.clear: - self.hor_align.close() diff --git a/GPy/defaults.cfg b/GPy/defaults.cfg index b23bb815..7f837109 100644 --- a/GPy/defaults.cfg +++ b/GPy/defaults.cfg @@ -1,19 +1,16 @@ # This is the default configuration file for GPy - -# Do note edit this file. - +# For user specific changes edit $HOME/.config/GPy/user.cfg # For machine specific changes (i.e. those specific to a given installation) edit GPy/installation.cfg -# For user specific changes edit $HOME/.gpy_user.cfg [parallel] # Enable openmp support. This speeds up some computations, depending on the number # of cores available. Setting up a compiler with openmp support can be difficult on # some platforms, hence by default it is off. -openmp=False +openmp = False [datasets] # location for the local data cache -dir=$HOME/tmp/GPy-datasets/ +dir = $HOME/tmp/GPy-datasets/ [anaconda] # if you have an anaconda python installation please specify it here. @@ -30,4 +27,7 @@ working = True working = True [plotting] +# Currently supported libraries are: matplotlib, plotly, none. +# for plotly make sure you have setup plotly to load your account. +# none means no plotting will be loaded. library = matplotlib \ No newline at end of file diff --git a/GPy/examples/dimensionality_reduction.py b/GPy/examples/dimensionality_reduction.py index 0d72949a..024b12ee 100644 --- a/GPy/examples/dimensionality_reduction.py +++ b/GPy/examples/dimensionality_reduction.py @@ -393,12 +393,12 @@ def ssgplvm_simulation(optimize=True, verbose=1, def bgplvm_simulation_missing_data(optimize=True, verbose=1, plot=True, plot_sim=False, - max_iters=2e4, percent_missing=.1, + max_iters=2e4, percent_missing=.1, d=13, ): from GPy import kern from GPy.models.bayesian_gplvm_minibatch import BayesianGPLVMMiniBatch - D1, D2, D3, N, num_inducing, Q = 13, 5, 8, 400, 3, 4 + D1, D2, D3, N, num_inducing, Q = d, 5, 8, 400, 3, 4 _, _, Ylist = _simulate_matern(D1, D2, D3, N, num_inducing, plot_sim) Y = Ylist[0] k = kern.Linear(Q, ARD=True) # + kern.white(Q, _np.exp(-2)) # + kern.bias(Q) @@ -421,6 +421,36 @@ def bgplvm_simulation_missing_data(optimize=True, verbose=1, m.kern.plot_ARD('BGPLVM Simulation ARD Parameters') return m +def bgplvm_simulation_missing_data_stochastics(optimize=True, verbose=1, + plot=True, plot_sim=False, + max_iters=2e4, percent_missing=.1, d=13, batchsize=2, + ): + from GPy import kern + from GPy.models.bayesian_gplvm_minibatch import BayesianGPLVMMiniBatch + + D1, D2, D3, N, num_inducing, Q = d, 5, 8, 400, 3, 4 + _, _, Ylist = _simulate_matern(D1, D2, D3, N, num_inducing, plot_sim) + Y = Ylist[0] + k = kern.Linear(Q, ARD=True) # + kern.white(Q, _np.exp(-2)) # + kern.bias(Q) + + inan = _np.random.binomial(1, percent_missing, size=Y.shape).astype(bool) # 80% missing data + Ymissing = Y.copy() + Ymissing[inan] = _np.nan + + m = BayesianGPLVMMiniBatch(Ymissing, Q, init="random", num_inducing=num_inducing, + kernel=k, missing_data=True, stochastic=True, batchsize=batchsize) + + m.Yreal = Y + + if optimize: + print("Optimizing model:") + m.optimize('bfgs', messages=verbose, max_iters=max_iters, + gtol=.05) + if plot: + m.X.plot("BGPLVM Latent Space 1D") + m.kern.plot_ARD('BGPLVM Simulation ARD Parameters') + return m + def mrd_simulation(optimize=True, verbose=True, plot=True, plot_sim=True, **kw): from GPy import kern diff --git a/GPy/examples/regression.py b/GPy/examples/regression.py index 1ce2f6f4..11734564 100644 --- a/GPy/examples/regression.py +++ b/GPy/examples/regression.py @@ -275,7 +275,7 @@ def toy_rbf_1d_50(optimize=True, plot=True): def toy_poisson_rbf_1d_laplace(optimize=True, plot=True): """Run a simple demonstration of a standard Gaussian process fitting it to data sampled from an RBF covariance.""" optimizer='scg' - x_len = 30 + x_len = 100 X = np.linspace(0, 10, x_len)[:, None] f_true = np.random.multivariate_normal(np.zeros(x_len), GPy.kern.RBF(1).K(X)) Y = np.array([np.random.poisson(np.exp(f)) for f in f_true])[:,None] diff --git a/GPy/inference/__init__.py b/GPy/inference/__init__.py index c5044582..4d94c619 100644 --- a/GPy/inference/__init__.py +++ b/GPy/inference/__init__.py @@ -1,3 +1,7 @@ -from . import latent_function_inference from . import optimization +from . import latent_function_inference from . import mcmc + +import sys +sys.modules['GPy.inference.optimization'] = optimization +sys.modules['GPy.inference.optimization.optimization'] = optimization diff --git a/GPy/inference/latent_function_inference/exact_gaussian_inference.py b/GPy/inference/latent_function_inference/exact_gaussian_inference.py index 2d8fb691..0ab85586 100644 --- a/GPy/inference/latent_function_inference/exact_gaussian_inference.py +++ b/GPy/inference/latent_function_inference/exact_gaussian_inference.py @@ -22,7 +22,7 @@ class ExactGaussianInference(LatentFunctionInference): def __init__(self): pass#self._YYTfactor_cache = caching.cache() - def inference(self, kern, X, likelihood, Y, mean_function=None, Y_metadata=None, K=None, precision=None): + def inference(self, kern, X, likelihood, Y, mean_function=None, Y_metadata=None, K=None, precision=None, Z_tilde=None): """ Returns a Posterior class containing essential quantities of the posterior """ @@ -49,9 +49,15 @@ class ExactGaussianInference(LatentFunctionInference): log_marginal = 0.5*(-Y.size * log_2_pi - Y.shape[1] * W_logdet - np.sum(alpha * YYT_factor)) + if Z_tilde is not None: + # This is a correction term for the log marginal likelihood + # In EP this is log Z_tilde, which is the difference between the + # Gaussian marginal and Z_EP + log_marginal += Z_tilde + dL_dK = 0.5 * (tdot(alpha) - Y.shape[1] * Wi) - dL_dthetaL = likelihood.exact_inference_gradients(np.diag(dL_dK),Y_metadata) + dL_dthetaL = likelihood.exact_inference_gradients(np.diag(dL_dK), Y_metadata) return Posterior(woodbury_chol=LW, woodbury_vector=alpha, K=K), log_marginal, {'dL_dK':dL_dK, 'dL_dthetaL':dL_dthetaL, 'dL_dm':alpha} diff --git a/GPy/inference/latent_function_inference/expectation_propagation.py b/GPy/inference/latent_function_inference/expectation_propagation.py index d293d4de..b2a3d4b6 100644 --- a/GPy/inference/latent_function_inference/expectation_propagation.py +++ b/GPy/inference/latent_function_inference/expectation_propagation.py @@ -2,14 +2,14 @@ # Licensed under the BSD 3-clause license (see LICENSE.txt) import numpy as np from ...util.linalg import jitchol, DSYR, dtrtrs, dtrtri -from ...core.parameterization.observable_array import ObsAr +from paramz import ObsAr from . import ExactGaussianInference, VarDTC from ...util import diag log_2_pi = np.log(2*np.pi) class EPBase(object): - def __init__(self, epsilon=1e-6, eta=1., delta=1.): + def __init__(self, epsilon=1e-6, eta=1., delta=1., always_reset=False): """ The expectation-propagation algorithm. For nomenclature see Rasmussen & Williams 2006. @@ -20,8 +20,12 @@ class EPBase(object): :type eta: float64 :param delta: damping EP updates factor. :type delta: float64 + :param always_reset: setting to always reset the approximation at the beginning of every inference call. + :type always_reest: boolean + """ super(EPBase, self).__init__() + self.always_reset = always_reset self.epsilon, self.eta, self.delta = epsilon, eta, delta self.reset() @@ -38,37 +42,46 @@ class EPBase(object): class EP(EPBase, ExactGaussianInference): def inference(self, kern, X, likelihood, Y, mean_function=None, Y_metadata=None, precision=None, K=None): + if self.always_reset: + self.reset() + num_data, output_dim = Y.shape - assert output_dim ==1, "ep in 1D only (for now!)" + assert output_dim == 1, "ep in 1D only (for now!)" if K is None: K = kern.K(X) if self._ep_approximation is None: #if we don't yet have the results of runnign EP, run EP and store the computed factors in self._ep_approximation - mu, Sigma, mu_tilde, tau_tilde, Z_hat = self._ep_approximation = self.expectation_propagation(K, Y, likelihood, Y_metadata) + mu, Sigma, mu_tilde, tau_tilde, Z_tilde = self._ep_approximation = self.expectation_propagation(K, Y, likelihood, Y_metadata) else: #if we've already run EP, just use the existing approximation stored in self._ep_approximation - mu, Sigma, mu_tilde, tau_tilde, Z_hat = self._ep_approximation + mu, Sigma, mu_tilde, tau_tilde, Z_tilde = self._ep_approximation - return super(EP, self).inference(kern, X, likelihood, mu_tilde[:,None], mean_function=mean_function, Y_metadata=Y_metadata, precision=1./tau_tilde, K=K) + return super(EP, self).inference(kern, X, likelihood, mu_tilde[:,None], mean_function=mean_function, Y_metadata=Y_metadata, precision=1./tau_tilde, K=K, Z_tilde=np.log(Z_tilde).sum()) def expectation_propagation(self, K, Y, likelihood, Y_metadata): num_data, data_dim = Y.shape assert data_dim == 1, "This EP methods only works for 1D outputs" - #Initial values - Posterior distribution parameters: q(f|X,Y) = N(f|mu,Sigma) mu = np.zeros(num_data) Sigma = K.copy() diag.add(Sigma, 1e-7) + # Makes computing the sign quicker if we work with numpy arrays rather + # than ObsArrays + Y = Y.values.copy() + #Initial values - Marginal moments Z_hat = np.empty(num_data,dtype=np.float64) mu_hat = np.empty(num_data,dtype=np.float64) sigma2_hat = np.empty(num_data,dtype=np.float64) + tau_cav = np.empty(num_data,dtype=np.float64) + v_cav = np.empty(num_data,dtype=np.float64) + #initial values - Gaussian factors if self.old_mutilde is None: tau_tilde, mu_tilde, v_tilde = np.zeros((3, num_data)) @@ -80,22 +93,32 @@ class EP(EPBase, ExactGaussianInference): #Approximation tau_diff = self.epsilon + 1. v_diff = self.epsilon + 1. + tau_tilde_old = np.nan + v_tilde_old = np.nan iterations = 0 while (tau_diff > self.epsilon) or (v_diff > self.epsilon): update_order = np.random.permutation(num_data) for i in update_order: #Cavity distribution parameters - tau_cav = 1./Sigma[i,i] - self.eta*tau_tilde[i] - v_cav = mu[i]/Sigma[i,i] - self.eta*v_tilde[i] + tau_cav[i] = 1./Sigma[i,i] - self.eta*tau_tilde[i] + v_cav[i] = mu[i]/Sigma[i,i] - self.eta*v_tilde[i] + if Y_metadata is not None: + # Pick out the relavent metadata for Yi + Y_metadata_i = {} + for key in Y_metadata.keys(): + Y_metadata_i[key] = Y_metadata[key][i, :] + else: + Y_metadata_i = None #Marginal moments - Z_hat[i], mu_hat[i], sigma2_hat[i] = likelihood.moments_match_ep(Y[i], tau_cav, v_cav)#, Y_metadata=None)#=(None if Y_metadata is None else Y_metadata[i])) + Z_hat[i], mu_hat[i], sigma2_hat[i] = likelihood.moments_match_ep(Y[i], tau_cav[i], v_cav[i], Y_metadata_i=Y_metadata_i) #Site parameters update delta_tau = self.delta/self.eta*(1./sigma2_hat[i] - 1./Sigma[i,i]) delta_v = self.delta/self.eta*(mu_hat[i]/sigma2_hat[i] - mu[i]/Sigma[i,i]) tau_tilde[i] += delta_tau v_tilde[i] += delta_v #Posterior distribution parameters update - DSYR(Sigma, Sigma[:,i].copy(), -delta_tau/(1.+ delta_tau*Sigma[i,i])) + ci = delta_tau/(1.+ delta_tau*Sigma[i,i]) + DSYR(Sigma, Sigma[:,i].copy(), -ci) mu = np.dot(Sigma, v_tilde) #(re) compute Sigma and mu using full Cholesky decompy @@ -108,7 +131,7 @@ class EP(EPBase, ExactGaussianInference): mu = np.dot(Sigma,v_tilde) #monitor convergence - if iterations>0: + if iterations > 0: tau_diff = np.mean(np.square(tau_tilde-tau_tilde_old)) v_diff = np.mean(np.square(v_tilde-v_tilde_old)) tau_tilde_old = tau_tilde.copy() @@ -117,7 +140,11 @@ class EP(EPBase, ExactGaussianInference): iterations += 1 mu_tilde = v_tilde/tau_tilde - return mu, Sigma, mu_tilde, tau_tilde, Z_hat + mu_cav = v_cav/tau_cav + sigma2_sigma2tilde = 1./tau_cav + 1./tau_tilde + Z_tilde = np.exp(np.log(Z_hat) + 0.5*np.log(2*np.pi) + 0.5*np.log(sigma2_sigma2tilde) + + 0.5*((mu_cav - mu_tilde)**2) / (sigma2_sigma2tilde)) + return mu, Sigma, mu_tilde, tau_tilde, Z_tilde class EPDTC(EPBase, VarDTC): def inference(self, kern, X, Z, likelihood, Y, mean_function=None, Y_metadata=None, Lm=None, dL_dKmm=None, psi0=None, psi1=None, psi2=None): @@ -133,16 +160,16 @@ class EPDTC(EPBase, VarDTC): Kmn = psi1.T if self._ep_approximation is None: - mu, Sigma, mu_tilde, tau_tilde, Z_hat = self._ep_approximation = self.expectation_propagation(Kmm, Kmn, Y, likelihood, Y_metadata) + mu, Sigma, mu_tilde, tau_tilde, Z_tilde = self._ep_approximation = self.expectation_propagation(Kmm, Kmn, Y, likelihood, Y_metadata) else: - mu, Sigma, mu_tilde, tau_tilde, Z_hat = self._ep_approximation + mu, Sigma, mu_tilde, tau_tilde, Z_tilde = self._ep_approximation return super(EPDTC, self).inference(kern, X, Z, likelihood, mu_tilde, mean_function=mean_function, Y_metadata=Y_metadata, precision=tau_tilde, Lm=Lm, dL_dKmm=dL_dKmm, - psi0=psi0, psi1=psi1, psi2=psi2) + psi0=psi0, psi1=psi1, psi2=psi2, Z_tilde=np.log(Z_tilde).sum()) def expectation_propagation(self, Kmm, Kmn, Y, likelihood, Y_metadata): num_data, output_dim = Y.shape @@ -167,6 +194,9 @@ class EPDTC(EPBase, VarDTC): mu_hat = np.zeros(num_data,dtype=np.float64) sigma2_hat = np.zeros(num_data,dtype=np.float64) + tau_cav = np.empty(num_data,dtype=np.float64) + v_cav = np.empty(num_data,dtype=np.float64) + #initial values - Gaussian factors if self.old_mutilde is None: tau_tilde, mu_tilde, v_tilde = np.zeros((3, num_data)) @@ -186,10 +216,10 @@ class EPDTC(EPBase, VarDTC): while (tau_diff > self.epsilon) or (v_diff > self.epsilon): for i in update_order: #Cavity distribution parameters - tau_cav = 1./Sigma_diag[i] - self.eta*tau_tilde[i] - v_cav = mu[i]/Sigma_diag[i] - self.eta*v_tilde[i] + tau_cav[i] = 1./Sigma_diag[i] - self.eta*tau_tilde[i] + v_cav[i] = mu[i]/Sigma_diag[i] - self.eta*v_tilde[i] #Marginal moments - Z_hat[i], mu_hat[i], sigma2_hat[i] = likelihood.moments_match_ep(Y[i], tau_cav, v_cav)#, Y_metadata=None)#=(None if Y_metadata is None else Y_metadata[i])) + Z_hat[i], mu_hat[i], sigma2_hat[i] = likelihood.moments_match_ep(Y[i], tau_cav[i], v_cav[i])#, Y_metadata=None)#=(None if Y_metadata is None else Y_metadata[i])) #Site parameters update delta_tau = self.delta/self.eta*(1./sigma2_hat[i] - 1./Sigma_diag[i]) delta_v = self.delta/self.eta*(mu_hat[i]/sigma2_hat[i] - mu[i]/Sigma_diag[i]) @@ -233,5 +263,8 @@ class EPDTC(EPBase, VarDTC): iterations += 1 mu_tilde = v_tilde/tau_tilde - return mu, Sigma, ObsAr(mu_tilde[:,None]), tau_tilde, Z_hat - + mu_cav = v_cav/tau_cav + sigma2_sigma2tilde = 1./tau_cav + 1./tau_tilde + Z_tilde = np.exp(np.log(Z_hat) + 0.5*np.log(2*np.pi) + 0.5*np.log(sigma2_sigma2tilde) + + 0.5*((mu_cav - mu_tilde)**2) / (sigma2_sigma2tilde)) + return mu, Sigma, ObsAr(mu_tilde[:,None]), tau_tilde, Z_tilde diff --git a/GPy/inference/latent_function_inference/inferenceX.py b/GPy/inference/latent_function_inference/inferenceX.py index f253a31e..60a29952 100644 --- a/GPy/inference/latent_function_inference/inferenceX.py +++ b/GPy/inference/latent_function_inference/inferenceX.py @@ -3,9 +3,8 @@ import numpy as np from ...core import Model -from ...core.parameterization import variational +from GPy.core.parameterization import variational from ...util.linalg import tdot -from GPy.core.parameterization.variational import VariationalPosterior def infer_newX(model, Y_new, optimize=True, init='L2'): """ @@ -62,14 +61,12 @@ class InferenceX(Model): # self.kern.GPU(True) from copy import deepcopy self.posterior = deepcopy(model.posterior) - from ...core.parameterization.variational import VariationalPosterior - if isinstance(model.X, VariationalPosterior): + if isinstance(model.X, variational.VariationalPosterior): self.uncertain_input = True from ...models.ss_gplvm import IBPPrior from ...models.ss_mrd import IBPPrior_SSMRD if isinstance(model.variational_prior, IBPPrior) or isinstance(model.variational_prior, IBPPrior_SSMRD): - from ...core.parameterization.variational import SpikeAndSlabPrior - self.variational_prior = SpikeAndSlabPrior(pi=0.5, learnPi=False, group_spike=False) + self.variational_prior = variational.SpikeAndSlabPrior(pi=0.5, learnPi=False, group_spike=False) else: self.variational_prior = model.variational_prior.copy() else: @@ -105,17 +102,16 @@ class InferenceX(Model): idx = dist.argmin(axis=1) from ...models import SSGPLVM - from ...util.misc import param_to_array if isinstance(model, SSGPLVM): - X = variational.SpikeAndSlabPosterior(param_to_array(model.X.mean[idx]), param_to_array(model.X.variance[idx]), param_to_array(model.X.gamma[idx])) + X = variational.SpikeAndSlabPosterior((model.X.mean[idx].values), (model.X.variance[idx].values), (model.X.gamma[idx].values)) if model.group_spike: X.gamma.fix() else: if self.uncertain_input and self.sparse_gp: - X = variational.NormalPosterior(param_to_array(model.X.mean[idx]), param_to_array(model.X.variance[idx])) + X = variational.NormalPosterior((model.X.mean[idx].values), (model.X.variance[idx].values)) else: from ...core import Param - X = Param('latent mean',param_to_array(model.X[idx]).copy()) + X = Param('latent mean',(model.X[idx].values).copy()) return X @@ -160,8 +156,7 @@ class InferenceX(Model): self.X.gradient = X_grad if self.uncertain_input: - from ...core.parameterization.variational import SpikeAndSlabPrior - if isinstance(self.variational_prior, SpikeAndSlabPrior): + if isinstance(self.variational_prior, variational.SpikeAndSlabPrior): # Update Log-likelihood KL_div = self.variational_prior.KL_divergence(self.X) # update for the KL divergence diff --git a/GPy/inference/latent_function_inference/var_dtc.py b/GPy/inference/latent_function_inference/var_dtc.py index bb114050..e05dbaf9 100644 --- a/GPy/inference/latent_function_inference/var_dtc.py +++ b/GPy/inference/latent_function_inference/var_dtc.py @@ -4,7 +4,7 @@ from .posterior import Posterior from ...util.linalg import mdot, jitchol, backsub_both_sides, tdot, dtrtrs, dtrtri, dpotri, dpotrs, symmetrify from ...util import diag -from ...core.parameterization.variational import VariationalPosterior +from GPy.core.parameterization.variational import VariationalPosterior import numpy as np from . import LatentFunctionInference log_2_pi = np.log(2*np.pi) @@ -23,8 +23,7 @@ class VarDTC(LatentFunctionInference): """ const_jitter = 1e-8 def __init__(self, limit=1): - #self._YYTfactor_cache = caching.cache() - from ...util.caching import Cacher + from paramz.caching import Cacher self.limit = limit self.get_trYYT = Cacher(self._get_trYYT, limit) self.get_YYTfactor = Cacher(self._get_YYTfactor, limit) @@ -45,7 +44,7 @@ class VarDTC(LatentFunctionInference): def __setstate__(self, state): # has to be overridden, as Cacher objects cannot be pickled. self.limit = state - from ...util.caching import Cacher + from paramz.caching import Cacher self.get_trYYT = Cacher(self._get_trYYT, self.limit) self.get_YYTfactor = Cacher(self._get_YYTfactor, self.limit) @@ -64,7 +63,7 @@ class VarDTC(LatentFunctionInference): def get_VVTfactor(self, Y, prec): return Y * prec # TODO chache this, and make it effective - def inference(self, kern, X, Z, likelihood, Y, Y_metadata=None, mean_function=None, precision=None, Lm=None, dL_dKmm=None, psi0=None, psi1=None, psi2=None): + def inference(self, kern, X, Z, likelihood, Y, Y_metadata=None, mean_function=None, precision=None, Lm=None, dL_dKmm=None, psi0=None, psi1=None, psi2=None, Z_tilde=None): assert mean_function is None, "inference with a mean function not implemented" num_data, output_dim = Y.shape @@ -152,6 +151,12 @@ class VarDTC(LatentFunctionInference): log_marginal = _compute_log_marginal_likelihood(likelihood, num_data, output_dim, precision, het_noise, psi0, A, LB, trYYT, data_fit, Y) + if Z_tilde is not None: + # This is a correction term for the log marginal likelihood + # In EP this is log Z_tilde, which is the difference between the + # Gaussian marginal and Z_EP + log_marginal += Z_tilde + #noise derivatives dL_dR = _compute_dL_dR(likelihood, het_noise, uncertain_inputs, LB, diff --git a/GPy/inference/latent_function_inference/var_dtc_parallel.py b/GPy/inference/latent_function_inference/var_dtc_parallel.py index 457ede66..b72e4fd2 100644 --- a/GPy/inference/latent_function_inference/var_dtc_parallel.py +++ b/GPy/inference/latent_function_inference/var_dtc_parallel.py @@ -4,7 +4,7 @@ from .posterior import Posterior from ...util.linalg import jitchol, backsub_both_sides, tdot, dtrtrs, dtrtri,pdinv from ...util import diag -from ...core.parameterization.variational import VariationalPosterior +from GPy.core.parameterization.variational import VariationalPosterior import numpy as np from . import LatentFunctionInference log_2_pi = np.log(2*np.pi) @@ -28,7 +28,7 @@ class VarDTC_minibatch(LatentFunctionInference): self.limit = limit # Cache functions - from ...util.caching import Cacher + from paramz.caching import Cacher self.get_trYYT = Cacher(self._get_trYYT, limit) self.get_YYTfactor = Cacher(self._get_YYTfactor, limit) @@ -46,7 +46,7 @@ class VarDTC_minibatch(LatentFunctionInference): self.mpi_comm = None self.midRes = {} self.batch_pos = 0 - from ...util.caching import Cacher + from paramz.caching import Cacher self.get_trYYT = Cacher(self._get_trYYT, self.limit) self.get_YYTfactor = Cacher(self._get_YYTfactor, self.limit) diff --git a/GPy/inference/optimization/__init__.py b/GPy/inference/optimization/__init__.py index 909f897b..24ca752a 100644 --- a/GPy/inference/optimization/__init__.py +++ b/GPy/inference/optimization/__init__.py @@ -1,2 +1,5 @@ -from .scg import SCG -from .optimization import * +from paramz.optimization import stochastics, Optimizer +from paramz.optimization import * +import sys +sys.modules['GPy.inference.optimization.stochastics'] = stochastics +sys.modules['GPy.inference.optimization.Optimizer'] = Optimizer \ No newline at end of file diff --git a/GPy/inference/optimization/conjugate_gradient_descent.py b/GPy/inference/optimization/conjugate_gradient_descent.py deleted file mode 100644 index fc2d8b61..00000000 --- a/GPy/inference/optimization/conjugate_gradient_descent.py +++ /dev/null @@ -1,285 +0,0 @@ -# Copyright (c) 2012-2014, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) - -from .gradient_descent_update_rules import FletcherReeves, \ - PolakRibiere -from Queue import Empty -from multiprocessing import Value -from multiprocessing.queues import Queue -from multiprocessing.synchronize import Event -from scipy.optimize.linesearch import line_search_wolfe1, line_search_wolfe2 -from threading import Thread -import numpy -import sys -import time - -RUNNING = "running" -CONVERGED = "converged" -MAXITER = "maximum number of iterations reached" -MAX_F_EVAL = "maximum number of function calls reached" -LINE_SEARCH = "line search failed" -KBINTERRUPT = "interrupted" - -class _Async_Optimization(Thread): - - def __init__(self, f, df, x0, update_rule, runsignal, SENTINEL, - report_every=10, messages=0, maxiter=5e3, max_f_eval=15e3, - gtol=1e-6, outqueue=None, *args, **kw): - """ - Helper Process class for async optimization - - f_call and df_call are Multiprocessing Values, for synchronized assignment - """ - self.f_call = Value('i', 0) - self.df_call = Value('i', 0) - self.f = self.f_wrapper(f, self.f_call) - self.df = self.f_wrapper(df, self.df_call) - self.x0 = x0 - self.update_rule = update_rule - self.report_every = report_every - self.messages = messages - self.maxiter = maxiter - self.max_f_eval = max_f_eval - self.gtol = gtol - self.SENTINEL = SENTINEL - self.runsignal = runsignal -# self.parent = parent -# self.result = None - self.outq = outqueue - super(_Async_Optimization, self).__init__(target=self.run, - name="CG Optimization", - *args, **kw) - -# def __enter__(self): -# return self -# -# def __exit__(self, type, value, traceback): -# return isinstance(value, TypeError) - - def f_wrapper(self, f, counter): - def f_w(*a, **kw): - counter.value += 1 - return f(*a, **kw) - return f_w - - def callback(self, *a): - if self.outq is not None: - self.outq.put(a) -# self.parent and self.parent.callback(*a, **kw) - pass - # print "callback done" - - def callback_return(self, *a): - self.callback(*a) - if self.outq is not None: - self.outq.put(self.SENTINEL) - if self.messages: - print("") - self.runsignal.clear() - - def run(self, *args, **kwargs): - raise NotImplementedError("Overwrite this with optimization (for async use)") - pass - -class _CGDAsync(_Async_Optimization): - - def reset(self, xi, *a, **kw): - gi = -self.df(xi, *a, **kw) - si = gi - ur = self.update_rule(gi) - return gi, ur, si - - def run(self, *a, **kw): - status = RUNNING - - fi = self.f(self.x0) - fi_old = fi + 5000 - - gi, ur, si = self.reset(self.x0, *a, **kw) - xi = self.x0 - xi_old = numpy.nan - it = 0 - - while it < self.maxiter: - if not self.runsignal.is_set(): - break - - if self.f_call.value > self.max_f_eval: - status = MAX_F_EVAL - - gi = -self.df(xi, *a, **kw) - if numpy.dot(gi.T, gi) <= self.gtol: - status = CONVERGED - break - if numpy.isnan(numpy.dot(gi.T, gi)): - if numpy.any(numpy.isnan(xi_old)): - status = CONVERGED - break - self.reset(xi_old) - - gammai = ur(gi) - if gammai < 1e-6 or it % xi.shape[0] == 0: - gi, ur, si = self.reset(xi, *a, **kw) - si = gi + gammai * si - alphai, _, _, fi2, fi_old2, gfi = line_search_wolfe1(self.f, - self.df, - xi, - si, gi, - fi, fi_old) - if alphai is None: - alphai, _, _, fi2, fi_old2, gfi = \ - line_search_wolfe2(self.f, self.df, - xi, si, gi, - fi, fi_old) - if alphai is None: - # This line search also failed to find a better solution. - status = LINE_SEARCH - break - if fi2 < fi: - fi, fi_old = fi2, fi_old2 - if gfi is not None: - gi = gfi - - if numpy.isnan(fi) or fi_old < fi: - gi, ur, si = self.reset(xi, *a, **kw) - - else: - xi += numpy.dot(alphai, si) - if self.messages: - sys.stdout.write("\r") - sys.stdout.flush() - sys.stdout.write("iteration: {0:> 6g} f:{1:> 12e} |g|:{2:> 12e}".format(it, fi, numpy.dot(gi.T, gi))) - - if it % self.report_every == 0: - self.callback(xi, fi, gi, it, self.f_call.value, self.df_call.value, status) - it += 1 - else: - status = MAXITER - self.callback_return(xi, fi, gi, it, self.f_call.value, self.df_call.value, status) - self.result = [xi, fi, gi, it, self.f_call.value, self.df_call.value, status] - -class Async_Optimize(object): - callback = lambda *x: None - runsignal = Event() - SENTINEL = "SENTINEL" - - def async_callback_collect(self, q): - while self.runsignal.is_set(): - try: - for ret in iter(lambda: q.get(timeout=1), self.SENTINEL): - self.callback(*ret) - self.runsignal.clear() - except Empty: - pass - - def opt_async(self, f, df, x0, callback, update_rule=PolakRibiere, - messages=0, maxiter=5e3, max_f_eval=15e3, gtol=1e-6, - report_every=10, *args, **kwargs): - self.runsignal.set() - c = None - outqueue = None - if callback: - outqueue = Queue() - self.callback = callback - c = Thread(target=self.async_callback_collect, args=(outqueue,)) - c.start() - p = _CGDAsync(f, df, x0, update_rule, self.runsignal, self.SENTINEL, - report_every=report_every, messages=messages, maxiter=maxiter, - max_f_eval=max_f_eval, gtol=gtol, outqueue=outqueue, *args, **kwargs) - p.start() - return p, c - - def opt(self, f, df, x0, callback=None, update_rule=FletcherReeves, - messages=0, maxiter=5e3, max_f_eval=15e3, gtol=1e-6, - report_every=10, *args, **kwargs): - p, c = self.opt_async(f, df, x0, callback, update_rule, messages, - maxiter, max_f_eval, gtol, - report_every, *args, **kwargs) - while self.runsignal.is_set(): - try: - p.join(1) - if c: c.join(1) - except KeyboardInterrupt: - # print "^C" - self.runsignal.clear() - p.join() - if c: c.join() - if c and c.is_alive(): -# self.runsignal.set() -# while self.runsignal.is_set(): -# try: -# c.join(.1) -# except KeyboardInterrupt: -# # print "^C" -# self.runsignal.clear() -# c.join() - print("WARNING: callback still running, optimisation done!") - return p.result - -class CGD(Async_Optimize): - ''' - Conjugate gradient descent algorithm to minimize - function f with gradients df, starting at x0 - with update rule update_rule - - if df returns tuple (grad, natgrad) it will optimize according - to natural gradient rules - ''' - opt_name = "Conjugate Gradient Descent" - - def opt_async(self, *a, **kw): - """ - opt_async(self, f, df, x0, callback, update_rule=FletcherReeves, - messages=0, maxiter=5e3, max_f_eval=15e3, gtol=1e-6, - report_every=10, \*args, \*\*kwargs) - - callback gets called every `report_every` iterations - - callback(xi, fi, gi, iteration, function_calls, gradient_calls, status_message) - - if df returns tuple (grad, natgrad) it will optimize according - to natural gradient rules - - f, and df will be called with - - f(xi, \*args, \*\*kwargs) - df(xi, \*args, \*\*kwargs) - - **Returns:** - - Started `Process` object, optimizing asynchronously - - **Calls:** - - callback(x_opt, f_opt, g_opt, iteration, function_calls, gradient_calls, status_message) - - at end of optimization! - """ - return super(CGD, self).opt_async(*a, **kw) - - def opt(self, *a, **kw): - """ - opt(self, f, df, x0, callback=None, update_rule=FletcherReeves, - messages=0, maxiter=5e3, max_f_eval=15e3, gtol=1e-6, - report_every=10, \*args, \*\*kwargs) - - Minimize f, calling callback every `report_every` iterations with following syntax: - - callback(xi, fi, gi, iteration, function_calls, gradient_calls, status_message) - - if df returns tuple (grad, natgrad) it will optimize according - to natural gradient rules - - f, and df will be called with - - f(xi, \*args, \*\*kwargs) - df(xi, \*args, \*\*kwargs) - - **returns** - - x_opt, f_opt, g_opt, iteration, function_calls, gradient_calls, status_message - - at end of optimization - """ - return super(CGD, self).opt(*a, **kw) - diff --git a/GPy/inference/optimization/gradient_descent_update_rules.py b/GPy/inference/optimization/gradient_descent_update_rules.py deleted file mode 100644 index 9536549c..00000000 --- a/GPy/inference/optimization/gradient_descent_update_rules.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2012-2014, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) - -import numpy - -class GDUpdateRule(): - _gradnat = None - _gradnatold = None - def __init__(self, initgrad, initgradnat=None): - self.grad = initgrad - if initgradnat: - self.gradnat = initgradnat - else: - self.gradnat = initgrad - # self.grad, self.gradnat - def _gamma(self): - raise NotImplemented("""Implement gamma update rule here, - you can use self.grad and self.gradold for parameters, as well as - self.gradnat and self.gradnatold for natural gradients.""") - def __call__(self, grad, gradnat=None, si=None, *args, **kw): - """ - Return gamma for given gradients and optional natural gradients - """ - if not gradnat: - gradnat = grad - self.gradold = self.grad - self.gradnatold = self.gradnat - self.grad = grad - self.gradnat = gradnat - self.si = si - return self._gamma(*args, **kw) - -class FletcherReeves(GDUpdateRule): - ''' - Fletcher Reeves update rule for gamma - ''' - def _gamma(self, *a, **kw): - tmp = numpy.dot(self.grad.T, self.gradnat) - if tmp: - return tmp / numpy.dot(self.gradold.T, self.gradnatold) - return tmp - -class PolakRibiere(GDUpdateRule): - ''' - Fletcher Reeves update rule for gamma - ''' - def _gamma(self, *a, **kw): - tmp = numpy.dot((self.grad - self.gradold).T, self.gradnat) - if tmp: - return tmp / numpy.dot(self.gradold.T, self.gradnatold) - return tmp diff --git a/GPy/inference/optimization/scg.py b/GPy/inference/optimization/scg.py deleted file mode 100644 index 8960de1d..00000000 --- a/GPy/inference/optimization/scg.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright I. Nabney, N.Lawrence and James Hensman (1996 - 2014) - -# Scaled Conjuagte Gradients, originally in Matlab as part of the Netlab toolbox by I. Nabney, converted to python N. Lawrence and given a pythonic interface by James Hensman - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT -# HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT -# NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -from __future__ import print_function -import numpy as np -import sys - -def print_out(len_maxiters, fnow, current_grad, beta, iteration): - print('\r', end=' ') - print('{0:>0{mi}g} {1:> 12e} {2:< 12.6e} {3:> 12e}'.format(iteration, float(fnow), float(beta), float(current_grad), mi=len_maxiters), end=' ') # print 'Iteration:', iteration, ' Objective:', fnow, ' Scale:', beta, '\r', - sys.stdout.flush() - -def exponents(fnow, current_grad): - exps = [np.abs(np.float(fnow)), current_grad] - return np.sign(exps) * np.log10(exps).astype(int) - -def SCG(f, gradf, x, optargs=(), maxiters=500, max_f_eval=np.inf, display=True, xtol=None, ftol=None, gtol=None): - """ - Optimisation through Scaled Conjugate Gradients (SCG) - - f: the objective function - gradf : the gradient function (should return a 1D np.ndarray) - x : the initial condition - - Returns - x the optimal value for x - flog : a list of all the objective values - function_eval number of fn evaluations - status: string describing convergence status - """ - if xtol is None: - xtol = 1e-6 - if ftol is None: - ftol = 1e-6 - if gtol is None: - gtol = 1e-5 - - sigma0 = 1.0e-7 - fold = f(x, *optargs) # Initial function value. - function_eval = 1 - fnow = fold - gradnew = gradf(x, *optargs) # Initial gradient. - function_eval += 1 - #if any(np.isnan(gradnew)): - # raise UnexpectedInfOrNan, "Gradient contribution resulted in a NaN value" - current_grad = np.dot(gradnew, gradnew) - gradold = gradnew.copy() - d = -gradnew # Initial search direction. - success = True # Force calculation of directional derivs. - nsuccess = 0 # nsuccess counts number of successes. - beta = 1.0 # Initial scale parameter. - betamin = 1.0e-15 # Lower bound on scale. - betamax = 1.0e15 # Upper bound on scale. - status = "Not converged" - - flog = [fold] - - iteration = 0 - - len_maxiters = len(str(maxiters)) - if display: - print(' {0:{mi}s} {1:11s} {2:11s} {3:11s}'.format("I", "F", "Scale", "|g|", mi=len_maxiters)) - exps = exponents(fnow, current_grad) - p_iter = iteration - - # Main optimization loop. - while iteration < maxiters: - - # Calculate first and second directional derivatives. - if success: - mu = np.dot(d, gradnew) - if mu >= 0: - d = -gradnew - mu = np.dot(d, gradnew) - kappa = np.dot(d, d) - sigma = sigma0 / np.sqrt(kappa) - xplus = x + sigma * d - gplus = gradf(xplus, *optargs) - function_eval += 1 - theta = np.dot(d, (gplus - gradnew)) / sigma - - # Increase effective curvature and evaluate step size alpha. - delta = theta + beta * kappa - if delta <= 0: - delta = beta * kappa - beta = beta - theta / kappa - - alpha = -mu / delta - - # Calculate the comparison ratio. - xnew = x + alpha * d - fnew = f(xnew, *optargs) - function_eval += 1 - - if function_eval >= max_f_eval: - status = "maximum number of function evaluations exceeded" - break - return x, flog, function_eval, status - - Delta = 2.*(fnew - fold) / (alpha * mu) - if Delta >= 0.: - success = True - nsuccess += 1 - x = xnew - fnow = fnew - else: - success = False - fnow = fold - - # Store relevant variables - flog.append(fnow) # Current function value - - iteration += 1 - if display: - print_out(len_maxiters, fnow, current_grad, beta, iteration) - n_exps = exponents(fnow, current_grad) - if iteration - p_iter >= 20 * np.random.rand(): - a = iteration >= p_iter * 2.78 - b = np.any(n_exps < exps) - if a or b: - p_iter = iteration - print('') - if b: - exps = n_exps - - if success: - # Test for termination - - if (np.abs(fnew - fold) < ftol): - status = 'converged - relative reduction in objective' - break -# return x, flog, function_eval, status - elif (np.max(np.abs(alpha * d)) < xtol): - status = 'converged - relative stepsize' - break - else: - # Update variables for new position - gradold = gradnew - gradnew = gradf(x, *optargs) - function_eval += 1 - current_grad = np.dot(gradnew, gradnew) - fold = fnew - # If the gradient is zero then we are done. - if current_grad <= gtol: - status = 'converged - relative reduction in gradient' - break - # return x, flog, function_eval, status - - # Adjust beta according to comparison ratio. - if Delta < 0.25: - beta = min(4.0 * beta, betamax) - if Delta > 0.75: - beta = max(0.25 * beta, betamin) - - # Update search direction using Polak-Ribiere formula, or re-start - # in direction of negative gradient after nparams steps. - if nsuccess == x.size: - d = -gradnew - beta = 1. # This is not in the original paper - nsuccess = 0 - elif success: - Gamma = np.dot(gradold - gradnew, gradnew) / (mu) - d = Gamma * d - gradnew - else: - # If we get here, then we haven't terminated in the given number of - # iterations. - status = "maxiter exceeded" - - if display: - print_out(len_maxiters, fnow, current_grad, beta, iteration) - print("") - print(status) - return x, flog, function_eval, status diff --git a/GPy/inference/optimization/stochastics.py b/GPy/inference/optimization/stochastics.py deleted file mode 100644 index 902c4290..00000000 --- a/GPy/inference/optimization/stochastics.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2012-2014, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) - -class StochasticStorage(object): - ''' - This is a container for holding the stochastic parameters, - such as subset indices or step length and so on. - - self.d has to be a list of lists: - [dimension indices, nan indices for those dimensions] - so that the minibatches can be used as efficiently as possible.10 - ''' - def __init__(self, model): - """ - Initialize this stochastic container using the given model - """ - - def do_stochastics(self): - """ - Update the internal state to the next batch of the stochastic - descent algorithm. - """ - pass - - def reset(self): - """ - Reset the state of this stochastics generator. - """ - -class SparseGPMissing(StochasticStorage): - def __init__(self, model, batchsize=1): - """ - Here we want to loop over all dimensions everytime. - Thus, we can just make sure the loop goes over self.d every - time. We will try to get batches which look the same together - which speeds up calculations significantly. - """ - import numpy as np - self.Y = model.Y_normalized - bdict = {} - #For N > 1000 array2string default crops - opt = np.get_printoptions() - np.set_printoptions(threshold=np.inf) - for d in range(self.Y.shape[1]): - inan = np.isnan(self.Y)[:, d] - arr_str = np.array2string(inan, np.inf, 0, True, '', formatter={'bool':lambda x: '1' if x else '0'}) - try: - bdict[arr_str][0].append(d) - except: - bdict[arr_str] = [[d], ~inan] - np.set_printoptions(**opt) - self.d = bdict.values() - -class SparseGPStochastics(StochasticStorage): - """ - For the sparse gp we need to store the dimension we are in, - and the indices corresponding to those - """ - def __init__(self, model, batchsize=1, missing_data=True): - self.batchsize = batchsize - self.output_dim = model.Y.shape[1] - self.Y = model.Y_normalized - self.missing_data = missing_data - self.reset() - self.do_stochastics() - - def do_stochastics(self): - import numpy as np - if self.batchsize == 1: - self.current_dim = (self.current_dim+1)%self.output_dim - self.d = [[[self.current_dim], np.isnan(self.Y[:, self.current_dim]) if self.missing_data else None]] - else: - self.d = np.random.choice(self.output_dim, size=self.batchsize, replace=False) - bdict = {} - if self.missing_data: - opt = np.get_printoptions() - np.set_printoptions(threshold=np.inf) - for d in self.d: - inan = np.isnan(self.Y[:, d]) - arr_str = np.array2string(inan,np.inf, 0,True, '',formatter={'bool':lambda x: '1' if x else '0'}) - try: - bdict[arr_str][0].append(d) - except: - bdict[arr_str] = [[d], ~inan] - np.set_printoptions(**opt) - self.d = bdict.values() - else: - self.d = [[self.d, None]] - - def reset(self): - self.current_dim = -1 - self.d = None diff --git a/GPy/kern/src/ODE_UY.py b/GPy/kern/src/ODE_UY.py index ae9c4574..19fb1e94 100644 --- a/GPy/kern/src/ODE_UY.py +++ b/GPy/kern/src/ODE_UY.py @@ -4,7 +4,7 @@ from .kern import Kern from .independent_outputs import index_to_slices from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp import numpy as np class ODE_UY(Kern): diff --git a/GPy/kern/src/ODE_UYC.py b/GPy/kern/src/ODE_UYC.py index ff75a328..d02eb1d9 100644 --- a/GPy/kern/src/ODE_UYC.py +++ b/GPy/kern/src/ODE_UYC.py @@ -3,7 +3,7 @@ from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp import numpy as np from .independent_outputs import index_to_slices diff --git a/GPy/kern/src/ODE_st.py b/GPy/kern/src/ODE_st.py index afa46d09..f9d4e684 100644 --- a/GPy/kern/src/ODE_st.py +++ b/GPy/kern/src/ODE_st.py @@ -2,7 +2,7 @@ # Licensed under the BSD 3-clause license (see LICENSE.txt) from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp import numpy as np from .independent_outputs import index_to_slices diff --git a/GPy/kern/src/ODE_t.py b/GPy/kern/src/ODE_t.py index 80625f51..ffd349ec 100644 --- a/GPy/kern/src/ODE_t.py +++ b/GPy/kern/src/ODE_t.py @@ -1,6 +1,6 @@ from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp import numpy as np from .independent_outputs import index_to_slices diff --git a/GPy/kern/src/add.py b/GPy/kern/src/add.py index 06bb4870..86bceac7 100644 --- a/GPy/kern/src/add.py +++ b/GPy/kern/src/add.py @@ -3,7 +3,7 @@ import numpy as np import itertools -from ...util.caching import Cache_this +from paramz.caching import Cache_this from .kern import CombinationKernel, Kern from functools import reduce diff --git a/GPy/kern/src/basis_funcs.py b/GPy/kern/src/basis_funcs.py index 3d644af2..7a5f84dd 100644 --- a/GPy/kern/src/basis_funcs.py +++ b/GPy/kern/src/basis_funcs.py @@ -3,8 +3,8 @@ import numpy as np from .kern import Kern from ...core.parameterization.param import Param -from ...core.parameterization.transformations import Logexp -from ...util.caching import Cache_this +from paramz.transformations import Logexp +from paramz.caching import Cache_this from ...util.linalg import tdot, mdot class BasisFuncKernel(Kern): diff --git a/GPy/kern/src/brownian.py b/GPy/kern/src/brownian.py index d403fce7..68da4435 100644 --- a/GPy/kern/src/brownian.py +++ b/GPy/kern/src/brownian.py @@ -3,7 +3,7 @@ from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp import numpy as np class Brownian(Kern): diff --git a/GPy/kern/src/coregionalize.py b/GPy/kern/src/coregionalize.py index 1ce4bff6..197d7ece 100644 --- a/GPy/kern/src/coregionalize.py +++ b/GPy/kern/src/coregionalize.py @@ -4,7 +4,7 @@ from .kern import Kern import numpy as np from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp from ...util.config import config # for assesing whether to use cython try: from . import coregionalize_cython diff --git a/GPy/kern/src/eq_ode2.py b/GPy/kern/src/eq_ode2.py index 2d42a3e6..ef71ffe0 100644 --- a/GPy/kern/src/eq_ode2.py +++ b/GPy/kern/src/eq_ode2.py @@ -5,8 +5,8 @@ import numpy as np from scipy.special import wofz from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp -from ...util.caching import Cache_this +from paramz.transformations import Logexp +from paramz.caching import Cache_this class EQ_ODE2(Kern): """ diff --git a/GPy/kern/src/kern.py b/GPy/kern/src/kern.py index 4d535b60..ad41355f 100644 --- a/GPy/kern/src/kern.py +++ b/GPy/kern/src/kern.py @@ -3,8 +3,8 @@ import sys import numpy as np from ...core.parameterization.parameterized import Parameterized -from ...core.parameterization.observable_array import ObsAr -from ...util.caching import Cache_this +from paramz.core.observable_array import ObsAr +from paramz.caching import Cache_this from .kernel_slice_operations import KernCallsViaSlicerMeta from functools import reduce import six @@ -30,18 +30,16 @@ class Kern(Parameterized): tight dimensionality of inputs. You most likely want this to be the integer telling the number of input dimensions of the kernel. - If this is not an integer (!) we will work on the whole input matrix X, - and not check whether dimensions match or not (!). - _all_dims_active: + active_dims: is the active_dimensions of inputs X we will work on. All kernels will get sliced Xes as inputs, if _all_dims_active is not None - Only positive integers are allowed in _all_dims_active! - if _all_dims_active is None, slicing is switched off and all X will be passed through as given. + Only positive integers are allowed in active_dims! + if active_dims is None, slicing is switched off and all X will be passed through as given. :param int input_dim: the number of input dimensions to the function - :param array-like|None _all_dims_active: list of indices on which dimensions this kernel works on, or none if no slicing + :param array-like|None active_dims: list of indices on which dimensions this kernel works on, or none if no slicing Do not instantiate. """ @@ -60,7 +58,11 @@ class Kern(Parameterized): self.useGPU = self._support_GPU and useGPU from .psi_comp import PSICOMP_GH - self.psicomp = PSICOMP_GH() + self.psicomp = PSICOMP_GH() + + def __setstate__(self, state): + self._all_dims_active = range(0, max(state['active_dims'])+1) + super(Kern, self).__setstate__(state) @property def _effective_input_dim(self): @@ -211,15 +213,15 @@ class Kern(Parameterized): def get_most_significant_input_dimensions(self, which_indices=None): """ Determine which dimensions should be plotted - + Returns the top three most signification input dimensions - + if less then three dimensions, the non existing dimensions are labeled as None, so for a 1 dimensional input this returns (0, None, None). - - :param which_indices: force the indices to be the given indices. - :type which_indices: int or tuple(int,int) or tuple(int,int,int) + + :param which_indices: force the indices to be the given indices. + :type which_indices: int or tuple(int,int) or tuple(int,int,int) """ if which_indices is None: which_indices = np.argsort(self.input_sensitivity())[::-1][:3] @@ -235,7 +237,7 @@ class Kern(Parameterized): input_1, input_2 = which_indices, None except ValueError: # which_indices was a list or array like with only one int - input_1, input_2 = which_indices[0], None + input_1, input_2 = which_indices[0], None return input_1, input_2, input_3 diff --git a/GPy/kern/src/kernel_slice_operations.py b/GPy/kern/src/kernel_slice_operations.py index 2bd1f923..57b34de9 100644 --- a/GPy/kern/src/kernel_slice_operations.py +++ b/GPy/kern/src/kernel_slice_operations.py @@ -7,9 +7,9 @@ This module provides a meta class for the kernels. The meta class is for slicing the inputs (X, X2) for the kernels, before K (or any other method involving X) gets calls. The `_all_dims_active` of a kernel decide which dimensions the kernel works on. ''' -from ...core.parameterization.parameterized import ParametersChangedMeta import numpy as np from functools import wraps +from paramz.parameterized import ParametersChangedMeta def put_clean(dct, name, func): if name in dct: diff --git a/GPy/kern/src/linear.py b/GPy/kern/src/linear.py index 0c897c74..59595fea 100644 --- a/GPy/kern/src/linear.py +++ b/GPy/kern/src/linear.py @@ -6,8 +6,8 @@ import numpy as np from .kern import Kern from ...util.linalg import tdot from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp -from ...util.caching import Cache_this +from paramz.transformations import Logexp +from paramz.caching import Cache_this from .psi_comp import PSICOMP_Linear class Linear(Kern): diff --git a/GPy/kern/src/mlp.py b/GPy/kern/src/mlp.py index 8f9a276c..d86e5b15 100644 --- a/GPy/kern/src/mlp.py +++ b/GPy/kern/src/mlp.py @@ -3,9 +3,9 @@ from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp import numpy as np -from ...util.caching import Cache_this +from paramz.caching import Cache_this four_over_tau = 2./np.pi class MLP(Kern): diff --git a/GPy/kern/src/periodic.py b/GPy/kern/src/periodic.py index 4c4d2234..fe0c6670 100644 --- a/GPy/kern/src/periodic.py +++ b/GPy/kern/src/periodic.py @@ -7,7 +7,7 @@ from .kern import Kern from ...util.linalg import mdot from ...util.decorators import silence_errors from ...core.parameterization.param import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp class Periodic(Kern): def __init__(self, input_dim, variance, lengthscale, period, n_freq, lower, upper, active_dims, name): diff --git a/GPy/kern/src/poly.py b/GPy/kern/src/poly.py index a5306c2a..216e3a00 100644 --- a/GPy/kern/src/poly.py +++ b/GPy/kern/src/poly.py @@ -4,7 +4,8 @@ import numpy as np from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp + class Poly(Kern): """ Polynomial kernel diff --git a/GPy/kern/src/prod.py b/GPy/kern/src/prod.py index 68883af4..ae00a949 100644 --- a/GPy/kern/src/prod.py +++ b/GPy/kern/src/prod.py @@ -3,7 +3,7 @@ import numpy as np from .kern import CombinationKernel -from ...util.caching import Cache_this +from paramz.caching import Cache_this import itertools from functools import reduce diff --git a/GPy/kern/src/psi_comp/__init__.py b/GPy/kern/src/psi_comp/__init__.py index 90ceca6b..9afa8e8c 100644 --- a/GPy/kern/src/psi_comp/__init__.py +++ b/GPy/kern/src/psi_comp/__init__.py @@ -1,9 +1,9 @@ # Copyright (c) 2012, GPy authors (see AUTHORS.txt). # Licensed under the BSD 3-clause license (see LICENSE.txt) -from ....core.parameterization.parameter_core import Pickleable -from ....util.caching import Cache_this -from ....core.parameterization import variational +from paramz.core.pickleable import Pickleable +from paramz.caching import Cache_this +from GPy.core.parameterization import variational #from linear_psi_comp import LINEAr class PSICOMP(Pickleable): diff --git a/GPy/kern/src/psi_comp/gaussherm.py b/GPy/kern/src/psi_comp/gaussherm.py index c491983b..5fac6619 100644 --- a/GPy/kern/src/psi_comp/gaussherm.py +++ b/GPy/kern/src/psi_comp/gaussherm.py @@ -8,7 +8,7 @@ An approximated psi-statistics implementation based on Gauss-Hermite Quadrature import numpy as np from ....core.parameterization import Param -from ....util.caching import Cache_this +from paramz.caching import Cache_this from ....util.linalg import tdot from . import PSICOMP @@ -30,7 +30,7 @@ class PSICOMP_GH(PSICOMP): @Cache_this(limit=10, ignore_args=(0,)) def comp_K(self, Z, qX): if self.Xs is None or self.Xs.shape != qX.mean.shape: - from ....core.parameterization import ObsAr + from paramz import ObsAr self.Xs = ObsAr(np.empty((self.degree,)+qX.mean.shape)) mu, S = qX.mean.values, qX.variance.values S_sq = np.sqrt(S) diff --git a/GPy/kern/src/psi_comp/rbf_psi_comp.py b/GPy/kern/src/psi_comp/rbf_psi_comp.py index 6e6c1957..bf954717 100644 --- a/GPy/kern/src/psi_comp/rbf_psi_comp.py +++ b/GPy/kern/src/psi_comp/rbf_psi_comp.py @@ -3,7 +3,7 @@ The module for psi-statistics for RBF kernel """ import numpy as np -from GPy.util.caching import Cacher +from paramz.caching import Cacher def psicomputations(variance, lengthscale, Z, variational_posterior, return_psi2_n=False): # here are the "statistics" for psi0, psi1 and psi2 diff --git a/GPy/kern/src/psi_comp/rbf_psi_gpucomp.py b/GPy/kern/src/psi_comp/rbf_psi_gpucomp.py index ea0b1673..baab83ec 100644 --- a/GPy/kern/src/psi_comp/rbf_psi_gpucomp.py +++ b/GPy/kern/src/psi_comp/rbf_psi_gpucomp.py @@ -3,7 +3,7 @@ The module for psi-statistics for RBF kernel """ import numpy as np -from ....util.caching import Cache_this +from paramz.caching import Cache_this from . import PSICOMP_RBF from ....util import gpu_init diff --git a/GPy/kern/src/psi_comp/ssrbf_psi_gpucomp.py b/GPy/kern/src/psi_comp/ssrbf_psi_gpucomp.py index 46f4a06e..844f944e 100644 --- a/GPy/kern/src/psi_comp/ssrbf_psi_gpucomp.py +++ b/GPy/kern/src/psi_comp/ssrbf_psi_gpucomp.py @@ -4,7 +4,7 @@ The module for psi-statistics for RBF kernel for Spike-and-Slab GPLVM """ import numpy as np -from ....util.caching import Cache_this +from paramz.caching import Cache_this from . import PSICOMP_RBF diff --git a/GPy/kern/src/rbf.py b/GPy/kern/src/rbf.py index 3607bea9..ff86561d 100644 --- a/GPy/kern/src/rbf.py +++ b/GPy/kern/src/rbf.py @@ -6,7 +6,7 @@ import numpy as np from .stationary import Stationary from .psi_comp import PSICOMP_RBF, PSICOMP_RBF_GPU from ...core import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp class RBF(Stationary): """ @@ -47,12 +47,13 @@ class RBF(Stationary): return dc def __setstate__(self, state): + self.use_invLengthscale = False return super(RBF, self).__setstate__(state) def spectrum(self, omega): assert self.input_dim == 1 #TODO: higher dim spectra? return self.variance*np.sqrt(2*np.pi)*self.lengthscale*np.exp(-self.lengthscale*2*omega**2/2) - + def parameters_changed(self): if self.use_invLengthscale: self.lengthscale[:] = 1./np.sqrt(self.inv_l+1e-200) super(RBF,self).parameters_changed() @@ -85,7 +86,7 @@ class RBF(Stationary): def gradients_qX_expectations(self, dL_dpsi0, dL_dpsi1, dL_dpsi2, Z, variational_posterior): return self.psicomp.psiDerivativecomputations(self, dL_dpsi0, dL_dpsi1, dL_dpsi2, Z, variational_posterior)[3:] - + def update_gradients_diag(self, dL_dKdiag, X): super(RBF,self).update_gradients_diag(dL_dKdiag, X) if self.use_invLengthscale: self.inv_l.gradient =self.lengthscale.gradient*(self.lengthscale**3/-2.) diff --git a/GPy/kern/src/spline.py b/GPy/kern/src/spline.py index c1b28764..2d822399 100644 --- a/GPy/kern/src/spline.py +++ b/GPy/kern/src/spline.py @@ -4,7 +4,7 @@ import numpy as np from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp class Spline(Kern): """ diff --git a/GPy/kern/src/standard_periodic.py b/GPy/kern/src/standard_periodic.py index 3da7a124..bc27107e 100644 --- a/GPy/kern/src/standard_periodic.py +++ b/GPy/kern/src/standard_periodic.py @@ -15,7 +15,7 @@ Neural Networks and Machine Learning, pages 133-165. Springer, 1998. from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp import numpy as np diff --git a/GPy/kern/src/static.py b/GPy/kern/src/static.py index bcfec4a7..dc6fe7a0 100644 --- a/GPy/kern/src/static.py +++ b/GPy/kern/src/static.py @@ -5,7 +5,7 @@ from .kern import Kern import numpy as np from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp +from paramz.transformations import Logexp class Static(Kern): def __init__(self, input_dim, variance, active_dims, name): diff --git a/GPy/kern/src/stationary.py b/GPy/kern/src/stationary.py index d5f26798..106e0098 100644 --- a/GPy/kern/src/stationary.py +++ b/GPy/kern/src/stationary.py @@ -2,15 +2,15 @@ # Licensed under the BSD 3-clause license (see LICENSE.txt) -from .kern import Kern -from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp -from ...util.linalg import tdot -from ... import util import numpy as np from scipy import integrate +from .kern import Kern +from ...core.parameterization import Param +from ...util.linalg import tdot +from ... import util from ...util.config import config # for assesing whether to use cython -from ...util.caching import Cache_this +from paramz.caching import Cache_this +from paramz.transformations import Logexp try: from . import stationary_cython diff --git a/GPy/kern/src/trunclinear.py b/GPy/kern/src/trunclinear.py index 81d7376f..3a35744f 100644 --- a/GPy/kern/src/trunclinear.py +++ b/GPy/kern/src/trunclinear.py @@ -5,8 +5,8 @@ import numpy as np from .kern import Kern from ...core.parameterization import Param -from ...core.parameterization.transformations import Logexp -from ...util.caching import Cache_this +from paramz.transformations import Logexp +from paramz.caching import Cache_this class TruncLinear(Kern): """ diff --git a/GPy/likelihoods/bernoulli.py b/GPy/likelihoods/bernoulli.py index 856de40f..1997db06 100644 --- a/GPy/likelihoods/bernoulli.py +++ b/GPy/likelihoods/bernoulli.py @@ -43,7 +43,7 @@ class Bernoulli(Likelihood): Y_prep[Y.flatten() == 0] = -1 return Y_prep - def moments_match_ep(self, Y_i, tau_i, v_i): + def moments_match_ep(self, Y_i, tau_i, v_i, Y_metadata_i=None): """ Moments match of the marginal approximation in EP algorithm @@ -62,6 +62,7 @@ class Bernoulli(Likelihood): Z_hat = std_norm_cdf(z) Z_hat = np.where(Z_hat==0, 1e-15, Z_hat) phi = std_norm_pdf(z) + mu_hat = v_i/tau_i + sign*phi/(Z_hat*np.sqrt(tau_i**2 + tau_i)) sigma2_hat = 1./tau_i - (phi/((tau_i**2+tau_i)*Z_hat))*(z+phi/Z_hat) @@ -140,7 +141,7 @@ class Bernoulli(Likelihood): Each y_i must be in {0, 1} """ #objective = (inv_link_f**y) * ((1.-inv_link_f)**(1.-y)) - return np.where(y, inv_link_f, 1.-inv_link_f) + return np.where(y==1, inv_link_f, 1.-inv_link_f) def logpdf_link(self, inv_link_f, y, Y_metadata=None): """ @@ -179,7 +180,7 @@ class Bernoulli(Likelihood): #grad = (y/inv_link_f) - (1.-y)/(1-inv_link_f) #grad = np.where(y, 1./inv_link_f, -1./(1-inv_link_f)) ff = np.clip(inv_link_f, 1e-9, 1-1e-9) - denom = np.where(y, ff, -(1-ff)) + denom = np.where(y==1, ff, -(1-ff)) return 1./denom def d2logpdf_dlink2(self, inv_link_f, y, Y_metadata=None): @@ -205,7 +206,7 @@ class Bernoulli(Likelihood): """ #d2logpdf_dlink2 = -y/(inv_link_f**2) - (1-y)/((1-inv_link_f)**2) #d2logpdf_dlink2 = np.where(y, -1./np.square(inv_link_f), -1./np.square(1.-inv_link_f)) - arg = np.where(y, inv_link_f, 1.-inv_link_f) + arg = np.where(y==1, inv_link_f, 1.-inv_link_f) ret = -1./np.square(np.clip(arg, 1e-9, 1e9)) if np.any(np.isinf(ret)): stop @@ -230,7 +231,7 @@ class Bernoulli(Likelihood): #d3logpdf_dlink3 = 2*(y/(inv_link_f**3) - (1-y)/((1-inv_link_f)**3)) state = np.seterr(divide='ignore') # TODO check y \in {0, 1} or {-1, 1} - d3logpdf_dlink3 = np.where(y, 2./(inv_link_f**3), -2./((1.-inv_link_f)**3)) + d3logpdf_dlink3 = np.where(y==1, 2./(inv_link_f**3), -2./((1.-inv_link_f)**3)) np.seterr(**state) return d3logpdf_dlink3 @@ -243,8 +244,6 @@ class Bernoulli(Likelihood): p = self.predictive_mean(mu, var) return [np.asarray(p>(q/100.), dtype=np.int32) for q in quantiles] - - def samples(self, gp, Y_metadata=None): """ Returns a set of samples of observations based on a given value of the latent variable. diff --git a/GPy/likelihoods/gaussian.py b/GPy/likelihoods/gaussian.py index e1299f73..533c6558 100644 --- a/GPy/likelihoods/gaussian.py +++ b/GPy/likelihoods/gaussian.py @@ -16,7 +16,7 @@ from scipy import stats, special from . import link_functions from .likelihood import Likelihood from ..core.parameterization import Param -from ..core.parameterization.transformations import Logexp +from paramz.transformations import Logexp from scipy import stats class Gaussian(Likelihood): @@ -67,7 +67,7 @@ class Gaussian(Likelihood): """ return Y - def _moments_match_ep(self, data_i, tau_i, v_i): + def moments_match_ep(self, data_i, tau_i, v_i, Y_metadata_i=None): """ Moments match of the marginal approximation in EP algorithm diff --git a/GPy/likelihoods/likelihood.py b/GPy/likelihoods/likelihood.py index 74c4c6fd..78f72d9d 100644 --- a/GPy/likelihoods/likelihood.py +++ b/GPy/likelihoods/likelihood.py @@ -49,8 +49,8 @@ class Likelihood(Parameterized): """ return Y.shape[1] - def _gradients(self,partial): - return np.zeros(0) + def exact_inference_gradients(self, dL_dKdiag,Y_metadata=None): + return np.zeros(self.size) def update_gradients(self, partial): if self.size > 0: @@ -176,8 +176,7 @@ class Likelihood(Parameterized): log_p_ystar = np.array(log_p_ystar).reshape(*y_test.shape) return log_p_ystar - - def _moments_match_ep(self,obs,tau,v): + def moments_match_ep(self,obs,tau,v,Y_metadata_i=None): """ Calculation of moments using quadrature @@ -188,20 +187,26 @@ class Likelihood(Parameterized): #Compute first integral for zeroth moment. #NOTE constant np.sqrt(2*pi/tau) added at the end of the function mu = v/tau + sigma2 = 1./tau + #Lets do these for now based on the same idea as Gaussian quadrature + # i.e. multiply anything by close to zero, and its zero. + f_min = mu - 20*np.sqrt(sigma2) + f_max = mu + 20*np.sqrt(sigma2) + def int_1(f): - return self.pdf(f, obs)*np.exp(-0.5*tau*np.square(mu-f)) - z_scaled, accuracy = quad(int_1, -np.inf, np.inf) + return self.pdf(f, obs, Y_metadata=Y_metadata_i)*np.exp(-0.5*tau*np.square(mu-f)) + z_scaled, accuracy = quad(int_1, f_min, f_max) #Compute second integral for first moment def int_2(f): - return f*self.pdf(f, obs)*np.exp(-0.5*tau*np.square(mu-f)) - mean, accuracy = quad(int_2, -np.inf, np.inf) + return f*self.pdf(f, obs, Y_metadata=Y_metadata_i)*np.exp(-0.5*tau*np.square(mu-f)) + mean, accuracy = quad(int_2, f_min, f_max) mean /= z_scaled #Compute integral for variance def int_3(f): - return (f**2)*self.pdf(f, obs)*np.exp(-0.5*tau*np.square(mu-f)) - Ef2, accuracy = quad(int_3, -np.inf, np.inf) + return (f**2)*self.pdf(f, obs, Y_metadata=Y_metadata_i)*np.exp(-0.5*tau*np.square(mu-f)) + Ef2, accuracy = quad(int_3, f_min, f_max) Ef2 /= z_scaled variance = Ef2 - mean**2 diff --git a/GPy/likelihoods/mixed_noise.py b/GPy/likelihoods/mixed_noise.py index 84b3001d..db230b13 100644 --- a/GPy/likelihoods/mixed_noise.py +++ b/GPy/likelihoods/mixed_noise.py @@ -7,7 +7,7 @@ from . import link_functions from .likelihood import Likelihood from .gaussian import Gaussian from ..core.parameterization import Param -from ..core.parameterization.transformations import Logexp +from paramz.transformations import Logexp from ..core.parameterization import Parameterized import itertools diff --git a/GPy/likelihoods/poisson.py b/GPy/likelihoods/poisson.py index cfe279bb..d3eef7a4 100644 --- a/GPy/likelihoods/poisson.py +++ b/GPy/likelihoods/poisson.py @@ -28,7 +28,7 @@ class Poisson(Likelihood): """ the expected value of y given a value of f """ - return self.gp_link.transf(gp) + return self.gp_link.transf(f) def pdf_link(self, link_f, y, Y_metadata=None): """ @@ -46,7 +46,8 @@ class Poisson(Likelihood): :rtype: float """ assert np.atleast_1d(link_f).shape == np.atleast_1d(y).shape - return np.prod(stats.poisson.pmf(y,link_f)) + return np.exp(self.logpdf_link(link_f, y, Y_metadata)) + # return np.prod(stats.poisson.pmf(y,link_f)) def logpdf_link(self, link_f, y, Y_metadata=None): """ diff --git a/GPy/likelihoods/student_t.py b/GPy/likelihoods/student_t.py index 79745ff6..e8de3c40 100644 --- a/GPy/likelihoods/student_t.py +++ b/GPy/likelihoods/student_t.py @@ -9,7 +9,7 @@ from scipy import stats, integrate from scipy.special import gammaln, gamma from .likelihood import Likelihood from ..core.parameterization import Param -from ..core.parameterization.transformations import Logexp +from paramz.transformations import Logexp from scipy.special import psi as digamma class StudentT(Likelihood): diff --git a/GPy/models/bayesian_gplvm.py b/GPy/models/bayesian_gplvm.py index fd02cb3e..86638eb9 100644 --- a/GPy/models/bayesian_gplvm.py +++ b/GPy/models/bayesian_gplvm.py @@ -5,7 +5,7 @@ import numpy as np from .. import kern from ..core.sparse_gp_mpi import SparseGP_MPI from ..likelihoods import Gaussian -from ..core.parameterization.variational import NormalPosterior, NormalPrior +from GPy.core.parameterization.variational import NormalPosterior, NormalPrior from ..inference.latent_function_inference.var_dtc_parallel import VarDTC_minibatch import logging diff --git a/GPy/models/bayesian_gplvm_minibatch.py b/GPy/models/bayesian_gplvm_minibatch.py index 3ef43753..73324386 100644 --- a/GPy/models/bayesian_gplvm_minibatch.py +++ b/GPy/models/bayesian_gplvm_minibatch.py @@ -2,14 +2,12 @@ # Licensed under the BSD 3-clause license (see LICENSE.txt) import numpy as np +import logging from .. import kern from ..likelihoods import Gaussian -from ..core.parameterization.variational import NormalPosterior, NormalPrior -from ..inference.latent_function_inference.var_dtc_parallel import VarDTC_minibatch -import logging -from GPy.models.sparse_gp_minibatch import SparseGPMiniBatch -from GPy.core.parameterization.param import Param -from GPy.core.parameterization.observable_array import ObsAr +from GPy.core.parameterization.variational import NormalPosterior, NormalPrior +from .sparse_gp_minibatch import SparseGPMiniBatch +from ..core.parameterization.param import Param class BayesianGPLVMMiniBatch(SparseGPMiniBatch): """ diff --git a/GPy/models/dpgplvm.py b/GPy/models/dpgplvm.py index 7f947c53..b4b1bbef 100644 --- a/GPy/models/dpgplvm.py +++ b/GPy/models/dpgplvm.py @@ -1,10 +1,7 @@ # Copyright (c) 2015 the GPy Austhors (see AUTHORS.txt) # Licensed under the BSD 3-clause license (see LICENSE.txt) -import numpy as np -from .. import kern from .bayesian_gplvm import BayesianGPLVM -from ..core.parameterization.variational import NormalPosterior, NormalPrior class DPBayesianGPLVM(BayesianGPLVM): """ diff --git a/GPy/models/gp_coregionalized_regression.py b/GPy/models/gp_coregionalized_regression.py index be5b9ac3..c8ee5f67 100644 --- a/GPy/models/gp_coregionalized_regression.py +++ b/GPy/models/gp_coregionalized_regression.py @@ -36,7 +36,9 @@ class GPCoregionalizedRegression(GP): #Kernel if kernel is None: - kernel = util.multioutput.ICM(input_dim=X.shape[1]-1, num_outputs=Ny, kernel=kern.RBF(X.shape[1]-1), W_rank=1,name=kernel_name) + kernel = kern.RBF(X.shape[1]-1) + + kernel = util.multioutput.ICM(input_dim=X.shape[1]-1, num_outputs=Ny, kernel=kernel, W_rank=1,name=kernel_name) #Likelihood likelihood = util.multioutput.build_likelihood(Y_list,self.output_index,likelihoods_list) diff --git a/GPy/models/gp_kronecker_gaussian_regression.py b/GPy/models/gp_kronecker_gaussian_regression.py index 5b2fb41c..37a33bef 100644 --- a/GPy/models/gp_kronecker_gaussian_regression.py +++ b/GPy/models/gp_kronecker_gaussian_regression.py @@ -2,8 +2,8 @@ # Licensed under the BSD 3-clause license (see LICENSE.txt) import numpy as np -from ..core.model import Model -from ..core.parameterization import ObsAr +from ..core import Model +from paramz import ObsAr from .. import likelihoods class GPKroneckerGaussianRegression(Model): diff --git a/GPy/models/gp_var_gauss.py b/GPy/models/gp_var_gauss.py index 6cce8640..05c55625 100644 --- a/GPy/models/gp_var_gauss.py +++ b/GPy/models/gp_var_gauss.py @@ -3,8 +3,6 @@ import numpy as np from ..core import GP -from ..core.parameterization import ObsAr -from .. import kern from ..core.parameterization.param import Param from ..inference.latent_function_inference import VarGauss diff --git a/GPy/models/gradient_checker.py b/GPy/models/gradient_checker.py index dce3e5a2..f7c17376 100644 --- a/GPy/models/gradient_checker.py +++ b/GPy/models/gradient_checker.py @@ -1,11 +1,11 @@ # ## Copyright (c) 2012, GPy authors (see AUTHORS.txt). # Licensed under the BSD 3-clause license (see LICENSE.txt) -from ..core.model import Model -import itertools import numpy -from ..core.parameterization import Param np = numpy + +from ..core.parameterization import Param +from GPy.core.model import Model from ..util.block_matrices import get_blocks, get_block_shapes, unblock, get_blocks_3d, get_block_shapes_3d def get_shape(x): @@ -62,7 +62,7 @@ class GradientChecker(Model): grad.randomize() grad.checkgrad(verbose=1) """ - Model.__init__(self, 'GradientChecker') + super(GradientChecker, self).__init__(name='GradientChecker') if isinstance(x0, (list, tuple)) and names is None: self.shapes = [get_shape(xi) for xi in x0] self.names = ['X{i}'.format(i=i) for i in range(len(x0))] diff --git a/GPy/models/mrd.py b/GPy/models/mrd.py index 7832e155..be28d1a5 100644 --- a/GPy/models/mrd.py +++ b/GPy/models/mrd.py @@ -5,18 +5,14 @@ import numpy as np import itertools, logging from ..kern import Kern -from ..core.parameterization.variational import NormalPosterior, NormalPrior -from ..core.parameterization import Param, Parameterized -from ..core.parameterization.observable_array import ObsAr +from GPy.core.parameterization.variational import NormalPrior +from ..core.parameterization import Param +from paramz import ObsAr from ..inference.latent_function_inference.var_dtc import VarDTC from ..inference.latent_function_inference import InferenceMethodList from ..likelihoods import Gaussian from ..util.initialization import initialize_latent -from ..core.sparse_gp import SparseGP, GP -from GPy.core.parameterization.variational import VariationalPosterior from GPy.models.bayesian_gplvm_minibatch import BayesianGPLVMMiniBatch -from GPy.models.bayesian_gplvm import BayesianGPLVM -from GPy.models.sparse_gp_minibatch import SparseGPMiniBatch class MRD(BayesianGPLVMMiniBatch): """ diff --git a/GPy/models/one_vs_all_classification.py b/GPy/models/one_vs_all_classification.py index 10457d75..d8024019 100644 --- a/GPy/models/one_vs_all_classification.py +++ b/GPy/models/one_vs_all_classification.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, the GPy Authors (see AUTHORS.txt) # Licensed under the BSD 3-clause license (see LICENSE.txt) -from ..core import GP from . import SparseGPClassification from .. import likelihoods from .. import kern diff --git a/GPy/models/sparse_gp_classification.py b/GPy/models/sparse_gp_classification.py index e1c468d1..a996732a 100644 --- a/GPy/models/sparse_gp_classification.py +++ b/GPy/models/sparse_gp_classification.py @@ -62,7 +62,7 @@ class SparseGPClassificationUncertainInput(SparseGP): .. Note:: Multiple independent outputs are allowed using columns of Y """ def __init__(self, X, X_variance, Y, kernel=None, Z=None, num_inducing=10, Y_metadata=None, normalizer=None): - from ..core.parameterization.variational import NormalPosterior + from GPy.core.parameterization.variational import NormalPosterior if kernel is None: kernel = kern.RBF(X.shape[1]) diff --git a/GPy/models/sparse_gp_coregionalized_regression.py b/GPy/models/sparse_gp_coregionalized_regression.py index 797d8b30..2997993e 100644 --- a/GPy/models/sparse_gp_coregionalized_regression.py +++ b/GPy/models/sparse_gp_coregionalized_regression.py @@ -4,7 +4,6 @@ import numpy as np from ..core import SparseGP from ..inference.latent_function_inference import VarDTC -from .. import likelihoods from .. import kern from .. import util @@ -43,7 +42,9 @@ class SparseGPCoregionalizedRegression(SparseGP): #Kernel if kernel is None: - kernel = util.multioutput.ICM(input_dim=X.shape[1]-1, num_outputs=Ny, kernel=kern.RBF(X.shape[1]-1), W_rank=1,name=kernel_name) + kernel = kern.RBF(X.shape[1]-1) + + kernel = util.multioutput.ICM(input_dim=X.shape[1]-1, num_outputs=Ny, kernel=kernel, W_rank=1,name=kernel_name) #Likelihood likelihood = util.multioutput.build_likelihood(Y_list,self.output_index,likelihoods_list) diff --git a/GPy/models/sparse_gp_minibatch.py b/GPy/models/sparse_gp_minibatch.py index 54160e6f..73393d85 100644 --- a/GPy/models/sparse_gp_minibatch.py +++ b/GPy/models/sparse_gp_minibatch.py @@ -4,18 +4,15 @@ from __future__ import print_function import numpy as np from ..core.parameterization.param import Param +from GPy.core.parameterization.variational import VariationalPosterior from ..core.sparse_gp import SparseGP from ..core.gp import GP from ..inference.latent_function_inference import var_dtc from .. import likelihoods -from ..core.parameterization.variational import VariationalPosterior import logging -from GPy.inference.latent_function_inference.posterior import Posterior -from GPy.inference.optimization.stochastics import SparseGPStochastics,\ - SparseGPMissing -#no stochastics.py file added! from GPy.inference.optimization.stochastics import SparseGPStochastics,\ - #SparseGPMissing +from ..inference.latent_function_inference.posterior import Posterior +from ..inference.optimization.stochastics import SparseGPStochastics, SparseGPMissing logger = logging.getLogger("sparse gp") class SparseGPMiniBatch(SparseGP): diff --git a/GPy/models/sparse_gp_regression.py b/GPy/models/sparse_gp_regression.py index faca7e9e..31bde23d 100644 --- a/GPy/models/sparse_gp_regression.py +++ b/GPy/models/sparse_gp_regression.py @@ -3,12 +3,11 @@ import numpy as np -from ..core import SparseGP from ..core.sparse_gp_mpi import SparseGP_MPI from .. import likelihoods from .. import kern from ..inference.latent_function_inference import VarDTC -from ..core.parameterization.variational import NormalPosterior +from GPy.core.parameterization.variational import NormalPosterior class SparseGPRegression(SparseGP_MPI): """ diff --git a/GPy/models/sparse_gplvm.py b/GPy/models/sparse_gplvm.py index d1ad5884..22852d93 100644 --- a/GPy/models/sparse_gplvm.py +++ b/GPy/models/sparse_gplvm.py @@ -2,9 +2,8 @@ # Licensed under the BSD 3-clause license (see LICENSE.txt) -import numpy as np import sys -from GPy.models.sparse_gp_regression import SparseGPRegression +from .sparse_gp_regression import SparseGPRegression class SparseGPLVM(SparseGPRegression): """ diff --git a/GPy/models/ss_gplvm.py b/GPy/models/ss_gplvm.py index 0123d629..2c413ecd 100644 --- a/GPy/models/ss_gplvm.py +++ b/GPy/models/ss_gplvm.py @@ -7,7 +7,7 @@ from ..core.sparse_gp_mpi import SparseGP_MPI from .. import kern from ..core.parameterization import Param from ..likelihoods import Gaussian -from ..core.parameterization.variational import SpikeAndSlabPrior, SpikeAndSlabPosterior,VariationalPrior +from GPy.core.parameterization.variational import SpikeAndSlabPrior, SpikeAndSlabPosterior,VariationalPrior from ..inference.latent_function_inference.var_dtc_parallel import update_gradients, VarDTC_minibatch from ..kern.src.psi_comp.ssrbf_psi_gpucomp import PSICOMP_SSRBF_GPU @@ -19,7 +19,7 @@ class IBPPosterior(SpikeAndSlabPosterior): """ binary_prob : the probability of the distribution on the slab part. """ - from ..core.parameterization.transformations import Logexp + from paramz.transformations import Logexp super(IBPPosterior, self).__init__(means, variances, binary_prob, group_spike=True, name=name) self.sharedX = sharedX if sharedX: @@ -60,7 +60,7 @@ class IBPPosterior(SpikeAndSlabPosterior): class IBPPrior(VariationalPrior): def __init__(self, input_dim, alpha =2., name='IBPPrior', **kw): super(IBPPrior, self).__init__(name=name, **kw) - from ..core.parameterization.transformations import Logexp, __fixed__ + from paramz.transformations import Logexp, __fixed__ self.input_dim = input_dim self.variance = 1. self.alpha = Param('alpha', alpha, __fixed__) @@ -224,4 +224,4 @@ class SSGPLVM(SparseGP_MPI): - \ No newline at end of file + diff --git a/GPy/models/ss_mrd.py b/GPy/models/ss_mrd.py index 41289d3f..d571a542 100644 --- a/GPy/models/ss_mrd.py +++ b/GPy/models/ss_mrd.py @@ -5,7 +5,7 @@ The Maniforld Relevance Determination model with the spike-and-slab prior import numpy as np from ..core import Model from .ss_gplvm import SSGPLVM -from ..core.parameterization.variational import SpikeAndSlabPrior,NormalPosterior,VariationalPrior +from GPy.core.parameterization.variational import SpikeAndSlabPrior,NormalPosterior,VariationalPrior from ..util.misc import param_to_array from ..kern import RBF from ..core import Param @@ -214,7 +214,7 @@ class SpikeAndSlabPrior_SSMRD(SpikeAndSlabPrior): class IBPPrior_SSMRD(VariationalPrior): def __init__(self, nModels, input_dim, alpha =2., tau=None, name='IBPPrior', **kw): super(IBPPrior_SSMRD, self).__init__(name=name, **kw) - from ..core.parameterization.transformations import Logexp, __fixed__ + from paramz.transformations import Logexp, __fixed__ self.nModels = nModels self._b_prob_all = 0.5 self.input_dim = input_dim diff --git a/GPy/plotting/__init__.py b/GPy/plotting/__init__.py index e4fe7080..c46d5281 100644 --- a/GPy/plotting/__init__.py +++ b/GPy/plotting/__init__.py @@ -1,8 +1,10 @@ # Copyright (c) 2014, GPy authors (see AUTHORS.txt). # Licensed under the BSD 3-clause license (see LICENSE.txt) - current_lib = [None] +supported_libraries = ['matplotlib', 'plotly', 'none'] +error_suggestion = "Please make sure you specify your plotting library in your configuration file (/.config/GPy/user.cfg).\n\n[plotting]\nlibrary = \n\nCurrently supported libraries: {}".format(", ".join(supported_libraries)) + def change_plotting_library(lib): try: #=========================================================================== @@ -10,6 +12,8 @@ def change_plotting_library(lib): # save it under the name plotting_library! # This is hooking the library in # for the usage in GPy: + if lib not in supported_libraries: + raise ValueError("Warning: Plotting library {} not recognized, currently supported libraries are: \n {}".format(lib, ", ".join(supported_libraries))) if lib == 'matplotlib': import matplotlib from .matplot_dep.plot_definitions import MatplotlibPlots @@ -23,16 +27,20 @@ def change_plotting_library(lib): current_lib[0] = None #=========================================================================== except (ImportError, NameError): - raise config.set('plotting', 'library', 'none') import warnings - warnings.warn(ImportWarning("{} not available, install newest version of {} for plotting".format(lib, lib))) + warnings.warn(ImportWarning("You spevified {} in your configuration, but is not available. Install newest version of {} for plotting".format(lib, lib))) -from ..util.config import config -lib = config.get('plotting', 'library') -change_plotting_library(lib) +from ..util.config import config, NoOptionError +try: + lib = config.get('plotting', 'library') + change_plotting_library(lib) +except NoOptionError: + print("No plotting library was specified in config file. \n{}".format(error_suggestion)) def plotting_library(): + if current_lib[0] is None: + raise RuntimeError("No plotting library was loaded. \n{}".format(error_suggestion)) return current_lib[0] def show(figure, **kwargs): diff --git a/GPy/plotting/gpy_plot/gp_plots.py b/GPy/plotting/gpy_plot/gp_plots.py index da92748d..4d467e62 100644 --- a/GPy/plotting/gpy_plot/gp_plots.py +++ b/GPy/plotting/gpy_plot/gp_plots.py @@ -393,7 +393,7 @@ def plot_f(self, plot_limits=None, fixed_inputs=None, apply_link, which_data_ycols, which_data_rows, visible_dims, levels, samples, 0, lower, upper, plot_data, plot_inducing, - plot_density, predict_kw, projection, legend) + plot_density, predict_kw, projection, legend, **kwargs) diff --git a/GPy/plotting/gpy_plot/latent_plots.py b/GPy/plotting/gpy_plot/latent_plots.py index 2e5c7148..240f35ae 100644 --- a/GPy/plotting/gpy_plot/latent_plots.py +++ b/GPy/plotting/gpy_plot/latent_plots.py @@ -123,13 +123,16 @@ def plot_latent_inducing(self, :param kwargs: the kwargs for the scatter plots """ input_1, input_2, input_3 = sig_dims = self.get_most_significant_input_dimensions(which_indices) + if input_3 is None: zlabel=None + else: zlabel = 'latent dimension %i' % input_3 + if 'color' not in kwargs: kwargs['color'] = 'white' canvas, kwargs = pl().new_canvas(projection=projection, xlabel='latent dimension %i' % input_1, ylabel='latent dimension %i' % input_2, - zlabel='latent dimension %i' % input_3, **kwargs) + zlabel=zlabel, **kwargs) Z = self.Z.values labels = np.array(['inducing'] * Z.shape[0]) scatters = _plot_latent_scatter(canvas, Z, sig_dims, labels, marker, num_samples, projection=projection, **kwargs) @@ -195,7 +198,7 @@ def plot_magnification(self, labels=None, which_indices=None, labels = np.ones(self.num_data) legend = False scatters = _plot_latent_scatter(canvas, X, which_indices, labels, marker, num_samples, projection='2d', **scatter_kwargs or {}) - view = _plot_magnification(self, canvas, which_indices[:2], Xgrid, xmin, xmax, resolution, updates, mean, covariance, kern, **imshow_kwargs) + view = _plot_magnification(self, canvas, which_indices, Xgrid, xmin, xmax, resolution, updates, mean, covariance, kern, **imshow_kwargs) retval = pl().add_to_canvas(canvas, dict(scatter=scatters, imshow=view), legend=legend, ) diff --git a/GPy/plotting/gpy_plot/plot_util.py b/GPy/plotting/gpy_plot/plot_util.py index e89aae0f..254886a2 100644 --- a/GPy/plotting/gpy_plot/plot_util.py +++ b/GPy/plotting/gpy_plot/plot_util.py @@ -131,6 +131,8 @@ def helper_for_plot_data(self, X, plot_limits, visible_dims, fixed_inputs, resol Xnew, x, y, xmin, xmax = x_frame2D(X[:,free_dims], plot_limits, resolution) Xgrid = np.zeros((Xnew.shape[0], self.input_dim)) Xgrid[:,free_dims] = Xnew + #xmin = Xgrid.min(0)[free_dims] + #xmax = Xgrid.max(0)[free_dims] for i,v in fixed_inputs: Xgrid[:,i] = v else: @@ -305,7 +307,7 @@ def get_free_dims(model, visible_dims, fixed_dims): visible_dims = np.arange(model.input_dim) dims = np.asanyarray(visible_dims) if fixed_dims is not None: - dims = np.setdiff1d(dims, fixed_dims) + dims = [dim for dim in dims if dim not in fixed_dims] return np.asanyarray([dim for dim in dims if dim is not None]) @@ -337,7 +339,7 @@ def x_frame1D(X,plot_limits=None,resolution=None): """ assert X.shape[1] ==1, "x_frame1D is defined for one-dimensional inputs" if plot_limits is None: - from ...core.parameterization.variational import VariationalPosterior + from GPy.core.parameterization.variational import VariationalPosterior if isinstance(X, VariationalPosterior): xmin,xmax = X.mean.min(0),X.mean.max(0) else: @@ -357,7 +359,7 @@ def x_frame2D(X,plot_limits=None,resolution=None): """ assert X.shape[1]==2, "x_frame2D is defined for two-dimensional inputs" if plot_limits is None: - xmin, xmax = X.min(0),X.max(0) + xmin, xmax = X.min(0), X.max(0) xmin, xmax = xmin-0.075*(xmax-xmin), xmax+0.075*(xmax-xmin) elif len(plot_limits) == 2: xmin, xmax = plot_limits diff --git a/GPy/plotting/matplot_dep/visualize.py b/GPy/plotting/matplot_dep/visualize.py index c5d2fe14..ce95d1ef 100644 --- a/GPy/plotting/matplot_dep/visualize.py +++ b/GPy/plotting/matplot_dep/visualize.py @@ -1,6 +1,7 @@ import numpy as np import time from ...core.parameterization.variational import VariationalPosterior + try: import matplotlib.pyplot as plt import matplotlib as mpl diff --git a/GPy/testing/baseline/bayesian_gplvm_gradient.png b/GPy/testing/baseline/bayesian_gplvm_gradient.png new file mode 100644 index 00000000..9ceec5df Binary files /dev/null and b/GPy/testing/baseline/bayesian_gplvm_gradient.png differ diff --git a/GPy/testing/baseline/bayesian_gplvm_inducing.png b/GPy/testing/baseline/bayesian_gplvm_inducing.png new file mode 100644 index 00000000..cbf7c344 Binary files /dev/null and b/GPy/testing/baseline/bayesian_gplvm_inducing.png differ diff --git a/GPy/testing/baseline/bayesian_gplvm_inducing_3d.png b/GPy/testing/baseline/bayesian_gplvm_inducing_3d.png new file mode 100644 index 00000000..edff93ef Binary files /dev/null and b/GPy/testing/baseline/bayesian_gplvm_inducing_3d.png differ diff --git a/GPy/testing/baseline/bayesian_gplvm_latent.png b/GPy/testing/baseline/bayesian_gplvm_latent.png new file mode 100644 index 00000000..626bcb8b Binary files /dev/null and b/GPy/testing/baseline/bayesian_gplvm_latent.png differ diff --git a/GPy/testing/baseline/bayesian_gplvm_latent_3d.png b/GPy/testing/baseline/bayesian_gplvm_latent_3d.png new file mode 100644 index 00000000..795e89f9 Binary files /dev/null and b/GPy/testing/baseline/bayesian_gplvm_latent_3d.png differ diff --git a/GPy/testing/baseline/bayesian_gplvm_magnification.png b/GPy/testing/baseline/bayesian_gplvm_magnification.png new file mode 100644 index 00000000..85c3eb7f Binary files /dev/null and b/GPy/testing/baseline/bayesian_gplvm_magnification.png differ diff --git a/GPy/testing/plotting_tests/baseline/coverage_3d_plot.png b/GPy/testing/baseline/coverage_3d_plot.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/coverage_3d_plot.png rename to GPy/testing/baseline/coverage_3d_plot.png diff --git a/GPy/testing/plotting_tests/baseline/coverage_annotation_interact.png b/GPy/testing/baseline/coverage_annotation_interact.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/coverage_annotation_interact.png rename to GPy/testing/baseline/coverage_annotation_interact.png diff --git a/GPy/testing/baseline/coverage_gradient.png b/GPy/testing/baseline/coverage_gradient.png new file mode 100644 index 00000000..60bd7fb9 Binary files /dev/null and b/GPy/testing/baseline/coverage_gradient.png differ diff --git a/GPy/testing/plotting_tests/baseline/coverage_imshow_interact.png b/GPy/testing/baseline/coverage_imshow_interact.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/coverage_imshow_interact.png rename to GPy/testing/baseline/coverage_imshow_interact.png diff --git a/GPy/testing/plotting_tests/baseline/gp_2d_data.png b/GPy/testing/baseline/gp_2d_data.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_2d_data.png rename to GPy/testing/baseline/gp_2d_data.png diff --git a/GPy/testing/plotting_tests/baseline/gp_2d_in_error.png b/GPy/testing/baseline/gp_2d_in_error.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_2d_in_error.png rename to GPy/testing/baseline/gp_2d_in_error.png diff --git a/GPy/testing/plotting_tests/baseline/gp_2d_inducing.png b/GPy/testing/baseline/gp_2d_inducing.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_2d_inducing.png rename to GPy/testing/baseline/gp_2d_inducing.png diff --git a/GPy/testing/plotting_tests/baseline/gp_2d_mean.png b/GPy/testing/baseline/gp_2d_mean.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_2d_mean.png rename to GPy/testing/baseline/gp_2d_mean.png diff --git a/GPy/testing/plotting_tests/baseline/gp_3d_data.png b/GPy/testing/baseline/gp_3d_data.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_3d_data.png rename to GPy/testing/baseline/gp_3d_data.png diff --git a/GPy/testing/plotting_tests/baseline/gp_3d_inducing.png b/GPy/testing/baseline/gp_3d_inducing.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_3d_inducing.png rename to GPy/testing/baseline/gp_3d_inducing.png diff --git a/GPy/testing/plotting_tests/baseline/gp_3d_mean.png b/GPy/testing/baseline/gp_3d_mean.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_3d_mean.png rename to GPy/testing/baseline/gp_3d_mean.png diff --git a/GPy/testing/plotting_tests/baseline/gp_class_likelihood.png b/GPy/testing/baseline/gp_class_likelihood.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_class_likelihood.png rename to GPy/testing/baseline/gp_class_likelihood.png diff --git a/GPy/testing/plotting_tests/baseline/gp_class_raw.png b/GPy/testing/baseline/gp_class_raw.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_class_raw.png rename to GPy/testing/baseline/gp_class_raw.png diff --git a/GPy/testing/plotting_tests/baseline/gp_class_raw_link.png b/GPy/testing/baseline/gp_class_raw_link.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_class_raw_link.png rename to GPy/testing/baseline/gp_class_raw_link.png diff --git a/GPy/testing/plotting_tests/baseline/gp_conf.png b/GPy/testing/baseline/gp_conf.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_conf.png rename to GPy/testing/baseline/gp_conf.png diff --git a/GPy/testing/plotting_tests/baseline/gp_data.png b/GPy/testing/baseline/gp_data.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_data.png rename to GPy/testing/baseline/gp_data.png diff --git a/GPy/testing/plotting_tests/baseline/gp_density.png b/GPy/testing/baseline/gp_density.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_density.png rename to GPy/testing/baseline/gp_density.png diff --git a/GPy/testing/plotting_tests/baseline/gp_in_error.png b/GPy/testing/baseline/gp_in_error.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_in_error.png rename to GPy/testing/baseline/gp_in_error.png diff --git a/GPy/testing/plotting_tests/baseline/gp_mean.png b/GPy/testing/baseline/gp_mean.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_mean.png rename to GPy/testing/baseline/gp_mean.png diff --git a/GPy/testing/plotting_tests/baseline/gp_out_error.png b/GPy/testing/baseline/gp_out_error.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_out_error.png rename to GPy/testing/baseline/gp_out_error.png diff --git a/GPy/testing/plotting_tests/baseline/gp_samples.png b/GPy/testing/baseline/gp_samples.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/gp_samples.png rename to GPy/testing/baseline/gp_samples.png diff --git a/GPy/testing/baseline/gplvm_gradient.png b/GPy/testing/baseline/gplvm_gradient.png new file mode 100644 index 00000000..338326f6 Binary files /dev/null and b/GPy/testing/baseline/gplvm_gradient.png differ diff --git a/GPy/testing/baseline/gplvm_latent.png b/GPy/testing/baseline/gplvm_latent.png new file mode 100644 index 00000000..305ec046 Binary files /dev/null and b/GPy/testing/baseline/gplvm_latent.png differ diff --git a/GPy/testing/baseline/gplvm_latent_3d.png b/GPy/testing/baseline/gplvm_latent_3d.png new file mode 100644 index 00000000..795e89f9 Binary files /dev/null and b/GPy/testing/baseline/gplvm_latent_3d.png differ diff --git a/GPy/testing/baseline/gplvm_magnification.png b/GPy/testing/baseline/gplvm_magnification.png new file mode 100644 index 00000000..dc7d7101 Binary files /dev/null and b/GPy/testing/baseline/gplvm_magnification.png differ diff --git a/GPy/testing/plotting_tests/baseline/kern_ARD.png b/GPy/testing/baseline/kern_ARD.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/kern_ARD.png rename to GPy/testing/baseline/kern_ARD.png diff --git a/GPy/testing/plotting_tests/baseline/kern_cov_1d.png b/GPy/testing/baseline/kern_cov_1d.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/kern_cov_1d.png rename to GPy/testing/baseline/kern_cov_1d.png diff --git a/GPy/testing/plotting_tests/baseline/kern_cov_2d.png b/GPy/testing/baseline/kern_cov_2d.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/kern_cov_2d.png rename to GPy/testing/baseline/kern_cov_2d.png diff --git a/GPy/testing/plotting_tests/baseline/kern_cov_3d.png b/GPy/testing/baseline/kern_cov_3d.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/kern_cov_3d.png rename to GPy/testing/baseline/kern_cov_3d.png diff --git a/GPy/testing/plotting_tests/baseline/kern_cov_no_lim.png b/GPy/testing/baseline/kern_cov_no_lim.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/kern_cov_no_lim.png rename to GPy/testing/baseline/kern_cov_no_lim.png diff --git a/GPy/testing/plotting_tests/baseline/sparse_gp_class_likelihood.png b/GPy/testing/baseline/sparse_gp_class_likelihood.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/sparse_gp_class_likelihood.png rename to GPy/testing/baseline/sparse_gp_class_likelihood.png diff --git a/GPy/testing/plotting_tests/baseline/sparse_gp_class_raw.png b/GPy/testing/baseline/sparse_gp_class_raw.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/sparse_gp_class_raw.png rename to GPy/testing/baseline/sparse_gp_class_raw.png diff --git a/GPy/testing/plotting_tests/baseline/sparse_gp_class_raw_link.png b/GPy/testing/baseline/sparse_gp_class_raw_link.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/sparse_gp_class_raw_link.png rename to GPy/testing/baseline/sparse_gp_class_raw_link.png diff --git a/GPy/testing/plotting_tests/baseline/sparse_gp_data_error.png b/GPy/testing/baseline/sparse_gp_data_error.png similarity index 100% rename from GPy/testing/plotting_tests/baseline/sparse_gp_data_error.png rename to GPy/testing/baseline/sparse_gp_data_error.png diff --git a/GPy/testing/cacher_tests.py b/GPy/testing/cacher_tests.py deleted file mode 100644 index 60f79ba2..00000000 --- a/GPy/testing/cacher_tests.py +++ /dev/null @@ -1,37 +0,0 @@ -''' -Created on 4 Sep 2015 - -@author: maxz -''' -import unittest -from GPy.util.caching import Cacher -from pickle import PickleError - - -class Test(unittest.TestCase): - def setUp(self): - def op(x): - return x - self.cache = Cacher(op, 1) - - def test_pickling(self): - self.assertRaises(PickleError, self.cache.__getstate__) - self.assertRaises(PickleError, self.cache.__setstate__) - - def test_copy(self): - tmp = self.cache.__deepcopy__() - assert(tmp.operation is self.cache.operation) - self.assertEqual(tmp.limit, self.cache.limit) - - def test_reset(self): - self.cache.reset() - self.assertDictEqual(self.cache.cached_input_ids, {}, ) - self.assertDictEqual(self.cache.cached_outputs, {}, ) - self.assertDictEqual(self.cache.inputs_changed, {}, ) - - def test_name(self): - assert(self.cache.__name__ == self.cache.operation.__name__) - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() \ No newline at end of file diff --git a/GPy/testing/gp_tests.py b/GPy/testing/gp_tests.py index 63345c18..b8cd89e2 100644 --- a/GPy/testing/gp_tests.py +++ b/GPy/testing/gp_tests.py @@ -22,7 +22,7 @@ class Test(unittest.TestCase): def test_setxy_bgplvm(self): k = GPy.kern.RBF(1) - m = GPy.models.BayesianGPLVM(self.Y, 2, kernel=k) + m = GPy.models.BayesianGPLVM(self.Y, 1, kernel=k) mu, var = m.predict(m.X) X = m.X.copy() Xnew = NormalPosterior(m.X.mean[:10].copy(), m.X.variance[:10].copy()) @@ -32,10 +32,11 @@ class Test(unittest.TestCase): mu2, var2 = m.predict(m.X) np.testing.assert_allclose(mu, mu2) np.testing.assert_allclose(var, var2) + def test_setxy_gplvm(self): k = GPy.kern.RBF(1) - m = GPy.models.GPLVM(self.Y, 2, kernel=k) + m = GPy.models.GPLVM(self.Y, 1, kernel=k) mu, var = m.predict(m.X) X = m.X.copy() Xnew = X[:10].copy() diff --git a/GPy/testing/index_operations_tests.py b/GPy/testing/index_operations_tests.py deleted file mode 100644 index a97f1beb..00000000 --- a/GPy/testing/index_operations_tests.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2014, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) - -import unittest -import numpy as np -from GPy.core.parameterization.index_operations import ParameterIndexOperations,\ - ParameterIndexOperationsView - -one, two, three = 'one', 'two', 'three' - -class Test(unittest.TestCase): - - def setUp(self): - self.param_index = ParameterIndexOperations() - self.param_index.add(one, [3,9]) - self.param_index.add(two, [0,5]) - self.param_index.add(three, [2,4,7,10]) - self.view = ParameterIndexOperationsView(self.param_index, 2, 6) - - def test_clear(self): - self.param_index.clear() - self.assertDictEqual(self.param_index._properties, {}) - - def test_remove(self): - removed = self.param_index.remove(three, np.r_[3:13]) - self.assertListEqual(removed.tolist(), [4,7,10]) - self.assertListEqual(self.param_index[three].tolist(), [2]) - removed = self.param_index.remove(one, [1]) - self.assertListEqual(removed.tolist(), []) - self.assertListEqual(self.param_index[one].tolist(), [3,9]) - self.assertListEqual(self.param_index.remove('not in there', []).tolist(), []) - removed = self.param_index.remove(one, [9]) - self.assertListEqual(removed.tolist(), [9]) - self.assertListEqual(self.param_index[one].tolist(), [3]) - self.assertListEqual(self.param_index.remove('not in there', [2,3,4]).tolist(), []) - self.assertListEqual(self.view.remove('not in there', [2,3,4]).tolist(), []) - - def test_shift_left(self): - self.view.shift_left(0, 2) - self.assertListEqual(self.param_index[three].tolist(), [2,5,8]) - self.assertListEqual(self.param_index[two].tolist(), [0,3]) - self.assertListEqual(self.param_index[one].tolist(), [7]) - #======================================================================= - # 0 1 2 3 4 5 6 7 8 9 10 - # one - # two two - # three three three - # view: [0 1 2 3 4 5 ] - #======================================================================= - self.assertListEqual(self.view[three].tolist(), [0,3]) - self.assertListEqual(self.view[two].tolist(), [1]) - self.assertListEqual(self.view[one].tolist(), [5]) - self.param_index.shift_left(7, 1) - #======================================================================= - # 0 1 2 3 4 5 6 7 8 9 10 - # - # two two - # three three three - # view: [0 1 2 3 4 5 ] - #======================================================================= - self.assertListEqual(self.param_index[three].tolist(), [2,5,7]) - self.assertListEqual(self.param_index[two].tolist(), [0,3]) - self.assertListEqual(self.param_index[one].tolist(), []) - self.assertListEqual(self.view[three].tolist(), [0,3,5]) - self.assertListEqual(self.view[two].tolist(), [1]) - self.assertListEqual(self.view[one].tolist(), []) - - def test_shift_right(self): - self.view.shift_right(3, 2) - self.assertListEqual(self.param_index[three].tolist(), [2,4,9,12]) - self.assertListEqual(self.param_index[two].tolist(), [0,7]) - self.assertListEqual(self.param_index[one].tolist(), [3,11]) - - def test_index_view(self): - #======================================================================= - # 0 1 2 3 4 5 6 7 8 9 10 - # one one - # two two - # three three three three - # view: [0 1 2 3 4 5 ] - #======================================================================= - self.view = ParameterIndexOperationsView(self.param_index, 2, 6) - self.assertSetEqual(set(self.view.properties()), set([one, two, three])) - for v,p in zip(self.view.properties_for(np.r_[:6]), self.param_index.properties_for(np.r_[2:2+6])): - self.assertEqual(v, p) - self.assertSetEqual(set(self.view[two]), set([3])) - self.assertSetEqual(set(self.param_index[two]), set([0, 5])) - self.view.add(two, np.array([0])) - self.assertSetEqual(set(self.view[two]), set([0,3])) - self.assertSetEqual(set(self.param_index[two]), set([0, 2, 5])) - self.view.clear() - for v,p in zip(self.view.properties_for(np.r_[:6]), self.param_index.properties_for(np.r_[2:2+6])): - self.assertEqual(v, p) - self.assertEqual(v, []) - param_index = ParameterIndexOperations() - param_index.add(one, [3,9]) - param_index.add(two, [0,5]) - param_index.add(three, [2,4,7,10]) - view2 = ParameterIndexOperationsView(param_index, 2, 8) - self.view.update(view2) - for [i,v],[i2,v2] in zip(sorted(param_index.items()), sorted(self.param_index.items())): - self.assertEqual(i, i2) - np.testing.assert_equal(v, v2) - - def test_view_of_view(self): - #======================================================================= - # 0 1 2 3 4 5 6 7 8 9 10 - # one one - # two two - # three three three three - # view: [0 1 2 3 4 5 ] - # view2: [0 1 2 3 4 5 ] - #======================================================================= - view2 = ParameterIndexOperationsView(self.view, 2, 6) - view2.shift_right(0, 2) - - def test_indexview_remove(self): - removed = self.view.remove(two, [3]) - self.assertListEqual(removed.tolist(), [3]) - removed = self.view.remove(three, np.r_[:5]) - self.assertListEqual(removed.tolist(), [0, 2]) - - def test_misc(self): - #py3 fix - #for k,v in self.param_index.copy()._properties.iteritems(): - for k,v in self.param_index.copy()._properties.items(): - self.assertListEqual(self.param_index[k].tolist(), v.tolist()) - self.assertEqual(self.param_index.size, 8) - self.assertEqual(self.view.size, 5) - - def test_print(self): - print(self.param_index) - print(self.view) - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.test_index_view'] - unittest.main() diff --git a/GPy/testing/inference_tests.py b/GPy/testing/inference_tests.py index 92def798..7a091589 100644 --- a/GPy/testing/inference_tests.py +++ b/GPy/testing/inference_tests.py @@ -5,7 +5,7 @@ The test cases for various inference algorithms """ -import unittest, itertools +import unittest import numpy as np import GPy #np.seterr(invalid='raise') @@ -28,7 +28,10 @@ class InferenceXTestCase(unittest.TestCase): def test_inferenceX_BGPLVM_RBF(self): Ys = self.genData() m = GPy.models.BayesianGPLVM(Ys,3,kernel=GPy.kern.RBF(3,ARD=True)) - m.optimize() + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + m.optimize() x, mi = m.infer_newX(m.Y, optimize=True) np.testing.assert_array_almost_equal(m.X.mean, mi.X.mean, decimal=2) np.testing.assert_array_almost_equal(m.X.variance, mi.X.variance, decimal=2) diff --git a/GPy/testing/kernel_tests.py b/GPy/testing/kernel_tests.py index bbfb565b..2eebb6e3 100644 --- a/GPy/testing/kernel_tests.py +++ b/GPy/testing/kernel_tests.py @@ -4,7 +4,6 @@ import unittest import numpy as np import GPy -import sys from GPy.core.parameterization.param import Param from ..util.config import config @@ -24,7 +23,7 @@ class Kern_check_model(GPy.core.Model): checkgrad() to be called independently on a kernel. """ def __init__(self, kernel=None, dL_dK=None, X=None, X2=None): - GPy.core.Model.__init__(self, 'kernel_test_model') + super(Kern_check_model, self).__init__('kernel_test_model') if kernel==None: kernel = GPy.kern.RBF(1) kernel.randomize(loc=1, scale=0.1) diff --git a/GPy/testing/likelihood_tests.py b/GPy/testing/likelihood_tests.py index de347192..2fb255c9 100644 --- a/GPy/testing/likelihood_tests.py +++ b/GPy/testing/likelihood_tests.py @@ -113,6 +113,7 @@ class TestNoiseModels(object): self.Y = (np.sin(self.X[:, 0]*2*np.pi) + noise)[:, None] self.f = np.random.rand(self.N, 1) self.binary_Y = np.asarray(np.random.rand(self.N) > 0.5, dtype=np.int)[:, None] + self.binary_Y[self.binary_Y == 0.0] = -1.0 self.positive_Y = np.exp(self.Y.copy()) tmp = np.round(self.X[:, 0]*3-3)[:, None] + np.random.randint(0,3, self.X.shape[0])[:, None] self.integer_Y = np.where(tmp > 0, tmp, 0) @@ -164,15 +165,18 @@ class TestNoiseModels(object): }, "laplace": True }, - "Student_t_small_deg_free": { - "model": GPy.likelihoods.StudentT(deg_free=1.5, sigma2=self.var), - "grad_params": { - "names": [".*t_scale2"], - "vals": [self.var], - "constraints": [(".*t_scale2", self.constrain_positive), (".*deg_free", self.constrain_fixed)] - }, - "laplace": True - }, + # FIXME: This is a known failure point, when the degrees of freedom + # are very small, and the variance is relatively small, the + # likelihood is log-concave and problems occur + # "Student_t_small_deg_free": { + # "model": GPy.likelihoods.StudentT(deg_free=1.5, sigma2=self.var), + # "grad_params": { + # "names": [".*t_scale2"], + # "vals": [self.var], + # "constraints": [(".*t_scale2", self.constrain_positive), (".*deg_free", self.constrain_fixed)] + # }, + # "laplace": True + # }, "Student_t_small_var": { "model": GPy.likelihoods.StudentT(deg_free=self.deg_free, sigma2=self.var), "grad_params": { @@ -253,7 +257,7 @@ class TestNoiseModels(object): "link_f_constraints": [partial(self.constrain_bounded, lower=0, upper=1)], "laplace": True, "Y": self.binary_Y, - "ep": False, # FIXME: Should be True when we have it working again + "ep": True, # FIXME: Should be True when we have it working again "variational_expectations": True }, "Exponential_default": { @@ -561,18 +565,20 @@ class TestNoiseModels(object): print("\n{}".format(inspect.stack()[0][3])) np.random.seed(111) #Normalize - Y = Y/Y.max() - + # Y = Y/Y.max() + white_var = 1e-4 kernel = GPy.kern.RBF(X.shape[1]) + GPy.kern.White(X.shape[1]) laplace_likelihood = GPy.inference.latent_function_inference.Laplace() m = GPy.core.GP(X.copy(), Y.copy(), kernel, likelihood=model, Y_metadata=Y_metadata, inference_method=laplace_likelihood) - m.randomize() + m.kern.white.constrain_fixed(white_var) #Set constraints for constrain_param, constraint in constraints: constraint(constrain_param, m) + m.randomize() + #Set params for param_num in range(len(param_names)): name = param_names[param_num] @@ -590,8 +596,8 @@ class TestNoiseModels(object): def t_ep_fit_rbf_white(self, model, X, Y, f, Y_metadata, step, param_vals, param_names, constraints): print("\n{}".format(inspect.stack()[0][3])) #Normalize - Y = Y/Y.max() - white_var = 1e-6 + # Y = Y/Y.max() + white_var = 1e-4 kernel = GPy.kern.RBF(X.shape[1]) + GPy.kern.White(X.shape[1]) ep_inf = GPy.inference.latent_function_inference.EP() diff --git a/GPy/testing/observable_tests.py b/GPy/testing/observable_tests.py deleted file mode 100644 index 84059d98..00000000 --- a/GPy/testing/observable_tests.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) 2014, Max Zwiessele -# Licensed under the BSD 3-clause license (see LICENSE.txt) -import unittest -from GPy.core.parameterization.parameterized import Parameterized -from GPy.core.parameterization.param import Param -import numpy - -# One trigger in init -_trigger_start = -1 - -class ParamTestParent(Parameterized): - parent_changed_count = _trigger_start - def parameters_changed(self): - self.parent_changed_count += 1 - -class ParameterizedTest(Parameterized): - # One trigger after initialization - params_changed_count = _trigger_start - def parameters_changed(self): - self.params_changed_count += 1 - -class Test(unittest.TestCase): - - def setUp(self): - self.parent = ParamTestParent('test parent') - self.par = ParameterizedTest('test model') - self.par2 = ParameterizedTest('test model 2') - self.p = Param('test parameter', numpy.random.normal(1,2,(10,3))) - - self.par.link_parameter(self.p) - self.par.link_parameter(Param('test1', numpy.random.normal(0,1,(1,)))) - self.par.link_parameter(Param('test2', numpy.random.normal(0,1,(1,)))) - - self.par2.link_parameter(Param('par2 test1', numpy.random.normal(0,1,(1,)))) - self.par2.link_parameter(Param('par2 test2', numpy.random.normal(0,1,(1,)))) - - self.parent.link_parameter(self.par) - self.parent.link_parameter(self.par2) - - self._observer_triggered = None - self._trigger_count = 0 - self._first = None - self._second = None - - def _trigger(self, me, which): - self._observer_triggered = which - self._trigger_count += 1 - if self._first is not None: - self._second = self._trigger - else: - self._first = self._trigger - - def _trigger_priority(self, me, which): - if self._first is not None: - self._second = self._trigger_priority - else: - self._first = self._trigger_priority - - def test_observable(self): - self.par.add_observer(self, self._trigger, -1) - self.assertEqual(self.par.params_changed_count, 0, 'no params changed yet') - self.assertEqual(self.par.params_changed_count, self.parent.parent_changed_count, 'parent should be triggered as often as param') - - self.p[0,1] = 3 # trigger observers - self.assertIs(self._observer_triggered, self.p, 'observer should have triggered') - self.assertEqual(self._trigger_count, 1, 'observer should have triggered once') - self.assertEqual(self.par.params_changed_count, 1, 'params changed once') - self.assertEqual(self.par.params_changed_count, self.parent.parent_changed_count, 'parent should be triggered as often as param') - - self.par.remove_observer(self) - self.p[0,1] = 4 - self.assertIs(self._observer_triggered, self.p, 'observer should not have triggered') - self.assertEqual(self._trigger_count, 1, 'observer should have triggered once') - self.assertEqual(self.par.params_changed_count, 2, 'params changed second') - self.assertEqual(self.par.params_changed_count, self.parent.parent_changed_count, 'parent should be triggered as often as param') - - self.par.add_observer(self, self._trigger, -1) - self.p[0,1] = 4 - self.assertIs(self._observer_triggered, self.p, 'observer should have triggered') - self.assertEqual(self._trigger_count, 2, 'observer should have triggered once') - self.assertEqual(self.par.params_changed_count, 3, 'params changed second') - self.assertEqual(self.par.params_changed_count, self.parent.parent_changed_count, 'parent should be triggered as often as param') - - self.par.remove_observer(self, self._trigger) - self.p[0,1] = 3 - self.assertIs(self._observer_triggered, self.p, 'observer should not have triggered') - self.assertEqual(self._trigger_count, 2, 'observer should have triggered once') - self.assertEqual(self.par.params_changed_count, 4, 'params changed second') - self.assertEqual(self.par.params_changed_count, self.parent.parent_changed_count, 'parent should be triggered as often as param') - - def test_set_params(self): - self.assertEqual(self.par.params_changed_count, 0, 'no params changed yet') - self.par.param_array[:] = 1 - self.par._trigger_params_changed() - self.assertEqual(self.par.params_changed_count, 1, 'now params changed') - self.assertEqual(self.parent.parent_changed_count, self.par.params_changed_count) - - self.par.param_array[:] = 2 - self.par._trigger_params_changed() - self.assertEqual(self.par.params_changed_count, 2, 'now params changed') - self.assertEqual(self.parent.parent_changed_count, self.par.params_changed_count) - - - def test_priority_notify(self): - self.assertEqual(self.par.params_changed_count, 0) - self.par.notify_observers(0, None) - self.assertEqual(self.par.params_changed_count, 1) - self.assertEqual(self.parent.parent_changed_count, self.par.params_changed_count) - - self.par.notify_observers(0, -numpy.inf) - self.assertEqual(self.par.params_changed_count, 2) - self.assertEqual(self.parent.parent_changed_count, 1) - - def test_priority(self): - self.par.add_observer(self, self._trigger, -1) - self.par.add_observer(self, self._trigger_priority, 0) - self.par.notify_observers(0) - self.assertEqual(self._first, self._trigger_priority, 'priority should be first') - self.assertEqual(self._second, self._trigger, 'priority should be first') - - self.par.remove_observer(self) - self._first = self._second = None - - self.par.add_observer(self, self._trigger, 1) - self.par.add_observer(self, self._trigger_priority, 0) - self.par.notify_observers(0) - self.assertEqual(self._first, self._trigger, 'priority should be second') - self.assertEqual(self._second, self._trigger_priority, 'priority should be second') - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() diff --git a/GPy/testing/parameterized_tests.py b/GPy/testing/parameterized_tests.py deleted file mode 100644 index 762a82a1..00000000 --- a/GPy/testing/parameterized_tests.py +++ /dev/null @@ -1,264 +0,0 @@ -''' -Created on Feb 13, 2014 - -@author: maxzwiessele -''' -import unittest -import GPy -import numpy as np -from GPy.core.parameterization.parameter_core import HierarchyError -from GPy.core.parameterization.observable_array import ObsAr -from GPy.core.parameterization.transformations import NegativeLogexp, Logistic -from GPy.core.parameterization.parameterized import Parameterized -from GPy.core.parameterization.param import Param -from GPy.core.parameterization.index_operations import ParameterIndexOperations -from functools import reduce - -class ArrayCoreTest(unittest.TestCase): - def setUp(self): - self.X = np.random.normal(1,1, size=(100,10)) - self.obsX = ObsAr(self.X) - - def test_init(self): - X = ObsAr(self.X) - X2 = ObsAr(X) - self.assertIs(X, X2, "no new Observable array, when Observable is given") - - def test_slice(self): - t1 = self.X[2:78] - t2 = self.obsX[2:78] - self.assertListEqual(t1.tolist(), t2.tolist(), "Slicing should be the exact same, as in ndarray") - -class ParameterizedTest(unittest.TestCase): - - def setUp(self): - self.rbf = GPy.kern.RBF(20) - self.white = GPy.kern.White(1) - from GPy.core.parameterization import Param - from GPy.core.parameterization.transformations import Logistic - self.param = Param('param', np.random.uniform(0,1,(10,5)), Logistic(0, 1)) - - self.test1 = GPy.core.Parameterized("test model") - self.test1.param = self.param - self.test1.kern = self.rbf+self.white - self.test1.link_parameter(self.test1.kern) - self.test1.link_parameter(self.param, 0) - - # print self.test1: - #============================================================================= - # test_model. | Value | Constraint | Prior | Tied to - # param | (25L, 2L) | {0.0,1.0} | | - # add.rbf.variance | 1.0 | 0.0,1.0 +ve | | - # add.rbf.lengthscale | 1.0 | 0.0,1.0 +ve | | - # add.white.variance | 1.0 | 0.0,1.0 +ve | | - #============================================================================= - - x = np.linspace(-2,6,4)[:,None] - y = np.sin(x) - self.testmodel = GPy.models.GPRegression(x,y) - # print self.testmodel: - #============================================================================= - # GP_regression. | Value | Constraint | Prior | Tied to - # rbf.variance | 1.0 | +ve | | - # rbf.lengthscale | 1.0 | +ve | | - # Gaussian_noise.variance | 1.0 | +ve | | - #============================================================================= - - def test_add_parameter(self): - self.assertEquals(self.rbf._parent_index_, 0) - self.assertEquals(self.white._parent_index_, 1) - self.assertEquals(self.param._parent_index_, 0) - pass - - def test_fixes(self): - self.white.fix(warning=False) - self.test1.unlink_parameter(self.param) - self.assertTrue(self.test1._has_fixes()) - from GPy.core.parameterization.transformations import FIXED, UNFIXED - self.assertListEqual(self.test1._fixes_.tolist(),[UNFIXED,UNFIXED,FIXED]) - self.test1.kern.link_parameter(self.white, 0) - self.assertListEqual(self.test1._fixes_.tolist(),[FIXED,UNFIXED,UNFIXED]) - self.test1.kern.rbf.fix() - self.assertListEqual(self.test1._fixes_.tolist(),[FIXED]*3) - self.test1.fix() - self.assertTrue(self.test1.is_fixed) - self.assertListEqual(self.test1._fixes_.tolist(),[FIXED]*self.test1.size) - - def test_remove_parameter(self): - from GPy.core.parameterization.transformations import FIXED, UNFIXED, __fixed__, Logexp - self.white.fix() - self.test1.kern.unlink_parameter(self.white) - self.assertIs(self.test1._fixes_,None) - - self.assertIsInstance(self.white.constraints, ParameterIndexOperations) - self.assertListEqual(self.white._fixes_.tolist(), [FIXED]) - self.assertIs(self.test1.constraints, self.rbf.constraints._param_index_ops) - self.assertIs(self.test1.constraints, self.param.constraints._param_index_ops) - - self.test1.link_parameter(self.white, 0) - self.assertIs(self.test1.constraints, self.white.constraints._param_index_ops) - self.assertIs(self.test1.constraints, self.rbf.constraints._param_index_ops) - self.assertIs(self.test1.constraints, self.param.constraints._param_index_ops) - self.assertListEqual(self.test1.constraints[__fixed__].tolist(), [0]) - self.assertIs(self.white._fixes_,None) - self.assertListEqual(self.test1._fixes_.tolist(),[FIXED] + [UNFIXED] * 52) - - self.test1.unlink_parameter(self.white) - self.assertIs(self.test1._fixes_,None) - self.assertListEqual(self.white._fixes_.tolist(), [FIXED]) - self.assertIs(self.test1.constraints, self.rbf.constraints._param_index_ops) - self.assertIs(self.test1.constraints, self.param.constraints._param_index_ops) - self.assertListEqual(self.test1.constraints[Logexp()].tolist(), list(range(self.param.size, self.param.size+self.rbf.size))) - - def test_remove_parameter_param_array_grad_array(self): - val = self.test1.kern.param_array.copy() - self.test1.kern.unlink_parameter(self.white) - self.assertListEqual(self.test1.kern.param_array.tolist(), val[:2].tolist()) - - def test_add_parameter_already_in_hirarchy(self): - self.assertRaises(HierarchyError, self.test1.link_parameter, self.white.parameters[0]) - - def test_default_constraints(self): - self.assertIs(self.rbf.variance.constraints._param_index_ops, self.rbf.constraints._param_index_ops) - self.assertIs(self.test1.constraints, self.rbf.constraints._param_index_ops) - self.assertListEqual(self.rbf.constraints.indices()[0].tolist(), list(range(2))) - from GPy.core.parameterization.transformations import Logexp - kern = self.test1.kern - self.test1.unlink_parameter(kern) - self.assertListEqual(kern.constraints[Logexp()].tolist(), list(range(3))) - - def test_constraints(self): - self.rbf.constrain(GPy.transformations.Square(), False) - self.assertListEqual(self.test1.constraints[GPy.transformations.Square()].tolist(), list(range(self.param.size, self.param.size+self.rbf.size))) - self.assertListEqual(self.test1.constraints[GPy.transformations.Logexp()].tolist(), [self.param.size+self.rbf.size]) - - self.test1.kern.unlink_parameter(self.rbf) - self.assertListEqual(self.test1.constraints[GPy.transformations.Square()].tolist(), []) - - def test_constraints_link_unlink(self): - self.test1.unlink_parameter(self.test1.kern) - self.test1.kern.rbf.unlink_parameter(self.test1.kern.rbf.lengthscale) - self.test1.kern.rbf.link_parameter(self.test1.kern.rbf.lengthscale) - self.test1.kern.rbf.unlink_parameter(self.test1.kern.rbf.lengthscale) - self.test1.link_parameter(self.test1.kern) - - def test_constraints_views(self): - self.assertEqual(self.white.constraints._offset, self.param.size+self.rbf.size) - self.assertEqual(self.rbf.constraints._offset, self.param.size) - self.assertEqual(self.param.constraints._offset, 0) - - def test_fixing_randomize(self): - self.white.fix(warning=True) - val = float(self.white.variance) - self.test1.randomize() - self.assertEqual(val, self.white.variance) - - def test_randomize(self): - ps = self.test1.param.view(np.ndarray).copy() - self.test1.param[2:5].fix() - self.test1.param.randomize() - self.assertFalse(np.all(ps==self.test1.param),str(ps)+str(self.test1.param)) - - def test_fixing_randomize_parameter_handling(self): - self.rbf.fix(warning=True) - val = float(self.rbf.variance) - self.test1.kern.randomize() - self.assertEqual(val, self.rbf.variance) - - def test_updates(self): - val = float(self.testmodel.log_likelihood()) - self.testmodel.update_model(False) - self.testmodel.kern.randomize() - self.testmodel.likelihood.randomize() - self.assertEqual(val, self.testmodel.log_likelihood()) - self.testmodel.update_model(True) - self.assertNotEqual(val, self.testmodel.log_likelihood()) - - def test_fixing_optimize(self): - self.testmodel.kern.lengthscale.fix() - val = float(self.testmodel.kern.lengthscale) - self.testmodel.randomize() - self.assertEqual(val, self.testmodel.kern.lengthscale) - - def test_add_parameter_in_hierarchy(self): - self.test1.kern.rbf.link_parameter(Param("NEW", np.random.rand(2), NegativeLogexp()), 1) - self.assertListEqual(self.test1.constraints[NegativeLogexp()].tolist(), list(range(self.param.size+1, self.param.size+1 + 2))) - self.assertListEqual(self.test1.constraints[GPy.transformations.Logistic(0,1)].tolist(), list(range(self.param.size))) - self.assertListEqual(self.test1.constraints[GPy.transformations.Logexp(0,1)].tolist(), np.r_[50, 53:55].tolist()) - - def test_regular_expression_misc(self): - self.testmodel.kern.lengthscale.fix() - val = float(self.testmodel.kern.lengthscale) - self.testmodel.randomize() - self.assertEqual(val, self.testmodel.kern.lengthscale) - - variances = self.testmodel['.*var'].values() - self.testmodel['.*var'].fix() - self.testmodel.randomize() - np.testing.assert_equal(variances, self.testmodel['.*var'].values()) - - def test_fix_unfix(self): - fixed = self.testmodel.kern.lengthscale.fix() - self.assertListEqual(fixed.tolist(), [0]) - unfixed = self.testmodel.kern.lengthscale.unfix() - self.testmodel.kern.lengthscale.constrain_positive() - self.assertListEqual(unfixed.tolist(), [0]) - - fixed = self.testmodel.kern.fix() - self.assertListEqual(fixed.tolist(), [0,1]) - unfixed = self.testmodel.kern.unfix() - self.assertListEqual(unfixed.tolist(), [0,1]) - - def test_constraints_in_init(self): - class Test(Parameterized): - def __init__(self, name=None, parameters=[], *a, **kw): - super(Test, self).__init__(name=name) - self.x = Param('x', np.random.uniform(0,1,(3,4))) - self.x[0].constrain_bounded(0,1) - self.link_parameter(self.x) - self.x[1].fix() - t = Test() - c = {Logistic(0,1): np.array([0, 1, 2, 3]), 'fixed': np.array([4, 5, 6, 7])} - np.testing.assert_equal(t.x.constraints[Logistic(0,1)], c[Logistic(0,1)]) - np.testing.assert_equal(t.x.constraints['fixed'], c['fixed']) - - def test_parameter_modify_in_init(self): - class TestLikelihood(Parameterized): - def __init__(self, param1 = 2., param2 = 3.): - super(TestLikelihood, self).__init__("TestLike") - self.p1 = Param('param1', param1) - self.p2 = Param('param2', param2) - - self.link_parameter(self.p1) - self.link_parameter(self.p2) - - self.p1.fix() - self.p1.unfix() - self.p2.constrain_negative() - self.p1.fix() - self.p2.constrain_positive() - self.p2.fix() - self.p2.constrain_positive() - - m = TestLikelihood() - print(m) - val = m.p1.values.copy() - self.assert_(m.p1.is_fixed) - self.assert_(m.constraints[GPy.constraints.Logexp()].tolist(), [1]) - m.randomize() - self.assertEqual(m.p1, val) - - def test_checkgrad(self): - assert(self.testmodel.kern.checkgrad()) - assert(self.testmodel.kern.lengthscale.checkgrad()) - assert(self.testmodel.likelihood.checkgrad()) - - def test_printing(self): - print(self.test1) - print(self.param) - print(self.test1['']) - print(self.testmodel.hierarchy_name(False)) - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.test_add_parameter'] - unittest.main() diff --git a/GPy/testing/pickle_tests.py b/GPy/testing/pickle_tests.py index 575496e1..4c3ecd52 100644 --- a/GPy/testing/pickle_tests.py +++ b/GPy/testing/pickle_tests.py @@ -7,23 +7,12 @@ import unittest, itertools #import cPickle as pickle import pickle import numpy as np -from GPy.core.parameterization.index_operations import ParameterIndexOperations,\ - ParameterIndexOperationsView import tempfile -from GPy.core.parameterization.param import Param -from GPy.core.parameterization.observable_array import ObsAr -from GPy.core.parameterization.priors import Gaussian -from GPy.kern.src.rbf import RBF -from GPy.kern.src.linear import Linear -from GPy.kern.src.static import Bias, White from GPy.examples.dimensionality_reduction import mrd_simulation from GPy.core.parameterization.variational import NormalPosterior from GPy.models.gp_regression import GPRegression -from functools import reduce -from GPy.util.caching import Cacher -import GPy -from pickle import PicklingError import GPy +from nose import SkipTest def toy_model(): X = np.linspace(0,1,50)[:, None] @@ -42,95 +31,13 @@ class ListDictTestCase(unittest.TestCase): np.testing.assert_array_equal(a1, a2) class Test(ListDictTestCase): + @SkipTest def test_load_pickle(self): import os m = GPy.load(os.path.join(os.path.abspath(os.path.split(__file__)[0]), 'pickle_test.pickle')) self.assertTrue(m.checkgrad()) self.assertEqual(m.log_likelihood(), -4.7351019830022087) - def test_parameter_index_operations(self): - pio = ParameterIndexOperations(dict(test1=np.array([4,3,1,6,4]), test2=np.r_[2:130])) - piov = ParameterIndexOperationsView(pio, 20, 250) - #py3 fix - #self.assertListDictEquals(dict(piov.items()), dict(piov.copy().iteritems())) - self.assertListDictEquals(dict(piov.items()), dict(piov.copy().items())) - - #py3 fix - #self.assertListDictEquals(dict(pio.iteritems()), dict(pio.copy().items())) - self.assertListDictEquals(dict(pio.items()), dict(pio.copy().items())) - - self.assertArrayListEquals(pio.copy().indices(), pio.indices()) - self.assertArrayListEquals(piov.copy().indices(), piov.indices()) - - with tempfile.TemporaryFile('w+b') as f: - pickle.dump(pio, f) - f.seek(0) - pio2 = pickle.load(f) - self.assertListDictEquals(pio._properties, pio2._properties) - - f = tempfile.TemporaryFile('w+b') - pickle.dump(piov, f) - f.seek(0) - pio2 = GPy.load(f) - f.close() - - #py3 fix - #self.assertListDictEquals(dict(piov.items()), dict(pio2.iteritems())) - self.assertListDictEquals(dict(piov.items()), dict(pio2.items())) - - def test_param(self): - param = Param('test', np.arange(4*2).reshape(4,2)) - param[0].constrain_positive() - param[1].fix() - param[2].set_prior(Gaussian(0,1)) - pcopy = param.copy() - self.assertListEqual(param.tolist(), pcopy.tolist()) - self.assertListEqual(str(param).split('\n'), str(pcopy).split('\n')) - self.assertIsNot(param, pcopy) - with tempfile.TemporaryFile('w+b') as f: - pickle.dump(param, f) - f.seek(0) - pcopy = pickle.load(f) - self.assertListEqual(param.tolist(), pcopy.tolist()) - self.assertSequenceEqual(str(param), str(pcopy)) - - def test_observable_array(self): - obs = ObsAr(np.arange(4*2).reshape(4,2)) - pcopy = obs.copy() - self.assertListEqual(obs.tolist(), pcopy.tolist()) - with tempfile.TemporaryFile('w+b') as f: - pickle.dump(obs, f) - f.seek(0) - pcopy = pickle.load(f) - self.assertListEqual(obs.tolist(), pcopy.tolist()) - self.assertSequenceEqual(str(obs), str(pcopy)) - - def test_parameterized(self): - par = RBF(1, active_dims=[1]) + Linear(2, active_dims=[0,2]) + Bias(3) + White(3) - par.gradient = 10 - par.randomize() - pcopy = par.copy() - self.assertIsInstance(pcopy.constraints, ParameterIndexOperations) - self.assertIsInstance(pcopy.rbf.constraints, ParameterIndexOperationsView) - self.assertIs(pcopy.constraints, pcopy.rbf.constraints._param_index_ops) - self.assertIs(pcopy.constraints, pcopy.rbf.lengthscale.constraints._param_index_ops) - self.assertIs(pcopy.constraints, pcopy.linear.constraints._param_index_ops) - self.assertListEqual(par.param_array.tolist(), pcopy.param_array.tolist()) - pcopy.gradient = 10 # gradient does not get copied anymore - self.assertListEqual(par.gradient_full.tolist(), pcopy.gradient_full.tolist()) - self.assertSequenceEqual(str(par), str(pcopy)) - self.assertIsNot(par.param_array, pcopy.param_array) - self.assertIsNot(par.gradient_full, pcopy.gradient_full) - with tempfile.TemporaryFile('w+b') as f: - par.pickle(f) - f.seek(0) - pcopy = pickle.load(f) - self.assertListEqual(par.param_array.tolist(), pcopy.param_array.tolist()) - pcopy.gradient = 10 - np.testing.assert_allclose(par.linear.gradient_full, pcopy.linear.gradient_full) - np.testing.assert_allclose(pcopy.linear.gradient_full, 10) - self.assertSequenceEqual(str(par), str(pcopy)) - def test_model(self): par = toy_model() pcopy = par.copy() diff --git a/GPy/testing/plotting_tests.py b/GPy/testing/plotting_tests.py index a680fa6d..f833faf0 100644 --- a/GPy/testing/plotting_tests.py +++ b/GPy/testing/plotting_tests.py @@ -27,19 +27,30 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #=============================================================================== +import matplotlib +from unittest.case import TestCase +matplotlib.use('agg') + import numpy as np import GPy, os from nose import SkipTest -from ..util.config import config -from ..plotting import change_plotting_library -import unittest +from GPy.util.config import config +from GPy.plotting import change_plotting_library, plotting_library + +class ConfigTest(TestCase): + def tearDown(self): + change_plotting_library('matplotlib') + + def test_change_plotting(self): + self.assertRaises(ValueError, change_plotting_library, 'not+in9names') + change_plotting_library('none') + self.assertRaises(RuntimeError, plotting_library) change_plotting_library('matplotlib') if config.get('plotting', 'library') != 'matplotlib': raise SkipTest("Matplotlib not installed, not testing plots") - try: from matplotlib import cbook, pyplot as plt from matplotlib.testing.compare import compare_images @@ -54,12 +65,12 @@ def _image_directories(): Compute the baseline and result image directories for testing *func*. Create the result directory if it doesn't exist. """ - basedir = os.path.splitext(os.path.relpath(os.path.abspath(__file__)))[0] + basedir = os.path.dirname(os.path.relpath(os.path.abspath(__file__))) #module_name = __init__.__module__ #mods = module_name.split('.') #basedir = os.path.join(*mods) - result_dir = os.path.join(basedir, 'testresult') - baseline_dir = os.path.join(basedir, 'baseline') + result_dir = os.path.join(basedir, 'testresult','.') + baseline_dir = os.path.join(basedir, 'baseline','.') if not os.path.exists(result_dir): cbook.mkdirs(result_dir) return baseline_dir, result_dir @@ -73,7 +84,7 @@ def _sequenceEqual(a, b): def _notFound(path): raise IOError('File {} not in baseline') -def _image_comparison(baseline_images, extensions=['pdf','svg','ong'], tol=11): +def _image_comparison(baseline_images, extensions=['pdf','svg','png'], tol=11): baseline_dir, result_dir = _image_directories() for num, base in zip(plt.get_fignums(), baseline_images): for ext in extensions: @@ -101,40 +112,42 @@ def test_figure(): matplotlib.rcParams.update(matplotlib.rcParamsDefault) matplotlib.rcParams[u'figure.figsize'] = (4,3) matplotlib.rcParams[u'text.usetex'] = False + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore") - ax, _ = pl().new_canvas(num=1) - def test_func(x): - return x[:, 0].reshape(3,3) - pl().imshow_interact(ax, test_func, extent=(-1,1,-1,1), resolution=3) + ax, _ = pl().new_canvas(num=1) + def test_func(x): + return x[:, 0].reshape(3,3) + pl().imshow_interact(ax, test_func, extent=(-1,1,-1,1), resolution=3) - ax, _ = pl().new_canvas() - def test_func_2(x): - y = x[:, 0].reshape(3,3) - anno = np.argmax(x, axis=1).reshape(3,3) - return y, anno - pl().annotation_heatmap_interact(ax, test_func_2, extent=(-1,1,-1,1), resolution=3) - pl().annotation_heatmap_interact(ax, test_func_2, extent=(-1,1,-1,1), resolution=3, imshow_kwargs=dict(interpolation='nearest')) + ax, _ = pl().new_canvas() + def test_func_2(x): + y = x[:, 0].reshape(3,3) + anno = np.argmax(x, axis=1).reshape(3,3) + return y, anno + + pl().annotation_heatmap_interact(ax, test_func_2, extent=(-1,1,-1,1), resolution=3) + pl().annotation_heatmap_interact(ax, test_func_2, extent=(-1,1,-1,1), resolution=3, imshow_kwargs=dict(interpolation='nearest')) - ax, _ = pl().new_canvas(figsize=(4,3)) - x = np.linspace(0,1,100) - y = [0,1,2] - array = np.array([.4,.5]) - cmap = matplotlib.colors.LinearSegmentedColormap.from_list('WhToColor', ('r', 'b'), N=array.size) - pl().fill_gradient(ax, x, y, facecolors=['r', 'g'], array=array, cmap=cmap) - try: - pl().show_canvas(ax, tight_layout=True) - except: - # macosx tight layout not stable - pl().show_canvas(ax, tight_layout=False) + ax, _ = pl().new_canvas(figsize=(4,3)) + x = np.linspace(0,1,100) + y = [0,1,2] + array = np.array([.4,.5]) + cmap = matplotlib.colors.LinearSegmentedColormap.from_list('WhToColor', ('r', 'b'), N=array.size) + + pl().fill_gradient(ax, x, y, facecolors=['r', 'g'], array=array, cmap=cmap) - ax, _ = pl().new_canvas(num=4, figsize=(4,3), projection='3d', xlabel='x', ylabel='y', zlabel='z', title='awsome title', xlim=(-1,1), ylim=(-1,1), zlim=(-3,3)) - z = 2-np.abs(np.linspace(-2,2,(100)))+1 - x, y = z*np.sin(np.linspace(-2*np.pi,2*np.pi,(100))), z*np.cos(np.linspace(-np.pi,np.pi,(100))) - pl().plot(ax, x, y, z, linewidth=2) - for do_test in _image_comparison( - baseline_images=['coverage_{}'.format(sub) for sub in ["imshow_interact",'annotation_interact','gradient','3d_plot',]], - extensions=extensions): - yield (do_test, ) + ax, _ = pl().new_canvas(num=4, figsize=(4,3), projection='3d', xlabel='x', ylabel='y', zlabel='z', title='awsome title', xlim=(-1,1), ylim=(-1,1), zlim=(-3,3)) + z = 2-np.abs(np.linspace(-2,2,(100)))+1 + x, y = z*np.sin(np.linspace(-2*np.pi,2*np.pi,(100))), z*np.cos(np.linspace(-np.pi,np.pi,(100))) + + pl().plot(ax, x, y, z, linewidth=2) + + for do_test in _image_comparison( + baseline_images=['coverage_{}'.format(sub) for sub in ["imshow_interact",'annotation_interact','gradient','3d_plot',]], + extensions=extensions): + yield (do_test, ) def test_kernel(): @@ -143,19 +156,22 @@ def test_kernel(): matplotlib.rcParams.update(matplotlib.rcParamsDefault) matplotlib.rcParams[u'figure.figsize'] = (4,3) matplotlib.rcParams[u'text.usetex'] = False - k = GPy.kern.RBF(5, ARD=True) * GPy.kern.Linear(3, active_dims=[0,2,4], ARD=True) + GPy.kern.Bias(2) - k.randomize() - k2 = GPy.kern.RBF(5, ARD=True) * GPy.kern.Linear(3, active_dims=[0,2,4], ARD=True) + GPy.kern.Bias(2) + GPy.kern.White(4) - k2[:-1] = k[:] - k2.plot_ARD(['rbf', 'linear', 'bias'], legend=True) - k2.plot_covariance(visible_dims=[0, 3], plot_limits=(-1,3)) - k2.plot_covariance(visible_dims=[2], plot_limits=(-1, 3)) - k2.plot_covariance(visible_dims=[2, 4], plot_limits=((-1, 0), (5, 3)), projection='3d') - k2.plot_covariance(visible_dims=[1, 4]) - for do_test in _image_comparison( - baseline_images=['kern_{}'.format(sub) for sub in ["ARD", 'cov_2d', 'cov_1d', 'cov_3d', 'cov_no_lim']], - extensions=extensions): - yield (do_test, ) + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + k = GPy.kern.RBF(5, ARD=True) * GPy.kern.Linear(3, active_dims=[0,2,4], ARD=True) + GPy.kern.Bias(2) + k.randomize() + k2 = GPy.kern.RBF(5, ARD=True) * GPy.kern.Linear(3, active_dims=[0,2,4], ARD=True) + GPy.kern.Bias(2) + GPy.kern.White(4) + k2[:-1] = k[:] + k2.plot_ARD(['rbf', 'linear', 'bias'], legend=True) + k2.plot_covariance(visible_dims=[0, 3], plot_limits=(-1,3)) + k2.plot_covariance(visible_dims=[2], plot_limits=(-1, 3)) + k2.plot_covariance(visible_dims=[2, 4], plot_limits=((-1, 0), (5, 3)), projection='3d') + k2.plot_covariance(visible_dims=[1, 4]) + for do_test in _image_comparison( + baseline_images=['kern_{}'.format(sub) for sub in ["ARD", 'cov_2d', 'cov_1d', 'cov_3d', 'cov_no_lim']], + extensions=extensions): + yield (do_test, ) def test_plot(): np.random.seed(111) @@ -163,18 +179,21 @@ def test_plot(): matplotlib.rcParams.update(matplotlib.rcParamsDefault) matplotlib.rcParams[u'figure.figsize'] = (4,3) matplotlib.rcParams[u'text.usetex'] = False - X = np.random.uniform(-2, 2, (40, 1)) - f = .2 * np.sin(1.3*X) + 1.3*np.cos(2*X) - Y = f+np.random.normal(0, .1, f.shape) - m = GPy.models.SparseGPRegression(X, Y, X_variance=np.ones_like(X)*[0.06]) - #m.optimize() - m.plot_data() - m.plot_mean() - m.plot_confidence() - m.plot_density() - m.plot_errorbars_trainset() - m.plot_samples() - m.plot_data_error() + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + X = np.random.uniform(-2, 2, (40, 1)) + f = .2 * np.sin(1.3*X) + 1.3*np.cos(2*X) + Y = f+np.random.normal(0, .1, f.shape) + m = GPy.models.SparseGPRegression(X, Y, X_variance=np.ones_like(X)*[0.06]) + #m.optimize() + m.plot_data() + m.plot_mean() + m.plot_confidence() + m.plot_density() + m.plot_errorbars_trainset() + m.plot_samples() + m.plot_data_error() for do_test in _image_comparison(baseline_images=['gp_{}'.format(sub) for sub in ["data", "mean", 'conf', 'density', 'out_error', @@ -292,58 +311,81 @@ def test_gplvm(): from ..examples.dimensionality_reduction import _simulate_matern from ..kern import RBF from ..models import GPLVM - np.random.seed(11111) - import matplotlib + np.random.seed(12345) matplotlib.rcParams.update(matplotlib.rcParamsDefault) matplotlib.rcParams[u'figure.figsize'] = (4,3) matplotlib.rcParams[u'text.usetex'] = False Q = 3 - _, _, Ylist = _simulate_matern(5, 1, 1, 100, num_inducing=5, plot_sim=False) - Y = Ylist[0] - k = RBF(Q, ARD=True) # + kern.white(Q, _np.exp(-2)) # + kern.bias(Q) + # Define dataset + N = 10 + k1 = GPy.kern.RBF(5, variance=1, lengthscale=1./np.random.dirichlet(np.r_[10,10,10,0.1,0.1]), ARD=True) + k2 = GPy.kern.RBF(5, variance=1, lengthscale=1./np.random.dirichlet(np.r_[10,0.1,10,0.1,10]), ARD=True) + k3 = GPy.kern.RBF(5, variance=1, lengthscale=1./np.random.dirichlet(np.r_[0.1,0.1,10,10,10]), ARD=True) + X = np.random.normal(0, 1, (N, 5)) + A = np.random.multivariate_normal(np.zeros(N), k1.K(X), Q).T + B = np.random.multivariate_normal(np.zeros(N), k2.K(X), Q).T + C = np.random.multivariate_normal(np.zeros(N), k3.K(X), Q).T + + Y = np.vstack((A,B,C)) + labels = np.hstack((np.zeros(A.shape[0]), np.ones(B.shape[0]), np.ones(C.shape[0])*2)) + + k = RBF(Q, ARD=True, lengthscale=2) # + kern.white(Q, _np.exp(-2)) # + kern.bias(Q) m = GPLVM(Y, Q, init="PCA", kernel=k) - m.likelihood.variance = .1 + m.kern.lengthscale[:] = [1./.3, 1./.1, 1./.7] + m.likelihood.variance = .001 #m.optimize(messages=0) - labels = np.random.multinomial(1, np.random.dirichlet([.3333333, .3333333, .3333333]), size=(m.Y.shape[0])).nonzero()[1] np.random.seed(111) - m.plot_latent() + m.plot_latent(labels=labels) np.random.seed(111) m.plot_scatter(projection='3d', labels=labels) np.random.seed(111) m.plot_magnification(labels=labels) - m.plot_steepest_gradient_map(resolution=7) - for do_test in _image_comparison(baseline_images=['gplvm_{}'.format(sub) for sub in ["latent", "latent_3d", "magnification", 'gradient']], extensions=extensions): + m.plot_steepest_gradient_map(resolution=10, data_labels=labels) + for do_test in _image_comparison(baseline_images=['gplvm_{}'.format(sub) for sub in ["latent", "latent_3d", "magnification", 'gradient']], + extensions=extensions, + tol=12): yield (do_test, ) def test_bayesian_gplvm(): from ..examples.dimensionality_reduction import _simulate_matern from ..kern import RBF from ..models import BayesianGPLVM - import matplotlib + np.random.seed(12345) matplotlib.rcParams.update(matplotlib.rcParamsDefault) matplotlib.rcParams[u'figure.figsize'] = (4,3) matplotlib.rcParams[u'text.usetex'] = False - np.random.seed(111) Q = 3 - _, _, Ylist = _simulate_matern(5, 1, 1, 100, num_inducing=5, plot_sim=False) - Y = Ylist[0] - k = RBF(Q, ARD=True) # + kern.white(Q, _np.exp(-2)) # + kern.bias(Q) - # k = kern.RBF(Q, ARD=True, lengthscale=10.) + # Define dataset + N = 10 + k1 = GPy.kern.RBF(5, variance=1, lengthscale=1./np.random.dirichlet(np.r_[10,10,10,0.1,0.1]), ARD=True) + k2 = GPy.kern.RBF(5, variance=1, lengthscale=1./np.random.dirichlet(np.r_[10,0.1,10,0.1,10]), ARD=True) + k3 = GPy.kern.RBF(5, variance=1, lengthscale=1./np.random.dirichlet(np.r_[0.1,0.1,10,10,10]), ARD=True) + X = np.random.normal(0, 1, (N, 5)) + A = np.random.multivariate_normal(np.zeros(N), k1.K(X), Q).T + B = np.random.multivariate_normal(np.zeros(N), k2.K(X), Q).T + C = np.random.multivariate_normal(np.zeros(N), k3.K(X), Q).T + + Y = np.vstack((A,B,C)) + labels = np.hstack((np.zeros(A.shape[0]), np.ones(B.shape[0]), np.ones(C.shape[0])*2)) + + k = RBF(Q, ARD=True, lengthscale=2) # + kern.white(Q, _np.exp(-2)) # + kern.bias(Q) m = BayesianGPLVM(Y, Q, init="PCA", kernel=k) - m.likelihood.variance = .1 + m.kern.lengthscale[:] = [1./.3, 1./.1, 1./.7] + m.likelihood.variance = .001 #m.optimize(messages=0) - labels = np.random.multinomial(1, np.random.dirichlet([.3333333, .3333333, .3333333]), size=(m.Y.shape[0])).nonzero()[1] np.random.seed(111) m.plot_inducing(projection='2d') np.random.seed(111) m.plot_inducing(projection='3d') np.random.seed(111) - m.plot_scatter(projection='3d') + m.plot_latent(projection='2d', labels=labels) + np.random.seed(111) + m.plot_scatter(projection='3d', labels=labels) np.random.seed(111) m.plot_magnification(labels=labels) np.random.seed(111) - m.plot_steepest_gradient_map(resolution=7) - for do_test in _image_comparison(baseline_images=['bayesian_gplvm_{}'.format(sub) for sub in ["inducing", "inducing_3d", "latent_3d", "magnification", 'gradient']], extensions=extensions): + m.plot_steepest_gradient_map(resolution=10, data_labels=labels) + for do_test in _image_comparison(baseline_images=['bayesian_gplvm_{}'.format(sub) for sub in ["inducing", "inducing_3d", "latent", "latent_3d", "magnification", 'gradient']], extensions=extensions): yield (do_test, ) if __name__ == '__main__': diff --git a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_gradient.png b/GPy/testing/plotting_tests/baseline/bayesian_gplvm_gradient.png deleted file mode 100644 index ccc72002..00000000 Binary files a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_gradient.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_inducing.png b/GPy/testing/plotting_tests/baseline/bayesian_gplvm_inducing.png deleted file mode 100644 index 9ef57a9a..00000000 Binary files a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_inducing.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_inducing_3d.png b/GPy/testing/plotting_tests/baseline/bayesian_gplvm_inducing_3d.png deleted file mode 100644 index 6f46d423..00000000 Binary files a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_inducing_3d.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_latent_3d.png b/GPy/testing/plotting_tests/baseline/bayesian_gplvm_latent_3d.png deleted file mode 100644 index db81f483..00000000 Binary files a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_latent_3d.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_magnification.png b/GPy/testing/plotting_tests/baseline/bayesian_gplvm_magnification.png deleted file mode 100644 index ebb624a6..00000000 Binary files a/GPy/testing/plotting_tests/baseline/bayesian_gplvm_magnification.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/coverage_gradient.png b/GPy/testing/plotting_tests/baseline/coverage_gradient.png deleted file mode 100644 index de5fb4f3..00000000 Binary files a/GPy/testing/plotting_tests/baseline/coverage_gradient.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/gp_error.png b/GPy/testing/plotting_tests/baseline/gp_error.png deleted file mode 100644 index 38c65afc..00000000 Binary files a/GPy/testing/plotting_tests/baseline/gp_error.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/gplvm_gradient.png b/GPy/testing/plotting_tests/baseline/gplvm_gradient.png deleted file mode 100644 index 2dd49320..00000000 Binary files a/GPy/testing/plotting_tests/baseline/gplvm_gradient.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/gplvm_latent.png b/GPy/testing/plotting_tests/baseline/gplvm_latent.png deleted file mode 100644 index eb58a709..00000000 Binary files a/GPy/testing/plotting_tests/baseline/gplvm_latent.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/gplvm_latent_3d.png b/GPy/testing/plotting_tests/baseline/gplvm_latent_3d.png deleted file mode 100644 index da040371..00000000 Binary files a/GPy/testing/plotting_tests/baseline/gplvm_latent_3d.png and /dev/null differ diff --git a/GPy/testing/plotting_tests/baseline/gplvm_magnification.png b/GPy/testing/plotting_tests/baseline/gplvm_magnification.png deleted file mode 100644 index 2f09cf3c..00000000 Binary files a/GPy/testing/plotting_tests/baseline/gplvm_magnification.png and /dev/null differ diff --git a/GPy/util/__init__.py b/GPy/util/__init__.py index 6919f1a8..1c504f89 100644 --- a/GPy/util/__init__.py +++ b/GPy/util/__init__.py @@ -11,7 +11,6 @@ from . import mocap from . import decorators from . import classification from . import subarray_and_sorting -from . import caching from . import diag from . import initialization from . import multioutput diff --git a/GPy/util/caching.py b/GPy/util/caching.py deleted file mode 100644 index cbc1f3f1..00000000 --- a/GPy/util/caching.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright (c) 2012, GPy authors (see AUTHORS.txt). -# Licensed under the BSD 3-clause license (see LICENSE.txt) -from ..core.parameterization.observable import Observable -import collections, weakref -from functools import reduce -from pickle import PickleError - -class Cacher(object): - def __init__(self, operation, limit=5, ignore_args=(), force_kwargs=()): - """ - Parameters: - *********** - :param callable operation: function to cache - :param int limit: depth of cacher - :param [int] ignore_args: list of indices, pointing at arguments to ignore in *args of operation(*args). This includes self! - :param [str] force_kwargs: list of kwarg names (strings). If a kwarg with that name is given, the cacher will force recompute and wont cache anything. - :param int verbose: verbosity level. 0: no print outs, 1: casual print outs, 2: debug level print outs - """ - self.limit = int(limit) - self.ignore_args = ignore_args - self.force_kwargs = force_kwargs - self.operation = operation - self.order = collections.deque() - self.cached_inputs = {} # point from cache_ids to a list of [ind_ids], which where used in cache cache_id - - #======================================================================= - # point from each ind_id to [ref(obj), cache_ids] - # 0: a weak reference to the object itself - # 1: the cache_ids in which this ind_id is used (len will be how many times we have seen this ind_id) - self.cached_input_ids = {} - #======================================================================= - - self.cached_outputs = {} # point from cache_ids to outputs - self.inputs_changed = {} # point from cache_ids to bools - - def id(self, obj): - """returns the self.id of an object, to be used in caching individual self.ids""" - return hex(id(obj)) - - def combine_inputs(self, args, kw, ignore_args): - "Combines the args and kw in a unique way, such that ordering of kwargs does not lead to recompute" - inputs= args + tuple(c[1] for c in sorted(kw.items(), key=lambda x: x[0])) - # REMOVE the ignored arguments from input and PREVENT it from being checked!!! - return [a for i,a in enumerate(inputs) if i not in ignore_args] - - def prepare_cache_id(self, combined_args_kw): - "get the cacheid (conc. string of argument self.ids in order)" - cache_id = "".join(self.id(a) for a in combined_args_kw) - return cache_id - - def ensure_cache_length(self, cache_id): - "Ensures the cache is within its limits and has one place free" - if len(self.order) == self.limit: - # we have reached the limit, so lets release one element - cache_id = self.order.popleft() - combined_args_kw = self.cached_inputs[cache_id] - for ind in combined_args_kw: - if ind is not None: - ind_id = self.id(ind) - tmp = self.cached_input_ids.get(ind_id, None) - if tmp is not None: - ref, cache_ids = tmp - if len(cache_ids) == 1 and ref() is not None: - ref().remove_observer(self, self.on_cache_changed) - del self.cached_input_ids[ind_id] - else: - cache_ids.remove(cache_id) - self.cached_input_ids[ind_id] = [ref, cache_ids] - del self.cached_outputs[cache_id] - del self.inputs_changed[cache_id] - del self.cached_inputs[cache_id] - - def add_to_cache(self, cache_id, inputs, output): - """This adds cache_id to the cache, with inputs and output""" - self.inputs_changed[cache_id] = False - self.cached_outputs[cache_id] = output - self.order.append(cache_id) - self.cached_inputs[cache_id] = inputs - for a in inputs: - if a is not None and not isinstance(a, int): - ind_id = self.id(a) - v = self.cached_input_ids.get(ind_id, [weakref.ref(a), []]) - v[1].append(cache_id) - if len(v[1]) == 1: - a.add_observer(self, self.on_cache_changed) - self.cached_input_ids[ind_id] = v - - def __call__(self, *args, **kw): - """ - A wrapper function for self.operation, - """ - #======================================================================= - # !WARNING CACHE OFFSWITCH! - # return self.operation(*args, **kw) - #======================================================================= - - # 1: Check whether we have forced recompute arguments: - if len(self.force_kwargs) != 0: - for k in self.force_kwargs: - if k in kw and kw[k] is not None: - return self.operation(*args, **kw) - - # 2: prepare_cache_id and get the unique self.id string for this call - inputs = self.combine_inputs(args, kw, self.ignore_args) - cache_id = self.prepare_cache_id(inputs) - # 2: if anything is not cachable, we will just return the operation, without caching - if reduce(lambda a, b: a or (not (isinstance(b, Observable) or b is None or isinstance(b,int))), inputs, False): -# print 'WARNING: '+self.operation.__name__ + ' not cacheable!' -# print [not (isinstance(b, Observable)) for b in inputs] - return self.operation(*args, **kw) - # 3&4: check whether this cache_id has been cached, then has it changed? - try: - if(self.inputs_changed[cache_id]): - # 4: This happens, when elements have changed for this cache self.id - self.inputs_changed[cache_id] = False - self.cached_outputs[cache_id] = self.operation(*args, **kw) - except KeyError: - # 3: This is when we never saw this chache_id: - self.ensure_cache_length(cache_id) - self.add_to_cache(cache_id, inputs, self.operation(*args, **kw)) - except: - self.reset() - raise - # 5: We have seen this cache_id and it is cached: - return self.cached_outputs[cache_id] - - def on_cache_changed(self, direct, which=None): - """ - A callback funtion, which sets local flags when the elements of some cached inputs change - - this function gets 'hooked up' to the inputs when we cache them, and upon their elements being changed we update here. - """ - for what in [direct, which]: - if what is not None: - ind_id = self.id(what) - _, cache_ids = self.cached_input_ids.get(ind_id, [None, []]) - for cache_id in cache_ids: - self.inputs_changed[cache_id] = True - - def reset(self): - """ - Totally reset the cache - """ - [a().remove_observer(self, self.on_cache_changed) if (a() is not None) else None for [a, _] in self.cached_input_ids.values()] - self.cached_input_ids = {} - self.cached_outputs = {} - self.inputs_changed = {} - - def __deepcopy__(self, memo=None): - return Cacher(self.operation, self.limit, self.ignore_args, self.force_kwargs) - - def __getstate__(self, memo=None): - raise PickleError("Trying to pickle Cacher object with function {}, pickling functions not possible.".format(str(self.operation))) - - def __setstate__(self, memo=None): - raise PickleError("Trying to pickle Cacher object with function {}, pickling functions not possible.".format(str(self.operation))) - - @property - def __name__(self): - return self.operation.__name__ - -from functools import partial, update_wrapper - -class Cacher_wrap(object): - def __init__(self, f, limit, ignore_args, force_kwargs): - self.limit = limit - self.ignore_args = ignore_args - self.force_kwargs = force_kwargs - self.f = f - update_wrapper(self, self.f) - def __get__(self, obj, objtype=None): - return partial(self, obj) - def __call__(self, *args, **kwargs): - obj = args[0] - # import ipdb;ipdb.set_trace() - try: - caches = obj.__cachers - except AttributeError: - caches = obj.__cachers = {} - try: - cacher = caches[self.f] - except KeyError: - cacher = caches[self.f] = Cacher(self.f, self.limit, self.ignore_args, self.force_kwargs) - return cacher(*args, **kwargs) - -class Cache_this(object): - """ - A decorator which can be applied to bound methods in order to cache them - """ - def __init__(self, limit=5, ignore_args=(), force_kwargs=()): - self.limit = limit - self.ignore_args = ignore_args - self.force_args = force_kwargs - def __call__(self, f): - newf = Cacher_wrap(f, self.limit, self.ignore_args, self.force_args) - update_wrapper(newf, f) - return newf diff --git a/GPy/util/choleskies.py b/GPy/util/choleskies.py index e245d988..2676b6e6 100644 --- a/GPy/util/choleskies.py +++ b/GPy/util/choleskies.py @@ -25,8 +25,8 @@ def _flat_to_triang_pure(flat_mat): count = 0 for m in range(M): for mm in range(m+1): - ret[d,m, mm] = flat_mat[count, d]; - count = count+1 + ret[d,m, mm] = flat_mat[count, d]; + count = count+1 return ret def _flat_to_triang_cython(flat_mat): diff --git a/GPy/util/config.py b/GPy/util/config.py index c2b581f7..e47848a8 100644 --- a/GPy/util/config.py +++ b/GPy/util/config.py @@ -6,10 +6,12 @@ try: #Attempt Python 2 ConfigParser setup import ConfigParser config = ConfigParser.ConfigParser() + from ConfigParser import NoOptionError except ImportError: #Attempt Python 3 ConfigParser setup import configparser config = configparser.ConfigParser() + from configparser import NoOptionError # This is the default configuration file that always needs to be present. diff --git a/GPy/util/warping_functions.py b/GPy/util/warping_functions.py index b8b9a261..1617283c 100644 --- a/GPy/util/warping_functions.py +++ b/GPy/util/warping_functions.py @@ -3,7 +3,7 @@ import numpy as np from ..core.parameterization import Parameterized, Param -from ..core.parameterization.transformations import Logexp +from paramz.transformations import Logexp class WarpingFunction(Parameterized): diff --git a/MANIFEST.in b/MANIFEST.in index 91f053cd..be80e974 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,20 @@ -recursive-include doc *.cfg -include *.json +# Documentation: +include doc/source/conf.py +include doc/source/index.rst +include doc/source/tuto*.rst +include README.md +include README.rst + +# Data and config +recursive-include GPy *.json +include GPy/defaults.cfg +include GPy/installation.cfg + +# Cython recursive-include GPy *.c -recursive-include GPy *.so +recursive-include GPy *.h recursive-include GPy *.pyx -include GPy/testing/plotting_tests/baseline/*.png -include GPy/testing/pickle_test.pickle + +# Testing +include GPy/testing/baseline/*.png +#include GPy/testing/pickle_test.pickle diff --git a/README.md b/README.md index 7e277d6c..e415d58f 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,37 @@ The Gaussian processes framework in Python. -* [GPy homepage](http://sheffieldml.github.io/GPy/) -* [Tutorial notebooks](http://nbviewer.ipython.org/github/SheffieldML/notebook/blob/master/GPy/index.ipynb) -* [User mailing list](https://lists.shef.ac.uk/sympa/subscribe/gpy-users) -* [Developer documentation](http://gpy.readthedocs.org/en/devel/) -* [Unit tests (Travis-CI)](https://travis-ci.org/SheffieldML/GPy) +* GPy [homepage](http://sheffieldml.github.io/GPy/) +* Tutorial [notebooks](http://nbviewer.ipython.org/github/SheffieldML/notebook/blob/master/GPy/index.ipynb) +* User [mailing-list](https://lists.shef.ac.uk/sympa/subscribe/gpy-users) +* Developer [documentation](http://gpy.readthedocs.org/en/devel/) +* Travis-CI [unit-tests](https://travis-ci.org/SheffieldML/GPy) * [![licence](https://img.shields.io/badge/licence-BSD-blue.svg)](http://opensource.org/licenses/BSD-3-Clause) +## Updated Structure + +We have pulled the core parameterization out of GPy. It is a package called [paramz](https://github.com/sods/paramz) and is the pure gradient based model optimization. + +If you installed GPy with pip, just upgrade the package using: + + $ pip install --upgrade GPy + +If you have the developmental version of GPy (using the develop or -e option) just install the dependencies by running + + $ python setup.py develop + +again, in the GPy installation folder. + +A warning: This usually works, but sometimes `distutils/setuptools` opens a +whole can of worms here, specially when compiled extensions are involved. +If that is the case, it is best to clean the repo and reinstall. + ## Continuous integration | | Travis-CI | Codecov | RTFD | | ---: | :--: | :---: | :---: | -| **master:** | [![master](https://travis-ci.org/SheffieldML/GPy.svg?branch=master)](https://travis-ci.org/SheffieldML/GPy) | [![codecov.io master](http://codecov.io/github/SheffieldML/GPy/coverage.svg?branch=master)](http://codecov.io/github/SheffieldML/GPy?branch=master) | [![Documentation Status](https://readthedocs.org/projects/gpy/badge/?version=master)](http://gpy.readthedocs.org/en/master/) | -| **devel:** | [![devel](https://travis-ci.org/SheffieldML/GPy.svg?branch=devel)](https://travis-ci.org/SheffieldML/GPy) | [![codecov.io devel](http://codecov.io/github/SheffieldML/GPy/coverage.svg?branch=devel)](http://codecov.io/github/SheffieldML/GPy?branch=devel) | [![Documentation Status](https://readthedocs.org/projects/gpy/badge/?version=devel)](http://gpy.readthedocs.org/en/devel/) | +| **master:** | [![masterstat](https://travis-ci.org/SheffieldML/GPy.svg?branch=master)](https://travis-ci.org/SheffieldML/GPy) | [![covmaster](http://codecov.io/github/SheffieldML/GPy/coverage.svg?branch=master)](http://codecov.io/github/SheffieldML/GPy?branch=master) | [![docmaster](https://readthedocs.org/projects/gpy/badge/?version=master)](http://gpy.readthedocs.org/en/master/) | +| **devel:** | [![develstat](https://travis-ci.org/SheffieldML/GPy.svg?branch=devel)](https://travis-ci.org/SheffieldML/GPy) | [![covdevel](http://codecov.io/github/SheffieldML/GPy/coverage.svg?branch=devel)](http://codecov.io/github/SheffieldML/GPy?branch=devel) | [![docdevel](https://readthedocs.org/projects/gpy/badge/?version=devel)](http://gpy.readthedocs.org/en/devel/) | ## Supported Platforms: @@ -25,7 +43,6 @@ The Gaussian processes framework in Python. Python 2.7, 3.3 and higher - ## Citation @Misc{gpy2014, diff --git a/setup.cfg b/setup.cfg index d7da653a..0b515a11 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.8.8 +current_version = 0.9.4 tag = True commit = True @@ -10,3 +10,4 @@ universal = 1 [upload_docs] upload-dir = doc/build/html + diff --git a/setup.py b/setup.py index 82190c12..fee4a10d 100644 --- a/setup.py +++ b/setup.py @@ -49,11 +49,16 @@ def read_to_rst(fname): try: import pypandoc rstname = "{}.{}".format(os.path.splitext(fname)[0], 'rst') - return pypandoc.convert(read(fname), 'rst', format='md') + pypandoc.convert(read(fname), 'rst', format='md', outputfile=rstname) + with open(rstname, 'r') as f: + rststr = f.read() + return rststr #return read(rstname) except ImportError: return read(fname) +desc = read_to_rst('README.md') + version_dummy = {} exec(read('GPy/__version__.py'), version_dummy) __version__ = version_dummy['__version__'] @@ -68,8 +73,8 @@ if ismac(): compile_flags = [ '-O3', ] link_args = [] else: - compile_flags = [ '-fopenmp', '-O3', ] - link_args = ['-lgomp'] + compile_flags = [ '-fopenmp', '-O3'] + link_args = ['-lgomp' ] ext_mods = [Extension(name='GPy.kern.src.stationary_cython', sources=['GPy/kern/src/stationary_cython.c', @@ -123,20 +128,31 @@ setup(name = 'GPy', "GPy.plotting.plotly_dep", ], package_dir={'GPy': 'GPy'}, - package_data = {'GPy': ['defaults.cfg', 'installation.cfg', - 'util/data_resources.json', - 'util/football_teams.json', - 'testing/plotting_tests/baseline/*.png' - ]}, - data_files=[('GPy/testing/plotting_tests/baseline', 'testing/plotting_tests/baseline/*.png'), - ('GPy/testing/', 'GPy/testing/pickle_test.pickle'), - ], + #package_data = {'GPy': ['defaults.cfg', 'installation.cfg', + # 'util/data_resources.json', + # 'util/football_teams.json', + # 'testing/plotting_tests/baseline/*.png' + # ]}, + #data_files=[('GPy/testing/plotting_tests/baseline', 'testing/plotting_tests/baseline/*.png'), + # ('GPy/testing/', 'GPy/testing/pickle_test.pickle'), + # ], include_package_data = True, py_modules = ['GPy.__init__'], test_suite = 'GPy.testing', - long_description=read_to_rst('README.md'), - install_requires=['numpy>=1.7', 'scipy>=0.16', 'six'], - extras_require = {'docs':['matplotlib >=1.3','Sphinx','IPython'],'optional':['mpi4py']}, + long_description=desc, + install_requires=['numpy>=1.7', 'scipy>=0.16', 'six', 'paramz'], + extras_require = {'docs':['sphinx'], + 'optional':['mpi4py', + 'ipython>=4.0.0', + ], + 'plotting':['matplotlib >= 1.3', + 'plotly >= 1.8.6'], + 'notebook':['jupyter_client >= 4.0.6', + 'ipywidgets >= 4.0.3', + 'ipykernel >= 4.1.0', + 'notebook >= 4.0.5', + ], + }, classifiers=['License :: OSI Approved :: BSD License', 'Natural Language :: English', 'Operating System :: MacOS :: MacOS X', @@ -173,4 +189,4 @@ if not os.path.exists(user_file): tmp = l.read() f.write(tmp) else: - print("GPy: User configuration file at location {}".format(user_file)) \ No newline at end of file + print("GPy: User configuration file at location {}".format(user_file)) diff --git a/travis_tests.py b/travis_tests.py index d034fcfd..79a75f54 100644 --- a/travis_tests.py +++ b/travis_tests.py @@ -30,7 +30,6 @@ #=============================================================================== #!/usr/bin/env python - import matplotlib matplotlib.use('agg')