Source code for moe.tests.optimal_learning.python.gaussian_process_test_case

# -*- coding: utf-8 -*-
"""Base test case for tests that manipulate Gaussian Process data and supporting structures."""
import pytest

import collections

import numpy

from moe.optimal_learning.python.geometry_utils import ClosedInterval
from moe.optimal_learning.python.python_version.covariance import SquareExponential
from moe.optimal_learning.python.python_version.domain import TensorProductDomain
from moe.optimal_learning.python.python_version.gaussian_process import GaussianProcess
import moe.tests.optimal_learning.python.gaussian_process_test_utils as gp_utils
from moe.tests.optimal_learning.python.optimal_learning_test_case import OptimalLearningTestCase


# See GaussianProcessTestEnvironment (below) for docstring.
_BaseGaussianProcessTestEnvironment = collections.namedtuple('GaussianProcessTestEnvironment', [
    'domain',
    'gaussian_process',
])


[docs]class GaussianProcessTestEnvironment(_BaseGaussianProcessTestEnvironment): """An object for representing a (randomly generated) Gaussian Process. :ivar domain: (*interfaces.domain_interface.DomainInterface subclass*) domain the GP was built on :ivar gaussian_process: (*interfaces.gaussian_process_interface.GaussianProcessInterface subclass*) the constructed GP """ __slots__ = ()
[docs]class GaussianProcessTestEnvironmentInput(object): """A test environment for constructing randomly generated Gaussian Process priors within GaussianProcessTestCase. This is used only in testing. The intended use-case is that subclasses of GaussianProcessTestCase (below) will define one of these objects, and then GaussianProcessTestCase has some simple logic to precompute the requested GaussianProcess-derived test case(s). """ def __init__( self, dim, num_hyperparameters, num_sampled, noise_variance_base=0.0, hyperparameter_interval=ClosedInterval(0.2, 1.3), lower_bound_interval=ClosedInterval(-2.0, 0.5), upper_bound_interval=ClosedInterval(2.0, 3.5), covariance_class=SquareExponential, spatial_domain_class=TensorProductDomain, hyperparameter_domain_class=TensorProductDomain, gaussian_process_class=GaussianProcess, ): """Create a test environment: object with enough info to construct a Gaussian Process prior from repeated random draws. :param dim: number of (expected) spatial dimensions; None to skip check :type dim: int > 0 :param num_hyperparameters: number of hyperparemeters of the covariance function :type num_hyperparameters: int > 0 :param num_sampled: number of ``points_sampled`` to generate from the GP prior :type num_sampled: int > 0 :param noise_variance_base: noise variance to associate with each sampled point :type noise_variance_base: float64 >= 0.0 :param hyperparameter_interval: interval from which to draw hyperparameters (uniform random) :type hyperparameter_interval: non-empty ClosedInterval :param lower_bound_interval: interval from which to draw domain lower bounds (uniform random) :type lower_bound_interval: non-empty ClosedInterval; cannot overlap with upper_bound_interval :param upper_bound_interval: interval from which to draw domain upper bounds (uniform random) :type upper_bound_interval: non-empty ClosedInterval; cannot overlap with lower_bound_interval :param covariance_class: the type of covariance to use when building the GP :type covariance_class: type object of covariance_interface.CovarianceInterface (or one of its subclasses) :param spatial_domain_class: the type of the domain that the GP lives in :type spatial_domain_class: type object of domain_interface.DomainInterface (or one of its subclasses) :param hyperparameter_domain_class: the type of the domain that the hyperparameters live in :type hyperparameter_domain_class: type object of domain_interface.DomainInterface (or one of its subclasses) :param gaussian_process_class: the type of the Gaussian Process to draw from :type gaussian_process_class: type object of gaussian_process_interface.GaussianProcessInterface (or one of its subclasses) """ self.dim = dim self.num_hyperparameters = num_hyperparameters self.noise_variance_base = noise_variance_base self.num_sampled = num_sampled self.hyperparameter_interval = hyperparameter_interval self.lower_bound_interval = lower_bound_interval self.upper_bound_interval = upper_bound_interval self.covariance_class = covariance_class self.spatial_domain_class = spatial_domain_class self.hyperparameter_domain_class = hyperparameter_domain_class self.gaussian_process_class = gaussian_process_class @property def num_sampled(self): """Return the number of ``points_sampled`` that test GPs should be built with.""" return self._num_sampled @num_sampled.setter
[docs] def num_sampled(self, value): """Set num_sampled and resize dependent quantities (e.g., noise_variance).""" self._num_sampled = value self.noise_variance = numpy.full(self.num_sampled, self.noise_variance_base)
[docs]class GaussianProcessTestCase(OptimalLearningTestCase): """Base test case for tests that want to use random data generated from Gaussian Process(es). Users are required to set the *class variable* ``precompute_gaussian_process_data`` flag and define *class variables*: ``gp_test_environment_input`` and ``num_sampled_list`` (see base_setup() docstring). Using that info, base_setup will create the required test cases (in ``gp_test_environments``) for use in testing. The idea is that base_setup is run once per test class, so the (expensive) work of building GPs can be shared across numerous individual tests. """ precompute_gaussian_process_data = False noise_variance_base = 0.0002 dim = 3 num_hyperparameters = dim + 1 gp_test_environment_input = GaussianProcessTestEnvironmentInput( dim, num_hyperparameters, 0, noise_variance_base=noise_variance_base, hyperparameter_interval=ClosedInterval(0.1, 1.3), lower_bound_interval=ClosedInterval(-2.0, 0.5), upper_bound_interval=ClosedInterval(2.0, 3.5), covariance_class=SquareExponential, spatial_domain_class=TensorProductDomain, hyperparameter_domain_class=TensorProductDomain, gaussian_process_class=GaussianProcess, ) num_sampled_list = (1, 2, 3, 5, 10, 16, 20, 42) num_to_sample_list = (1, 2, 3, 8) @classmethod @pytest.fixture(autouse=True, scope='class')
[docs] def base_setup(cls): """Build a Gaussian Process prior for each problem size in ``cls.num_sampled_list`` if precomputation is desired. **Requires** * cls.num_sampled_list: (*list of int*) problem sizes to consider * cls.gp_test_environment_input: (*GaussianProcessTestEnvironmentInput*) specification of how to build the gaussian process prior **Outputs** * cls.gp_test_environments: (*list of GaussianProcessTestEnvironment*) gaussian process data for each of the specified problem sizes (``cls.num_sampled_list``) """ if cls.precompute_gaussian_process_data: cls.gp_test_environments = [] for num_sampled in cls.num_sampled_list: cls.gp_test_environment_input.num_sampled = num_sampled cls.gp_test_environments.append(cls._build_gaussian_process_test_data(cls.gp_test_environment_input))
@staticmethod def _build_gaussian_process_test_data(test_environment): """Build up a Gaussian Process randomly by repeatedly drawing from and then adding to the prior. :param test_environment: parameters describing how to construct a GP prior :type test_environment: GaussianProcessTestEnvironmentInput :return: gaussian process environments that can be used to run tests :rtype: GaussianProcessTestEnvironment """ covariance = gp_utils.fill_random_covariance_hyperparameters( test_environment.hyperparameter_interval, test_environment.num_hyperparameters, covariance_type=test_environment.covariance_class, ) domain_bounds = gp_utils.fill_random_domain_bounds( test_environment.lower_bound_interval, test_environment.upper_bound_interval, test_environment.dim, ) domain = test_environment.spatial_domain_class(ClosedInterval.build_closed_intervals_from_list(domain_bounds)) points_sampled = domain.generate_uniform_random_points_in_domain(test_environment.num_sampled) gaussian_process = gp_utils.build_random_gaussian_process( points_sampled, covariance, noise_variance=test_environment.noise_variance, gaussian_process_type=test_environment.gaussian_process_class, ) return GaussianProcessTestEnvironment(domain, gaussian_process)