diff --git a/.travis.yml b/.travis.yml index b3ebd70..1f29369 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ python: cache: pip install: - pip install -U pip wheel - - pip install tensorflow==1.0.1 + - pip install tensorflow==1.3.0rc0 - pip install --process-dependency-links . - pip install .[test] - pip install codecov diff --git a/GPflowOpt/__init__.py b/GPflowOpt/__init__.py index 89b71d8..cb9af09 100644 --- a/GPflowOpt/__init__.py +++ b/GPflowOpt/__init__.py @@ -20,4 +20,6 @@ from . import transforms from . import scaling from . import objective +from . import models from . import pareto +from . import models diff --git a/GPflowOpt/acquisition/acquisition.py b/GPflowOpt/acquisition/acquisition.py index 907ef8a..aa48e7f 100644 --- a/GPflowOpt/acquisition/acquisition.py +++ b/GPflowOpt/acquisition/acquisition.py @@ -14,8 +14,10 @@ from ..scaling import DataScaler from ..domain import UnitCube +from ..models import ModelWrapper from GPflow.param import Parameterized, AutoFlow, ParamList +from GPflow.model import Model from GPflow import settings import numpy as np @@ -48,7 +50,9 @@ def __init__(self, models=[], optimize_restarts=5): :param optimize_restarts: number of optimization restarts to use when training the models """ super(Acquisition, self).__init__() - self._models = ParamList([DataScaler(m) for m in np.atleast_1d(models).tolist()]) + models = np.atleast_1d(models) + assert all(isinstance(model, (Model, ModelWrapper))for model in models) + self._models = ParamList([DataScaler(m) for m in models]) self._default_params = list(map(lambda m: m.get_free_state(), self._models)) assert (optimize_restarts >= 0) diff --git a/GPflowOpt/acquisition/ei.py b/GPflowOpt/acquisition/ei.py index 90507cb..2c9ce87 100644 --- a/GPflowOpt/acquisition/ei.py +++ b/GPflowOpt/acquisition/ei.py @@ -57,7 +57,6 @@ def __init__(self, model): :param model: GPflow model (single output) representing our belief of the objective """ super(ExpectedImprovement, self).__init__(model) - assert (isinstance(model, Model)) self.fmin = DataHolder(np.zeros(1)) self.setup() diff --git a/GPflowOpt/models.py b/GPflowOpt/models.py new file mode 100644 index 0000000..9046030 --- /dev/null +++ b/GPflowOpt/models.py @@ -0,0 +1,167 @@ +# Copyright 2017 Joachim van der Herten, Nicolas Knudde +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .tf_wraps import rowwise_gradients + +from GPflow.param import Parameterized, AutoFlow +from GPflow.model import Model, GPModel +from GPflow.likelihoods import Gaussian +from GPflow import settings + +import tensorflow as tf + +float_type = settings.dtypes.float_type + + +class ModelWrapper(Parameterized): + """ + Class for fast implementation of a wrapper for models defined in GPflow. Once wrapped, all lookups for attributes + which are not found in the wrapper class are automatically forwarded to the wrapped model. + + To influence the I/O of methods on the wrapped class, simply implement the method in the wrapper and call the + appropriate methods on the wrapped class. Specific logic is included to make sure that if AutoFlow methods are + influenced following this pattern, the original AF storage (if existing) is unaffected and a new storage is added + to the subclass. + """ + def __init__(self, model): + """ + :param model: model to be wrapped + """ + super(ModelWrapper, self).__init__() + + assert isinstance(model, (Model, ModelWrapper)) + #: Wrapped model + self.wrapped = model + + def __getattr__(self, item): + """ + If an attribute is not found in this class, it is searched in the wrapped model + """ + # Exception for AF storages, if a method with the same name exists in this class, do not find the cache + # in the wrapped model. + if item.endswith('_AF_storage'): + method = item[1:].rstrip('_AF_storage') + if method in dir(self): + raise AttributeError("{0} has no attribute {1}".format(self.__class__.__name__, item)) + return getattr(self.wrapped, item) + + def __setattr__(self, key, value): + """ + 1) If setting :attr:`wrapped` attribute, point parent to this object (the datascaler). + 2) If setting the recompilation attribute, always do this on the wrapped class. + """ + if key is 'wrapped': + object.__setattr__(self, key, value) + value.__setattr__('_parent', self) + return + + try: + # If attribute is in this object, set it. Test by using getattribute instead of hasattr to avoid lookup in + # wrapped object. + self.__getattribute__(key) + super(ModelWrapper, self).__setattr__(key, value) + except AttributeError: + # Attribute is not in wrapper. + # In case no wrapped object is set yet (e.g. constructor), set in wrapper. + if 'wrapped' not in self.__dict__: + super(ModelWrapper, self).__setattr__(key, value) + return + + if hasattr(self, key): + # Now use hasattr, we know getattribute already failed so if it returns true, it must be in the wrapped + # object. Hasattr is called on self instead of self.wrapped to account for the different handling of + # AF storages. + # Prefer setting the attribute in the wrapped object if exists. + setattr(self.wrapped, key, value) + else: + # If not, set in wrapper nonetheless. + super(ModelWrapper, self).__setattr__(key, value) + + def __eq__(self, other): + return self.wrapped == other + + def __str__(self, prepend=''): + return self.wrapped.__str__(prepend) + + +class MGP(ModelWrapper): + """ + Marginalisation of the hyperparameters during evaluation time using a Laplace Approximation + Key reference: + + :: + + @article{Garnett:2013, + title={Active learning of linear embeddings for Gaussian processes}, + author={Garnett, Roman and Osborne, Michael A and Hennig, Philipp}, + journal={arXiv preprint arXiv:1310.6740}, + year={2013} + } + """ + + def __init__(self, model): + assert isinstance(model, GPModel), "Object has to be a GP model" + assert isinstance(model.likelihood, Gaussian), "Likelihood has to be Gaussian" + super(MGP, self).__init__(model) + + def build_predict(self, fmean, fvar, theta): + h = tf.hessians(self.build_likelihood() + self.build_prior(), theta)[0] + L = tf.cholesky(-h) + + N = tf.shape(fmean)[0] + D = tf.shape(fmean)[1] + + fmeanf = tf.reshape(fmean, [N * D, 1]) # N*D x 1 + fvarf = tf.reshape(fvar, [N * D, 1]) # N*D x 1 + + Dfmean = rowwise_gradients(fmeanf, theta) # N*D x k + Dfvar = rowwise_gradients(fvarf, theta) # N*D x k + + tmp1 = tf.transpose(tf.matrix_triangular_solve(L, tf.transpose(Dfmean))) # N*D x k + tmp2 = tf.transpose(tf.matrix_triangular_solve(L, tf.transpose(Dfvar))) # N*D x k + return fmean, 4 / 3 * fvar + tf.reshape(tf.reduce_sum(tf.square(tmp1), axis=1), [N, D]) \ + + 1 / 3 / (fvar + 1E-3) * tf.reshape(tf.reduce_sum(tf.square(tmp2), axis=1), [N, D]) + + @AutoFlow((float_type, [None, None])) + def predict_f(self, Xnew): + """ + Compute the mean and variance of the latent function(s) at the points + Xnew. + """ + theta = self._predict_f_AF_storage['free_vars'] + fmean, fvar = self.wrapped.build_predict(Xnew) + return self.build_predict(fmean, fvar, theta) + + @AutoFlow((float_type, [None, None])) + def predict_y(self, Xnew): + """ + Compute the mean and variance of held-out data at the points Xnew + """ + theta = self._predict_y_AF_storage['free_vars'] + pred_f_mean, pred_f_var = self.wrapped.build_predict(Xnew) + fmean, fvar = self.wrapped.likelihood.predict_mean_and_var(pred_f_mean, pred_f_var) + return self.build_predict(fmean, fvar, theta) + + @AutoFlow((float_type, [None, None]), (float_type, [None, None])) + def predict_density(self, Xnew, Ynew): + """ + Compute the (log) density of the data Ynew at the points Xnew + + Note that this computes the log density of the data individually, + ignoring correlations between them. The result is a matrix the same + shape as Ynew containing the log densities. + """ + theta = self._predict_density_AF_storage['free_vars'] + pred_f_mean, pred_f_var = self.wrapped.build_predict(Xnew) + pred_f_mean, pred_f_var = self.build_predict(pred_f_mean, pred_f_var, theta) + return self.likelihood.predict_density(pred_f_mean, pred_f_var, Ynew) diff --git a/GPflowOpt/scaling.py b/GPflowOpt/scaling.py index baa4f08..141cb57 100644 --- a/GPflowOpt/scaling.py +++ b/GPflowOpt/scaling.py @@ -12,17 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -from GPflow.param import DataHolder, AutoFlow, Parameterized -from GPflow.model import Model, GPModel +from GPflow.param import DataHolder, AutoFlow +from GPflow.model import GPModel from GPflow import settings import numpy as np from .transforms import LinearTransform, DataTransform from .domain import UnitCube +from .models import ModelWrapper float_type = settings.dtypes.float_type -class DataScaler(GPModel): +class DataScaler(ModelWrapper): """ Model-wrapping class, primarily intended to assure the data in GPflow models is scaled. One DataScaler wraps one GPflow model, and can scale the input as well as the output data. By default, if any kind of object attribute @@ -59,13 +60,8 @@ def __init__(self, model, domain=None, normalize_Y=False): :param normalize_Y: (default: False) enable automatic scaling of output values to zero mean and unit variance. """ - # model sanity checks - assert (model is not None) - assert (isinstance(model, GPModel)) - self._parent = None - - # Wrap model - self.wrapped = model + # model sanity checks, slightly stronger conditions than the wrapper + super(DataScaler, self).__init__(model) # Initial configuration of the datascaler n_inputs = model.X.shape[1] @@ -74,34 +70,8 @@ def __init__(self, model, domain=None, normalize_Y=False): self._normalize_Y = normalize_Y self._output_transform = LinearTransform(np.ones(n_outputs), np.zeros(n_outputs)) - # The assignments in the constructor of GPModel take care of initial re-scaling of model data. - super(DataScaler, self).__init__(model.X.value, model.Y.value, None, None, 1, name=model.name+"_datascaler") - del self.kern - del self.mean_function - del self.likelihood - - def __getattr__(self, item): - """ - If an attribute is not found in this class, it is searched in the wrapped model - """ - return self.wrapped.__getattribute__(item) - - def __setattr__(self, key, value): - """ - If setting :attr:`wrapped` attribute, point parent to this object (the datascaler) - """ - if key is 'wrapped': - object.__setattr__(self, key, value) - value.__setattr__('_parent', self) - return - - super(DataScaler, self).__setattr__(key, value) - - def __eq__(self, other): - return self.wrapped == other - - def __str__(self, prepend=''): - return self.wrapped.__str__(prepend) + self.X = model.X.value + self.Y = model.Y.value @property def input_transform(self): @@ -216,6 +186,20 @@ def build_predict(self, Xnew, full_cov=False): f, var = self.wrapped.build_predict(self.input_transform.build_forward(Xnew), full_cov=full_cov) return self.output_transform.build_backward(f), self.output_transform.build_backward_variance(var) + @AutoFlow((float_type, [None, None])) + def predict_f(self, Xnew): + """ + Compute the mean and variance of held-out data at the points Xnew + """ + return self.build_predict(Xnew) + + @AutoFlow((float_type, [None, None])) + def predict_f_full_cov(self, Xnew): + """ + Compute the mean and variance of held-out data at the points Xnew + """ + return self.build_predict(Xnew, full_cov=True) + @AutoFlow((float_type, [None, None])) def predict_y(self, Xnew): """ @@ -230,6 +214,6 @@ def predict_density(self, Xnew, Ynew): """ Compute the (log) density of the data Ynew at the points Xnew """ - mu, var = self.build_predict(Xnew) + mu, var = self.wrapped.build_predict(self.input_transform.build_forward(Xnew)) Ys = self.output_transform.build_forward(Ynew) return self.likelihood.predict_density(mu, var, Ys) diff --git a/GPflowOpt/tf_wraps.py b/GPflowOpt/tf_wraps.py new file mode 100644 index 0000000..860dc8c --- /dev/null +++ b/GPflowOpt/tf_wraps.py @@ -0,0 +1,42 @@ +# Copyright 2017 Joachim van der Herten, Nicolas Knudde +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import tensorflow as tf +from GPflow import settings + +float_type = settings.dtypes.float_type + + +def rowwise_gradients(Y, X): + """ + For a 2D Tensor Y, compute the derivative of each columns w.r.t a 2D tensor X. + + This is done with while_loop, because of a known incompatibility between map_fn and gradients. + """ + num_rows = tf.shape(Y)[0] + num_feat = tf.shape(X)[0] + + def body(old_grads, row): + g = tf.expand_dims(tf.gradients(Y[row], X)[0], axis=0) + new_grads = tf.concat([old_grads, g], axis=0) + return new_grads, row + 1 + + def cond(_, row): + return tf.less(row, num_rows) + + shape_invariants = [tf.TensorShape([None, None]), tf.TensorShape([])] + grads, _ = tf.while_loop(cond, body, [tf.zeros([0, num_feat], float_type), tf.constant(0)], + shape_invariants=shape_invariants) + + return grads \ No newline at end of file diff --git a/doc/source/interfaces.rst b/doc/source/interfaces.rst index 6756e40..86b0b0f 100644 --- a/doc/source/interfaces.rst +++ b/doc/source/interfaces.rst @@ -36,3 +36,11 @@ Transform :special-members: .. autoclass:: GPflowOpt.transforms.DataTransform :special-members: + +ModelWrapper +------------ +.. automodule:: GPflowOpt.models + :special-members: +.. autoclass:: GPflowOpt.models.ModelWrapper + :members: + :special-members: diff --git a/doc/source/notebooks/mgp.ipynb b/doc/source/notebooks/mgp.ipynb new file mode 100644 index 0000000..e9d0b09 --- /dev/null +++ b/doc/source/notebooks/mgp.ipynb @@ -0,0 +1,168 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Marginal GP\n", + "*Nicolas Knudde*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook demonstrates the approximate marginalisation of the hyperparameters during testing time. A Laplace approximation is used here and is described in Garnett et. al. (2013)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "from matplotlib import rc\n", + "rc('text', usetex=True)\n", + "import matplotlib.pyplot as plt\n", + "\n", + "\n", + "import GPflow\n", + "import GPflowOpt\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we set up a test dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "np.random.seed(3)\n", + "x = np.random.rand(8, 1) * 8-4\n", + "y = np.cos(x) +np.random.randn(8,1)*0.1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We proceed by constructing a Marginal GP wrapper around a GPR object and defining some suitable priors on the hyperparameters. Eventually we optimise the model." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "name_MGP.name.kern.\u001b[1mlengthscales\u001b[0m transform:+ve prior:Ga([ 3.],[ 0.33333333])\n[ 1.47220582]\nname_MGP.name.kern.\u001b[1mvariance\u001b[0m transform:+ve prior:Ga([ 3.],[ 0.33333333])\n[ 0.75133993]\nname_MGP.name.likelihood.\u001b[1mvariance\u001b[0m transform:+ve prior:logN([ 0.],[ 30.])\n[ 0.01172159]\n\n" + ] + } + ], + "source": [ + "m = GPflowOpt.models.MGP(GPflow.gpr.GPR(x, y, GPflow.kernels.RBF(1, lengthscales=1, variance=1)))\n", + "m.kern.lengthscales.prior = GPflow.priors.Gamma(3, 1/3)\n", + "m.kern.variance.prior = GPflow.priors.Gamma(3, 1/3)\n", + "m.likelihood.variance.prior = GPflow.priors.LogNormal(0, 30)\n", + "m.optimize()\n", + "print(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we compare the prediction of the MGP and the regular GPR model." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluated\n" + ] + }, + { + "data": { + "image/png": "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\nAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "X = np.array(np.linspace(-4, 4, 100)[:, None])\n", + "fms, fvs = m.predict_f(X)\n", + "print('Evaluated')\n", + "\n", + "plt.figure(figsize=(10,7))\n", + "plt.scatter(x, y)\n", + "plt.plot(X.flatten(), fms.flatten(), color='b', label='GPR-MGP')\n", + "plt.fill_between(X.flatten(), fms.flatten() - 2 * np.sqrt(fvs.flatten()), fms.flatten() + 2 * np.sqrt(fvs.flatten()),\n", + " alpha=0.2, color='b')\n", + "\n", + "fm, fv = m.wrapped.predict_f(X)\n", + "plt.plot(X.flatten(), fm.flatten(), color='r', label='GPR')\n", + "plt.fill_between(X.flatten(), fm.flatten() - 2 * np.sqrt(fv.flatten()), fm.flatten() + 2 * np.sqrt(fv.flatten()),\n", + " alpha=0.2, color='r')\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3.0 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/testing/test_datascaler.py b/testing/test_datascaler.py index 0abe6fd..cd4a4ed 100644 --- a/testing/test_datascaler.py +++ b/testing/test_datascaler.py @@ -26,8 +26,6 @@ def test_object_integrity(self): Xs, Ys = m.X.value, m.Y.value n = DataScaler(m, self.domain) - self.assertEqual(n.wrapped, m) - self.assertEqual(m._parent, n) self.assertTrue(np.allclose(Xs, n.X.value)) self.assertTrue(np.allclose(Ys, n.Y.value)) @@ -80,7 +78,7 @@ def test_enabling_transforms(self): def test_predict_scaling(self): m = self.create_parabola_model() - n = DataScaler(self.create_parabola_model(), self.domain) + n = DataScaler(self.create_parabola_model(), self.domain, normalize_Y=True) m.optimize() n.optimize() @@ -100,9 +98,7 @@ def test_predict_scaling(self): self.assertTrue(np.allclose(fr, fs, atol=1e-3)) self.assertTrue(np.allclose(vr, vs, atol=1e-3)) - Yt = parabola2d(Xt) #+ np.random.rand(20, 1) * 0.05 + Yt = parabola2d(Xt) fr = m.predict_density(Xt, Yt) fs = n.predict_density(Xt, Yt) - print(fr) - print(fs) - np.testing.assert_allclose(fr, fs, rtol=1e-3) + np.testing.assert_allclose(fr, fs, rtol=1e-2) diff --git a/testing/test_mgp.py b/testing/test_mgp.py new file mode 100644 index 0000000..b21b9b5 --- /dev/null +++ b/testing/test_mgp.py @@ -0,0 +1,58 @@ +import GPflowOpt +import GPflow +import numpy as np +import unittest +from GPflowOpt.models import MGP + + +def parabola2d(X): + return np.atleast_2d(np.sum(X ** 2, axis=1)).T + + +class TestMGP(unittest.TestCase): + @property + def domain(self): + return np.sum([GPflowOpt.domain.ContinuousParameter("x{0}".format(i), -1, 1) for i in range(1, 3)]) + + def create_parabola_model(self, design=None): + if design is None: + design = GPflowOpt.design.LatinHyperCube(16, self.domain) + X, Y = design.generate(), parabola2d(design.generate()) + m = GPflow.gpr.GPR(X, Y, GPflow.kernels.RBF(2, ARD=True)) + return m + + def test_object_integrity(self): + m = self.create_parabola_model() + Xs, Ys = m.X.value, m.Y.value + n = MGP(m) + + self.assertEqual(n.wrapped, m) + self.assertEqual(m._parent, n) + self.assertTrue(np.allclose(Xs, n.X.value)) + self.assertTrue(np.allclose(Ys, n.Y.value)) + + def test_predict(self): + m = self.create_parabola_model() + n = MGP(self.create_parabola_model()) + m.optimize() + n.optimize() + + Xt = GPflowOpt.design.RandomDesign(20, self.domain).generate() + fr, vr = m.predict_f(Xt) + fs, vs = n.predict_f(Xt) + self.assertTrue(np.shape(fr) == np.shape(fs)) + self.assertTrue(np.shape(vr) == np.shape(vs)) + self.assertTrue(np.allclose(fr, fs, atol=1e-3)) + self.assertTrue(np.all(vs >= vr)) + + fr, vr = m.predict_y(Xt) + fs, vs = n.predict_y(Xt) + self.assertTrue(np.shape(fr) == np.shape(fs)) + self.assertTrue(np.shape(vr) == np.shape(vs)) + self.assertTrue(np.allclose(fr, fs, atol=1e-3)) + self.assertTrue(np.all(vs >= vr)) + + Yt = parabola2d(Xt) + fr = m.predict_density(Xt, Yt) + fs = n.predict_density(Xt, Yt) + self.assertTrue(np.shape(fr) == np.shape(fs)) diff --git a/testing/test_modelwrapper.py b/testing/test_modelwrapper.py new file mode 100644 index 0000000..aa2cfa1 --- /dev/null +++ b/testing/test_modelwrapper.py @@ -0,0 +1,114 @@ +import GPflowOpt +import unittest +import GPflow +import numpy as np + +float_type = GPflow.settings.dtypes.float_type + + +class MethodOverride(GPflowOpt.models.ModelWrapper): + + def __init__(self, m): + super(MethodOverride, self).__init__(m) + self.A = GPflow.param.DataHolder(np.array([1.0])) + + @GPflow.param.AutoFlow((float_type, [None, None])) + def predict_f(self, Xnew): + """ + Compute the mean and variance of held-out data at the points Xnew + """ + m, v = self.build_predict(Xnew) + return self.A * m, v + + @property + def X(self): + return self.wrapped.X + + @X.setter + def X(self, Xc): + self.wrapped.X = Xc + + @property + def foo(self): + return 1 + + @foo.setter + def foo(self, val): + self.wrapped.foo = val + + +class TestModelWrapper(unittest.TestCase): + + def simple_model(self): + x = np.random.rand(10,2) * 2 * np.pi + y = np.sin(x[:,[0]]) + m = GPflow.gpr.GPR(x,y, kern=GPflow.kernels.RBF(1)) + return m + + def test_object_integrity(self): + m = self.simple_model() + w = GPflowOpt.models.ModelWrapper(m) + self.assertEqual(w.wrapped, m) + self.assertEqual(m._parent, w) + self.assertEqual(w.optimize, m.optimize) + + def test_optimize(self): + m = self.simple_model() + w = GPflowOpt.models.ModelWrapper(m) + logL = m.compute_log_likelihood() + self.assertTrue(np.allclose(logL, w.compute_log_likelihood())) + + # Check if compiled & optimized, verify attributes are set in the right object. + w.optimize(maxiter=5) + self.assertTrue(hasattr(m, '_minusF')) + self.assertFalse('_minusF' in w.__dict__) + self.assertGreater(m.compute_log_likelihood(), logL) + + def test_af_storage_detection(self): + # Regression test for a bug with predict_f/predict_y... etc. + m = self.simple_model() + x = np.random.rand(10,2) + m.predict_f(x) + self.assertTrue(hasattr(m, '_predict_f_AF_storage')) + w = MethodOverride(m) + self.assertFalse(hasattr(w, '_predict_f_AF_storage')) + w.predict_f(x) + self.assertTrue(hasattr(w, '_predict_f_AF_storage')) + + def test_set_wrapped_attributes(self): + # Regression test for setting certain keys in the right object + m = self.simple_model() + w = GPflowOpt.models.ModelWrapper(m) + w._needs_recompile = False + self.assertFalse('_needs_recompile' in w.__dict__) + self.assertTrue('_needs_recompile' in m.__dict__) + self.assertFalse(w._needs_recompile) + self.assertFalse(m._needs_recompile) + + def test_double_wrap(self): + m = self.simple_model() + n = GPflowOpt.models.ModelWrapper(MethodOverride(m)) + n.optimize(maxiter=10) + Xt = np.random.rand(10, 2) + n.predict_f(Xt) + self.assertFalse('_predict_f_AF_storage' in n.__dict__) + self.assertTrue('_predict_f_AF_storage' in n.wrapped.__dict__) + self.assertFalse('_predict_f_AF_storage' in n.wrapped.wrapped.__dict__) + + n = MethodOverride(GPflowOpt.models.ModelWrapper(m)) + Xn = np.random.rand(10, 2) + Yn = np.random.rand(10, 1) + n.X = Xn + n.Y = Yn + self.assertTrue(np.allclose(Xn, n.wrapped.wrapped.X.value)) + self.assertTrue(np.allclose(Yn, n.wrapped.wrapped.Y.value)) + self.assertFalse('Y' in n.wrapped.__dict__) + self.assertFalse('X' in n.wrapped.__dict__) + + n.foo = 5 + self.assertTrue('foo' in n.wrapped.__dict__) + self.assertFalse('foo' in n.wrapped.wrapped.__dict__) + + + +