From 9f4d0ac609f50f2b455e6fc4f1ffeeb05fc4667a Mon Sep 17 00:00:00 2001 From: jialeiwang Date: Fri, 23 Oct 2015 08:58:48 -0400 Subject: [PATCH 1/2] MOE now is safe to use python interfaces without building cpp components --- MANIFEST.in | 7 +- Makefile | 2 + docs/install.rst | 2 + moe/optimal_learning/python/constant.py | 4 + moe/optimal_learning/python/linkers.py | 200 ++++++++--- .../python/python_version/covariance.py | 5 + .../cpp_unit_test_wrapper_test.py | 20 +- .../python/cpp_wrappers/exception_test.py | 30 +- .../cpp_wrappers/expected_improvement_test.py | 151 ++++---- .../cpp_wrappers/gaussian_process_test.py | 325 +++++++++--------- .../cpp_wrappers/log_likelihood_test.py | 113 +++--- .../python/cpp_wrappers/optimization_test.py | 119 +++---- moe/tests/views/exceptions_test.py | 9 + moe/tests/views/rest/bandit_test.py | 1 - moe/tests/views/rest/gp_ei_test.py | 11 +- moe/tests/views/rest/gp_hyper_opt_test.py | 8 +- moe/tests/views/rest/gp_mean_var_test.py | 10 +- moe/tests/views/rest/gp_next_points_test.py | 8 +- moe/views/gp_next_points_pretty_view.py | 14 +- moe/views/rest/gp_ei.py | 4 +- moe/views/rest/gp_hyper_opt.py | 8 +- moe/views/utils.py | 32 +- moe_examples/combined_example.py | 4 +- .../hyper_opt_of_gp_from_historical_data.py | 4 +- .../mean_and_var_of_gp_from_historic_data.py | 4 +- .../next_point_via_simple_endpoint.py | 4 +- moe_examples/tests/combined_example_test.py | 4 +- ...per_opt_of_gp_from_historical_data_test.py | 4 +- ...n_and_var_of_gp_from_historic_data_test.py | 4 + .../next_point_via_simple_endpoint_test.py | 4 +- setup.py | 12 +- 31 files changed, 670 insertions(+), 457 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index c8305612..f897df0c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,10 +5,11 @@ include setup.py include *.ini # docs recursive-include doc * -# optimal learning -recursive-include moe/optimal_learning * # webapp -recursive-include moe * +recursive-include moe/templates * +recursive-include moe/static * +# cpp files +recursive-include moe/optimal_learning/cpp * # remove what we don't want, kept in step with .gitignore # Logs diff --git a/Makefile b/Makefile index b29fc27b..a965bdb5 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,8 @@ all: production clean: find . -name '*.pyc' -delete rm -rf moe/build + rm -rf build + rm -rf MOE.egg-info production: python setup.py install diff --git a/docs/install.rst b/docs/install.rst index df3265a5..a33d68f8 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -77,6 +77,8 @@ Requires: .. Warning:: Boost, MOE, and the virtualenv must be built with the same python. (OS X users: we recommend using the MacPorts Python: ``/opt/local/bin/python``.) +9. Should you have difficulty in compiling the C++ library and cannot work around, you could set environmental variable ```MOE_NO_BUILD_CPP=True``` to skip installation of C++ components. Note that if you choose to go this route, please do not use optimal learning modules in REST interface for now, as this part is not compatible with no C++ components build yet. + OSX Tips -------- diff --git a/moe/optimal_learning/python/constant.py b/moe/optimal_learning/python/constant.py index a40192f9..54ff5b31 100644 --- a/moe/optimal_learning/python/constant.py +++ b/moe/optimal_learning/python/constant.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- """Some default configuration parameters for optimal_learning components.""" from collections import namedtuple +import os import moe.optimal_learning.python.python_version.optimization as python_optimization import moe.views.constant as views_constant @@ -299,3 +300,6 @@ class DefaultOptimizerInfoTuple(_BaseDefaultOptimizerInfoTuple): # TODO(GH-257): Find a better default. DEFAULT_KRIGING_NOISE_VARIANCE = 1e-8 DEFAULT_KRIGING_STD_DEVIATION_COEF = 0.0 + +# Whether cpp components were installed +CPP_COMPONENT_INSTALLED = os.environ.get('MOE_NO_BUILD_CPP', 'False') == 'False' diff --git a/moe/optimal_learning/python/linkers.py b/moe/optimal_learning/python/linkers.py index c2ec7150..c8a29b63 100644 --- a/moe/optimal_learning/python/linkers.py +++ b/moe/optimal_learning/python/linkers.py @@ -2,54 +2,156 @@ """Links between the python and cpp_wrapper implementations of domains, covariances and optimizations.""" from collections import namedtuple -from moe.optimal_learning.python.constant import SQUARE_EXPONENTIAL_COVARIANCE_TYPE, TENSOR_PRODUCT_DOMAIN_TYPE, SIMPLEX_INTERSECT_TENSOR_PRODUCT_DOMAIN_TYPE, NULL_OPTIMIZER, NEWTON_OPTIMIZER, GRADIENT_DESCENT_OPTIMIZER, L_BFGS_B_OPTIMIZER, LOG_MARGINAL_LIKELIHOOD, LEAVE_ONE_OUT_LOG_LIKELIHOOD -import moe.optimal_learning.python.cpp_wrappers.covariance as cpp_covariance -import moe.optimal_learning.python.cpp_wrappers.domain as cpp_domain -from moe.optimal_learning.python.cpp_wrappers.log_likelihood import GaussianProcessLogMarginalLikelihood, GaussianProcessLeaveOneOutLogLikelihood -import moe.optimal_learning.python.cpp_wrappers.optimization as cpp_optimization +from moe.optimal_learning.python.constant import SQUARE_EXPONENTIAL_COVARIANCE_TYPE, TENSOR_PRODUCT_DOMAIN_TYPE, SIMPLEX_INTERSECT_TENSOR_PRODUCT_DOMAIN_TYPE, NULL_OPTIMIZER, NEWTON_OPTIMIZER, GRADIENT_DESCENT_OPTIMIZER, L_BFGS_B_OPTIMIZER, LOG_MARGINAL_LIKELIHOOD, LEAVE_ONE_OUT_LOG_LIKELIHOOD, CPP_COMPONENT_INSTALLED import moe.optimal_learning.python.python_version.covariance as python_covariance import moe.optimal_learning.python.python_version.domain as python_domain import moe.optimal_learning.python.python_version.optimization as python_optimization +from moe.optimal_learning.python.python_version.log_likelihood import GaussianProcessLogMarginalLikelihood as PythonGaussianProcessLogMarginalLikelihood +if CPP_COMPONENT_INSTALLED: + import moe.optimal_learning.python.cpp_wrappers.covariance as cpp_covariance + import moe.optimal_learning.python.cpp_wrappers.domain as cpp_domain + from moe.optimal_learning.python.cpp_wrappers.log_likelihood import GaussianProcessLogMarginalLikelihood, GaussianProcessLeaveOneOutLogLikelihood + import moe.optimal_learning.python.cpp_wrappers.optimization as cpp_optimization -# Covariance -CovarianceLinks = namedtuple( + # Covariance + CovarianceLinks = namedtuple( + 'CovarianceLinks', + [ + 'python_covariance_class', + 'cpp_covariance_class', + ], + ) + + COVARIANCE_TYPES_TO_CLASSES = { + SQUARE_EXPONENTIAL_COVARIANCE_TYPE: CovarianceLinks( + python_covariance.SquareExponential, + cpp_covariance.SquareExponential, + ), + } + + # Domain + DomainLinks = namedtuple( + 'DomainLinks', + [ + 'python_domain_class', + 'cpp_domain_class', + ], + ) + + DOMAIN_TYPES_TO_DOMAIN_LINKS = { + TENSOR_PRODUCT_DOMAIN_TYPE: DomainLinks( + python_domain.TensorProductDomain, + cpp_domain.TensorProductDomain, + ), + SIMPLEX_INTERSECT_TENSOR_PRODUCT_DOMAIN_TYPE: DomainLinks( + None, + cpp_domain.SimplexIntersectTensorProductDomain, + ), + } + + # Optimization + OptimizerMethod = namedtuple( + 'OptimizerMethod', + [ + 'optimizer_type', + 'python_parameters_class', + 'cpp_parameters_class', + 'python_optimizer_class', + 'cpp_optimizer_class', + ], + ) + + OPTIMIZER_TYPES_TO_OPTIMIZER_METHODS = { + NULL_OPTIMIZER: OptimizerMethod( + optimizer_type=NULL_OPTIMIZER, + python_parameters_class=python_optimization.NullParameters, + cpp_parameters_class=cpp_optimization.NullParameters, + python_optimizer_class=python_optimization.NullOptimizer, + cpp_optimizer_class=cpp_optimization.NullOptimizer, + ), + NEWTON_OPTIMIZER: OptimizerMethod( + optimizer_type=NEWTON_OPTIMIZER, + python_parameters_class=python_optimization.NewtonParameters, + cpp_parameters_class=cpp_optimization.NewtonParameters, + python_optimizer_class=None, + cpp_optimizer_class=cpp_optimization.NewtonOptimizer, + ), + GRADIENT_DESCENT_OPTIMIZER: OptimizerMethod( + optimizer_type=GRADIENT_DESCENT_OPTIMIZER, + python_parameters_class=python_optimization.GradientDescentParameters, + cpp_parameters_class=cpp_optimization.GradientDescentParameters, + python_optimizer_class=python_optimization.GradientDescentOptimizer, + cpp_optimizer_class=cpp_optimization.GradientDescentOptimizer, + ), + L_BFGS_B_OPTIMIZER: OptimizerMethod( + optimizer_type=L_BFGS_B_OPTIMIZER, + python_parameters_class=python_optimization.LBFGSBParameters, + cpp_parameters_class=None, + python_optimizer_class=python_optimization.LBFGSBOptimizer, + cpp_optimizer_class=None, + ), + } + + # Log Likelihood + LogLikelihoodMethod = namedtuple( + 'LogLikelihoodMethod', + [ + 'log_likelihood_type', + 'log_likelihood_class', + ] + ) + + LOG_LIKELIHOOD_TYPES_TO_LOG_LIKELIHOOD_METHODS = { + LOG_MARGINAL_LIKELIHOOD: LogLikelihoodMethod( + log_likelihood_type=LOG_MARGINAL_LIKELIHOOD, + log_likelihood_class=GaussianProcessLogMarginalLikelihood, + ), + LEAVE_ONE_OUT_LOG_LIKELIHOOD: LogLikelihoodMethod( + log_likelihood_type=LEAVE_ONE_OUT_LOG_LIKELIHOOD, + log_likelihood_class=GaussianProcessLeaveOneOutLogLikelihood, + ), + } +else: + + # Covariance + CovarianceLinks = namedtuple( 'CovarianceLinks', [ 'python_covariance_class', 'cpp_covariance_class', - ], - ) + ], + ) -COVARIANCE_TYPES_TO_CLASSES = { + COVARIANCE_TYPES_TO_CLASSES = { SQUARE_EXPONENTIAL_COVARIANCE_TYPE: CovarianceLinks( python_covariance.SquareExponential, - cpp_covariance.SquareExponential, - ), - } + None, + ), + } -# Domain -DomainLinks = namedtuple( + # Domain + DomainLinks = namedtuple( 'DomainLinks', [ 'python_domain_class', 'cpp_domain_class', - ], - ) + ], + ) -DOMAIN_TYPES_TO_DOMAIN_LINKS = { + DOMAIN_TYPES_TO_DOMAIN_LINKS = { TENSOR_PRODUCT_DOMAIN_TYPE: DomainLinks( python_domain.TensorProductDomain, - cpp_domain.TensorProductDomain, - ), + None, + ), SIMPLEX_INTERSECT_TENSOR_PRODUCT_DOMAIN_TYPE: DomainLinks( None, - cpp_domain.SimplexIntersectTensorProductDomain, - ), - } + None, + ), + } -# Optimization -OptimizerMethod = namedtuple( + # Optimization + OptimizerMethod = namedtuple( 'OptimizerMethod', [ 'optimizer_type', @@ -57,56 +159,56 @@ 'cpp_parameters_class', 'python_optimizer_class', 'cpp_optimizer_class', - ], - ) + ], + ) -OPTIMIZER_TYPES_TO_OPTIMIZER_METHODS = { + OPTIMIZER_TYPES_TO_OPTIMIZER_METHODS = { NULL_OPTIMIZER: OptimizerMethod( optimizer_type=NULL_OPTIMIZER, python_parameters_class=python_optimization.NullParameters, - cpp_parameters_class=cpp_optimization.NullParameters, + cpp_parameters_class=None, python_optimizer_class=python_optimization.NullOptimizer, - cpp_optimizer_class=cpp_optimization.NullOptimizer, - ), + cpp_optimizer_class=None, + ), NEWTON_OPTIMIZER: OptimizerMethod( optimizer_type=NEWTON_OPTIMIZER, python_parameters_class=python_optimization.NewtonParameters, - cpp_parameters_class=cpp_optimization.NewtonParameters, + cpp_parameters_class=None, python_optimizer_class=None, - cpp_optimizer_class=cpp_optimization.NewtonOptimizer, - ), + cpp_optimizer_class=None, + ), GRADIENT_DESCENT_OPTIMIZER: OptimizerMethod( optimizer_type=GRADIENT_DESCENT_OPTIMIZER, python_parameters_class=python_optimization.GradientDescentParameters, - cpp_parameters_class=cpp_optimization.GradientDescentParameters, + cpp_parameters_class=None, python_optimizer_class=python_optimization.GradientDescentOptimizer, - cpp_optimizer_class=cpp_optimization.GradientDescentOptimizer, - ), + cpp_optimizer_class=None, + ), L_BFGS_B_OPTIMIZER: OptimizerMethod( optimizer_type=L_BFGS_B_OPTIMIZER, python_parameters_class=python_optimization.LBFGSBParameters, cpp_parameters_class=None, python_optimizer_class=python_optimization.LBFGSBOptimizer, cpp_optimizer_class=None, - ), - } + ), + } -# Log Likelihood -LogLikelihoodMethod = namedtuple( + # Log Likelihood + LogLikelihoodMethod = namedtuple( 'LogLikelihoodMethod', [ 'log_likelihood_type', 'log_likelihood_class', - ] - ) + ] + ) -LOG_LIKELIHOOD_TYPES_TO_LOG_LIKELIHOOD_METHODS = { + LOG_LIKELIHOOD_TYPES_TO_LOG_LIKELIHOOD_METHODS = { LOG_MARGINAL_LIKELIHOOD: LogLikelihoodMethod( log_likelihood_type=LOG_MARGINAL_LIKELIHOOD, - log_likelihood_class=GaussianProcessLogMarginalLikelihood, - ), + log_likelihood_class=PythonGaussianProcessLogMarginalLikelihood, + ), LEAVE_ONE_OUT_LOG_LIKELIHOOD: LogLikelihoodMethod( log_likelihood_type=LEAVE_ONE_OUT_LOG_LIKELIHOOD, - log_likelihood_class=GaussianProcessLeaveOneOutLogLikelihood, - ), - } + log_likelihood_class=None, + ), + } diff --git a/moe/optimal_learning/python/python_version/covariance.py b/moe/optimal_learning/python/python_version/covariance.py index 25cab55e..d6ab5e60 100644 --- a/moe/optimal_learning/python/python_version/covariance.py +++ b/moe/optimal_learning/python/python_version/covariance.py @@ -56,6 +56,11 @@ def num_hyperparameters(self): """Return the number of hyperparameters of this covariance function.""" return self._hyperparameters.size + @staticmethod + def make_default_hyperparameters(dim): + """Return a default set up hyperparameters given the dimension of the space.""" + return numpy.ones(dim + 1) + def get_hyperparameters(self): """Get the hyperparameters (array of float64 with shape (num_hyperparameters)) of this covariance.""" return numpy.copy(self._hyperparameters) diff --git a/moe/tests/optimal_learning/python/cpp_unit_tests/cpp_unit_test_wrapper_test.py b/moe/tests/optimal_learning/python/cpp_unit_tests/cpp_unit_test_wrapper_test.py index 4678ba83..b0a7caab 100644 --- a/moe/tests/optimal_learning/python/cpp_unit_tests/cpp_unit_test_wrapper_test.py +++ b/moe/tests/optimal_learning/python/cpp_unit_tests/cpp_unit_test_wrapper_test.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- """Dummy test case that invokes all of the C++ unit tests.""" -import moe.build.GPP as C_GP +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED +if CPP_COMPONENT_INSTALLED: + import moe.build.GPP as C_GP -class TestCppUnitTestWrapper(object): + class TestCppUnitTestWrapper(object): - """Calls a C++ function that runs all C++ unit tests. + """Calls a C++ function that runs all C++ unit tests. - TODO(GH-115): Remove/fix this once C++ gets a proper unit testing framework. + TODO(GH-115): Remove/fix this once C++ gets a proper unit testing framework. - """ + """ - def test_run_cpp_unit_tests(self): - """Call C++ function that runs all C++ unit tests and assert 0 errors.""" - number_of_cpp_test_errors = C_GP.run_cpp_tests() - assert number_of_cpp_test_errors == 0 + def test_run_cpp_unit_tests(self): + """Call C++ function that runs all C++ unit tests and assert 0 errors.""" + number_of_cpp_test_errors = C_GP.run_cpp_tests() + assert number_of_cpp_test_errors == 0 diff --git a/moe/tests/optimal_learning/python/cpp_wrappers/exception_test.py b/moe/tests/optimal_learning/python/cpp_wrappers/exception_test.py index 6b71c49d..ac0eb8f5 100644 --- a/moe/tests/optimal_learning/python/cpp_wrappers/exception_test.py +++ b/moe/tests/optimal_learning/python/cpp_wrappers/exception_test.py @@ -2,23 +2,25 @@ """Tests for the C++-defined Python exception type objects.""" import pytest -import moe.build.GPP as C_GP +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED +if CPP_COMPONENT_INSTALLED: + import moe.build.GPP as C_GP -class TestExceptionStructure(object): + class TestExceptionStructure(object): - """Tests for the C++-defined Python exception type objects.""" + """Tests for the C++-defined Python exception type objects.""" - def test_exception_class_hierarchy(self): - """Test that the C++-defined Python exception type objects have the right class hiearchy.""" - # Base class inherits from Exception - assert issubclass(C_GP.OptimalLearningException, Exception) + def test_exception_class_hierarchy(self): + """Test that the C++-defined Python exception type objects have the right class hiearchy.""" + # Base class inherits from Exception + assert issubclass(C_GP.OptimalLearningException, Exception) - type_objects = (C_GP.BoundsException, C_GP.InvalidValueException, C_GP.SingularMatrixException) - for type_object in type_objects: - assert issubclass(type_object, C_GP.OptimalLearningException) + type_objects = (C_GP.BoundsException, C_GP.InvalidValueException, C_GP.SingularMatrixException) + for type_object in type_objects: + assert issubclass(type_object, C_GP.OptimalLearningException) - def test_exception_thrown_from_cpp(self): - """Test that a C++ interface function throws the expected type.""" - with pytest.raises(C_GP.BoundsException): - C_GP.GaussianProcess([-1.0, [1.0]], [], [], [], 1, 0) + def test_exception_thrown_from_cpp(self): + """Test that a C++ interface function throws the expected type.""" + with pytest.raises(C_GP.BoundsException): + C_GP.GaussianProcess([-1.0, [1.0]], [], [], [], 1, 0) diff --git a/moe/tests/optimal_learning/python/cpp_wrappers/expected_improvement_test.py b/moe/tests/optimal_learning/python/cpp_wrappers/expected_improvement_test.py index acefc1d2..e05077c4 100644 --- a/moe/tests/optimal_learning/python/cpp_wrappers/expected_improvement_test.py +++ b/moe/tests/optimal_learning/python/cpp_wrappers/expected_improvement_test.py @@ -1,91 +1,94 @@ # -*- coding: utf-8 -*- """Test the C++ implementation of expected improvement against the Python implementation.""" -import numpy +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED -import pytest +if CPP_COMPONENT_INSTALLED: + import numpy -import moe.optimal_learning.python.cpp_wrappers.covariance -import moe.optimal_learning.python.cpp_wrappers.expected_improvement -import moe.optimal_learning.python.cpp_wrappers.gaussian_process -from moe.optimal_learning.python.geometry_utils import ClosedInterval -import moe.optimal_learning.python.python_version.covariance -import moe.optimal_learning.python.python_version.domain -import moe.optimal_learning.python.python_version.expected_improvement -import moe.optimal_learning.python.python_version.gaussian_process -from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase, GaussianProcessTestEnvironmentInput + import pytest + import moe.optimal_learning.python.cpp_wrappers.covariance + import moe.optimal_learning.python.cpp_wrappers.expected_improvement + import moe.optimal_learning.python.cpp_wrappers.gaussian_process + from moe.optimal_learning.python.geometry_utils import ClosedInterval + import moe.optimal_learning.python.python_version.covariance + import moe.optimal_learning.python.python_version.domain + import moe.optimal_learning.python.python_version.expected_improvement + import moe.optimal_learning.python.python_version.gaussian_process + from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase, GaussianProcessTestEnvironmentInput -class TestExpectedImprovement(GaussianProcessTestCase): - """Test C++ vs Python implementations of Expected Improvement. + class TestExpectedImprovement(GaussianProcessTestCase): - Currently only checks that the 1D, analytic EI & gradient match. - Checking monte carlo would be very expensive (b/c of the need to converge the MC) or very difficult - (to make python & C++ use the exact same sequence of random numbers). + """Test C++ vs Python implementations of Expected Improvement. - """ - - precompute_gaussian_process_data = True - - 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, 0.3), - lower_bound_interval=ClosedInterval(-1.0, 0.5), - upper_bound_interval=ClosedInterval(2.0, 3.5), - covariance_class=moe.optimal_learning.python.python_version.covariance.SquareExponential, - spatial_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, - hyperparameter_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, - gaussian_process_class=moe.optimal_learning.python.python_version.gaussian_process.GaussianProcess, - ) - - num_sampled_list = (1, 2, 5, 10, 16, 20, 42, 50) - - @classmethod - @pytest.fixture(autouse=True, scope='class') - def base_setup(cls): - """Run the standard setup but seed the RNG first (for repeatability). - - It is easy to stumble into test cases where EI is very small (e.g., < 1.e-20), - which makes it difficult to set meaningful tolerances for the checks. + Currently only checks that the 1D, analytic EI & gradient match. + Checking monte carlo would be very expensive (b/c of the need to converge the MC) or very difficult + (to make python & C++ use the exact same sequence of random numbers). """ - numpy.random.seed(8794) - super(TestExpectedImprovement, cls).base_setup() - - def test_python_and_cpp_return_same_1d_analytic_ei_and_gradient(self): - """Compare the 1D analytic EI/grad EI results from Python & C++, checking several random points per test case.""" - num_tests_per_case = 10 - ei_tolerance = 6.0e-14 - # TODO(GH-240): set RNG seed for this case and restore toleranace to 6.0e-14 or better - grad_ei_tolerance = 6.0e-13 - for test_case in self.gp_test_environments: - domain, python_gp = test_case - python_cov, historical_data = python_gp.get_core_data_copy() + precompute_gaussian_process_data = True + + 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, 0.3), + lower_bound_interval=ClosedInterval(-1.0, 0.5), + upper_bound_interval=ClosedInterval(2.0, 3.5), + covariance_class=moe.optimal_learning.python.python_version.covariance.SquareExponential, + spatial_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, + hyperparameter_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, + gaussian_process_class=moe.optimal_learning.python.python_version.gaussian_process.GaussianProcess, + ) + + num_sampled_list = (1, 2, 5, 10, 16, 20, 42, 50) + + @classmethod + @pytest.fixture(autouse=True, scope='class') + def base_setup(cls): + """Run the standard setup but seed the RNG first (for repeatability). + + It is easy to stumble into test cases where EI is very small (e.g., < 1.e-20), + which makes it difficult to set meaningful tolerances for the checks. + + """ + numpy.random.seed(8794) + super(TestExpectedImprovement, cls).base_setup() + + def test_python_and_cpp_return_same_1d_analytic_ei_and_gradient(self): + """Compare the 1D analytic EI/grad EI results from Python & C++, checking several random points per test case.""" + num_tests_per_case = 10 + ei_tolerance = 6.0e-14 + # TODO(GH-240): set RNG seed for this case and restore toleranace to 6.0e-14 or better + grad_ei_tolerance = 6.0e-13 + + for test_case in self.gp_test_environments: + domain, python_gp = test_case + python_cov, historical_data = python_gp.get_core_data_copy() - points_to_sample = domain.generate_random_point_in_domain() - python_ei_eval = moe.optimal_learning.python.python_version.expected_improvement.ExpectedImprovement(python_gp, points_to_sample) + points_to_sample = domain.generate_random_point_in_domain() + python_ei_eval = moe.optimal_learning.python.python_version.expected_improvement.ExpectedImprovement(python_gp, points_to_sample) - cpp_cov = moe.optimal_learning.python.cpp_wrappers.covariance.SquareExponential(python_cov.hyperparameters) - cpp_gp = moe.optimal_learning.python.cpp_wrappers.gaussian_process.GaussianProcess(cpp_cov, historical_data) - cpp_ei_eval = moe.optimal_learning.python.cpp_wrappers.expected_improvement.ExpectedImprovement(cpp_gp, points_to_sample) + cpp_cov = moe.optimal_learning.python.cpp_wrappers.covariance.SquareExponential(python_cov.hyperparameters) + cpp_gp = moe.optimal_learning.python.cpp_wrappers.gaussian_process.GaussianProcess(cpp_cov, historical_data) + cpp_ei_eval = moe.optimal_learning.python.cpp_wrappers.expected_improvement.ExpectedImprovement(cpp_gp, points_to_sample) - for _ in xrange(num_tests_per_case): - points_to_sample = domain.generate_random_point_in_domain() - cpp_ei_eval.current_point = points_to_sample - python_ei_eval.current_point = points_to_sample + for _ in xrange(num_tests_per_case): + points_to_sample = domain.generate_random_point_in_domain() + cpp_ei_eval.current_point = points_to_sample + python_ei_eval.current_point = points_to_sample - cpp_ei = cpp_ei_eval.compute_expected_improvement() - python_ei = python_ei_eval.compute_expected_improvement(force_1d_ei=True) - self.assert_scalar_within_relative(python_ei, cpp_ei, ei_tolerance) + cpp_ei = cpp_ei_eval.compute_expected_improvement() + python_ei = python_ei_eval.compute_expected_improvement(force_1d_ei=True) + self.assert_scalar_within_relative(python_ei, cpp_ei, ei_tolerance) - cpp_grad_ei = cpp_ei_eval.compute_grad_expected_improvement() - python_grad_ei = python_ei_eval.compute_grad_expected_improvement() - self.assert_vector_within_relative(python_grad_ei, cpp_grad_ei, grad_ei_tolerance) + cpp_grad_ei = cpp_ei_eval.compute_grad_expected_improvement() + python_grad_ei = python_ei_eval.compute_grad_expected_improvement() + self.assert_vector_within_relative(python_grad_ei, cpp_grad_ei, grad_ei_tolerance) diff --git a/moe/tests/optimal_learning/python/cpp_wrappers/gaussian_process_test.py b/moe/tests/optimal_learning/python/cpp_wrappers/gaussian_process_test.py index 87013804..643714ee 100644 --- a/moe/tests/optimal_learning/python/cpp_wrappers/gaussian_process_test.py +++ b/moe/tests/optimal_learning/python/cpp_wrappers/gaussian_process_test.py @@ -1,163 +1,166 @@ # -*- coding: utf-8 -*- """Test the C++ implementation of Gaussian Process properties (mean, var, gradients thereof) against the Python version.""" -import copy - -import numpy - -import pytest - -import moe.build.GPP as C_GP -from moe.optimal_learning.python.cpp_wrappers.covariance import SquareExponential -from moe.optimal_learning.python.cpp_wrappers.gaussian_process import GaussianProcess -from moe.optimal_learning.python.data_containers import HistoricalData, SamplePoint -from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase - - -class TestGaussianProcess(GaussianProcessTestCase): - - """Test C++ vs Python implementations of Gaussian Process properties (mean, variance, cholesky variance, and their gradients).""" - - precompute_gaussian_process_data = True - - @classmethod - @pytest.fixture(autouse=True, scope='class') - def base_setup(cls): - """Run the standard setup but seed the RNG first (for repeatability). - - It is easy to stumble into test cases where mean, var terms are very small (e.g., < 1.e-20), - which makes it difficult to set meaningful tolerances for the checks. - - """ - numpy.random.seed(8794) - super(TestGaussianProcess, cls).base_setup() - - def test_sample_point_from_gp(self): - """Test that sampling points from the GP works.""" - point_one = SamplePoint([0.0, 1.0], -1.0, 0.0) - point_two = SamplePoint([2.0, 2.5], 1.0, 0.1) - covariance = SquareExponential([1.0, 1.0, 1.0]) - historical_data = HistoricalData(len(point_one.point), [point_one, point_two]) - - gaussian_process = GaussianProcess(covariance, historical_data) - out_values = numpy.zeros(3) - for i in xrange(3): - out_values[i] = gaussian_process.sample_point_from_gp(point_two.point, 0.001) - - gaussian_process._gaussian_process.reset_to_most_recent_seed() - out_values_test = numpy.ones(3) - for i in xrange(3): - out_values_test[i] = gaussian_process.sample_point_from_gp(point_two.point, 0.001) - - # Exact match b/c we should've run over the exact same computations - self.assert_vector_within_relative(out_values_test, out_values, 0.0) - - # Sampling from a historical point (that had 0 noise) should produce the same value associated w/that point - value = gaussian_process.sample_point_from_gp(point_one.point, 0.0) - self.assert_scalar_within_relative(value, point_one.value, numpy.finfo(numpy.float64).eps) - - def test_gp_construction_singular_covariance_matrix(self): - """Test that the GaussianProcess ctor indicates a singular covariance matrix when points_sampled contains duplicates (0 noise).""" - index = numpy.argmax(numpy.greater_equal(self.num_sampled_list, 1)) - domain, gaussian_process = self.gp_test_environments[index] - point_one = SamplePoint([0.0] * domain.dim, 1.0, 0.0) - # points two and three have duplicate coordinates and we have noise_variance = 0.0 - point_two = SamplePoint([1.0] * domain.dim, 1.0, 0.0) - point_three = point_two - - historical_data = HistoricalData(len(point_one.point), [point_one, point_two, point_three]) - with pytest.raises(C_GP.SingularMatrixException): - GaussianProcess(gaussian_process.get_covariance_copy(), historical_data) - - def test_gp_add_sampled_points_singular_covariance_matrix(self): - """Test that GaussianProcess.add_sampled_points indicates a singular covariance matrix when points_sampled contains duplicates (0 noise).""" - test_environment_input = copy.copy(self.gp_test_environment_input) - test_environment_input.num_sampled = 1 - test_environment_input.gaussian_process_class = GaussianProcess - _, gaussian_process = self._build_gaussian_process_test_data(test_environment_input) - - # points one and three have duplicate coordinates and we have noise_variance = 0.0 - point_one = SamplePoint([0.5] * gaussian_process.dim, 1.0, 0.0) - point_two = SamplePoint([1.0] * gaussian_process.dim, -1.0, 0.0) - point_three = point_one - - # points one and two are different, so this is safe - gaussian_process.add_sampled_points([point_one, point_two]) - # point_three is identical to point_one; this will produce a singular covariance matrix - with pytest.raises(C_GP.SingularMatrixException): - gaussian_process.add_sampled_points([point_three]) - - def test_python_and_cpp_return_same_mu_and_gradient(self): - """Compare mu/grad mu results from Python & C++, checking seeral random points per test case.""" - num_tests_per_case = 4 - mu_tolerance = 3.0e-13 - grad_mu_tolerance = 3.0e-12 - - for test_case in self.gp_test_environments: - domain, python_gp = test_case - python_cov, historical_data = python_gp.get_core_data_copy() - - cpp_cov = SquareExponential(python_cov.hyperparameters) - cpp_gp = GaussianProcess(cpp_cov, historical_data) - - for num_to_sample in self.num_to_sample_list: - for _ in xrange(num_tests_per_case): - points_to_sample = domain.generate_uniform_random_points_in_domain(num_to_sample) - - cpp_mu = cpp_gp.compute_mean_of_points(points_to_sample) - python_mu = python_gp.compute_mean_of_points(points_to_sample) - self.assert_vector_within_relative(python_mu, cpp_mu, mu_tolerance) - - cpp_grad_mu = cpp_gp.compute_grad_mean_of_points(points_to_sample) - python_grad_mu = python_gp.compute_grad_mean_of_points(points_to_sample) - self.assert_vector_within_relative(python_grad_mu, cpp_grad_mu, grad_mu_tolerance) - - def test_python_and_cpp_return_same_variance_and_gradient(self): - """Compare var/grad var results from Python & C++, checking seeral random points per test case.""" - num_tests_per_case = 2 - var_tolerance = 3.0e-13 - grad_var_tolerance = 3.0e-12 - - for test_case in self.gp_test_environments: - domain, python_gp = test_case - python_cov, historical_data = python_gp.get_core_data_copy() - - cpp_cov = SquareExponential(python_cov.hyperparameters) - cpp_gp = GaussianProcess(cpp_cov, historical_data) - - for num_to_sample in self.num_to_sample_list: - for _ in xrange(num_tests_per_case): - points_to_sample = domain.generate_uniform_random_points_in_domain(num_to_sample) - - cpp_var = cpp_gp.compute_variance_of_points(points_to_sample) - python_var = python_gp.compute_variance_of_points(points_to_sample) - self.assert_vector_within_relative(python_var, cpp_var, var_tolerance) - - cpp_grad_var = cpp_gp.compute_grad_variance_of_points(points_to_sample) - python_grad_var = python_gp.compute_grad_variance_of_points(points_to_sample) - self.assert_vector_within_relative(python_grad_var, cpp_grad_var, grad_var_tolerance) - - def test_python_and_cpp_return_same_cholesky_variance_and_gradient(self): - """Compare chol_var/grad chol_var results from Python & C++, checking seeral random points per test case.""" - num_tests_per_case = 2 - var_tolerance = 3.0e-12 - # TODO(GH-240): set RNG seed for this case and restore toleranace to 3.0e-12 or better - grad_var_tolerance = 3.0e-10 - - for test_case in self.gp_test_environments: - domain, python_gp = test_case - python_cov, historical_data = python_gp.get_core_data_copy() - - cpp_cov = SquareExponential(python_cov.hyperparameters) - cpp_gp = GaussianProcess(cpp_cov, historical_data) - - for num_to_sample in self.num_to_sample_list: - for _ in xrange(num_tests_per_case): - points_to_sample = domain.generate_uniform_random_points_in_domain(num_to_sample) - - cpp_var = cpp_gp.compute_cholesky_variance_of_points(points_to_sample) - python_var = python_gp.compute_cholesky_variance_of_points(points_to_sample) - self.assert_vector_within_relative(python_var, cpp_var, var_tolerance) - - cpp_grad_var = cpp_gp.compute_grad_cholesky_variance_of_points(points_to_sample) - python_grad_var = python_gp.compute_grad_cholesky_variance_of_points(points_to_sample) - self.assert_vector_within_relative(python_grad_var, cpp_grad_var, grad_var_tolerance) +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED + +if CPP_COMPONENT_INSTALLED: + import copy + + import numpy + + import pytest + + import moe.build.GPP as C_GP + from moe.optimal_learning.python.cpp_wrappers.covariance import SquareExponential + from moe.optimal_learning.python.cpp_wrappers.gaussian_process import GaussianProcess + from moe.optimal_learning.python.data_containers import HistoricalData, SamplePoint + from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase + + + class TestGaussianProcess(GaussianProcessTestCase): + + """Test C++ vs Python implementations of Gaussian Process properties (mean, variance, cholesky variance, and their gradients).""" + + precompute_gaussian_process_data = True + + @classmethod + @pytest.fixture(autouse=True, scope='class') + def base_setup(cls): + """Run the standard setup but seed the RNG first (for repeatability). + + It is easy to stumble into test cases where mean, var terms are very small (e.g., < 1.e-20), + which makes it difficult to set meaningful tolerances for the checks. + + """ + numpy.random.seed(8794) + super(TestGaussianProcess, cls).base_setup() + + def test_sample_point_from_gp(self): + """Test that sampling points from the GP works.""" + point_one = SamplePoint([0.0, 1.0], -1.0, 0.0) + point_two = SamplePoint([2.0, 2.5], 1.0, 0.1) + covariance = SquareExponential([1.0, 1.0, 1.0]) + historical_data = HistoricalData(len(point_one.point), [point_one, point_two]) + + gaussian_process = GaussianProcess(covariance, historical_data) + out_values = numpy.zeros(3) + for i in xrange(3): + out_values[i] = gaussian_process.sample_point_from_gp(point_two.point, 0.001) + + gaussian_process._gaussian_process.reset_to_most_recent_seed() + out_values_test = numpy.ones(3) + for i in xrange(3): + out_values_test[i] = gaussian_process.sample_point_from_gp(point_two.point, 0.001) + + # Exact match b/c we should've run over the exact same computations + self.assert_vector_within_relative(out_values_test, out_values, 0.0) + + # Sampling from a historical point (that had 0 noise) should produce the same value associated w/that point + value = gaussian_process.sample_point_from_gp(point_one.point, 0.0) + self.assert_scalar_within_relative(value, point_one.value, numpy.finfo(numpy.float64).eps) + + def test_gp_construction_singular_covariance_matrix(self): + """Test that the GaussianProcess ctor indicates a singular covariance matrix when points_sampled contains duplicates (0 noise).""" + index = numpy.argmax(numpy.greater_equal(self.num_sampled_list, 1)) + domain, gaussian_process = self.gp_test_environments[index] + point_one = SamplePoint([0.0] * domain.dim, 1.0, 0.0) + # points two and three have duplicate coordinates and we have noise_variance = 0.0 + point_two = SamplePoint([1.0] * domain.dim, 1.0, 0.0) + point_three = point_two + + historical_data = HistoricalData(len(point_one.point), [point_one, point_two, point_three]) + with pytest.raises(C_GP.SingularMatrixException): + GaussianProcess(gaussian_process.get_covariance_copy(), historical_data) + + def test_gp_add_sampled_points_singular_covariance_matrix(self): + """Test that GaussianProcess.add_sampled_points indicates a singular covariance matrix when points_sampled contains duplicates (0 noise).""" + test_environment_input = copy.copy(self.gp_test_environment_input) + test_environment_input.num_sampled = 1 + test_environment_input.gaussian_process_class = GaussianProcess + _, gaussian_process = self._build_gaussian_process_test_data(test_environment_input) + + # points one and three have duplicate coordinates and we have noise_variance = 0.0 + point_one = SamplePoint([0.5] * gaussian_process.dim, 1.0, 0.0) + point_two = SamplePoint([1.0] * gaussian_process.dim, -1.0, 0.0) + point_three = point_one + + # points one and two are different, so this is safe + gaussian_process.add_sampled_points([point_one, point_two]) + # point_three is identical to point_one; this will produce a singular covariance matrix + with pytest.raises(C_GP.SingularMatrixException): + gaussian_process.add_sampled_points([point_three]) + + def test_python_and_cpp_return_same_mu_and_gradient(self): + """Compare mu/grad mu results from Python & C++, checking seeral random points per test case.""" + num_tests_per_case = 4 + mu_tolerance = 3.0e-13 + grad_mu_tolerance = 3.0e-12 + + for test_case in self.gp_test_environments: + domain, python_gp = test_case + python_cov, historical_data = python_gp.get_core_data_copy() + + cpp_cov = SquareExponential(python_cov.hyperparameters) + cpp_gp = GaussianProcess(cpp_cov, historical_data) + + for num_to_sample in self.num_to_sample_list: + for _ in xrange(num_tests_per_case): + points_to_sample = domain.generate_uniform_random_points_in_domain(num_to_sample) + + cpp_mu = cpp_gp.compute_mean_of_points(points_to_sample) + python_mu = python_gp.compute_mean_of_points(points_to_sample) + self.assert_vector_within_relative(python_mu, cpp_mu, mu_tolerance) + + cpp_grad_mu = cpp_gp.compute_grad_mean_of_points(points_to_sample) + python_grad_mu = python_gp.compute_grad_mean_of_points(points_to_sample) + self.assert_vector_within_relative(python_grad_mu, cpp_grad_mu, grad_mu_tolerance) + + def test_python_and_cpp_return_same_variance_and_gradient(self): + """Compare var/grad var results from Python & C++, checking seeral random points per test case.""" + num_tests_per_case = 2 + var_tolerance = 3.0e-13 + grad_var_tolerance = 3.0e-12 + + for test_case in self.gp_test_environments: + domain, python_gp = test_case + python_cov, historical_data = python_gp.get_core_data_copy() + + cpp_cov = SquareExponential(python_cov.hyperparameters) + cpp_gp = GaussianProcess(cpp_cov, historical_data) + + for num_to_sample in self.num_to_sample_list: + for _ in xrange(num_tests_per_case): + points_to_sample = domain.generate_uniform_random_points_in_domain(num_to_sample) + + cpp_var = cpp_gp.compute_variance_of_points(points_to_sample) + python_var = python_gp.compute_variance_of_points(points_to_sample) + self.assert_vector_within_relative(python_var, cpp_var, var_tolerance) + + cpp_grad_var = cpp_gp.compute_grad_variance_of_points(points_to_sample) + python_grad_var = python_gp.compute_grad_variance_of_points(points_to_sample) + self.assert_vector_within_relative(python_grad_var, cpp_grad_var, grad_var_tolerance) + + def test_python_and_cpp_return_same_cholesky_variance_and_gradient(self): + """Compare chol_var/grad chol_var results from Python & C++, checking seeral random points per test case.""" + num_tests_per_case = 2 + var_tolerance = 3.0e-12 + # TODO(GH-240): set RNG seed for this case and restore toleranace to 3.0e-12 or better + grad_var_tolerance = 3.0e-10 + + for test_case in self.gp_test_environments: + domain, python_gp = test_case + python_cov, historical_data = python_gp.get_core_data_copy() + + cpp_cov = SquareExponential(python_cov.hyperparameters) + cpp_gp = GaussianProcess(cpp_cov, historical_data) + + for num_to_sample in self.num_to_sample_list: + for _ in xrange(num_tests_per_case): + points_to_sample = domain.generate_uniform_random_points_in_domain(num_to_sample) + + cpp_var = cpp_gp.compute_cholesky_variance_of_points(points_to_sample) + python_var = python_gp.compute_cholesky_variance_of_points(points_to_sample) + self.assert_vector_within_relative(python_var, cpp_var, var_tolerance) + + cpp_grad_var = cpp_gp.compute_grad_cholesky_variance_of_points(points_to_sample) + python_grad_var = python_gp.compute_grad_cholesky_variance_of_points(points_to_sample) + self.assert_vector_within_relative(python_grad_var, cpp_grad_var, grad_var_tolerance) diff --git a/moe/tests/optimal_learning/python/cpp_wrappers/log_likelihood_test.py b/moe/tests/optimal_learning/python/cpp_wrappers/log_likelihood_test.py index 046be98f..7908a402 100644 --- a/moe/tests/optimal_learning/python/cpp_wrappers/log_likelihood_test.py +++ b/moe/tests/optimal_learning/python/cpp_wrappers/log_likelihood_test.py @@ -1,57 +1,60 @@ # -*- coding: utf-8 -*- """Test cases to check that C++ and Python implementations of :mod:`moe.optimal_learning.python.interfaces.log_likelihood_interface` match.""" -import moe.optimal_learning.python.cpp_wrappers.covariance -import moe.optimal_learning.python.cpp_wrappers.log_likelihood -from moe.optimal_learning.python.geometry_utils import ClosedInterval -import moe.optimal_learning.python.python_version.covariance -import moe.optimal_learning.python.python_version.domain -import moe.optimal_learning.python.python_version.log_likelihood -from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase, GaussianProcessTestEnvironmentInput - - -class TestLogLikelihood(GaussianProcessTestCase): - - """Test that the C++ and Python implementations of the Log Marginal Likelihood match (value and gradient).""" - - 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.2, 1.5), - lower_bound_interval=ClosedInterval(-2.0, 0.5), - upper_bound_interval=ClosedInterval(2.0, 3.5), - covariance_class=moe.optimal_learning.python.python_version.covariance.SquareExponential, - spatial_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, - hyperparameter_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, - ) - - num_sampled_list = (1, 2, 5, 10, 16, 20, 42) - - def test_python_and_cpp_return_same_log_likelihood_and_gradient(self): - """Check that the C++ and Python log likelihood + gradients match over a series of randomly built data sets.""" - tolerance_log_like = 5.0e-11 - tolerance_grad_log_like = 4.0e-12 - - for num_sampled in self.num_sampled_list: - self.gp_test_environment_input.num_sampled = num_sampled - _, python_gp = self._build_gaussian_process_test_data(self.gp_test_environment_input) - python_cov, historical_data = python_gp.get_core_data_copy() - - python_lml = moe.optimal_learning.python.python_version.log_likelihood.GaussianProcessLogMarginalLikelihood(python_cov, historical_data) - cpp_cov = moe.optimal_learning.python.cpp_wrappers.covariance.SquareExponential(python_cov.hyperparameters) - cpp_lml = moe.optimal_learning.python.cpp_wrappers.log_likelihood.GaussianProcessLogMarginalLikelihood(cpp_cov, historical_data) - - python_log_like = python_lml.compute_log_likelihood() - cpp_log_like = cpp_lml.compute_log_likelihood() - self.assert_scalar_within_relative(python_log_like, cpp_log_like, tolerance_log_like) - - python_grad_log_like = python_lml.compute_grad_log_likelihood() - cpp_grad_log_like = cpp_lml.compute_grad_log_likelihood() - self.assert_vector_within_relative(python_grad_log_like, cpp_grad_log_like, tolerance_grad_log_like) +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED + +if CPP_COMPONENT_INSTALLED: + import moe.optimal_learning.python.cpp_wrappers.covariance + import moe.optimal_learning.python.cpp_wrappers.log_likelihood + from moe.optimal_learning.python.geometry_utils import ClosedInterval + import moe.optimal_learning.python.python_version.covariance + import moe.optimal_learning.python.python_version.domain + import moe.optimal_learning.python.python_version.log_likelihood + from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase, GaussianProcessTestEnvironmentInput + + + class TestLogLikelihood(GaussianProcessTestCase): + + """Test that the C++ and Python implementations of the Log Marginal Likelihood match (value and gradient).""" + + 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.2, 1.5), + lower_bound_interval=ClosedInterval(-2.0, 0.5), + upper_bound_interval=ClosedInterval(2.0, 3.5), + covariance_class=moe.optimal_learning.python.python_version.covariance.SquareExponential, + spatial_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, + hyperparameter_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, + ) + + num_sampled_list = (1, 2, 5, 10, 16, 20, 42) + + def test_python_and_cpp_return_same_log_likelihood_and_gradient(self): + """Check that the C++ and Python log likelihood + gradients match over a series of randomly built data sets.""" + tolerance_log_like = 5.0e-11 + tolerance_grad_log_like = 4.0e-12 + + for num_sampled in self.num_sampled_list: + self.gp_test_environment_input.num_sampled = num_sampled + _, python_gp = self._build_gaussian_process_test_data(self.gp_test_environment_input) + python_cov, historical_data = python_gp.get_core_data_copy() + + python_lml = moe.optimal_learning.python.python_version.log_likelihood.GaussianProcessLogMarginalLikelihood(python_cov, historical_data) + cpp_cov = moe.optimal_learning.python.cpp_wrappers.covariance.SquareExponential(python_cov.hyperparameters) + cpp_lml = moe.optimal_learning.python.cpp_wrappers.log_likelihood.GaussianProcessLogMarginalLikelihood(cpp_cov, historical_data) + + python_log_like = python_lml.compute_log_likelihood() + cpp_log_like = cpp_lml.compute_log_likelihood() + self.assert_scalar_within_relative(python_log_like, cpp_log_like, tolerance_log_like) + + python_grad_log_like = python_lml.compute_grad_log_likelihood() + cpp_grad_log_like = cpp_lml.compute_grad_log_likelihood() + self.assert_vector_within_relative(python_grad_log_like, cpp_grad_log_like, tolerance_grad_log_like) diff --git a/moe/tests/optimal_learning/python/cpp_wrappers/optimization_test.py b/moe/tests/optimal_learning/python/cpp_wrappers/optimization_test.py index 7086b517..2250b097 100644 --- a/moe/tests/optimal_learning/python/cpp_wrappers/optimization_test.py +++ b/moe/tests/optimal_learning/python/cpp_wrappers/optimization_test.py @@ -6,61 +6,64 @@ MOE does not yet support the ability to optimize arbitrary Python functions through C++-coded optimizers. """ -import copy - -import pytest - -from moe.optimal_learning.python.cpp_wrappers.optimization import NewtonParameters, GradientDescentParameters - - -class TestOptimizerParameters(object): - - """Test that the various optimizer parameter classes (wrapping C++ objects) work.""" - - @classmethod - @pytest.fixture(autouse=True, scope='class') - def base_setup(cls): - """Set up dummy parameters for testing optimization parameter structs.""" - cls.newton_param_dict = { - 'num_multistarts': 10, - 'max_num_steps': 20, - 'gamma': 1.05, - 'time_factor': 1.0e-5, - 'max_relative_change': 0.8, - 'tolerance': 3.7e-8, - } - - # new object b/c we want to modify it - cls.gd_param_dict = copy.deepcopy(cls.newton_param_dict) - del cls.gd_param_dict['time_factor'] - cls.gd_param_dict.update({ - 'max_num_restarts': 50, - 'num_steps_averaged': 11, - 'pre_mult': 0.45, - }) - - @staticmethod - def _parameter_test_core(param_type, param_dict): - """Test param struct construction, member read/write, and equality check.""" - # Check construction - params = param_type(**param_dict) - - # Check that the internal state matches the input - test_params_dict = dict(params._get_member_dict()) - assert test_params_dict == param_dict - - # New object is equal to old when params match - params_other = param_type(**param_dict) - assert params_other == params - - # Inequality when we change a param - params_other.gamma += 1.2 - assert params_other != params - - def test_newton_parameters(self): - """Test that ``NewtonParameters`` is created correctly and comparison works.""" - self._parameter_test_core(NewtonParameters, self.newton_param_dict) - - def test_gradient_descent_parameters(self): - """Test that ``GradientDescentParameters`` is created correctly and comparison works.""" - self._parameter_test_core(GradientDescentParameters, self.gd_param_dict) +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED + +if CPP_COMPONENT_INSTALLED: + import copy + + import pytest + + from moe.optimal_learning.python.cpp_wrappers.optimization import NewtonParameters, GradientDescentParameters + + + class TestOptimizerParameters(object): + + """Test that the various optimizer parameter classes (wrapping C++ objects) work.""" + + @classmethod + @pytest.fixture(autouse=True, scope='class') + def base_setup(cls): + """Set up dummy parameters for testing optimization parameter structs.""" + cls.newton_param_dict = { + 'num_multistarts': 10, + 'max_num_steps': 20, + 'gamma': 1.05, + 'time_factor': 1.0e-5, + 'max_relative_change': 0.8, + 'tolerance': 3.7e-8, + } + + # new object b/c we want to modify it + cls.gd_param_dict = copy.deepcopy(cls.newton_param_dict) + del cls.gd_param_dict['time_factor'] + cls.gd_param_dict.update({ + 'max_num_restarts': 50, + 'num_steps_averaged': 11, + 'pre_mult': 0.45, + }) + + @staticmethod + def _parameter_test_core(param_type, param_dict): + """Test param struct construction, member read/write, and equality check.""" + # Check construction + params = param_type(**param_dict) + + # Check that the internal state matches the input + test_params_dict = dict(params._get_member_dict()) + assert test_params_dict == param_dict + + # New object is equal to old when params match + params_other = param_type(**param_dict) + assert params_other == params + + # Inequality when we change a param + params_other.gamma += 1.2 + assert params_other != params + + def test_newton_parameters(self): + """Test that ``NewtonParameters`` is created correctly and comparison works.""" + self._parameter_test_core(NewtonParameters, self.newton_param_dict) + + def test_gradient_descent_parameters(self): + """Test that ``GradientDescentParameters`` is created correctly and comparison works.""" + self._parameter_test_core(GradientDescentParameters, self.gd_param_dict) diff --git a/moe/tests/views/exceptions_test.py b/moe/tests/views/exceptions_test.py index 8780517f..5af92ca2 100644 --- a/moe/tests/views/exceptions_test.py +++ b/moe/tests/views/exceptions_test.py @@ -11,6 +11,7 @@ from webtest.app import AppError +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase from moe.tests.views.rest_test_case import RestTestCase from moe.views.constant import ALL_REST_MOE_ROUTES, GP_MEAN_VAR_ENDPOINT, GP_NEXT_POINTS_EPI_ENDPOINT @@ -39,12 +40,16 @@ class TestRestGaussianProcessWithExceptions(GaussianProcessTestCase, RestTestCas def test_empty_json_payload_invalid(self): """Test empty json payload causes an AppError.""" + if not CPP_COMPONENT_INSTALLED: + return for moe_route in ALL_REST_MOE_ROUTES: with pytest.raises(AppError): self.testapp.post(moe_route.endpoint, {}) def test_badly_formed_json_payload_invalid(self): """Test malformed json payload causes a ValueError.""" + if not CPP_COMPONENT_INSTALLED: + return truth_result = self.testapp.post(GP_MEAN_VAR_ENDPOINT, '}', expect_errors=True) for moe_route in ALL_REST_MOE_ROUTES: test_result = self.testapp.post(moe_route.endpoint, '}', expect_errors=True) @@ -52,6 +57,8 @@ def test_badly_formed_json_payload_invalid(self): def test_invalid_hyperparameters_input(self): """Test that invalid hyperparameters (via GP_MEAN_VAR_ENDPOINT) generate expected Response with error message.""" + if not CPP_COMPONENT_INSTALLED: + return endpoint = GP_MEAN_VAR_ENDPOINT dict_payload = copy.deepcopy(GpMeanVarView._pretty_default_request) @@ -69,6 +76,8 @@ def test_invalid_hyperparameters_input(self): def test_invalid_points_sampled_input(self): """Test that duplicate points_sampled (via GP_NEXT_POINTS_EPI_ENDPOINT) generate expected Response with error message.""" + if not CPP_COMPONENT_INSTALLED: + return endpoint = GP_NEXT_POINTS_EPI_ENDPOINT dict_payload = copy.deepcopy(GpNextPointsPrettyView._pretty_default_request) diff --git a/moe/tests/views/rest/bandit_test.py b/moe/tests/views/rest/bandit_test.py index c2c12612..d1bc7c63 100644 --- a/moe/tests/views/rest/bandit_test.py +++ b/moe/tests/views/rest/bandit_test.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Test class for bandit views.""" import pyramid.testing - import simplejson as json from moe.bandit.linkers import BANDIT_ENDPOINTS_TO_SUBTYPES diff --git a/moe/tests/views/rest/gp_ei_test.py b/moe/tests/views/rest/gp_ei_test.py index 7305c2e2..e3232927 100644 --- a/moe/tests/views/rest/gp_ei_test.py +++ b/moe/tests/views/rest/gp_ei_test.py @@ -4,14 +4,17 @@ import simplejson as json -from moe.optimal_learning.python.cpp_wrappers.covariance import SquareExponential -from moe.optimal_learning.python.cpp_wrappers.expected_improvement import ExpectedImprovement -from moe.optimal_learning.python.cpp_wrappers.gaussian_process import GaussianProcess +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase from moe.tests.views.rest_test_case import RestTestCase from moe.views.constant import GP_EI_ENDPOINT from moe.views.schemas.rest.gp_ei import GpEiResponse +if CPP_COMPONENT_INSTALLED: + from moe.optimal_learning.python.cpp_wrappers.covariance import SquareExponential + from moe.optimal_learning.python.cpp_wrappers.expected_improvement import ExpectedImprovement + from moe.optimal_learning.python.cpp_wrappers.gaussian_process import GaussianProcess + class TestGpEiView(GaussianProcessTestCase, RestTestCase): @@ -35,6 +38,8 @@ def _build_json_payload(domain, covariance, historical_data, points_to_evaluate) def test_interface_returns_same_as_cpp(self): """Test that the /gp/ei endpoint does the same thing as the C++ interface.""" + if not CPP_COMPONENT_INSTALLED: + return tolerance = 1.0e-11 for test_case in self.gp_test_environments: python_domain, python_gp = test_case diff --git a/moe/tests/views/rest/gp_hyper_opt_test.py b/moe/tests/views/rest/gp_hyper_opt_test.py index 5bde3a2d..ca55b496 100644 --- a/moe/tests/views/rest/gp_hyper_opt_test.py +++ b/moe/tests/views/rest/gp_hyper_opt_test.py @@ -4,7 +4,7 @@ import simplejson as json -from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_GRADIENT_DESCENT_PARAMETERS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, GRADIENT_DESCENT_OPTIMIZER +from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_GRADIENT_DESCENT_PARAMETERS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, GRADIENT_DESCENT_OPTIMIZER, CPP_COMPONENT_INSTALLED from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase from moe.tests.views.rest_test_case import RestTestCase from moe.views.constant import GP_HYPER_OPT_MOE_ROUTE @@ -49,6 +49,8 @@ def _build_json_payload(domain, covariance, historical_data): def test_hyperparameters_passed_through(self): """Test that the hyperparameters get passed through to the endpoint.""" + if not CPP_COMPONENT_INSTALLED: + return test_case = self.gp_test_environments[0] python_domain, python_gp = test_case @@ -81,6 +83,8 @@ def test_hyperparameters_passed_through(self): def test_optimizer_params_passed_through(self): """Test that the optimizer parameters get passed through to the endpoint.""" + if not CPP_COMPONENT_INSTALLED: + return test_case = self.gp_test_environments[0] python_domain, python_gp = test_case @@ -118,6 +122,8 @@ def test_optimizer_params_passed_through(self): def test_interface_returns_same_as_cpp(self): """Integration test for the /gp/hyper_opt endpoint.""" + if not CPP_COMPONENT_INSTALLED: + return moe_route = GP_HYPER_OPT_MOE_ROUTE for test_case in self.gp_test_environments: python_domain, python_gp = test_case diff --git a/moe/tests/views/rest/gp_mean_var_test.py b/moe/tests/views/rest/gp_mean_var_test.py index 199b954f..d4f857e3 100644 --- a/moe/tests/views/rest/gp_mean_var_test.py +++ b/moe/tests/views/rest/gp_mean_var_test.py @@ -4,13 +4,15 @@ import simplejson as json -from moe.optimal_learning.python.cpp_wrappers.covariance import SquareExponential -from moe.optimal_learning.python.cpp_wrappers.gaussian_process import GaussianProcess +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase from moe.tests.views.rest_test_case import RestTestCase from moe.views.constant import GP_MEAN_ENDPOINT, GP_VAR_ENDPOINT, GP_VAR_DIAG_ENDPOINT, GP_MEAN_VAR_ENDPOINT, GP_MEAN_VAR_DIAG_ENDPOINT from moe.views.schemas.rest.gp_mean_var import GpMeanResponse, GpVarResponse, GpVarDiagResponse, GpMeanVarResponse, GpMeanVarDiagResponse +if CPP_COMPONENT_INSTALLED: + from moe.optimal_learning.python.cpp_wrappers.covariance import SquareExponential + from moe.optimal_learning.python.cpp_wrappers.gaussian_process import GaussianProcess class TestGpMeanVarView(GaussianProcessTestCase, RestTestCase): @@ -33,6 +35,8 @@ def _build_json_payload(domain, covariance, historical_data, points_to_evaluate) def test_mean_var_interface_returns_same_as_cpp(self): """Test that the /gp/mean_var endpoint does the same thing as the C++ interface.""" + if not CPP_COMPONENT_INSTALLED: + return tolerance = 1.0e-11 for test_case in self.gp_test_environments: python_domain, python_gp = test_case @@ -95,6 +99,8 @@ def _compare_endpoint_mean_var_results( def test_interfaces_equivalent(self): """Test that the /gp/mean, var, mean_var, etc. endpoints are consistent.""" + if not CPP_COMPONENT_INSTALLED: + return tolerance = numpy.finfo(numpy.float64).eps for test_case in self.gp_test_environments: python_domain, python_gp = test_case diff --git a/moe/tests/views/rest/gp_next_points_test.py b/moe/tests/views/rest/gp_next_points_test.py index 3de569d2..f59a19a2 100644 --- a/moe/tests/views/rest/gp_next_points_test.py +++ b/moe/tests/views/rest/gp_next_points_test.py @@ -4,7 +4,7 @@ import simplejson as json -from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_GRADIENT_DESCENT_PARAMETERS, TEST_LBFGSB_PARAMETERS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_EXPECTED_IMPROVEMENT_MC_ITERATIONS, CONSTANT_LIAR_METHODS +from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_GRADIENT_DESCENT_PARAMETERS, TEST_LBFGSB_PARAMETERS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_EXPECTED_IMPROVEMENT_MC_ITERATIONS, CONSTANT_LIAR_METHODS, CPP_COMPONENT_INSTALLED from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase from moe.tests.views.rest_test_case import RestTestCase from moe.views.constant import ALL_NEXT_POINTS_MOE_ROUTES, GP_NEXT_POINTS_CONSTANT_LIAR_ROUTE_NAME, GP_NEXT_POINTS_CONSTANT_LIAR_ENDPOINT, GP_NEXT_POINTS_EPI_ROUTE_NAME @@ -65,6 +65,8 @@ def test_optimizer_params_passed_through(self): """Test that the optimizer parameters get passed through to the endpoint.""" # TODO(GH-305): turn this into a unit test by going through OptimizableGpPrettyView # and mocking out dependencies (instead of awkwardly constructing a more complex object). + if not CPP_COMPONENT_INSTALLED: + return test_case = self.gp_test_environments[0] num_to_sample = 1 @@ -108,6 +110,8 @@ def test_optimizer_params_passed_through(self): def test_all_constant_liar_methods_function(self): """Test that each contant liar ``lie_method`` runs to completion. This is an integration test.""" + if not CPP_COMPONENT_INSTALLED: + return for test_case in self.gp_test_environments: python_domain, python_gp = test_case python_cov, historical_data = python_gp.get_core_data_copy() @@ -136,6 +140,8 @@ def test_all_constant_liar_methods_function(self): def test_interface_returns_same_as_cpp(self): """Integration test for the /gp/next_points/* endpoints.""" + if not CPP_COMPONENT_INSTALLED: + return for moe_route in ALL_NEXT_POINTS_MOE_ROUTES: for test_case in self.gp_test_environments: for num_to_sample in (1, 2, 4): diff --git a/moe/views/gp_next_points_pretty_view.py b/moe/views/gp_next_points_pretty_view.py index c7b432ad..ec05780e 100644 --- a/moe/views/gp_next_points_pretty_view.py +++ b/moe/views/gp_next_points_pretty_view.py @@ -8,17 +8,20 @@ """ import numpy -import moe.optimal_learning.python.cpp_wrappers.expected_improvement -from moe.optimal_learning.python.cpp_wrappers.expected_improvement import ExpectedImprovement from moe.optimal_learning.python.python_version.expected_improvement import ExpectedImprovement as PythonExpectedImprovement from moe.optimal_learning.python.repeated_domain import RepeatedDomain import moe.optimal_learning.python.python_version.optimization as python_optimization from moe.optimal_learning.python.timing import timing_context +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED from moe.views.gp_pretty_view import GpPrettyView from moe.views.optimizable_gp_pretty_view import OptimizableGpPrettyView from moe.views.schemas.gp_next_points_pretty_view import GpNextPointsRequest, GpNextPointsResponse from moe.views.utils import _make_gp_from_params, _make_domain_from_params, _make_optimizer_parameters_from_params, _make_mvndst_parameters_from_params +if CPP_COMPONENT_INSTALLED: + import moe.optimal_learning.python.cpp_wrappers.expected_improvement + from moe.optimal_learning.python.cpp_wrappers.expected_improvement import ExpectedImprovement + EPI_OPTIMIZATION_TIMING_LABEL = 'EPI optimization time' @@ -75,7 +78,7 @@ def compute_next_points_to_sample_response(self, params, optimizer_method_name, num_mc_iterations = params.get('mc_iterations') max_num_threads = params.get('max_num_threads') - gaussian_process = _make_gp_from_params(params) + gaussian_process = _make_gp_from_params(params, python_version=True) ei_opt_status = {} # TODO(GH-89): Make the optimal_learning library handle this case 'organically' with @@ -95,7 +98,7 @@ def compute_next_points_to_sample_response(self, params, optimizer_method_name, optimizer_class, optimizer_parameters, num_random_samples = _make_optimizer_parameters_from_params(params) - if optimizer_class == python_optimization.LBFGSBOptimizer: + if optimizer_class == python_optimization.LBFGSBOptimizer or not CPP_COMPONENT_INSTALLED: domain = RepeatedDomain(num_to_sample, _make_domain_from_params(params, python_version=True)) expected_improvement_evaluator = PythonExpectedImprovement( gaussian_process, @@ -107,8 +110,9 @@ def compute_next_points_to_sample_response(self, params, optimizer_method_name, opt_method = getattr(moe.optimal_learning.python.python_version.expected_improvement, optimizer_method_name) else: domain = _make_domain_from_params(params, python_version=False) + cpp_gaussian_process = _make_gp_from_params(params) expected_improvement_evaluator = ExpectedImprovement( - gaussian_process, + cpp_gaussian_process, points_being_sampled=points_being_sampled, num_mc_iterations=num_mc_iterations, ) diff --git a/moe/views/rest/gp_ei.py b/moe/views/rest/gp_ei.py index f8901bc2..fbba5491 100644 --- a/moe/views/rest/gp_ei.py +++ b/moe/views/rest/gp_ei.py @@ -10,7 +10,7 @@ from pyramid.view import view_config -from moe.optimal_learning.python.cpp_wrappers.expected_improvement import ExpectedImprovement +from moe.optimal_learning.python.python_version.expected_improvement import ExpectedImprovement from moe.optimal_learning.python.timing import timing_context from moe.views.constant import GP_EI_ROUTE_NAME, GP_EI_PRETTY_ROUTE_NAME from moe.views.gp_pretty_view import GpPrettyView @@ -74,7 +74,7 @@ def gp_ei_view(self): points_being_sampled = numpy.array(params.get('points_being_sampled')) num_mc_iterations = params.get('mc_iterations') max_num_threads = params.get('max_num_threads') - gaussian_process = _make_gp_from_params(params) + gaussian_process = _make_gp_from_params(params, python_version=True) expected_improvement_evaluator = ExpectedImprovement( gaussian_process, diff --git a/moe/views/rest/gp_hyper_opt.py b/moe/views/rest/gp_hyper_opt.py index a397ed58..1a3ef0aa 100644 --- a/moe/views/rest/gp_hyper_opt.py +++ b/moe/views/rest/gp_hyper_opt.py @@ -8,8 +8,7 @@ """ from pyramid.view import view_config -from moe.optimal_learning.python.constant import OPTIMIZER_TYPE_AND_OBJECTIVE_TO_DEFAULT_PARAMETERS, ENDPOINT_TO_DEFAULT_OPTIMIZER_TYPE -from moe.optimal_learning.python.cpp_wrappers.log_likelihood import multistart_hyperparameter_optimization +from moe.optimal_learning.python.constant import OPTIMIZER_TYPE_AND_OBJECTIVE_TO_DEFAULT_PARAMETERS, ENDPOINT_TO_DEFAULT_OPTIMIZER_TYPE, CPP_COMPONENT_INSTALLED from moe.optimal_learning.python.linkers import LOG_LIKELIHOOD_TYPES_TO_LOG_LIKELIHOOD_METHODS from moe.optimal_learning.python.timing import timing_context from moe.views.constant import GP_HYPER_OPT_ROUTE_NAME, GP_HYPER_OPT_PRETTY_ROUTE_NAME @@ -19,6 +18,9 @@ from moe.views.schemas.rest.gp_hyper_opt import GpHyperOptRequest, GpHyperOptResponse from moe.views.utils import _make_domain_from_params, _make_gp_from_params, _make_optimizer_parameters_from_params +if CPP_COMPONENT_INSTALLED: + from moe.optimal_learning.python.cpp_wrappers.log_likelihood import multistart_hyperparameter_optimization + MODEL_SELECTION_TIMING_LABEL = 'model selection time' @@ -104,6 +106,8 @@ def gp_hyper_opt_view(self): :status 500: server error """ + if not CPP_COMPONENT_INSTALLED: + return params = self.get_params_from_request() max_num_threads = params.get('max_num_threads') diff --git a/moe/views/utils.py b/moe/views/utils.py index 1ac8f6b9..4980b610 100644 --- a/moe/views/utils.py +++ b/moe/views/utils.py @@ -1,12 +1,17 @@ # -*- coding: utf-8 -*- """Utilities for MOE views.""" + from moe.bandit.data_containers import HistoricalData as BanditHistoricalData from moe.bandit.data_containers import SampleArm -from moe.optimal_learning.python.cpp_wrappers.gaussian_process import GaussianProcess +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED from moe.optimal_learning.python.data_containers import SamplePoint, HistoricalData -from moe.optimal_learning.python.geometry_utils import ClosedInterval from moe.optimal_learning.python.linkers import DOMAIN_TYPES_TO_DOMAIN_LINKS, COVARIANCE_TYPES_TO_CLASSES, OPTIMIZER_TYPES_TO_OPTIMIZER_METHODS +from moe.optimal_learning.python.geometry_utils import ClosedInterval from moe.optimal_learning.python.python_version.expected_improvement import MVNDSTParameters +from moe.optimal_learning.python.python_version.gaussian_process import GaussianProcess as PythonGaussianProcess + +if CPP_COMPONENT_INSTALLED: + from moe.optimal_learning.python.cpp_wrappers.gaussian_process import GaussianProcess def _make_domain_from_params(params, domain_info_key="domain_info", python_version=False): @@ -32,7 +37,7 @@ def _make_domain_from_params(params, domain_info_key="domain_info", python_versi return domain_class(domain_bounds_iterable) -def _make_covariance_of_process_from_params(params): +def _make_covariance_of_process_from_params(params, python_version=False): """Create and return a C++ backed covariance_of_process from the request params as a dict. ``params`` has the following form:: @@ -47,7 +52,10 @@ def _make_covariance_of_process_from_params(params): """ covariance_info = params.get("covariance_info") - covariance_class = COVARIANCE_TYPES_TO_CLASSES[covariance_info.get('covariance_type')].cpp_covariance_class + if python_version == False: + covariance_class = COVARIANCE_TYPES_TO_CLASSES[covariance_info.get('covariance_type')].cpp_covariance_class + else: + covariance_class = COVARIANCE_TYPES_TO_CLASSES[covariance_info.get('covariance_type')].python_covariance_class hyperparameters = covariance_info.get('hyperparameters') if hyperparameters is None: @@ -71,7 +79,7 @@ def _make_optimizer_parameters_from_params(params): optimizer_method = OPTIMIZER_TYPES_TO_OPTIMIZER_METHODS[optimizer_info.get('optimizer_type')] - if optimizer_method.cpp_optimizer_class is not None: + if optimizer_method.cpp_optimizer_class is not None and CPP_COMPONENT_INSTALLED: # TODO(GH-167): Kill this when you reoganize num_multistarts for C++. validated_optimizer_parameters['num_multistarts'] = optimizer_info['num_multistarts'] optimizer_parameters = optimizer_method.cpp_parameters_class(**validated_optimizer_parameters) @@ -92,7 +100,7 @@ def _make_mvndst_parameters_from_params(params): return MVNDSTParameters(**mvndst_parameters) -def _make_gp_from_params(params): +def _make_gp_from_params(params, python_version=False): """Create and return a C++ backed gaussian_process from the request params as a dict. ``params`` has the following form:: @@ -112,7 +120,7 @@ def _make_gp_from_params(params): domain_info = params.get("domain_info") points_sampled = gp_historical_info.get('points_sampled') - covariance_of_process = _make_covariance_of_process_from_params(params) + covariance_of_process = _make_covariance_of_process_from_params(params, python_version) sample_point_list = [] for point in points_sampled: @@ -124,10 +132,16 @@ def _make_gp_from_params(params): ) ) - gaussian_process = GaussianProcess( + if python_version == False: + gaussian_process = GaussianProcess( + covariance_of_process, + HistoricalData(domain_info.get('dim'), sample_point_list), + ) + else: + gaussian_process = PythonGaussianProcess( covariance_of_process, HistoricalData(domain_info.get('dim'), sample_point_list), - ) + ) return gaussian_process diff --git a/moe_examples/combined_example.py b/moe_examples/combined_example.py index 4ec055db..f075eaad 100644 --- a/moe_examples/combined_example.py +++ b/moe_examples/combined_example.py @@ -19,6 +19,7 @@ from moe.easy_interface.experiment import Experiment from moe.easy_interface.simple_endpoint import gp_next_points, gp_hyper_opt, gp_mean_var +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED def function_to_minimize(x): @@ -113,4 +114,5 @@ def run_example( if __name__ == '__main__': - run_example() + if CPP_COMPONENT_INSTALLED: + run_example() diff --git a/moe_examples/hyper_opt_of_gp_from_historical_data.py b/moe_examples/hyper_opt_of_gp_from_historical_data.py index f8c310fe..8bd3ed82 100644 --- a/moe_examples/hyper_opt_of_gp_from_historical_data.py +++ b/moe_examples/hyper_opt_of_gp_from_historical_data.py @@ -11,6 +11,7 @@ from moe.easy_interface.simple_endpoint import gp_hyper_opt from moe.optimal_learning.python.data_containers import SamplePoint +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED # Randomly generate some historical data # points_sampled is an iterable of iterables of the form [point_as_a_list, objective_function_value, value_variance] @@ -31,4 +32,5 @@ def run_example(verbose=True, **kwargs): if __name__ == '__main__': - run_example() + if CPP_COMPONENT_INSTALLED: + run_example() diff --git a/moe_examples/mean_and_var_of_gp_from_historic_data.py b/moe_examples/mean_and_var_of_gp_from_historic_data.py index 62b1bce8..e90afa3c 100644 --- a/moe_examples/mean_and_var_of_gp_from_historic_data.py +++ b/moe_examples/mean_and_var_of_gp_from_historic_data.py @@ -11,6 +11,7 @@ import numpy from moe.easy_interface.simple_endpoint import gp_mean_var +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED # Randomly generate some historical data # points_sampled is an iterable of iterables of the form [point_as_a_list, objective_function_value, value_variance] @@ -36,4 +37,5 @@ def run_example(verbose=True, testapp=None, **kwargs): if __name__ == '__main__': - run_example() + if CPP_COMPONENT_INSTALLED: + run_example() diff --git a/moe_examples/next_point_via_simple_endpoint.py b/moe_examples/next_point_via_simple_endpoint.py index e5771018..96ce75e3 100644 --- a/moe_examples/next_point_via_simple_endpoint.py +++ b/moe_examples/next_point_via_simple_endpoint.py @@ -17,6 +17,7 @@ from moe.easy_interface.experiment import Experiment from moe.easy_interface.simple_endpoint import gp_next_points from moe.optimal_learning.python.data_containers import SamplePoint +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED # Note: this function can be anything, the output of a batch, results of an A/B experiment, the value of a physical experiment etc. @@ -48,4 +49,5 @@ def run_example(num_points_to_sample=20, verbose=True, **kwargs): if __name__ == '__main__': - run_example() + if CPP_COMPONENT_INSTALLED: + run_example() diff --git a/moe_examples/tests/combined_example_test.py b/moe_examples/tests/combined_example_test.py index 8a799aaf..8de8a04b 100644 --- a/moe_examples/tests/combined_example_test.py +++ b/moe_examples/tests/combined_example_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """Integration test for combined_example MOE example.""" -from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_GRADIENT_DESCENT_PARAMETERS +from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_GRADIENT_DESCENT_PARAMETERS, CPP_COMPONENT_INSTALLED from moe_examples.tests.moe_example_test_case import MoeExampleTestCase from moe_examples.combined_example import run_example @@ -12,6 +12,8 @@ class TestCombinedExample(MoeExampleTestCase): def test_example_runs_with_non_default_optimizer_kwargs(self): """Simple integration test for example with non default kwargs.""" + if not CPP_COMPONENT_INSTALLED: + return run_example( num_to_sample=1, verbose=False, diff --git a/moe_examples/tests/hyper_opt_of_gp_from_historical_data_test.py b/moe_examples/tests/hyper_opt_of_gp_from_historical_data_test.py index 06a47b16..1b529939 100644 --- a/moe_examples/tests/hyper_opt_of_gp_from_historical_data_test.py +++ b/moe_examples/tests/hyper_opt_of_gp_from_historical_data_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """Integration test for hyper_opt_of_gp_from_historical_data MOE example.""" -from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_GRADIENT_DESCENT_PARAMETERS, GRADIENT_DESCENT_OPTIMIZER +from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_GRADIENT_DESCENT_PARAMETERS, GRADIENT_DESCENT_OPTIMIZER, CPP_COMPONENT_INSTALLED from moe_examples.tests.moe_example_test_case import MoeExampleTestCase from moe_examples.hyper_opt_of_gp_from_historical_data import run_example @@ -12,6 +12,8 @@ class TestHyperOptOfGpFromHistoricalData(MoeExampleTestCase): def test_example_runs(self): """Simple integration test for example.""" + if not CPP_COMPONENT_INSTALLED: + return run_example( verbose=False, testapp=self.testapp, diff --git a/moe_examples/tests/mean_and_var_of_gp_from_historic_data_test.py b/moe_examples/tests/mean_and_var_of_gp_from_historic_data_test.py index 75cf0858..00f7d866 100644 --- a/moe_examples/tests/mean_and_var_of_gp_from_historic_data_test.py +++ b/moe_examples/tests/mean_and_var_of_gp_from_historic_data_test.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- """Integration test for mean_and_var_of_gp_from_historic_data MOE example.""" +from moe.optimal_learning.python.constant import CPP_COMPONENT_INSTALLED + from moe_examples.tests.moe_example_test_case import MoeExampleTestCase from moe_examples.mean_and_var_of_gp_from_historic_data import run_example @@ -10,6 +12,8 @@ class TestMeanAndVarOfGpFromHistoricData(MoeExampleTestCase): def test_example_runs(self): """Simple integration test for example.""" + if not CPP_COMPONENT_INSTALLED: + return run_example( verbose=False, testapp=self.testapp, diff --git a/moe_examples/tests/next_point_via_simple_endpoint_test.py b/moe_examples/tests/next_point_via_simple_endpoint_test.py index f1a47214..0a6bf855 100644 --- a/moe_examples/tests/next_point_via_simple_endpoint_test.py +++ b/moe_examples/tests/next_point_via_simple_endpoint_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """Integration test for next_point_via_simple_endpoint MOE example.""" -from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_GRADIENT_DESCENT_PARAMETERS +from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_GRADIENT_DESCENT_PARAMETERS, CPP_COMPONENT_INSTALLED from moe_examples.tests.moe_example_test_case import MoeExampleTestCase from moe_examples.next_point_via_simple_endpoint import run_example @@ -12,6 +12,8 @@ class TestNextPointsViaSimpleEndpoint(MoeExampleTestCase): def test_example_runs(self): """Simple integration test for example.""" + if not CPP_COMPONENT_INSTALLED: + return run_example( num_points_to_sample=1, verbose=False, diff --git a/setup.py b/setup.py index e2391e3a..7d8970e3 100644 --- a/setup.py +++ b/setup.py @@ -84,6 +84,16 @@ def find_path(moe_executable): return path +def find_custom_packages(): + """ If MOE_NO_BUILD_CPP set to True, python modules that expose CPP functions + will not be installed. + :return: list of package names + """ + env = os.environ.copy() + if env.get('MOE_NO_BUILD_CPP', 'False') == 'True': + return find_packages(exclude=['*cpp*']) + else: + return find_packages() class InstallCppComponents(install): @@ -221,7 +231,7 @@ def run(self): author_email='opensource+moe@yelp.com', url='https://github.com/Yelp/MOE', keywords='bayesian global optimization optimal learning expected improvement experiment design', - packages=find_packages(), + packages=find_custom_packages(), include_package_data=True, zip_safe=False, install_requires=requires, From 9845ddbfd0c1c313bdf9cd43822d8c9d07f83ebb Mon Sep 17 00:00:00 2001 From: jialeiwang Date: Fri, 23 Oct 2015 09:25:56 -0400 Subject: [PATCH 2/2] minor style fix --- moe/views/utils.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/moe/views/utils.py b/moe/views/utils.py index 4980b610..c507893d 100644 --- a/moe/views/utils.py +++ b/moe/views/utils.py @@ -52,10 +52,7 @@ def _make_covariance_of_process_from_params(params, python_version=False): """ covariance_info = params.get("covariance_info") - if python_version == False: - covariance_class = COVARIANCE_TYPES_TO_CLASSES[covariance_info.get('covariance_type')].cpp_covariance_class - else: - covariance_class = COVARIANCE_TYPES_TO_CLASSES[covariance_info.get('covariance_type')].python_covariance_class + covariance_class = COVARIANCE_TYPES_TO_CLASSES[covariance_info.get('covariance_type')].cpp_covariance_class if not python_version else COVARIANCE_TYPES_TO_CLASSES[covariance_info.get('covariance_type')].python_covariance_class hyperparameters = covariance_info.get('hyperparameters') if hyperparameters is None: @@ -132,7 +129,7 @@ def _make_gp_from_params(params, python_version=False): ) ) - if python_version == False: + if not python_version: gaussian_process = GaussianProcess( covariance_of_process, HistoricalData(domain_info.get('dim'), sample_point_list),