├── .gitignore ├── .mailmap ├── .travis.yml ├── AUTHORS.md ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── __init__.py ├── conda-recipe ├── README.md ├── Vagrantfile ├── boost │ ├── 6bb71fdd.patch │ ├── build.sh │ ├── e4bde20f.patch │ └── meta.yaml ├── certifi │ ├── build.sh │ └── meta.yaml ├── colander │ ├── build.sh │ └── meta.yaml ├── libstdcplusplus │ ├── build.sh │ └── meta.yaml ├── mako │ ├── build.sh │ └── meta.yaml ├── moe │ ├── build.sh │ └── meta.yaml ├── paste │ ├── build.sh │ └── meta.yaml ├── pyramid_mako │ ├── build.sh │ └── meta.yaml ├── simplejson │ ├── build.sh │ └── meta.yaml ├── waitress │ ├── build.sh │ └── meta.yaml └── webtest │ ├── build.sh │ └── meta.yaml ├── development.ini ├── docs ├── Makefile ├── bandit.rst ├── conf.py ├── contributing.rst ├── cpp_rst_maker.py ├── demo_tutorial.rst ├── doxygen_config ├── examples.rst ├── faq.rst ├── index.rst ├── install.rst ├── make.bat ├── modules.rst ├── moe_math.rst ├── objective_functions.rst ├── pretty_endpoints.rst └── why_moe.rst ├── moe ├── __init__.py ├── bandit │ ├── __init__.py │ ├── bandit_interface.py │ ├── bla │ │ ├── __init__.py │ │ └── bla.py │ ├── constant.py │ ├── data_containers.py │ ├── epsilon │ │ ├── __init__.py │ │ ├── epsilon_first.py │ │ ├── epsilon_greedy.py │ │ └── epsilon_interface.py │ ├── linkers.py │ ├── ucb │ │ ├── __init__.py │ │ ├── ucb1.py │ │ ├── ucb1_tuned.py │ │ └── ucb_interface.py │ └── utils.py ├── easy_interface │ ├── __init__.py │ ├── bandit_simple_endpoint.py │ ├── experiment.py │ └── simple_endpoint.py ├── optimal_learning │ ├── __init__.py │ ├── cpp │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── gpp_common.hpp │ │ ├── gpp_covariance.cpp │ │ ├── gpp_covariance.hpp │ │ ├── gpp_covariance_test.cpp │ │ ├── gpp_covariance_test.hpp │ │ ├── gpp_domain.cpp │ │ ├── gpp_domain.hpp │ │ ├── gpp_domain_test.cpp │ │ ├── gpp_domain_test.hpp │ │ ├── gpp_exception.cpp │ │ ├── gpp_exception.hpp │ │ ├── gpp_expected_improvement_demo.cpp │ │ ├── gpp_expected_improvement_gpu.cpp │ │ ├── gpp_expected_improvement_gpu.hpp │ │ ├── gpp_expected_improvement_gpu_test.cpp │ │ ├── gpp_expected_improvement_gpu_test.hpp │ │ ├── gpp_geometry.hpp │ │ ├── gpp_geometry_test.cpp │ │ ├── gpp_geometry_test.hpp │ │ ├── gpp_heuristic_expected_improvement_optimization.cpp │ │ ├── gpp_heuristic_expected_improvement_optimization.hpp │ │ ├── gpp_heuristic_expected_improvement_optimization_test.cpp │ │ ├── gpp_heuristic_expected_improvement_optimization_test.hpp │ │ ├── gpp_hyper_and_EI_demo.cpp │ │ ├── gpp_hyperparameter_optimization_demo.cpp │ │ ├── gpp_linear_algebra-inl.hpp │ │ ├── gpp_linear_algebra.cpp │ │ ├── gpp_linear_algebra.hpp │ │ ├── gpp_linear_algebra_test.cpp │ │ ├── gpp_linear_algebra_test.hpp │ │ ├── gpp_logging.cpp │ │ ├── gpp_logging.hpp │ │ ├── gpp_math.cpp │ │ ├── gpp_math.hpp │ │ ├── gpp_math_test.cpp │ │ ├── gpp_math_test.hpp │ │ ├── gpp_mock_optimization_objective_functions.hpp │ │ ├── gpp_model_selection.cpp │ │ ├── gpp_model_selection.hpp │ │ ├── gpp_model_selection_test.cpp │ │ ├── gpp_model_selection_test.hpp │ │ ├── gpp_optimization.hpp │ │ ├── gpp_optimization_test.cpp │ │ ├── gpp_optimization_test.hpp │ │ ├── gpp_optimizer_parameters.hpp │ │ ├── gpp_python.cpp │ │ ├── gpp_python_common.cpp │ │ ├── gpp_python_common.hpp │ │ ├── gpp_python_expected_improvement.cpp │ │ ├── gpp_python_expected_improvement.hpp │ │ ├── gpp_python_gaussian_process.cpp │ │ ├── gpp_python_gaussian_process.hpp │ │ ├── gpp_python_model_selection.cpp │ │ ├── gpp_python_model_selection.hpp │ │ ├── gpp_python_test.cpp │ │ ├── gpp_python_test.hpp │ │ ├── gpp_random.cpp │ │ ├── gpp_random.hpp │ │ ├── gpp_random_test.cpp │ │ ├── gpp_random_test.hpp │ │ ├── gpp_test_utils.cpp │ │ ├── gpp_test_utils.hpp │ │ ├── gpp_test_utils_test.cpp │ │ ├── gpp_test_utils_test.hpp │ │ └── gpu │ │ │ ├── CMakeLists.txt │ │ │ ├── gpp_cuda_math.cu │ │ │ └── gpp_cuda_math.hpp │ └── python │ │ ├── __init__.py │ │ ├── comparison.py │ │ ├── constant.py │ │ ├── cpp_wrappers │ │ ├── __init__.py │ │ ├── covariance.py │ │ ├── cpp_utils.py │ │ ├── domain.py │ │ ├── expected_improvement.py │ │ ├── gaussian_process.py │ │ ├── log_likelihood.py │ │ └── optimization.py │ │ ├── data_containers.py │ │ ├── geometry_utils.py │ │ ├── interfaces │ │ ├── __init__.py │ │ ├── covariance_interface.py │ │ ├── domain_interface.py │ │ ├── expected_improvement_interface.py │ │ ├── gaussian_process_interface.py │ │ ├── log_likelihood_interface.py │ │ └── optimization_interface.py │ │ ├── linkers.py │ │ ├── python_version │ │ ├── __init__.py │ │ ├── covariance.py │ │ ├── domain.py │ │ ├── expected_improvement.py │ │ ├── gaussian_process.py │ │ ├── log_likelihood.py │ │ ├── optimization.py │ │ └── python_utils.py │ │ ├── repeated_domain.py │ │ └── timing.py ├── resources.py ├── static │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── d3.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── ico │ │ └── favicon.ico │ ├── img │ │ ├── MOE_full_logo.png │ │ ├── MOE_full_logo_2x.png │ │ ├── ad_unit_examples.png │ │ ├── moe_demo_four_point.png │ │ ├── moe_demo_hyper_big_length.png │ │ ├── moe_demo_hyper_big_sig_var.png │ │ ├── moe_demo_hyper_default.png │ │ ├── moe_demo_one_point.png │ │ ├── moe_demo_pretty_endpoint.png │ │ ├── moe_demo_start.png │ │ ├── moe_demo_ten_point.png │ │ ├── moe_demo_three_point.png │ │ ├── moe_demo_two_point.png │ │ ├── moe_gui_index.png │ │ ├── moe_logo_24.png │ │ ├── moe_logo_48.png │ │ ├── moe_logo_96.png │ │ ├── moe_loop.png │ │ └── moe_loop_biz_dist.png │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ ├── exception.js │ │ └── gp_plot.js ├── templates │ ├── gp_plot.mako │ ├── index.mako │ ├── layout.mako │ └── pretty_input.mako ├── tests │ ├── __init__.py │ ├── bandit │ │ ├── __init__.py │ │ ├── bandit_interface_test.py │ │ ├── bandit_test_case.py │ │ ├── bla │ │ │ ├── __init__.py │ │ │ └── bla_test.py │ │ ├── data_containers_test.py │ │ ├── epsilon │ │ │ ├── __init__.py │ │ │ ├── epsilon_first_test.py │ │ │ ├── epsilon_greedy_test.py │ │ │ ├── epsilon_test.py │ │ │ └── epsilon_test_case.py │ │ ├── linkers_test.py │ │ ├── ucb │ │ │ ├── __init__.py │ │ │ ├── ucb1_test.py │ │ │ ├── ucb1_tuned_test.py │ │ │ └── ucb_test_case.py │ │ └── utils_test.py │ ├── optimal_learning │ │ ├── __init__.py │ │ └── python │ │ │ ├── __init__.py │ │ │ ├── comparison_test.py │ │ │ ├── cpp_unit_tests │ │ │ ├── __init__.py │ │ │ └── cpp_unit_test_wrapper_test.py │ │ │ ├── cpp_wrappers │ │ │ ├── __init__.py │ │ │ ├── exception_test.py │ │ │ ├── expected_improvement_test.py │ │ │ ├── gaussian_process_test.py │ │ │ ├── log_likelihood_test.py │ │ │ └── optimization_test.py │ │ │ ├── gaussian_process_test_case.py │ │ │ ├── gaussian_process_test_utils.py │ │ │ ├── geometry_utils_test.py │ │ │ ├── linkers_test.py │ │ │ ├── optimal_learning_test_case.py │ │ │ └── python_version │ │ │ ├── __init__.py │ │ │ ├── covariance_test.py │ │ │ ├── expected_improvement_test.py │ │ │ ├── log_likelihood_test.py │ │ │ └── optimization_test.py │ └── views │ │ ├── __init__.py │ │ ├── exceptions_test.py │ │ ├── rest │ │ ├── __init__.py │ │ ├── bandit_bla_test.py │ │ ├── bandit_epsilon_test.py │ │ ├── bandit_test.py │ │ ├── bandit_ucb_test.py │ │ ├── gp_ei_test.py │ │ ├── gp_hyper_opt_test.py │ │ ├── gp_mean_var_test.py │ │ └── gp_next_points_test.py │ │ ├── rest_test_case.py │ │ └── utils_test.py └── views │ ├── __init__.py │ ├── bandit_pretty_view.py │ ├── constant.py │ ├── exceptions.py │ ├── frontend.py │ ├── gp_next_points_pretty_view.py │ ├── gp_pretty_view.py │ ├── optimizable_gp_pretty_view.py │ ├── pretty_view.py │ ├── rest │ ├── __init__.py │ ├── bandit_bla.py │ ├── bandit_epsilon.py │ ├── bandit_ucb.py │ ├── gp_ei.py │ ├── gp_hyper_opt.py │ ├── gp_mean_var.py │ ├── gp_next_points_constant_liar.py │ ├── gp_next_points_epi.py │ └── gp_next_points_kriging.py │ ├── schemas │ ├── __init__.py │ ├── bandit_pretty_view.py │ ├── base_schemas.py │ ├── gp_next_points_pretty_view.py │ └── rest │ │ ├── __init__.py │ │ ├── bandit_bla.py │ │ ├── bandit_epsilon.py │ │ ├── bandit_ucb.py │ │ ├── gp_ei.py │ │ ├── gp_hyper_opt.py │ │ ├── gp_mean_var.py │ │ ├── gp_next_points_constant_liar.py │ │ └── gp_next_points_kriging.py │ └── utils.py ├── moe_examples ├── __init__.py ├── bandit_example.py ├── blog_post_example_ab_testing.py ├── combined_example.py ├── hyper_opt_of_gp_from_historical_data.py ├── mean_and_var_of_gp_from_historic_data.py ├── next_point_via_simple_endpoint.py └── tests │ ├── __init__.py │ ├── bandit_example_test.py │ ├── combined_example_test.py │ ├── hyper_opt_of_gp_from_historical_data_test.py │ ├── mean_and_var_of_gp_from_historic_data_test.py │ ├── moe_example_test_case.py │ └── next_point_via_simple_endpoint_test.py ├── pep8-blacklist.txt ├── production.ini ├── requirements.txt ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | *.log 3 | 4 | # Swap files 5 | *.sw[nop] 6 | *~ 7 | 8 | # Data files 9 | *.mat 10 | 11 | # OSX crap 12 | *.DS_Store 13 | 14 | # Python setuptools 15 | *.egg-info 16 | 17 | # Compiled Object files 18 | *.slo 19 | *.lo 20 | *.o 21 | *.pyc 22 | 23 | # Compiled Dynamic libraries 24 | *.so 25 | *.dylib 26 | 27 | # Compiled Static libraries 28 | *.lai 29 | *.la 30 | *.a 31 | 32 | # built docs files 33 | docs/_* 34 | docs/xml/* 35 | docs/latex/* 36 | docs/html/* 37 | docs/.doctrees/* 38 | docs/moe.*.rst 39 | docs/moe.rst 40 | docs/moe_examples.rst 41 | docs/moe_examples.*.rst 42 | docs/gpp_*.rst 43 | docs/cpp_tree.rst 44 | 45 | # built binaries 46 | build 47 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Scott Clark 2 | Scott Clark 3 | Eric Liu 4 | Deniz Oktay 5 | Deniz Oktay 6 | norases 7 | JiaLei Wang 8 | JiaLei Wang 9 | JiaLei Wang 10 | JiaLei Wang 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | cache: 5 | - apt 6 | before_install: 7 | - sudo apt-get update -qq 8 | - sudo apt-get install -y build-essential libblas-dev liblapack-dev gfortran make libedit-dev 9 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo apt-get update -q && sudo apt-get install -y gcc-4.7 g++-4.7 && sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.7 20 && sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.7 20 && sudo update-alternatives --config gcc && sudo update-alternatives --config g++ 10 | - sudo add-apt-repository -y ppa:boost-latest/ppa && sudo apt-get update -q && sudo apt-get install -y boost1.55 11 | - sudo add-apt-repository -y ppa:kalakris/cmake && sudo apt-get update -q && sudo apt-get install -y cmake 12 | - sudo add-apt-repository -y ppa:chris-lea/python-numpy && sudo apt-get update -q && sudo apt-get install -y python-numpy 13 | - wget http://ppa.launchpad.net/libreoffice/ppa/ubuntu/pool/main/d/doxygen/doxygen_1.8.7-2~precise1_amd64.deb -O doxygen.deb 14 | - sudo dpkg -i doxygen.deb 15 | install: 16 | - pip install -r requirements.txt 17 | - pip install flake8 flake8-import-order pep8-naming flake8-docstrings 18 | # Tell MOE to install the Python specified in Travis (b/c many are installed) 19 | # The env-var may be "x.x_with_system_site_packages" so we need to extract just the front of the string 20 | - export MOE_CMAKE_OPTS="-D MOE_PYTHON_ADDITIONAL_VERSIONS='`echo $TRAVIS_PYTHON_VERSION | cut -d '_' -f 1`'" 21 | - python setup.py install 22 | script: 23 | - make -B test 24 | - make -B style-test 25 | - make -B docs 26 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | MOE Authors 2 | =========== 3 | 4 | MOE was developed at [Yelp Inc][0] and [Cornell University][1]. It spawned from [Scott Clark's PhD thesis][2] and original implementation. 5 | 6 | Co-Devlopers 7 | ------------ 8 | 9 | - Scott Clark (sclark@yelp.com, [@DrScottClark][3], http://scottclark.io) 10 | - Eric Liu (eliu@yelp.com, [@ehliu][4]) 11 | 12 | Contributors 13 | ------------ 14 | 15 | - Peter Frazier (pf98@cornell.edu, http://people.orie.cornell.edu/pfrazier/) 16 | - JiaLei Wang (jw865@cornell.edu, http://www.jialeiwang.net, [@garywang304][5]) 17 | - Deniz Oktay (denizokt@mit.edu) 18 | - Norases Vesdapunt (norases@cs.stanford.edu, [@norasesv][6]) 19 | [0]: http://www.yelp.com/careers 20 | [1]: http://www.orie.cornell.edu/ 21 | [2]: https://github.com/sc932/Thesis 22 | [3]: https://twitter.com/drscottclark 23 | [4]: https://twitter.com/ehliu 24 | [5]: https://twitter.com/garywang304 25 | [6]: https://twitter.com/norasesv 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | * Features 2 | 3 | * Changes 4 | 5 | * Bugs 6 | 7 | ## v0.2.2 (2014-12-5) 8 | 9 | SHA: ``83a71512e6a4aa553866e4301ef11f172d485e23`` 10 | 11 | * Features 12 | 13 | * Added startup message to REST server including tips for OSX users (#400) 14 | * Added GPU support to ``cpp_wrappers.expected_improvement.multistart_expected_improvement_optimization``; requires ``max_num_threads == 1`` until future multi-GPU support (#368) 15 | * Added the COBYLA optimizer to the expected improvement optimization class. (#370) 16 | * OptimizerParameter struct members now directly readable/writeable from Python; added EqualityComparisonMixin (#138) 17 | * C++ GradientDescentParameters object now stores ``num_steps_averaged`` (but does not use it yet) (#391) 18 | * conda build system for MOE (#417) 19 | 20 | * Changes 21 | 22 | * Switched from `testify` to `py.test` - http://pytest.org/ (#36) 23 | * [cleanup] Moved bandits into their own sub directories (#375) 24 | * Supply PYTHON_LIBRARY and PYTHON_INCLUDE_DIR vars to cmake automatically in ``setup.py`` (#412) 25 | * Added warning when colander version is out of date (#413) 26 | 27 | * Bugs 28 | 29 | * Fixed UCB1 and UCB1-tuned algorithm confidence bound calculation (#432) 30 | 31 | ## v0.2.1 (2014-09-22) 32 | 33 | SHA: ``ab6f959c11a0cacbed6dad618fe6ffed71092116`` 34 | 35 | * Features 36 | 37 | * Implemented BLA (Bayesian Learning Automaton). (#373) 38 | * Connected GPU functions to multistart gradient descent optimizer. (#270) 39 | 40 | * Changes 41 | 42 | * Bugs 43 | 44 | * variance in a sample arm was dropped in _make_bandit_historical_info_from_params. (#385) 45 | * SampleArm's ``__add__`` and ``__str__`` were broken. (#387) 46 | * Specifying ``max_num_threads`` on GPU compute paths caused a segfault (#394) 47 | 48 | ## v0.2.0 (2014-08-15) 49 | 50 | SHA: ``8201917e3f9b47b8edd8039ea3278ef8631b0f2a`` 51 | 52 | * Features 53 | 54 | * Added multi-armed bandit endpoint. (#255) 55 | * Implemented epsilon-greedy. (#255) 56 | * Implemented epsilon-first. (#335) 57 | * Implemented UCB1. (#354) 58 | * Implemented UCB1-tuned. (#366) 59 | * Added support for the L-BFGS-B optimizer. (#296) 60 | * Added GPU implementation for q,p-EI and its gradient computation. (#219) 61 | * Speed up GPU functions by redesign of memory allocation. (#297) 62 | 63 | * Changes 64 | 65 | * Split up old ``schemas.py`` file into ``schemas/`` directory with several subfiles (#291) 66 | * Improved Dockerfile, reducing Docker-based install times substantially, https://hub.docker.com/u/yelpmoe/ (#332) 67 | * Created ``min_reqs`` docker container which is a snapshot of all MOE third-party requirements 68 | * Created ``latest``, which tracks the latest MOE build 69 | * Started releasing docker containers for each tagged MOE release (currently just ``v0.1.0``) 70 | * ``GradientDescentOptimization`` (C++) no longer has a separate ``next_points`` output (#186) 71 | * LogLikelihood evaluate at point list and latin hypercube search now return status dicts like every other optimizer (#189) 72 | * status dicts also a little more informative/standardized now 73 | * Update C++ autodoc tools to handle the new ``gpu`` directory (#353) 74 | * Added ``__version__`` to ``moe/__init__.py`` (#353) 75 | 76 | * Bugs 77 | 78 | * Throw exceptions (C++) if ``num_multistarts`` or ``num_random_samples`` is 0 (#345) 79 | * ``combined_example`` endpoint was not passing ``kwargs`` through so users could not change the default server (#356) 80 | * fix sometimes dropped general ``kwargs`` (#358) 81 | * ``mean_var_of_gp_from_historic_data`` was also not passing ``kwargs`` (#359) 82 | 83 | ## v0.1.0 (2014-07-29) 84 | 85 | SHA: ``5fef1d242cc8b6e0d6443522f8ba73ba743607de`` 86 | 87 | * Features 88 | 89 | * initial open source release 90 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM yelpmoe/min_reqs 2 | MAINTAINER Scott Clark and Eric Liu 3 | 4 | # Install pip systemwide for Python. 5 | ADD https://raw.github.com/pypa/pip/master/contrib/get-pip.py /tmp/get-pip.py 6 | RUN python /tmp/get-pip.py 7 | 8 | # Install python requirements (these should be all in yelpmoe/min_reqs, but it is done again here to be safe) 9 | ADD requirements.txt /home/app/MOE/ 10 | RUN cd /home/app/MOE/ && pip install -r requirements.txt 11 | 12 | # Copy over the code 13 | ADD . /home/app/MOE/ 14 | WORKDIR /home/app/MOE 15 | 16 | # Install the python 17 | ENV MOE_NO_BUILD_CPP True 18 | RUN python setup.py install 19 | 20 | # Build the C++ 21 | WORKDIR /home/app/MOE/moe 22 | RUN rm -rf build 23 | RUN mkdir build 24 | WORKDIR /home/app/MOE/moe/build 25 | RUN cmake /home/app/MOE/moe/optimal_learning/cpp/ 26 | RUN make 27 | 28 | # Copy the built C++ into the python 29 | RUN cp -r /home/app/MOE/moe/build $(python -c "import site; print(site.getsitepackages()[0])")/moe/. 30 | 31 | RUN chown -R app:app /home/app/MOE && chmod -R a+r /home/app/MOE 32 | WORKDIR /home/app/MOE 33 | 34 | # Run tests 35 | RUN make test 36 | 37 | # Configure docker container. 38 | EXPOSE 6543 39 | 40 | # Set up the webserver 41 | USER app 42 | CMD ["development.ini"] 43 | ENTRYPOINT ["pserve"] 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012-2014 Yelp 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | # top level scripts 3 | include setup.py 4 | # config files 5 | include *.ini 6 | # docs 7 | recursive-include doc * 8 | # optimal learning 9 | recursive-include moe/optimal_learning * 10 | # webapp 11 | recursive-include moe * 12 | 13 | # remove what we don't want, kept in step with .gitignore 14 | # Logs 15 | global-exclude *.log 16 | # Swap files 17 | global-exclude *.sw[nop] 18 | global-exclude *~ 19 | # Data files 20 | global-exclude *.mat 21 | # OSX crap 22 | global-exclude *.DS_Store 23 | # Python setuptools 24 | global-exclude *.egg-info 25 | # Compiled Object files 26 | global-exclude *.slo 27 | global-exclude *.lo 28 | global-exclude *.o 29 | global-exclude *.pyc 30 | # Compiled Dynamic libraries 31 | global-exclude *.so 32 | global-exclude *.dylib 33 | # Compiled Static libraries 34 | global-exclude *.lai 35 | global-exclude *.la 36 | global-exclude *.a 37 | # built doc files 38 | recursive-exclude doc/_* * 39 | recursive-exclude doc/xml/* * 40 | recursive-exclude doc/latex/* * 41 | recursive-exclude doc/html/* * 42 | recursive-exclude doc/.doctrees/* * 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: production 2 | 3 | clean: 4 | find . -name '*.pyc' -delete 5 | rm -rf moe/build 6 | 7 | production: 8 | python setup.py install 9 | 10 | test: 11 | py.test -v moe/tests moe_examples/tests 12 | 13 | style-test: 14 | pip install flake8 flake8-import-order pep8-naming flake8-docstrings pyflakes 15 | flake8 --ignore=E501,E126,E123,I101,I100,N806 moe 16 | pep257 moe 17 | pyflakes moe 18 | flake8 --ignore=E501,E126,E123,I101,I100,N806 moe_examples 19 | pep257 moe_examples 20 | pyflakes moe_examples 21 | 22 | docs: 23 | python docs/cpp_rst_maker.py 24 | doxygen docs/doxygen_config 25 | sphinx-apidoc -f -o docs moe 26 | sphinx-apidoc -f -T -o docs moe_examples 27 | sphinx-build -b html docs docs/_build/html 28 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /conda-recipe/README.md: -------------------------------------------------------------------------------- 1 | conda-moe 2 | ========= 3 | 4 | This repository provides the recipes for building binary packages of [MOE](https://github.com/yelp/moe), a global, black box optimization engine for real world metric optimization, to be used with the Anaconda Python distribution or other conda environments. 5 | 6 | The built binaries are available on binstar.org, and can be installed using the following command 7 | 8 | ``` 9 | conda config --add channels https://conda.binstar.org/rmcgibbo 10 | conda install moe 11 | ``` 12 | 13 | This package is available for linux-64, targetting Ubuntu 10.04 or later, CentOS 6.0+ or later, or similar. 14 | 15 | ### Notes 16 | This repository obviously includes recipes for a number of packages other than MOE. These are all direct or indirect dependencies of MOE, which are not included with the `default` conda channels, and we therefore build and push to binstar to make things _just work_. 17 | 18 | ### libstdc++ 19 | 20 | MOE uses C++11 features, which requires a quite modern version of GCC (4.7.3+), and a similarly new version of the runtime c++ standard library, libstd++. The version of libstdc++ distributed with Ubuntu 10.04 and CentOS 6.0 is 21 | too old to support runtime C++11 code. Therefore, this conda build of MOE depends on an external libstdc++ conda package, which povides a more recent library. See [the recipe](https://github.com/rmcgibbo/conda-moe/blob/master/libstdcplusplus/build.sh) for the dirty, dirty hack. 22 | 23 | ### building the binaries 24 | 25 | Our builds of these conda recipes were performed on a fresh Ubuntu 10.04 64-bit VM (vagrant) with the following provisioning: 26 | 27 | ``` 28 | sudo apt-get update 29 | sudo apt-get install python-software-properties 30 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 31 | sudo apt-get update 32 | sudo apt-get install libgcc-4.7-dev cpp-4.7 gcc-4.7-base g++-4.7 make libbz2-1.0 libbz2-dev git-core 33 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.7 20 34 | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.7 20 35 | 36 | wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh 37 | bash Miniconda-latest-Linux-x86_64.sh -b 38 | export PATH=$HOME/miniconda/bin/:$PATH 39 | conda install conda-build 40 | 41 | # The conda recipes for the boost dependency are in a separate github 42 | # repository: 43 | # https://github.com/rmcgibbo/conda-cheminformatics/tree/master/boost 44 | conda config --add channels https://conda.binstar.org/rmcgibbo 45 | 46 | git clone https://github.com/rmcgibbo/conda-moe.git 47 | cd conda-moe 48 | conda build * 49 | ``` 50 | -------------------------------------------------------------------------------- /conda-recipe/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | $script = < 14 | 15 | 16 | 17 | 18 | 19 | 20 | 39 | 40 |
41 | ${next.body()} 42 |
43 | 44 |
45 |
© 2012-2014 Yelp
46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /moe/templates/pretty_input.mako: -------------------------------------------------------------------------------- 1 | <%inherit file="moe:templates/layout.mako"/> 2 | 3 |
4 |

Pretty input for ${ endpoint } endpoint

5 |

Documentation 6 |

7 |

8 |

9 |
10 |
11 | 12 | 13 | 29 | -------------------------------------------------------------------------------- /moe/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test directory for all MOE unit and integration tests.""" 3 | -------------------------------------------------------------------------------- /moe/tests/bandit/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Testing code for the (Python) bandit library. 3 | 4 | Testing is done via the Testify package: 5 | https://github.com/Yelp/Testify 6 | 7 | This package includes: 8 | 9 | * Test cases/test setup files 10 | * Tests for bandit/epsilon: :mod:`moe.tests.bandit.epsilon` 11 | * Tests for bandit/ucb: :mod:`moe.tests.bandit.ucb` 12 | * Tests for bandit/bla: :mod:`moe.tests.bandit.bla` 13 | 14 | This package includes: 15 | 16 | * Test cases/test setup files 17 | * Tests for classes and utils in :mod:`moe.bandit` 18 | 19 | **Files in this package** 20 | 21 | * :mod:`moe.tests.bandit.bandit_interface_test`: tests for :mod:`moe.bandit.interfaces.bandit_interface.BanditInterface` 22 | * :mod:`moe.tests.bandit.bandit_test_case`: base test case for bandit tests with a simple integration test case 23 | * :mod:`moe.tests.bandit.linkers_test`: tests for :mod:`moe.bandit.linkers` 24 | * :mod:`moe.tests.bandit.utils_test`: tests for :mod:`moe.bandit.utils` 25 | 26 | """ 27 | -------------------------------------------------------------------------------- /moe/tests/bandit/bandit_interface_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test bandit interface implementation.""" 3 | import pytest 4 | 5 | import logging 6 | 7 | from moe.bandit.bandit_interface import BanditInterface 8 | from moe.tests.bandit.bandit_test_case import BanditTestCase 9 | 10 | 11 | @pytest.fixture() 12 | def disable_logging(request): 13 | """Disable logging (for the duration of this test case).""" 14 | logging.disable(logging.CRITICAL) 15 | 16 | def finalize(): 17 | """Re-enable logging (so other test cases are unaffected).""" 18 | logging.disable(logging.NOTSET) 19 | request.addfinalizer(finalize) 20 | 21 | 22 | class TestBanditInterface(BanditTestCase): 23 | 24 | """Verify that different historical infos return correct results.""" 25 | 26 | @pytest.mark.usefixtures("disable_logging") 27 | def test_empty_arm_invalid(self): 28 | """Test empty ``arms_to_allocations`` causes an ValueError.""" 29 | with pytest.raises(ValueError): 30 | BanditInterface.choose_arm({}) 31 | 32 | def test_one_arm(self): 33 | """Check that the one-arm case always returns the given arm as the winning arm.""" 34 | arms_to_allocations = {"arm1": 1.0} 35 | assert BanditInterface.choose_arm(arms_to_allocations) == "arm1" 36 | 37 | def test_two_arms_one_winner(self): 38 | """Check that the two-arms case with one winner always returns the winning arm.""" 39 | arms_to_allocations = {"arm1": 1.0, "arm2": 0.0} 40 | assert BanditInterface.choose_arm(arms_to_allocations) == "arm1" 41 | 42 | def test_three_arms_two_winners(self): 43 | """Check that the three-arms cases with two winners return one of the two winners.""" 44 | arms_to_allocations = {"arm1": 0.5, "arm2": 0.5, "arm3": 0.0} 45 | assert BanditInterface.choose_arm(arms_to_allocations) in frozenset(["arm1", "arm2"]) 46 | -------------------------------------------------------------------------------- /moe/tests/bandit/bla/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Testing code for the (Python) BLA bandit policies. 3 | 4 | **Files in this package** 5 | * :mod:`moe.tests.bandit.bla.bla_test`: tests for :mod:`moe.bandit.bla.bla.BLA` 6 | 7 | """ 8 | -------------------------------------------------------------------------------- /moe/tests/bandit/bla/bla_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test UCB1 bandit implementation. 3 | 4 | Test that the one arm case returns the given arm as the winning arm. 5 | Test that two-arm cases with specified random seeds return expected results. 6 | 7 | """ 8 | import numpy 9 | 10 | from moe.bandit.bla.bla import BLA 11 | from moe.bandit.constant import DEFAULT_BLA_SUBTYPE 12 | from moe.tests.bandit.bandit_test_case import BanditTestCase 13 | 14 | 15 | class TestBLA(BanditTestCase): 16 | 17 | """Verify that different historical infos return correct results.""" 18 | 19 | bandit_class = BLA 20 | 21 | def test_one_arm(self): 22 | """Check that the one-arm case always returns the given arm as the winning arm and the allocation is 1.0.""" 23 | bandit = self.bandit_class(self.one_arm_test_case) 24 | self._test_one_arm(bandit) 25 | 26 | def test_two_arms_one_winner(self): 27 | """Check that the two-arms case with random seed 0 always allocate arm1:1.0 and arm2:0.0.""" 28 | old_state = numpy.random.get_state() 29 | numpy.random.seed(0) 30 | bandit = self.bandit_class(self.two_arms_test_case, DEFAULT_BLA_SUBTYPE) 31 | arms_to_allocations = bandit.allocate_arms() 32 | assert arms_to_allocations == {"arm1": 1.0, "arm2": 0.0} 33 | assert bandit.choose_arm(arms_to_allocations) == "arm1" 34 | numpy.random.set_state(old_state) 35 | -------------------------------------------------------------------------------- /moe/tests/bandit/epsilon/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Testing code for the (Python) epsilon bandit policies. 3 | 4 | **Files in this package** 5 | * :mod:`moe.tests.bandit.epsilon.epsilon_first_test`: tests for :mod:`moe.bandit.epsilon.epsilon_greedy.EpsilonFirst` 6 | * :mod:`moe.tests.bandit.epsilon.epsilon_greedy_test`: tests for :mod:`moe.bandit.epsilon.epsilon_greedy.EpsilonGreedy` 7 | * :mod:`moe.tests.bandit.epsilon.epsilon_test`: tests for :mod:`moe.bandit.epsilon.epsilon_greedy.Epsilon` 8 | * :mod:`moe.tests.bandit.epsilon.epsilon_test_case`: test cases for classes under :mod:`moe.bandit.epsilon.epsilon.Epsilon` 9 | 10 | """ 11 | -------------------------------------------------------------------------------- /moe/tests/bandit/epsilon/epsilon_greedy_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test epsilon-greedy bandit implementation. 3 | 4 | Test default values with one, two, and three arms. 5 | Test one arm with various epsilon values. 6 | 7 | """ 8 | from moe.bandit.epsilon.epsilon_greedy import EpsilonGreedy 9 | from moe.tests.bandit.epsilon.epsilon_test_case import EpsilonTestCase 10 | 11 | 12 | class TestEpsilonGreedy(EpsilonTestCase): 13 | 14 | """Verify that different epsilon values and historical infos return correct results.""" 15 | 16 | bandit_class = EpsilonGreedy 17 | 18 | def test_init_default(self): 19 | """Verify that default values do not throw and error. This is purely an integration test.""" 20 | self._test_init_default() 21 | 22 | def test_one_arm(self): 23 | """Check that the one-arm case always returns the given arm as the winning arm and the allocation is 1.0.""" 24 | for epsilon in self.epsilons_to_test: 25 | bandit = self.bandit_class(self.one_arm_test_case, epsilon) 26 | self._test_one_arm(bandit) 27 | 28 | def test_two_unsampled_arms(self): 29 | """Check that the two-unsampled-arms case always allocate each arm equally (the allocation is 0.5 for both arms). This tests num_winning_arms == num_arms > 1.""" 30 | for epsilon in self.epsilons_to_test: 31 | bandit = self.bandit_class(self.two_unsampled_arms_test_case, epsilon) 32 | assert bandit.allocate_arms() == {"arm1": 0.5, "arm2": 0.5} 33 | 34 | def test_two_arms_epsilon_zero(self): 35 | """Check that the two-arms case with zero epsilon always allocate arm1:1.0 and arm2:0.0 when average payoffs are arm1:1.0 and arm2:0.0.""" 36 | epsilon = 0.0 37 | bandit = self.bandit_class(self.two_arms_test_case, epsilon) 38 | arms_to_allocations = bandit.allocate_arms() 39 | assert arms_to_allocations == {"arm1": 1.0, "arm2": 0.0} 40 | assert bandit.choose_arm(arms_to_allocations) == "arm1" 41 | 42 | def test_two_arms_epsilon_one(self): 43 | """Check that the two-arms case with one epsilon always allocate arm1:0.5 and arm2:0.5 when average payoffs are arm1:1.0 and arm2:0.0.""" 44 | epsilon = 1.0 45 | bandit = self.bandit_class(self.two_arms_test_case, epsilon) 46 | assert bandit.allocate_arms() == {"arm1": 0.5, "arm2": 0.5} 47 | 48 | def test_three_arms(self): 49 | """Check that the three-arms cases with integer and float payoffs return the expected arm allocations.""" 50 | epsilon = 0.03 51 | for historical_info in [self.three_arms_test_case, self.three_arms_float_payoffs_test_case]: 52 | bandit = self.bandit_class(historical_info, epsilon) 53 | assert bandit.allocate_arms() == {"arm1": 0.98, "arm2": 0.01, "arm3": 0.01} 54 | 55 | def test_three_arms_two_winners(self): 56 | """Check that the three-arms cases with two winners return the expected arm allocations. This tests num_arms > num_winning_arms > 1.""" 57 | epsilon = 0.03 58 | bandit = self.bandit_class(self.three_arms_two_winners_test_case, epsilon) 59 | assert bandit.allocate_arms() == {"arm1": 0.495, "arm2": 0.495, "arm3": 0.01} 60 | -------------------------------------------------------------------------------- /moe/tests/bandit/epsilon/epsilon_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test epsilon bandit implementation (functions common to epsilon bandit). 3 | 4 | Test functions in :class:`moe.bandit.epsilon.epsilon_interface.EpsilonInterface` 5 | 6 | """ 7 | import pytest 8 | 9 | import logging 10 | 11 | from moe.bandit.epsilon.epsilon_interface import EpsilonInterface 12 | from moe.tests.bandit.epsilon.epsilon_test_case import EpsilonTestCase 13 | 14 | 15 | @pytest.fixture() 16 | def disable_logging(request): 17 | """Disable logging (for the duration of this test case).""" 18 | logging.disable(logging.CRITICAL) 19 | 20 | def finalize(): 21 | """Re-enable logging (so other test cases are unaffected).""" 22 | logging.disable(logging.NOTSET) 23 | request.addfinalizer(finalize) 24 | 25 | 26 | class TestEpsilon(EpsilonTestCase): 27 | 28 | """Verify that different sample_arms return correct results.""" 29 | 30 | @pytest.mark.usefixtures("disable_logging") 31 | def test_empty_arm_invalid(self): 32 | """Test empty ``sample_arms`` causes an ValueError.""" 33 | with pytest.raises(ValueError): 34 | EpsilonInterface.get_winning_arm_names({}) 35 | 36 | def test_two_unsampled_arms(self): 37 | """Check that the two-unsampled-arms case always returns both arms as winning arms. This tests num_winning_arms == num_arms > 1.""" 38 | assert EpsilonInterface.get_winning_arm_names(self.two_unsampled_arms_test_case.arms_sampled) == frozenset(["arm1", "arm2"]) 39 | 40 | def test_three_arms_two_winners(self): 41 | """Check that the three-arms cases with two winners return the expected winning arms. This tests num_arms > num_winning_arms > 1.""" 42 | assert EpsilonInterface.get_winning_arm_names(self.three_arms_two_winners_test_case.arms_sampled) == frozenset(["arm1", "arm2"]) 43 | -------------------------------------------------------------------------------- /moe/tests/bandit/epsilon/epsilon_test_case.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Base test case class for bandit epsilon tests; includes different epsilon values to test.""" 3 | from moe.bandit.constant import DEFAULT_EPSILON 4 | from moe.tests.bandit.bandit_test_case import BanditTestCase 5 | 6 | 7 | class EpsilonTestCase(BanditTestCase): 8 | 9 | """Base test case for the bandit library. 10 | 11 | Test different epsilon values. 12 | 13 | """ 14 | 15 | epsilons_to_test = [DEFAULT_EPSILON, 0.0, 0.5, 1.0] 16 | -------------------------------------------------------------------------------- /moe/tests/bandit/linkers_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tests that linkers contain all possible types defined in constants.""" 3 | from moe.bandit.constant import BANDIT_ENDPOINTS, EPSILON_SUBTYPES, UCB_SUBTYPES 4 | from moe.bandit.linkers import BANDIT_ENDPOINTS_TO_SUBTYPES, EPSILON_SUBTYPES_TO_BANDIT_METHODS, UCB_SUBTYPES_TO_BANDIT_METHODS 5 | 6 | 7 | class TestLinkers(object): 8 | 9 | """Tests that linkers contain all possible types defined in constants.""" 10 | 11 | def test_bandit_links_have_all_bandit_endpoints(self): 12 | """Test each bandit endpoint is in a linker, and every linker key is a bandit endpoint.""" 13 | assert set(BANDIT_ENDPOINTS) == set(BANDIT_ENDPOINTS_TO_SUBTYPES.keys()) 14 | 15 | def test_epsilon_links_have_all_epsilon_subtypes(self): 16 | """Test each epsilon subtype is in a linker, and every linker key is an epsilon subtype.""" 17 | assert set(EPSILON_SUBTYPES) == set(EPSILON_SUBTYPES_TO_BANDIT_METHODS.keys()) 18 | 19 | def test_ucb_links_have_all_ucb_subtypes(self): 20 | """Test each UCB subtype is in a linker, and every linker key is a UCB subtype.""" 21 | assert set(UCB_SUBTYPES) == set(UCB_SUBTYPES_TO_BANDIT_METHODS.keys()) 22 | -------------------------------------------------------------------------------- /moe/tests/bandit/ucb/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Testing code for the (Python) UCB bandit policies. 3 | 4 | **Files in this package** 5 | * :mod:`moe.tests.bandit.ucb.ucb_test_case`: base test case for UCB tests 6 | * :mod:`moe.tests.bandit.ucb.ucb1_test`: tests for :mod:`moe.bandit.ucb.ucb1.UCB1` 7 | * :mod:`moe.tests.bandit.ucb.ucb1_tuned_test`: tests for :mod:`moe.bandit.ucb.ucb1_tuned.UCB1Tuned` 8 | 9 | """ 10 | -------------------------------------------------------------------------------- /moe/tests/bandit/ucb/ucb1_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test UCB1 bandit implementation. 3 | 4 | Test default values with one, two, and three arms. 5 | Test different cases including unsampled arms and multiple winners. 6 | 7 | """ 8 | from moe.bandit.ucb.ucb1 import UCB1 9 | from moe.tests.bandit.ucb.ucb_test_case import UCBTestCase 10 | 11 | 12 | class TestUCB1(UCBTestCase): 13 | 14 | """Verify that different historical infos return correct results.""" 15 | 16 | bandit_class = UCB1 17 | 18 | def test_init_default(self): 19 | """Verify that default values do not throw and error. This is purely an integration test.""" 20 | self._test_init_default() 21 | 22 | def test_one_arm(self): 23 | """Check that the one-arm case always returns the given arm as the winning arm and the allocation is 1.0.""" 24 | bandit = self.bandit_class(self.one_arm_test_case) 25 | self._test_one_arm(bandit) 26 | 27 | def test_two_unsampled_arms(self): 28 | """Check that the two-unsampled-arms case always allocate each arm equally (the allocation is 0.5 for both arms). This tests num_unsampled_arms == num_arms > 1.""" 29 | self._test_two_unsampled_arms() 30 | 31 | def test_three_arms_one_unsampled_arm(self): 32 | """Check that the three-arms cases with integer and float payoffs return the expected arm allocations. When arm3 is the only unsampled arm, we expect all allocation is given to arm3.""" 33 | self._test_three_arms_one_unsampled_arm() 34 | 35 | def test_three_arms_two_winners(self): 36 | """Check that the three-arms cases with two winners return the expected arm allocations. This tests num_arms > num_winning_arms > 1.""" 37 | self._test_three_arms_two_winners() 38 | -------------------------------------------------------------------------------- /moe/tests/bandit/ucb/ucb1_tuned_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test UCB1-tuned bandit implementation. 3 | 4 | Test default values with one, two, and three arms. 5 | Test different cases including unsampled arms and multiple winners. 6 | 7 | """ 8 | from moe.bandit.ucb.ucb1_tuned import UCB1Tuned 9 | from moe.tests.bandit.ucb.ucb_test_case import UCBTestCase 10 | 11 | 12 | class TestUCB1Tuned(UCBTestCase): 13 | 14 | """Verify that different historical infos return correct results.""" 15 | 16 | bandit_class = UCB1Tuned 17 | 18 | def test_init_default(self): 19 | """Verify that default values do not throw and error. This is purely an integration test.""" 20 | self._test_init_default() 21 | 22 | def test_one_arm(self): 23 | """Check that the one-arm case always returns the given arm as the winning arm and the allocation is 1.0.""" 24 | bandit = self.bandit_class(self.one_arm_test_case) 25 | self._test_one_arm(bandit) 26 | 27 | def test_two_unsampled_arms(self): 28 | """Check that the two-unsampled-arms case always allocate each arm equally (the allocation is 0.5 for both arms). This tests num_unsampled_arms == num_arms > 1.""" 29 | self._test_two_unsampled_arms() 30 | 31 | def test_three_arms_one_unsampled_arm(self): 32 | """Check that the three-arms cases with integer and float payoffs return the expected arm allocations. When arm3 is the only unsampled arm, we expect all allocation is given to arm3.""" 33 | self._test_three_arms_one_unsampled_arm() 34 | 35 | def test_three_arms_two_winners(self): 36 | """Check that the three-arms cases with two winners return the expected arm allocations. This tests num_arms > num_winning_arms > 1.""" 37 | self._test_three_arms_two_winners() 38 | 39 | def test_three_arms_diferent_variance(self): 40 | """Check that the three-arms cases with different variance (same average payoff) return the expected arm allocations. The highest variance wins.""" 41 | bandit = self.bandit_class(self.three_arms_with_variance_no_unsampled_arm_test_case) 42 | assert bandit.allocate_arms() == {"arm1": 1.0, "arm2": 0.0, "arm3": 0.0} 43 | -------------------------------------------------------------------------------- /moe/tests/bandit/ucb/ucb_test_case.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Base test case class for UCB tests; includes different cases where unsampled arms are winners.""" 3 | from moe.tests.bandit.bandit_test_case import BanditTestCase 4 | 5 | 6 | class UCBTestCase(BanditTestCase): 7 | 8 | """Base test case for the UCB bandit library.""" 9 | 10 | def _test_two_unsampled_arms(self): 11 | """Check that the two-unsampled-arms case always allocate each arm equally (the allocation is 0.5 for both arms). This tests num_unsampled_arms == num_arms > 1.""" 12 | bandit = self.bandit_class(self.two_unsampled_arms_test_case) 13 | assert bandit.allocate_arms() == {"arm1": 0.5, "arm2": 0.5} 14 | 15 | def _test_three_arms_one_unsampled_arm(self): 16 | """Check that the three-arms cases with integer and float payoffs return the expected arm allocations. When arm3 is the only unsampled arm, we expect all allocation is given to arm3.""" 17 | for historical_info in [self.three_arms_test_case, self.three_arms_float_payoffs_test_case, self.three_arms_two_winners_test_case]: 18 | bandit = self.bandit_class(historical_info) 19 | assert bandit.allocate_arms() == {"arm1": 0.0, "arm2": 0.0, "arm3": 1.0} 20 | 21 | def _test_three_arms_two_winners(self): 22 | """Check that the three-arms cases with two winners return the expected arm allocations. This tests num_arms > num_winning_arms > 1.""" 23 | bandit = self.bandit_class(self.three_arms_two_winners_no_unsampled_arm_test_case) 24 | assert bandit.allocate_arms() == {"arm1": 0.5, "arm2": 0.5, "arm3": 0.0} 25 | 26 | def test_three_arms_ucb_value(self): 27 | """Check that the three-arms case returns the correct upper confidence bound (that arms with fewer trials can beat arms with more trials).""" 28 | bandit = self.bandit_class(self.three_arms_ucb_value_test_case) 29 | assert bandit.allocate_arms() == {"arm1": 1.0, "arm2": 0.0, "arm3": 0.0} 30 | -------------------------------------------------------------------------------- /moe/tests/bandit/utils_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tests for functions in utils.""" 3 | import pytest 4 | 5 | import logging 6 | 7 | from moe.bandit.utils import get_winning_arm_names_from_payoff_arm_name_list, get_equal_arm_allocations 8 | from moe.tests.bandit.bandit_test_case import BanditTestCase 9 | 10 | 11 | @pytest.fixture() 12 | def disable_logging(request): 13 | """Disable logging (for the duration of this test case).""" 14 | logging.disable(logging.CRITICAL) 15 | 16 | def finalize(): 17 | """Re-enable logging (so other test cases are unaffected).""" 18 | logging.disable(logging.NOTSET) 19 | request.addfinalizer(finalize) 20 | 21 | 22 | class TestUtils(BanditTestCase): 23 | 24 | """Tests :func:`moe.bandit.utils.get_winning_arm_names_from_payoff_arm_name_list` and :func:`moe.bandit.utils.get_equal_arm_allocations`.""" 25 | 26 | @pytest.mark.usefixtures("disable_logging") 27 | def test_get_winning_arm_names_from_payoff_arm_name_list_empty_list_invalid(self): 28 | """Test empty ``payoff_arm_name_list`` causes an ValueError.""" 29 | with pytest.raises(ValueError): 30 | get_winning_arm_names_from_payoff_arm_name_list([]) 31 | 32 | def test_get_winning_arm_names_from_payoff_arm_name_list_one_winner(self): 33 | """Test winning arm name matches the winner.""" 34 | assert get_winning_arm_names_from_payoff_arm_name_list([(0.5, "arm1"), (0.0, "arm2")]) == frozenset(["arm1"]) 35 | 36 | def test_get_winning_arm_names_from_payoff_arm_name_list_two_winners(self): 37 | """Test winning arm names match the winners.""" 38 | assert get_winning_arm_names_from_payoff_arm_name_list([(0.5, "arm1"), (0.5, "arm2"), (0.4, "arm3")]) == frozenset(["arm1", "arm2"]) 39 | 40 | @pytest.mark.usefixtures("disable_logging") 41 | def test_get_equal_arm_allocations_empty_arm_invalid(self): 42 | """Test empty ``arms_sampled`` causes an ValueError.""" 43 | with pytest.raises(ValueError): 44 | get_equal_arm_allocations({}) 45 | 46 | def test_get_equal_arm_allocations_no_winner(self): 47 | """Test allocations split among all sample arms when there is no winner.""" 48 | assert get_equal_arm_allocations(self.two_unsampled_arms_test_case.arms_sampled) == {"arm1": 0.5, "arm2": 0.5} 49 | 50 | def test_get_equal_arm_allocations_one_winner(self): 51 | """Test all allocation given to the winning arm.""" 52 | assert get_equal_arm_allocations(self.three_arms_test_case.arms_sampled, frozenset(["arm1"])) == {"arm1": 1.0, "arm2": 0.0, "arm3": 0.0} 53 | 54 | def test_get_equal_arm_allocations_two_winners(self): 55 | """Test allocations split between two winning arms.""" 56 | assert get_equal_arm_allocations(self.three_arms_two_winners_test_case.arms_sampled, frozenset(["arm1", "arm2"])) == {"arm1": 0.5, "arm2": 0.5, "arm3": 0.0} 57 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Optimal learning tests.""" 3 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/python/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Testing code for the (Python) optimal_learning library. 3 | 4 | Testing is done via the Testify package: 5 | https://github.com/Yelp/Testify 6 | 7 | This package includes: 8 | 9 | * Test cases/test setup files 10 | * Tests for classes and utils in :mod:`moe.optimal_learning.python` 11 | * Tests for classes and functions in :mod:`moe.optimal_learning.python.python_version` 12 | * Tests for classes and functions in :mod:`moe.optimal_learning.python.cpp_wrappers` 13 | 14 | **Files in this package** 15 | 16 | * :mod:`moe.tests.optimal_learning.python.optimal_learning_test_case`: base test case for optimal_learning tests with some extra asserts for checking relative differences of floats (scalar, vector) 17 | * :mod:`moe.tests.optimal_learning.python.gaussian_process_test_case`: test case for tests that manipulate GPs, includes extra 18 | logic to construct random gaussian process priors; meant to provide 19 | a well-behaved source of random data to unit tests. 20 | * :mod:`moe.tests.optimal_learning.python.gaussian_process_test_utils`: utilities for constructing a random domain, covariance, and GaussianProcess 21 | * :mod:`moe.tests.optimal_learning.python.geometry_utils_test`: tests for :mod:`moe.optimal_learning.python.geometry_utils` 22 | 23 | **Subpackages** 24 | 25 | * :mod:`moe.tests.optimal_learning.python.python_version`: tests for the Python implementation of optimal_learning. These include some manual checks, ping tests, 26 | and some high level integration tests. Python testing is currently relatively sparse; we rely heavily on 27 | the C++ comparison. 28 | * :mod:`moe.tests.optimal_learning.python.cpp_wrappers`: tests that check the equivalence of the C++ implementation and the Python implementation of 29 | optimal_learning (where applicable). Also runs the C++ unit tests. 30 | 31 | """ 32 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/python/cpp_unit_tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Single test case that invokes the C++ unit tests. 3 | 4 | These live in a separate directory so it's easier to only invoke the Python cpp_wrappers tests without 5 | hitting this (slower-running) suite. 6 | 7 | """ 8 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/python/cpp_unit_tests/cpp_unit_test_wrapper_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Dummy test case that invokes all of the C++ unit tests.""" 3 | import moe.build.GPP as C_GP 4 | 5 | 6 | class TestCppUnitTestWrapper(object): 7 | 8 | """Calls a C++ function that runs all C++ unit tests. 9 | 10 | TODO(GH-115): Remove/fix this once C++ gets a proper unit testing framework. 11 | 12 | """ 13 | 14 | def test_run_cpp_unit_tests(self): 15 | """Call C++ function that runs all C++ unit tests and assert 0 errors.""" 16 | number_of_cpp_test_errors = C_GP.run_cpp_tests() 17 | assert number_of_cpp_test_errors == 0 18 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/python/cpp_wrappers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Tests checking that results (e.g., log likelihood, expected improvement, gradients thereof) computed by Python and C++ are the same. 3 | 4 | These tests are just meant as an extra check for equivalence since we have two "identical" implementations of optimal_learning. 5 | The C++ is independently tested (see ``moe/optimal_learning/cpp/*test.cpp`` files) as is the Python\* 6 | (see moe/tests/optimal_learning/python/python_version), so the tests in this package generally are not very exhaustive. 7 | 8 | \* The C++ tests are much more extensive than the Python tests, which still need substantial development. 9 | 10 | """ 11 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/python/cpp_wrappers/exception_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tests for the C++-defined Python exception type objects.""" 3 | import pytest 4 | 5 | import moe.build.GPP as C_GP 6 | 7 | 8 | class TestExceptionStructure(object): 9 | 10 | """Tests for the C++-defined Python exception type objects.""" 11 | 12 | def test_exception_class_hierarchy(self): 13 | """Test that the C++-defined Python exception type objects have the right class hiearchy.""" 14 | # Base class inherits from Exception 15 | assert issubclass(C_GP.OptimalLearningException, Exception) 16 | 17 | type_objects = (C_GP.BoundsException, C_GP.InvalidValueException, C_GP.SingularMatrixException) 18 | for type_object in type_objects: 19 | assert issubclass(type_object, C_GP.OptimalLearningException) 20 | 21 | def test_exception_thrown_from_cpp(self): 22 | """Test that a C++ interface function throws the expected type.""" 23 | with pytest.raises(C_GP.BoundsException): 24 | C_GP.GaussianProcess([-1.0, [1.0]], [], [], [], 1, 0) 25 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/python/cpp_wrappers/log_likelihood_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test cases to check that C++ and Python implementations of :mod:`moe.optimal_learning.python.interfaces.log_likelihood_interface` match.""" 3 | import moe.optimal_learning.python.cpp_wrappers.covariance 4 | import moe.optimal_learning.python.cpp_wrappers.log_likelihood 5 | from moe.optimal_learning.python.geometry_utils import ClosedInterval 6 | import moe.optimal_learning.python.python_version.covariance 7 | import moe.optimal_learning.python.python_version.domain 8 | import moe.optimal_learning.python.python_version.log_likelihood 9 | from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase, GaussianProcessTestEnvironmentInput 10 | 11 | 12 | class TestLogLikelihood(GaussianProcessTestCase): 13 | 14 | """Test that the C++ and Python implementations of the Log Marginal Likelihood match (value and gradient).""" 15 | 16 | precompute_gaussian_process_data = False 17 | 18 | noise_variance_base = 0.0002 19 | dim = 3 20 | num_hyperparameters = dim + 1 21 | 22 | gp_test_environment_input = GaussianProcessTestEnvironmentInput( 23 | dim, 24 | num_hyperparameters, 25 | 0, 26 | noise_variance_base=noise_variance_base, 27 | hyperparameter_interval=ClosedInterval(0.2, 1.5), 28 | lower_bound_interval=ClosedInterval(-2.0, 0.5), 29 | upper_bound_interval=ClosedInterval(2.0, 3.5), 30 | covariance_class=moe.optimal_learning.python.python_version.covariance.SquareExponential, 31 | spatial_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, 32 | hyperparameter_domain_class=moe.optimal_learning.python.python_version.domain.TensorProductDomain, 33 | ) 34 | 35 | num_sampled_list = (1, 2, 5, 10, 16, 20, 42) 36 | 37 | def test_python_and_cpp_return_same_log_likelihood_and_gradient(self): 38 | """Check that the C++ and Python log likelihood + gradients match over a series of randomly built data sets.""" 39 | tolerance_log_like = 5.0e-11 40 | tolerance_grad_log_like = 4.0e-12 41 | 42 | for num_sampled in self.num_sampled_list: 43 | self.gp_test_environment_input.num_sampled = num_sampled 44 | _, python_gp = self._build_gaussian_process_test_data(self.gp_test_environment_input) 45 | python_cov, historical_data = python_gp.get_core_data_copy() 46 | 47 | python_lml = moe.optimal_learning.python.python_version.log_likelihood.GaussianProcessLogMarginalLikelihood(python_cov, historical_data) 48 | cpp_cov = moe.optimal_learning.python.cpp_wrappers.covariance.SquareExponential(python_cov.hyperparameters) 49 | cpp_lml = moe.optimal_learning.python.cpp_wrappers.log_likelihood.GaussianProcessLogMarginalLikelihood(cpp_cov, historical_data) 50 | 51 | python_log_like = python_lml.compute_log_likelihood() 52 | cpp_log_like = cpp_lml.compute_log_likelihood() 53 | self.assert_scalar_within_relative(python_log_like, cpp_log_like, tolerance_log_like) 54 | 55 | python_grad_log_like = python_lml.compute_grad_log_likelihood() 56 | cpp_grad_log_like = cpp_lml.compute_grad_log_likelihood() 57 | self.assert_vector_within_relative(python_grad_log_like, cpp_grad_log_like, tolerance_grad_log_like) 58 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/python/cpp_wrappers/optimization_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tests for the ``cpp_wrappers.optimization`` module. 3 | 4 | Currently these objects either inherit from Boost Python objects or are very thin data containers. So tests 5 | just verify that objects are created & behave as expected. 6 | MOE does not yet support the ability to optimize arbitrary Python functions through C++-coded optimizers. 7 | 8 | """ 9 | import copy 10 | 11 | import pytest 12 | 13 | from moe.optimal_learning.python.cpp_wrappers.optimization import NewtonParameters, GradientDescentParameters 14 | 15 | 16 | class TestOptimizerParameters(object): 17 | 18 | """Test that the various optimizer parameter classes (wrapping C++ objects) work.""" 19 | 20 | @classmethod 21 | @pytest.fixture(autouse=True, scope='class') 22 | def base_setup(cls): 23 | """Set up dummy parameters for testing optimization parameter structs.""" 24 | cls.newton_param_dict = { 25 | 'num_multistarts': 10, 26 | 'max_num_steps': 20, 27 | 'gamma': 1.05, 28 | 'time_factor': 1.0e-5, 29 | 'max_relative_change': 0.8, 30 | 'tolerance': 3.7e-8, 31 | } 32 | 33 | # new object b/c we want to modify it 34 | cls.gd_param_dict = copy.deepcopy(cls.newton_param_dict) 35 | del cls.gd_param_dict['time_factor'] 36 | cls.gd_param_dict.update({ 37 | 'max_num_restarts': 50, 38 | 'num_steps_averaged': 11, 39 | 'pre_mult': 0.45, 40 | }) 41 | 42 | @staticmethod 43 | def _parameter_test_core(param_type, param_dict): 44 | """Test param struct construction, member read/write, and equality check.""" 45 | # Check construction 46 | params = param_type(**param_dict) 47 | 48 | # Check that the internal state matches the input 49 | test_params_dict = dict(params._get_member_dict()) 50 | assert test_params_dict == param_dict 51 | 52 | # New object is equal to old when params match 53 | params_other = param_type(**param_dict) 54 | assert params_other == params 55 | 56 | # Inequality when we change a param 57 | params_other.gamma += 1.2 58 | assert params_other != params 59 | 60 | def test_newton_parameters(self): 61 | """Test that ``NewtonParameters`` is created correctly and comparison works.""" 62 | self._parameter_test_core(NewtonParameters, self.newton_param_dict) 63 | 64 | def test_gradient_descent_parameters(self): 65 | """Test that ``GradientDescentParameters`` is created correctly and comparison works.""" 66 | self._parameter_test_core(GradientDescentParameters, self.gd_param_dict) 67 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/python/linkers_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tests that linkers contain all possible types defined in constants.""" 3 | from moe.optimal_learning.python.constant import COVARIANCE_TYPES, DOMAIN_TYPES, OPTIMIZER_TYPES, LIKELIHOOD_TYPES 4 | from moe.optimal_learning.python.linkers import COVARIANCE_TYPES_TO_CLASSES, DOMAIN_TYPES_TO_DOMAIN_LINKS, OPTIMIZER_TYPES_TO_OPTIMIZER_METHODS, LOG_LIKELIHOOD_TYPES_TO_LOG_LIKELIHOOD_METHODS 5 | 6 | 7 | class TestLinkers(object): 8 | 9 | """Tests that linkers contain all possible types defined in constants.""" 10 | 11 | def test_covariance_links_have_all_covariance_types(self): 12 | """Test each covariance type is in a linker, and every linker key is a covariance type.""" 13 | assert set(COVARIANCE_TYPES) == set(COVARIANCE_TYPES_TO_CLASSES.keys()) 14 | 15 | def test_domain_links_have_all_domain_types(self): 16 | """Test each domain type is in a linker, and every linker is a domain type.""" 17 | assert set(DOMAIN_TYPES) == set(DOMAIN_TYPES_TO_DOMAIN_LINKS.keys()) 18 | 19 | def test_optimization_links_have_all_optimizer_types(self): 20 | """Test each optimizer type is in a linker, and every linker key is a optimizer type.""" 21 | assert set(OPTIMIZER_TYPES) == set(OPTIMIZER_TYPES_TO_OPTIMIZER_METHODS.keys()) 22 | 23 | def test_likelihood_links_have_all_likelihood_types(self): 24 | """Test each likelihood type is in a linker, and every linker key is a likelihood type.""" 25 | assert set(LIKELIHOOD_TYPES) == set(LOG_LIKELIHOOD_TYPES_TO_LOG_LIKELIHOOD_METHODS.keys()) 26 | -------------------------------------------------------------------------------- /moe/tests/optimal_learning/python/python_version/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test suite for the Python implementation of optimal_learning. 3 | 4 | * Lower level functions (e.g., covariance) are generally tested with a combination of manual verification and derivative pinging. 5 | * Mid-level level functions (e.g., log likelihood) are mostly tested with derivative pinging. 6 | * High-level functions (e.g., optimization of EI or log likelihood) are only loosely tested, only checking that outputs 7 | are valid (vs trying to verify them). 8 | 9 | .. Note:: the Python implementation is additionally tested against the C++ (same inputs, same results for the various 10 | optimal_learning features) implementation (see moe/tests/optimal_learning/python/cpp_wrappers). 11 | 12 | TODO(GH-178): in general, the Python test suite is lacking and we rely on comparison against 13 | the more extensively tested C++ implementation to check the Python. 14 | 15 | """ 16 | -------------------------------------------------------------------------------- /moe/tests/views/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Views tests.""" 3 | -------------------------------------------------------------------------------- /moe/tests/views/rest/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tests for the MOE REST interface.""" 3 | -------------------------------------------------------------------------------- /moe/tests/views/rest/bandit_bla_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test class for bandit_bla view.""" 3 | from moe.bandit.constant import BANDIT_BLA_ENDPOINT 4 | from moe.tests.bandit.bandit_test_case import BanditTestCase 5 | from moe.tests.views.rest.bandit_test import TestBanditViews 6 | from moe.views.constant import BANDIT_BLA_MOE_ROUTE 7 | from moe.views.rest.bandit_bla import BanditBLAView 8 | 9 | 10 | class TestBanditBLAViews(TestBanditViews): 11 | 12 | """Integration test for the /bandit/bla endpoint.""" 13 | 14 | _endpoint = BANDIT_BLA_ENDPOINT 15 | _historical_infos = BanditTestCase.bernoulli_historical_infos_to_test 16 | _moe_route = BANDIT_BLA_MOE_ROUTE 17 | _view = BanditBLAView 18 | 19 | def test_historical_info_passed_through(self): 20 | """Test that the historical info get passed through to the endpoint.""" 21 | self._test_historical_info_passed_through() 22 | 23 | def test_interface_returns_as_expected(self): 24 | """Integration test for the /bandit/bla endpoint.""" 25 | self._test_interface_returns_as_expected() 26 | -------------------------------------------------------------------------------- /moe/tests/views/rest/bandit_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test class for bandit views.""" 3 | import pyramid.testing 4 | 5 | import simplejson as json 6 | 7 | from moe.bandit.linkers import BANDIT_ENDPOINTS_TO_SUBTYPES 8 | from moe.tests.bandit.bandit_test_case import BanditTestCase 9 | from moe.tests.views.rest_test_case import RestTestCase 10 | from moe.views.schemas.bandit_pretty_view import BanditResponse 11 | 12 | 13 | class TestBanditViews(BanditTestCase, RestTestCase): 14 | 15 | """Integration test for the /bandit endpoints.""" 16 | 17 | _endpoint = None # Define in a subclass 18 | _historical_infos = None # Define in a subclass 19 | _moe_route = None # Define in a subclass 20 | _view = None # Define in a subclass 21 | 22 | @staticmethod 23 | def _build_json_payload(subtype, historical_info): 24 | """Create a json_payload to POST to the /bandit/* endpoint with all needed info.""" 25 | dict_to_dump = { 26 | 'subtype': subtype, 27 | 'historical_info': historical_info.json_payload(), 28 | } 29 | 30 | return json.dumps(dict_to_dump) 31 | 32 | def _test_historical_info_passed_through(self): 33 | """Test that the historical infos get passed through to the endpoint.""" 34 | for subtype in BANDIT_ENDPOINTS_TO_SUBTYPES[self._endpoint]: 35 | for historical_info in self._historical_infos: 36 | # Test default test parameters get passed through 37 | json_payload = json.loads(self._build_json_payload(subtype, historical_info)) 38 | 39 | request = pyramid.testing.DummyRequest(post=json_payload) 40 | request.json_body = json_payload 41 | view = self._view(request) 42 | params = view.get_params_from_request() 43 | 44 | assert params['historical_info'] == json_payload['historical_info'] 45 | 46 | def _test_interface_returns_as_expected(self): 47 | """Integration test for the bandit endpoints.""" 48 | for subtype in BANDIT_ENDPOINTS_TO_SUBTYPES[self._endpoint]: 49 | for historical_info in self._historical_infos: 50 | json_payload = self._build_json_payload(subtype, historical_info) 51 | arm_names = set([arm_name for arm_name in historical_info.arms_sampled.iterkeys()]) 52 | resp = self.testapp.post(self._moe_route.endpoint, json_payload) 53 | resp_schema = BanditResponse() 54 | resp_dict = resp_schema.deserialize(json.loads(resp.body)) 55 | resp_arm_names = set([arm_name for arm_name in resp_dict['arm_allocations'].iterkeys()]) 56 | assert arm_names == resp_arm_names 57 | # The allocations should be in range [0, 1] 58 | # The sum of all allocations should be 1.0. 59 | total_allocation = 0 60 | for allocation in resp_dict['arm_allocations'].itervalues(): 61 | assert allocation >= 0 62 | assert allocation <= 1 63 | total_allocation += allocation 64 | assert total_allocation == 1.0 65 | -------------------------------------------------------------------------------- /moe/tests/views/rest/bandit_ucb_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test class for bandit_ucb view.""" 3 | from moe.bandit.constant import BANDIT_UCB_ENDPOINT 4 | from moe.tests.bandit.bandit_test_case import BanditTestCase 5 | from moe.tests.views.rest.bandit_test import TestBanditViews 6 | from moe.views.constant import BANDIT_UCB_MOE_ROUTE 7 | from moe.views.rest.bandit_ucb import BanditUCBView 8 | 9 | 10 | class TestBanditUCBViews(TestBanditViews): 11 | 12 | """Integration test for the /bandit/ucb endpoint.""" 13 | 14 | _endpoint = BANDIT_UCB_ENDPOINT 15 | _historical_infos = BanditTestCase.historical_infos_to_test 16 | _moe_route = BANDIT_UCB_MOE_ROUTE 17 | _view = BanditUCBView 18 | 19 | def test_historical_info_passed_through(self): 20 | """Test that the historical info get passed through to the endpoint.""" 21 | self._test_historical_info_passed_through() 22 | 23 | def test_interface_returns_as_expected(self): 24 | """Integration test for the /bandit/ucb endpoint.""" 25 | self._test_interface_returns_as_expected() 26 | -------------------------------------------------------------------------------- /moe/tests/views/rest/gp_ei_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test class for gp_mean_var view.""" 3 | import numpy 4 | 5 | import simplejson as json 6 | 7 | from moe.optimal_learning.python.cpp_wrappers.covariance import SquareExponential 8 | from moe.optimal_learning.python.cpp_wrappers.expected_improvement import ExpectedImprovement 9 | from moe.optimal_learning.python.cpp_wrappers.gaussian_process import GaussianProcess 10 | from moe.tests.optimal_learning.python.gaussian_process_test_case import GaussianProcessTestCase 11 | from moe.tests.views.rest_test_case import RestTestCase 12 | from moe.views.constant import GP_EI_ENDPOINT 13 | from moe.views.schemas.rest.gp_ei import GpEiResponse 14 | 15 | 16 | class TestGpEiView(GaussianProcessTestCase, RestTestCase): 17 | 18 | """Test that the /gp/ei endpoint does the same thing as the C++ interface.""" 19 | 20 | precompute_gaussian_process_data = True 21 | endpoint = GP_EI_ENDPOINT 22 | 23 | @staticmethod 24 | def _build_json_payload(domain, covariance, historical_data, points_to_evaluate): 25 | """Create a json_payload to POST to the /gp/ei endpoint with all needed info.""" 26 | json_payload = json.dumps({ 27 | 'points_to_evaluate': points_to_evaluate, 28 | 'points_being_sampled': [], 29 | 'gp_historical_info': historical_data.json_payload(), 30 | 'covariance_info': covariance.get_json_serializable_info(), 31 | 'domain_info': domain.get_json_serializable_info(minimal=True), 32 | }) 33 | 34 | return json_payload 35 | 36 | def test_interface_returns_same_as_cpp(self): 37 | """Test that the /gp/ei endpoint does the same thing as the C++ interface.""" 38 | tolerance = 1.0e-11 39 | for test_case in self.gp_test_environments: 40 | python_domain, python_gp = test_case 41 | python_cov, historical_data = python_gp.get_core_data_copy() 42 | 43 | cpp_cov = SquareExponential(python_cov.hyperparameters) 44 | cpp_gp = GaussianProcess(cpp_cov, historical_data) 45 | 46 | points_to_evaluate = python_domain.generate_uniform_random_points_in_domain(10) 47 | 48 | # EI from C++ 49 | expected_improvement_evaluator = ExpectedImprovement( 50 | cpp_gp, 51 | None, 52 | ) 53 | # TODO(GH-99): Change test case to have the right shape: 54 | # (num_to_evaluate, num_to_sample, dim) 55 | # Here we assume the shape is (num_to_evaluate, dim) so we insert an axis, making num_to_sample = 1. 56 | # Also might be worth testing more num_to_sample values (will require manipulating C++ RNG state). 57 | cpp_expected_improvement = expected_improvement_evaluator.evaluate_at_point_list( 58 | points_to_evaluate[:, numpy.newaxis, :], 59 | ) 60 | 61 | # EI from REST 62 | json_payload = self._build_json_payload(python_domain, python_cov, historical_data, points_to_evaluate.tolist()) 63 | resp = self.testapp.post(self.endpoint, json_payload) 64 | resp_schema = GpEiResponse() 65 | resp_dict = resp_schema.deserialize(json.loads(resp.body)) 66 | rest_expected_improvement = numpy.asarray(resp_dict.get('expected_improvement')) 67 | 68 | self.assert_vector_within_relative( 69 | rest_expected_improvement, 70 | cpp_expected_improvement, 71 | tolerance, 72 | ) 73 | -------------------------------------------------------------------------------- /moe/tests/views/rest_test_case.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Base class for testing the REST interface against the C++ interface.""" 3 | import pytest 4 | 5 | 6 | class RestTestCase(object): 7 | 8 | """Base class for testing the REST interface against the C++ interface.""" 9 | 10 | endpoint = None 11 | 12 | @classmethod 13 | @pytest.fixture(autouse=True, scope='class') 14 | def create_webapp(cls): 15 | """Create a mocked webapp and store it in cls.testapp.""" 16 | from moe import main 17 | app = main({}, use_mongo='false') 18 | from webtest import TestApp 19 | cls.testapp = TestApp(app) 20 | -------------------------------------------------------------------------------- /moe/tests/views/utils_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tests for functions in utils.""" 3 | from moe.bandit.data_containers import BernoulliArm 4 | from moe.views.utils import _make_bandit_historical_info_from_params 5 | from moe.tests.bandit.bandit_test_case import BanditTestCase 6 | 7 | 8 | class TestUtils(BanditTestCase): 9 | 10 | """Tests :func:`moe.views.utils._make_bandit_historical_info_from_params`.""" 11 | 12 | def make_params_from_bandit_historical_info(self, historical_info): 13 | """Create params from given ``historical_info``.""" 14 | return { 15 | 'historical_info': historical_info.json_payload(), 16 | } 17 | 18 | def test_make_bandit_historical_info_from_params_make_bernoulli_arms(self): 19 | """Test that the function can make historical infos with Bernoulli arms.""" 20 | historical_info = self.three_arms_with_variance_no_unsampled_arm_test_case 21 | for historical_info in self.bernoulli_historical_infos_to_test: 22 | assert _make_bandit_historical_info_from_params(self.make_params_from_bandit_historical_info(historical_info), BernoulliArm).json_payload() == historical_info.json_payload() 23 | 24 | def test_make_bandit_historical_info_from_params_variance_passed_through(self): 25 | """Test that the variance of a given sample arm got passed through.""" 26 | historical_info = self.three_arms_with_variance_no_unsampled_arm_test_case 27 | assert _make_bandit_historical_info_from_params(self.make_params_from_bandit_historical_info(historical_info)).json_payload() == historical_info.json_payload() 28 | -------------------------------------------------------------------------------- /moe/views/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Views for the MOE frontend and REST interface. 3 | 4 | Contains: 5 | 6 | * :mod:`moe.views.frontend`: the frontend code 7 | * :mod:`moe.views.rest`: various REST endpoints for internal gaussian process information 8 | * :mod:`moe.views.pretty_view`: base view for all REST endpoints 9 | * :mod:`moe.views.bandit_pretty_view`: base view for all bandit REST endpoints 10 | * :mod:`moe.views.gp_pretty_view`: base view for all GP REST endpoints 11 | * :mod:`moe.views.optimizable_gp_pretty_view`: base view for REST endpoints that require optimization 12 | * :mod:`moe.views.gp_next_points_pretty_view`: base view for getting the next best points to sample 13 | * :mod:`moe.views.schemas`: schemas used to deserialize/serialize inputs/outputs in the REST interface 14 | * :mod:`moe.views.utils`: utils for constructing data structures/classes from :mod:`moe.optimal_learning.python` 15 | * :mod:`moe.views.constant`: constants shared by multiple views 16 | * :mod:`moe.views.exceptions`: exception handling views for giving detailed 500 errors 17 | 18 | """ 19 | -------------------------------------------------------------------------------- /moe/views/bandit_pretty_view.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """A class to encapsulate Bandit 'pretty' views.""" 3 | 4 | from moe.bandit.constant import DEFAULT_BANDIT_HISTORICAL_INFO 5 | from moe.views.pretty_view import PrettyView 6 | 7 | 8 | class BanditPrettyView(PrettyView): 9 | 10 | """A class to encapsulate Bandit 'pretty' views. 11 | 12 | See :class:`moe.views.pretty_view.PrettyView` superclass for more details. 13 | 14 | """ 15 | 16 | _pretty_default_historical_info = DEFAULT_BANDIT_HISTORICAL_INFO 17 | -------------------------------------------------------------------------------- /moe/views/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Views for handling Python exceptions. 3 | 4 | Includes special views that catch the following: 5 | 1. colander.Invalid 6 | 7 | """ 8 | import logging 9 | import pprint 10 | 11 | import colander 12 | 13 | from pyramid.response import Response 14 | from pyramid.view import view_config 15 | 16 | 17 | @view_config(context=Exception) 18 | def pyramid_error_view(exception, request): 19 | """Register a pyramid view to handle exceptions; i.e., log them and generate a Response. 20 | 21 | :param exception: exception to be handled 22 | :type exception: Exception (Python base exception type) (i.e., type of ``context``) 23 | :param request: the pyramid request that lead to the exception being raised. 24 | :type request: pyramid.request.Request 25 | :return: the pyramid response to be rendered 26 | :rtype: pyramid.response.Response 27 | 28 | """ 29 | if "log" not in general_error.__dict__: 30 | general_error.log = logging.getLogger(__name__) 31 | 32 | # Log exception with traceback 33 | general_error.log.error(request) 34 | general_error.log.exception(exception) 35 | 36 | # Specialized handlers for certain exception types 37 | if issubclass(type(exception), colander.Invalid): 38 | return failed_colander_validation(exception, request) 39 | 40 | return general_error(exception, request) 41 | 42 | 43 | def general_error(exception, request): 44 | """Catch any Python ``Exception``. 45 | 46 | :param exception: exception to be handled 47 | :type exception: Exception 48 | :param request: the pyramid request that lead to the exception being raised. 49 | :type request: pyramid.request.Request 50 | :return: the pyramid response to be rendered 51 | :rtype: pyramid.response.Response 52 | 53 | """ 54 | status_int = 500 55 | body = '{0:d}: {1:s}\n{2:s}'.format(status_int, request.referrer, exception) 56 | response = Response(body=body, status_int=status_int) 57 | return response 58 | 59 | 60 | def failed_colander_validation(exception, request): 61 | """Catch ``colander.Invalid`` and give an informative 500 response. 62 | 63 | :param exception: exception to be handled 64 | :type exception: colander.Invalid 65 | :param request: the pyramid request that lead to the exception being raised. 66 | :type request: pyramid.request.Request 67 | :return: the pyramid response to be rendered 68 | :rtype: pyramid.response.Response 69 | 70 | """ 71 | status_int = 500 72 | body = '{0:d}: {1:s}\nFailed validation:\n{2:s}'.format(status_int, request.referrer, pprint.pformat(exception.asdict())) 73 | response = Response(body=body, status_int=status_int) 74 | return response 75 | -------------------------------------------------------------------------------- /moe/views/frontend.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Frontend views for the MOE app.""" 3 | from pyramid.view import view_config 4 | 5 | from moe.optimal_learning.python.constant import DEFAULT_GAUSSIAN_PROCESS_PARAMETERS, DEMO_GRADIENT_DESCENT_PARAMETERS, DEMO_OPTIMIZER_MULTISTARTS 6 | 7 | 8 | @view_config(route_name='home', renderer='moe:templates/index.mako') 9 | def index_page(request): 10 | """The MOE index view. 11 | 12 | .. http:get:: / 13 | 14 | """ 15 | return { 16 | 'nav_active': 'home', 17 | } 18 | 19 | 20 | @view_config(route_name='gp_plot', renderer='moe:templates/gp_plot.mako') 21 | def gp_plot_page(request): 22 | """The MOE demo view. 23 | 24 | .. http:get:: /demo 25 | 26 | """ 27 | return { 28 | 'nav_active': 'demo', 29 | 'default_gaussian_process_parameters': DEFAULT_GAUSSIAN_PROCESS_PARAMETERS, 30 | 'default_ei_optimizer_parameters': DEMO_GRADIENT_DESCENT_PARAMETERS, 31 | 'default_num_multistarts': DEMO_OPTIMIZER_MULTISTARTS, 32 | } 33 | -------------------------------------------------------------------------------- /moe/views/gp_pretty_view.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """A class to encapsulate GP 'pretty' views.""" 3 | from moe.optimal_learning.python.constant import SQUARE_EXPONENTIAL_COVARIANCE_TYPE 4 | from moe.views.pretty_view import PrettyView 5 | 6 | 7 | class GpPrettyView(PrettyView): 8 | 9 | """A class to encapsulate GP 'pretty' views. 10 | 11 | See :class:`moe.views.pretty_view.PrettyView` superclass for more details. 12 | 13 | """ 14 | 15 | _pretty_default_gp_historical_info = { 16 | "points_sampled": [ 17 | {"value_var": 0.01, "value": 0.1, "point": [0.0]}, 18 | {"value_var": 0.01, "value": 0.2, "point": [1.0]}, 19 | ], 20 | } 21 | _pretty_default_covariance_info = { 22 | "covariance_type": SQUARE_EXPONENTIAL_COVARIANCE_TYPE, 23 | "hyperparameters": [1.0, 0.2], 24 | } 25 | _pretty_default_domain_info = { 26 | "dim": 1, 27 | "domain_type": "tensor_product", 28 | } 29 | -------------------------------------------------------------------------------- /moe/views/pretty_view.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """A class to encapsulate 'pretty' views.""" 3 | import logging 4 | 5 | import simplejson as json 6 | 7 | from moe.resources import Root 8 | from moe.views.constant import MoeRestLogLine 9 | 10 | PRETTY_RENDERER = 'moe:templates/pretty_input.mako' 11 | 12 | 13 | class PrettyView(Root): 14 | 15 | """A class to encapsulate 'pretty' views. 16 | 17 | These views have: 18 | 1. A backend endpoint 19 | 2. A pretty, browser interactable view with forms to test the backend endpoint 20 | 21 | """ 22 | 23 | _route_name = None 24 | _pretty_route_name = None 25 | 26 | request_schema = None # Define in a subclass 27 | response_schema = None # Define in a subclass 28 | 29 | _pretty_default_request = None 30 | 31 | def __init__(self, request): 32 | """Store the request for the view and set up the logger.""" 33 | super(PrettyView, self).__init__(request) 34 | 35 | # Set up logging 36 | self.log = logging.getLogger(__name__) 37 | 38 | def _create_moe_log_line(self, type, content): 39 | """Log a :class:`moe.views.constant.MoeLogLine` as a dict to the MOE logger.""" 40 | self.log.info( 41 | dict( 42 | MoeRestLogLine( 43 | endpoint=self._route_name, 44 | type=type, 45 | content=content 46 | )._asdict() 47 | ) 48 | ) 49 | 50 | def get_params_from_request(self): 51 | """Return the deserialized parameters from the json_body of a request. 52 | 53 | :returns: A deserialized self.request_schema object 54 | 55 | """ 56 | self._create_moe_log_line( 57 | type='request', 58 | content=self.request.json_body, 59 | ) 60 | 61 | return self.request_schema.deserialize(self.request.json_body) 62 | 63 | def pretty_response(self): 64 | """A pretty, browser interactive view for the interface. Includes form request and response. 65 | 66 | :returns: A dictionary with 'endpoint' and 'default_text' keys. 67 | 68 | """ 69 | return { 70 | 'endpoint': self._route_name, 71 | 'default_text': json.dumps(self._pretty_default_request), 72 | } 73 | 74 | def form_response(self, response_dict): 75 | """Return the serialized response object from a dict. 76 | 77 | :param response_dict: a dict that can be serialized by self.response_schema 78 | :type response_dict: dict 79 | :returns: a serialized self.response_schema object 80 | """ 81 | self._create_moe_log_line( 82 | type='response', 83 | content=response_dict, 84 | ) 85 | return self.response_schema.serialize(response_dict) 86 | -------------------------------------------------------------------------------- /moe/views/rest/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """The REST interface for the MOE webapp. 3 | 4 | **Internal Gaussian Process (GP) endpoints:** 5 | 6 | * :mod:`~moe.views.rest.gp_ei` 7 | 8 | .. http:post:: /gp/ei 9 | 10 | Calculates the Expected Improvement (EI) of a set of points, given historical data. 11 | 12 | .. http:get:: /gp/ei/pretty 13 | 14 | * :mod:`~moe.views.rest.gp_mean_var` 15 | 16 | .. http:post:: /gp/mean_var 17 | 18 | Calculates the GP mean and covariance of a set of points, given historical data. 19 | 20 | .. http:get:: /gp/mean_var/pretty 21 | 22 | **Next points endpoints:** 23 | 24 | * :mod:`~moe.views.rest.gp_next_points_epi` 25 | 26 | .. http:post:: /gp/next_points/epi 27 | 28 | Calculates the next best points to sample, given historical data, using Expected Parallel Improvement (EPI). 29 | 30 | .. http:get:: /gp/next_points/epi/pretty 31 | 32 | * :mod:`~moe.views.rest.gp_next_points_constant_liar` 33 | 34 | .. http:post:: /gp/next_points/constant_liar 35 | 36 | Calculates the next best points to sample, given historical data, using Constant Liar (CL). 37 | 38 | .. http:get:: /gp/next_points/constant_liar/pretty 39 | 40 | * :mod:`~moe.views.rest.gp_next_points_kriging` 41 | 42 | .. http:post:: /gp/next_points/kriging 43 | 44 | Calculates the next best points to sample, given historical data, using Kriging. 45 | 46 | .. http:get:: /gp/next_points/kriging/pretty 47 | 48 | **Bandit endpoints:** 49 | 50 | * :mod:`~moe.views.rest.bandit_epsilon` 51 | 52 | .. http:post:: /bandit/epsilon 53 | 54 | Calculates the arm allocations and the best arm to pull next using Epsilon policy, given subtype, historical data, hyperparameters. 55 | 56 | .. http:get:: /bandit/epsilon/pretty 57 | 58 | * :mod:`~moe.views.rest.bandit_ucb` 59 | 60 | .. http:post:: /bandit/ucb 61 | 62 | Calculates the arm allocations and the best arm to pull next using UCB policy, given subtype, historical data, hyperparameters. 63 | 64 | .. http:get:: /bandit/ucb/pretty 65 | 66 | * :mod:`~moe.views.rest.bandit_bla` 67 | 68 | .. http:post:: /bandit/bla 69 | 70 | Calculates the arm allocations and the best arm to pull next using BLA policy, given subtype, historical data, hyperparameters. 71 | 72 | .. http:get:: /bandit/bla/pretty 73 | 74 | """ 75 | -------------------------------------------------------------------------------- /moe/views/rest/bandit_bla.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Classes for ``bandit_bla`` endpoints. 3 | 4 | Includes: 5 | 6 | 1. pretty and backend views 7 | 8 | """ 9 | from pyramid.view import view_config 10 | 11 | from moe.bandit.constant import DEFAULT_BLA_SUBTYPE 12 | from moe.bandit.data_containers import BernoulliArm 13 | from moe.bandit.linkers import BLA_SUBTYPES_TO_BANDIT_METHODS 14 | from moe.views.bandit_pretty_view import BanditPrettyView 15 | from moe.views.constant import BANDIT_BLA_ROUTE_NAME, BANDIT_BLA_PRETTY_ROUTE_NAME 16 | from moe.views.pretty_view import PRETTY_RENDERER 17 | from moe.views.schemas.bandit_pretty_view import BanditResponse 18 | from moe.views.schemas.rest.bandit_bla import BanditBLARequest 19 | from moe.views.utils import _make_bandit_historical_info_from_params 20 | 21 | 22 | class BanditBLAView(BanditPrettyView): 23 | 24 | """Views for bandit_bla endpoints.""" 25 | 26 | _route_name = BANDIT_BLA_ROUTE_NAME 27 | _pretty_route_name = BANDIT_BLA_PRETTY_ROUTE_NAME 28 | 29 | request_schema = BanditBLARequest() 30 | response_schema = BanditResponse() 31 | 32 | _pretty_default_request = { 33 | "subtype": DEFAULT_BLA_SUBTYPE, 34 | "historical_info": BanditPrettyView._pretty_default_historical_info, 35 | } 36 | 37 | @view_config(route_name=_pretty_route_name, renderer=PRETTY_RENDERER) 38 | def pretty_view(self): 39 | """A pretty, browser interactive view for the interface. Includes form request and response. 40 | 41 | .. http:get:: /bandit/bla/pretty 42 | 43 | """ 44 | return self.pretty_response() 45 | 46 | @view_config(route_name=_route_name, renderer='json', request_method='POST') 47 | def bandit_bla_view(self): 48 | """Endpoint for bandit_epsilon POST requests. 49 | 50 | .. http:post:: /bandit/bla 51 | 52 | Predict the optimal arm from a set of arms, given historical data. 53 | 54 | :input: :class:`moe.views.schemas.rest.bandit_bla.BanditBLARequest` 55 | :output: :class:`moe.views.schemas.bandit_pretty_view.BanditResponse` 56 | 57 | :status 200: returns a response 58 | :status 500: server error 59 | 60 | """ 61 | params = self.get_params_from_request() 62 | 63 | subtype = params.get('subtype') 64 | historical_info = _make_bandit_historical_info_from_params(params, BernoulliArm) 65 | 66 | bandit_class = BLA_SUBTYPES_TO_BANDIT_METHODS[subtype].bandit_class(historical_info=historical_info) 67 | arms_to_allocations = bandit_class.allocate_arms() 68 | 69 | return self.form_response({ 70 | 'endpoint': self._route_name, 71 | 'arm_allocations': arms_to_allocations, 72 | 'winner': bandit_class.choose_arm(arms_to_allocations), 73 | }) 74 | -------------------------------------------------------------------------------- /moe/views/rest/bandit_ucb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Classes for ``bandit_ucb`` endpoints. 3 | 4 | Includes: 5 | 6 | 1. pretty and backend views 7 | 8 | """ 9 | from pyramid.view import view_config 10 | 11 | from moe.bandit.constant import DEFAULT_UCB_SUBTYPE 12 | from moe.bandit.linkers import UCB_SUBTYPES_TO_BANDIT_METHODS 13 | from moe.views.bandit_pretty_view import BanditPrettyView 14 | from moe.views.constant import BANDIT_UCB_ROUTE_NAME, BANDIT_UCB_PRETTY_ROUTE_NAME 15 | from moe.views.pretty_view import PRETTY_RENDERER 16 | from moe.views.schemas.bandit_pretty_view import BanditResponse 17 | from moe.views.schemas.rest.bandit_ucb import BanditUCBRequest 18 | from moe.views.utils import _make_bandit_historical_info_from_params 19 | 20 | 21 | class BanditUCBView(BanditPrettyView): 22 | 23 | """Views for bandit_ucb endpoints.""" 24 | 25 | _route_name = BANDIT_UCB_ROUTE_NAME 26 | _pretty_route_name = BANDIT_UCB_PRETTY_ROUTE_NAME 27 | 28 | request_schema = BanditUCBRequest() 29 | response_schema = BanditResponse() 30 | 31 | _pretty_default_request = { 32 | "subtype": DEFAULT_UCB_SUBTYPE, 33 | "historical_info": BanditPrettyView._pretty_default_historical_info, 34 | } 35 | 36 | @view_config(route_name=_pretty_route_name, renderer=PRETTY_RENDERER) 37 | def pretty_view(self): 38 | """A pretty, browser interactive view for the interface. Includes form request and response. 39 | 40 | .. http:get:: /bandit/ucb/pretty 41 | 42 | """ 43 | return self.pretty_response() 44 | 45 | @view_config(route_name=_route_name, renderer='json', request_method='POST') 46 | def bandit_ucb_view(self): 47 | """Endpoint for bandit_epsilon POST requests. 48 | 49 | .. http:post:: /bandit/ucb 50 | 51 | Predict the optimal arm from a set of arms, given historical data. 52 | 53 | :input: :class:`moe.views.schemas.rest.bandit_ucb.BanditUCBRequest` 54 | :output: :class:`moe.views.schemas.bandit_pretty_view.BanditResponse` 55 | 56 | :status 200: returns a response 57 | :status 500: server error 58 | 59 | """ 60 | params = self.get_params_from_request() 61 | 62 | subtype = params.get('subtype') 63 | historical_info = _make_bandit_historical_info_from_params(params) 64 | 65 | bandit_class = UCB_SUBTYPES_TO_BANDIT_METHODS[subtype].bandit_class(historical_info=historical_info) 66 | arms_to_allocations = bandit_class.allocate_arms() 67 | 68 | return self.form_response({ 69 | 'endpoint': self._route_name, 70 | 'arm_allocations': arms_to_allocations, 71 | 'winner': bandit_class.choose_arm(arms_to_allocations), 72 | }) 73 | -------------------------------------------------------------------------------- /moe/views/rest/gp_ei.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Classes for ``gp_ei`` endpoints. 3 | 4 | Includes: 5 | 6 | 1. pretty and backend views 7 | 8 | """ 9 | import numpy 10 | 11 | from pyramid.view import view_config 12 | 13 | from moe.optimal_learning.python.cpp_wrappers.expected_improvement import ExpectedImprovement 14 | from moe.optimal_learning.python.timing import timing_context 15 | from moe.views.constant import GP_EI_ROUTE_NAME, GP_EI_PRETTY_ROUTE_NAME 16 | from moe.views.gp_pretty_view import GpPrettyView 17 | from moe.views.pretty_view import PRETTY_RENDERER 18 | from moe.views.schemas.rest.gp_ei import GpEiRequest, GpEiResponse 19 | from moe.views.utils import _make_gp_from_params 20 | 21 | 22 | EI_COMPUTATION_TIMING_LABEL = 'EI computation time' 23 | 24 | 25 | class GpEiView(GpPrettyView): 26 | 27 | """Views for gp_ei endpoints.""" 28 | 29 | _route_name = GP_EI_ROUTE_NAME 30 | _pretty_route_name = GP_EI_PRETTY_ROUTE_NAME 31 | 32 | request_schema = GpEiRequest() 33 | response_schema = GpEiResponse() 34 | 35 | _pretty_default_request = { 36 | "points_to_evaluate": [ 37 | [0.1], [0.5], [0.9], 38 | ], 39 | "gp_historical_info": GpPrettyView._pretty_default_gp_historical_info, 40 | "covariance_info": GpPrettyView._pretty_default_covariance_info, 41 | "domain_info": GpPrettyView._pretty_default_domain_info, 42 | } 43 | 44 | @view_config(route_name=_pretty_route_name, renderer=PRETTY_RENDERER) 45 | def pretty_view(self): 46 | """A pretty, browser interactive view for the interface. Includes form request and response. 47 | 48 | .. http:get:: /gp/ei/pretty 49 | 50 | """ 51 | return self.pretty_response() 52 | 53 | @view_config(route_name=_route_name, renderer='json', request_method='POST') 54 | def gp_ei_view(self): 55 | """Endpoint for gp_ei POST requests. 56 | 57 | .. http:post:: /gp/ei 58 | 59 | Calculates the Expected Improvement (EI) of a set of points, given historical data. 60 | 61 | :input: :class:`moe.views.schemas.rest.GpEiRequest` 62 | :output: :class:`moe.views.schemas.rest.GpEiResponse` 63 | 64 | :status 200: returns a response 65 | :status 500: server error 66 | 67 | """ 68 | params = self.get_params_from_request() 69 | 70 | # TODO(GH-99): Change REST interface to give points_to_evaluate with shape 71 | # (num_to_evaluate, num_to_sample, dim) 72 | # Here we assume the shape is (num_to_evaluate, dim) so we insert an axis, making num_to_sample = 1. 73 | points_to_evaluate = numpy.array(params.get('points_to_evaluate'))[:, numpy.newaxis, :] 74 | points_being_sampled = numpy.array(params.get('points_being_sampled')) 75 | num_mc_iterations = params.get('mc_iterations') 76 | max_num_threads = params.get('max_num_threads') 77 | gaussian_process = _make_gp_from_params(params) 78 | 79 | expected_improvement_evaluator = ExpectedImprovement( 80 | gaussian_process, 81 | points_being_sampled=points_being_sampled, 82 | num_mc_iterations=num_mc_iterations, 83 | ) 84 | 85 | with timing_context(EI_COMPUTATION_TIMING_LABEL): 86 | expected_improvement = expected_improvement_evaluator.evaluate_at_point_list( 87 | points_to_evaluate, 88 | max_num_threads=max_num_threads, 89 | ) 90 | 91 | return self.form_response({ 92 | 'endpoint': self._route_name, 93 | 'expected_improvement': expected_improvement.tolist(), 94 | }) 95 | -------------------------------------------------------------------------------- /moe/views/rest/gp_next_points_kriging.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Classes for ``gp_next_points_kriging`` endpoints. 3 | 4 | Includes: 5 | 1. pretty and backend views 6 | 7 | """ 8 | from pyramid.view import view_config 9 | 10 | from moe.views.constant import GP_NEXT_POINTS_KRIGING_ROUTE_NAME, GP_NEXT_POINTS_KRIGING_PRETTY_ROUTE_NAME, GP_NEXT_POINTS_KRIGING_OPTIMIZER_METHOD_NAME 11 | from moe.views.gp_next_points_pretty_view import GpNextPointsPrettyView 12 | from moe.views.pretty_view import PRETTY_RENDERER 13 | from moe.views.schemas.rest.gp_next_points_kriging import GpNextPointsKrigingRequest 14 | 15 | 16 | class GpNextPointsKriging(GpNextPointsPrettyView): 17 | 18 | """Views for gp_next_points_kriging endpoints.""" 19 | 20 | _route_name = GP_NEXT_POINTS_KRIGING_ROUTE_NAME 21 | _pretty_route_name = GP_NEXT_POINTS_KRIGING_PRETTY_ROUTE_NAME 22 | 23 | request_schema = GpNextPointsKrigingRequest() 24 | 25 | _pretty_default_request = GpNextPointsPrettyView._pretty_default_request.copy() 26 | _pretty_default_request['std_deviation_coef'] = 0.0 27 | _pretty_default_request['kriging_noise_variance'] = 1e-8 28 | 29 | @view_config(route_name=_pretty_route_name, renderer=PRETTY_RENDERER) 30 | def pretty_view(self): 31 | """A pretty, browser interactive view for the interface. Includes form request and response.""" 32 | return self.pretty_response() 33 | 34 | @view_config(route_name=_route_name, renderer='json', request_method='POST') 35 | def gp_next_points_kriging_view(self): 36 | """Endpoint for gp_next_points_kriging POST requests. 37 | 38 | .. http:post:: /gp/next_points/kriging 39 | 40 | Calculates the next best points to sample, given historical data, using Kriging. 41 | 42 | :input: :class:`moe.views.schemas.rest.gp_next_points_kriging.GpNextPointsKrigingRequest` 43 | :output: :class:`moe.views.schemas.gp_next_points_pretty_view.GpNextPointsResponse` 44 | 45 | :status 200: returns a response 46 | :status 500: server error 47 | 48 | """ 49 | params = self.get_params_from_request() 50 | return self.compute_next_points_to_sample_response( 51 | params, 52 | GP_NEXT_POINTS_KRIGING_OPTIMIZER_METHOD_NAME, 53 | self._route_name, 54 | std_deviation_coef=params.get('std_deviation_coef'), 55 | kriging_noise_variance=params.get('kriging_noise_variance'), 56 | ) 57 | -------------------------------------------------------------------------------- /moe/views/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Request/Response schemas for the MOE REST interface and associated building blocks. 3 | 4 | Contains: 5 | 6 | * :mod:`moe.views.schemas.bandit_pretty_view`: common schemas for the ``bandit_*`` endpoints 7 | * :mod:`moe.views.schemas.base_schemas`: basic building-block schemas for use in other, more complex schemas 8 | * :mod:`moe.views.schemas.gp_next_points_pretty_view`: common schemas for the ``gp_next_points_*`` endpoints 9 | * :mod:`moe.views.rest`: schemas for specific REST endpoints 10 | 11 | .. Warning:: Outputs of colander schema serialization/deserialization should be treated as 12 | READ-ONLY. It appears that "missing=" and "default=" value are weak-copied (by reference). 13 | Thus changing missing/default fields in the output dict can modify the schema! 14 | 15 | """ 16 | -------------------------------------------------------------------------------- /moe/views/schemas/rest/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Request/response schemas for the MOE REST interface. 3 | 4 | Contains schemas for each endpoint represented in :mod:`moe.views.rest`. 5 | 6 | .. Warning:: Outputs of colander schema serialization/deserialization should be treated as 7 | READ-ONLY. It appears that "missing=" and "default=" value are weak-copied (by reference). 8 | Thus changing missing/default fields in the output dict can modify the schema! 9 | 10 | """ 11 | -------------------------------------------------------------------------------- /moe/views/schemas/rest/bandit_bla.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Request/response schemas for ``bandit_bla`` endpoints.""" 3 | import colander 4 | 5 | from moe.bandit.constant import DEFAULT_BLA_SUBTYPE, BLA_SUBTYPES 6 | from moe.views.schemas import base_schemas 7 | from moe.views.schemas.bandit_pretty_view import BanditHistoricalInfo 8 | 9 | 10 | class BanditBLARequest(base_schemas.StrictMappingSchema): 11 | 12 | """A :mod:`moe.views.rest.bandit_bla` request colander schema. 13 | 14 | **Required fields** 15 | 16 | :ivar historical_info: (:class:`moe.views.schemas.bandit_pretty_view.BanditHistoricalInfo`) object of historical data describing arm performance 17 | 18 | **Optional fields** 19 | 20 | :ivar subtype: (*str*) subtype of the BLA bandit algorithm (default: BLA) 21 | 22 | **Example Minimal Request** 23 | 24 | .. sourcecode:: http 25 | 26 | Content-Type: text/javascript 27 | 28 | { 29 | "historical_info": { 30 | "arms_sampled": { 31 | "arm1": {"win": 20, "total": 25}, 32 | "arm2": {"win": 20, "total": 30}, 33 | "arm3": {"win": 0, "total": 0}, 34 | }, 35 | }, 36 | } 37 | 38 | **Example Full Request** 39 | 40 | .. sourcecode:: http 41 | 42 | Content-Type: text/javascript 43 | 44 | { 45 | "subtype": "BLA", 46 | "historical_info": { 47 | "arms_sampled": { 48 | "arm1": {"win": 20, "loss": 0, "total": 25}, 49 | "arm2": {"win": 20, "loss": 0, "total": 30}, 50 | "arm3": {"win": 0, "loss": 0, "total": 0}, 51 | }, 52 | }, 53 | } 54 | 55 | """ 56 | 57 | subtype = colander.SchemaNode( 58 | colander.String(), 59 | validator=colander.OneOf(BLA_SUBTYPES), 60 | missing=DEFAULT_BLA_SUBTYPE, 61 | ) 62 | historical_info = BanditHistoricalInfo() 63 | -------------------------------------------------------------------------------- /moe/views/schemas/rest/bandit_epsilon.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Request/response schemas for ``bandit_epsilon`` endpoints.""" 3 | import colander 4 | 5 | from moe.bandit.constant import DEFAULT_EPSILON_SUBTYPE, EPSILON_SUBTYPES 6 | from moe.views.schemas import base_schemas 7 | from moe.views.schemas.bandit_pretty_view import BanditHistoricalInfo 8 | 9 | 10 | class BanditEpsilonRequest(base_schemas.StrictMappingSchema): 11 | 12 | """A :mod:`moe.views.rest.bandit_epsilon` request colander schema. 13 | 14 | **Required fields** 15 | 16 | :ivar historical_info: (:class:`moe.views.schemas.bandit_pretty_view.BanditHistoricalInfo`) object of historical data describing arm performance 17 | 18 | **Optional fields** 19 | 20 | :ivar subtype: (*str*) subtype of the epsilon bandit algorithm (default: greedy) 21 | :ivar hyperparameter_info: (:class:`~moe.views.schemas.bandit_pretty_view.BanditEpsilonFirstHyperparameterInfo` or :class:`~moe.views.schemas.bandit_pretty_view.BanditEpsilonGreedyHyperparameterInfo`) dict of hyperparameter information 22 | 23 | **Example Minimal Request** 24 | 25 | .. sourcecode:: http 26 | 27 | Content-Type: text/javascript 28 | 29 | { 30 | "historical_info": { 31 | "arms_sampled": { 32 | "arm1": {"win": 20, "loss": 5, "total": 25}, 33 | "arm2": {"win": 20, "loss": 10, "total": 30}, 34 | "arm3": {"win": 0, "loss": 0, "total": 0}, 35 | }, 36 | }, 37 | } 38 | 39 | **Example Full Request** 40 | 41 | .. sourcecode:: http 42 | 43 | Content-Type: text/javascript 44 | 45 | { 46 | "subtype": "greedy", 47 | "historical_info": { 48 | "arms_sampled": { 49 | "arm1": {"win": 20, "loss": 5, "total": 25}, 50 | "arm2": {"win": 20, "loss": 10, "total": 30}, 51 | "arm3": {"win": 0, "loss": 0, "total": 0}, 52 | }, 53 | }, 54 | "hyperparameter_info": { 55 | "epsilon": 0.05, 56 | }, 57 | } 58 | 59 | """ 60 | 61 | subtype = colander.SchemaNode( 62 | colander.String(), 63 | validator=colander.OneOf(EPSILON_SUBTYPES), 64 | missing=DEFAULT_EPSILON_SUBTYPE, 65 | ) 66 | historical_info = BanditHistoricalInfo() 67 | hyperparameter_info = colander.SchemaNode( 68 | colander.Mapping(unknown='preserve'), 69 | missing={}, 70 | ) 71 | -------------------------------------------------------------------------------- /moe/views/schemas/rest/bandit_ucb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Request/response schemas for ``bandit_ucb`` endpoints.""" 3 | import colander 4 | 5 | from moe.bandit.constant import DEFAULT_UCB_SUBTYPE, UCB_SUBTYPES 6 | from moe.views.schemas import base_schemas 7 | from moe.views.schemas.bandit_pretty_view import BanditHistoricalInfo 8 | 9 | 10 | class BanditUCBRequest(base_schemas.StrictMappingSchema): 11 | 12 | """A :mod:`moe.views.rest.bandit_ucb` request colander schema. 13 | 14 | **Required fields** 15 | 16 | :ivar historical_info: (:class:`moe.views.schemas.bandit_pretty_view.BanditHistoricalInfo`) object of historical data describing arm performance 17 | 18 | **Optional fields** 19 | 20 | :ivar subtype: (*str*) subtype of the UCB bandit algorithm (default: UCB1) 21 | 22 | **Example Minimal Request** 23 | 24 | .. sourcecode:: http 25 | 26 | Content-Type: text/javascript 27 | 28 | { 29 | "historical_info": { 30 | "arms_sampled": { 31 | "arm1": {"win": 20, "loss": 5, "total": 25}, 32 | "arm2": {"win": 20, "loss": 10, "total": 30}, 33 | "arm3": {"win": 0, "loss": 0, "total": 0}, 34 | }, 35 | }, 36 | } 37 | 38 | **Example Full Request** 39 | 40 | .. sourcecode:: http 41 | 42 | Content-Type: text/javascript 43 | 44 | { 45 | "subtype": "UCB1-tuned", 46 | "historical_info": { 47 | "arms_sampled": { 48 | "arm1": {"win": 20, "loss": 5, "total": 25, "variance": 0.1}, 49 | "arm2": {"win": 20, "loss": 10, "total": 30, "variance": 0.2}, 50 | "arm3": {"win": 0, "loss": 0, "total": 0}, 51 | }, 52 | }, 53 | } 54 | 55 | """ 56 | 57 | subtype = colander.SchemaNode( 58 | colander.String(), 59 | validator=colander.OneOf(UCB_SUBTYPES), 60 | missing=DEFAULT_UCB_SUBTYPE, 61 | ) 62 | historical_info = BanditHistoricalInfo() 63 | -------------------------------------------------------------------------------- /moe_examples/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Examples for using the moe package.""" 3 | -------------------------------------------------------------------------------- /moe_examples/hyper_opt_of_gp_from_historical_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """An example for accessing the gp_mean_var simple endpoint. 3 | 4 | :func:`moe.easy_interface.simple_endpoint.gp_mean_var` 5 | 6 | The function requires some historical information to inform the Gaussian Process 7 | 8 | The optimal hyperparameters are returned. 9 | """ 10 | import numpy 11 | 12 | from moe.easy_interface.simple_endpoint import gp_hyper_opt 13 | from moe.optimal_learning.python.data_containers import SamplePoint 14 | 15 | # Randomly generate some historical data 16 | # points_sampled is an iterable of iterables of the form [point_as_a_list, objective_function_value, value_variance] 17 | points_sampled = [ 18 | SamplePoint(numpy.array([x]), numpy.random.uniform(-1, 1), 0.01) for x in numpy.arange(0, 1, 0.1) 19 | ] 20 | 21 | 22 | def run_example(verbose=True, **kwargs): 23 | """Run the example, aksing MOE for optimal hyperparameters given historical data.""" 24 | covariance_info = gp_hyper_opt( 25 | points_sampled, 26 | **kwargs 27 | ) 28 | 29 | if verbose: 30 | print covariance_info 31 | 32 | 33 | if __name__ == '__main__': 34 | run_example() 35 | -------------------------------------------------------------------------------- /moe_examples/mean_and_var_of_gp_from_historic_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """An example for accessing the gp_mean_var simple endpoint. 3 | 4 | :func:`moe.easy_interface.simple_endpoint.gp_mean_var` 5 | 6 | The function requires some historical information to inform the Gaussian Process 7 | and a set of points to calculate the posterior mean and variance at. 8 | 9 | The posterior mean and variance is then printed for every point. 10 | """ 11 | import numpy 12 | 13 | from moe.easy_interface.simple_endpoint import gp_mean_var 14 | 15 | # Randomly generate some historical data 16 | # points_sampled is an iterable of iterables of the form [point_as_a_list, objective_function_value, value_variance] 17 | points_sampled = [ 18 | [[x], numpy.random.uniform(-1, 1), 0.01] for x in numpy.arange(0, 1, 0.1) 19 | ] 20 | 21 | 22 | def run_example(verbose=True, testapp=None, **kwargs): 23 | """Run the example, finding the posterior mean and variance for various poinst from a random GP.""" 24 | points_to_evaluate = [[x] for x in numpy.arange(0, 1, 0.05)] # uniform grid of points 25 | mean, var = gp_mean_var( 26 | points_sampled, # Historical data to inform Gaussian Process 27 | points_to_evaluate, # We will calculate the mean and variance of the GP at these points 28 | testapp=testapp, 29 | **kwargs 30 | ) 31 | 32 | if verbose: 33 | # Print out the mean and variance of the GP at each point_to_evaluate 34 | for i, point in enumerate(points_to_evaluate): 35 | print "GP({0:s}) ~ N({1:.18E}, {2:.18E})".format(str(point), mean[i], var[i][i]) 36 | 37 | 38 | if __name__ == '__main__': 39 | run_example() 40 | -------------------------------------------------------------------------------- /moe_examples/next_point_via_simple_endpoint.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """An example for accessing the gp_next_points_* endpoints. 3 | 4 | :func:`moe.easy_interface.simple_endpoint.gp_next_points` 5 | 6 | The function requires some historical information to inform the Gaussian Process 7 | 8 | The result is the next best set of point(s) to sample 9 | 10 | .. Note:: 11 | 12 | This file is copied in ``README.md`` and ``docs/index.rst``. Any changes here should be made there as well. 13 | """ 14 | import math 15 | import random 16 | 17 | from moe.easy_interface.experiment import Experiment 18 | from moe.easy_interface.simple_endpoint import gp_next_points 19 | from moe.optimal_learning.python.data_containers import SamplePoint 20 | 21 | 22 | # Note: this function can be anything, the output of a batch, results of an A/B experiment, the value of a physical experiment etc. 23 | def function_to_minimize(x): 24 | """Calculate an aribitrary 2-d function with some noise with minimum near [1, 2.6].""" 25 | return math.sin(x[0]) * math.cos(x[1]) + math.cos(x[0] + x[1]) + random.uniform(-0.02, 0.02) 26 | 27 | 28 | def run_example(num_points_to_sample=20, verbose=True, **kwargs): 29 | """Run the example, aksing MOE for ``num_points_to_sample`` optimal points to sample.""" 30 | exp = Experiment([[0, 2], [0, 4]]) # 2D experiment, we build a tensor product domain 31 | # Bootstrap with some known or already sampled point(s) 32 | exp.historical_data.append_sample_points([ 33 | SamplePoint([0, 0], function_to_minimize([0, 0]), 0.05), # Iterables of the form [point, f_val, f_var] are also allowed 34 | ]) 35 | 36 | # Sample num_points_to_sample points 37 | for _ in range(num_points_to_sample): 38 | # Use MOE to determine what is the point with highest Expected Improvement to use next 39 | next_point_to_sample = gp_next_points(exp, **kwargs)[0] # By default we only ask for one point 40 | # Sample the point from our objective function, we can replace this with any function 41 | value_of_next_point = function_to_minimize(next_point_to_sample) 42 | 43 | if verbose: 44 | print "Sampled f({0:s}) = {1:.18E}".format(str(next_point_to_sample), value_of_next_point) 45 | 46 | # Add the information about the point to the experiment historical data to inform the GP 47 | exp.historical_data.append_sample_points([SamplePoint(next_point_to_sample, value_of_next_point, 0.01)]) # We can add some noise 48 | 49 | 50 | if __name__ == '__main__': 51 | run_example() 52 | -------------------------------------------------------------------------------- /moe_examples/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tests for the moe examples.""" 3 | -------------------------------------------------------------------------------- /moe_examples/tests/bandit_example_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Integration test for bandit_example MOE example.""" 3 | from moe.bandit.constant import TEST_EPSILON 4 | 5 | from moe_examples.tests.moe_example_test_case import MoeExampleTestCase 6 | from moe_examples.bandit_example import run_example 7 | 8 | 9 | class TestBanditExample(MoeExampleTestCase): 10 | 11 | """Test the bandit_example MOE example.""" 12 | 13 | def test_example_runs_with_non_default_kwargs(self): 14 | """Simple integration test for example with non default kwargs.""" 15 | run_example( 16 | verbose=False, 17 | testapp=self.testapp, 18 | bandit_bla_kwargs={}, 19 | bandit_epsilon_kwargs={ 20 | 'hyperparameter_info': { 21 | 'epsilon': TEST_EPSILON, 22 | } 23 | }, 24 | bandit_ucb_kwargs={}, 25 | rest_port=1337, 26 | ) 27 | -------------------------------------------------------------------------------- /moe_examples/tests/combined_example_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Integration test for combined_example MOE example.""" 3 | from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_GRADIENT_DESCENT_PARAMETERS 4 | 5 | from moe_examples.tests.moe_example_test_case import MoeExampleTestCase 6 | from moe_examples.combined_example import run_example 7 | 8 | 9 | class TestCombinedExample(MoeExampleTestCase): 10 | 11 | """Test the combined_example MOE example.""" 12 | 13 | def test_example_runs_with_non_default_optimizer_kwargs(self): 14 | """Simple integration test for example with non default kwargs.""" 15 | run_example( 16 | num_to_sample=1, 17 | verbose=False, 18 | testapp=self.testapp, 19 | gp_next_points_kwargs={ 20 | 'optimizer_info': { 21 | 'num_multistarts': TEST_OPTIMIZER_MULTISTARTS, 22 | 'num_random_samples': TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, 23 | 'optimizer_parameters': TEST_GRADIENT_DESCENT_PARAMETERS._asdict(), 24 | } 25 | }, 26 | gp_hyper_opt_kwargs={ 27 | 'optimizer_info': { 28 | 'num_multistarts': TEST_OPTIMIZER_MULTISTARTS, 29 | 'num_random_samples': TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, 30 | 'optimizer_parameters': TEST_GRADIENT_DESCENT_PARAMETERS._asdict(), 31 | } 32 | }, 33 | gp_mean_var_kwargs={}, 34 | rest_port=1337, 35 | ) 36 | -------------------------------------------------------------------------------- /moe_examples/tests/hyper_opt_of_gp_from_historical_data_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Integration test for hyper_opt_of_gp_from_historical_data MOE example.""" 3 | from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_GRADIENT_DESCENT_PARAMETERS, GRADIENT_DESCENT_OPTIMIZER 4 | 5 | from moe_examples.tests.moe_example_test_case import MoeExampleTestCase 6 | from moe_examples.hyper_opt_of_gp_from_historical_data import run_example 7 | 8 | 9 | class TestHyperOptOfGpFromHistoricalData(MoeExampleTestCase): 10 | 11 | """Test the hyper_opt_of_gp_from_historical_data MOE example.""" 12 | 13 | def test_example_runs(self): 14 | """Simple integration test for example.""" 15 | run_example( 16 | verbose=False, 17 | testapp=self.testapp, 18 | optimizer_info={ 19 | 'optimizer_type': GRADIENT_DESCENT_OPTIMIZER, 20 | 'num_multistarts': TEST_OPTIMIZER_MULTISTARTS, 21 | 'num_random_samples': TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, 22 | 'optimizer_parameters': TEST_GRADIENT_DESCENT_PARAMETERS._asdict(), 23 | }, 24 | rest_port=1337, 25 | ) 26 | -------------------------------------------------------------------------------- /moe_examples/tests/mean_and_var_of_gp_from_historic_data_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Integration test for mean_and_var_of_gp_from_historic_data MOE example.""" 3 | from moe_examples.tests.moe_example_test_case import MoeExampleTestCase 4 | from moe_examples.mean_and_var_of_gp_from_historic_data import run_example 5 | 6 | 7 | class TestMeanAndVarOfGpFromHistoricData(MoeExampleTestCase): 8 | 9 | """Test the mean_and_var_of_gp_from_historic_data MOE example.""" 10 | 11 | def test_example_runs(self): 12 | """Simple integration test for example.""" 13 | run_example( 14 | verbose=False, 15 | testapp=self.testapp, 16 | rest_port=1337, 17 | ) 18 | -------------------------------------------------------------------------------- /moe_examples/tests/moe_example_test_case.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Base class for testing the moe examples.""" 3 | import pytest 4 | 5 | 6 | class MoeExampleTestCase(object): 7 | 8 | """Base class for testing the moe examples.""" 9 | 10 | @classmethod 11 | @pytest.fixture(autouse=True, scope='class') 12 | def create_webapp(cls): 13 | """Create a mocked webapp and store it in cls.testapp.""" 14 | from moe import main 15 | app = main({}, use_mongo='false') 16 | from webtest import TestApp 17 | cls.testapp = TestApp(app) 18 | -------------------------------------------------------------------------------- /moe_examples/tests/next_point_via_simple_endpoint_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Integration test for next_point_via_simple_endpoint MOE example.""" 3 | from moe.optimal_learning.python.constant import TEST_OPTIMIZER_MULTISTARTS, TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, TEST_GRADIENT_DESCENT_PARAMETERS 4 | 5 | from moe_examples.tests.moe_example_test_case import MoeExampleTestCase 6 | from moe_examples.next_point_via_simple_endpoint import run_example 7 | 8 | 9 | class TestNextPointsViaSimpleEndpoint(MoeExampleTestCase): 10 | 11 | """Test the next_point_via_simple_endpoint MOE example.""" 12 | 13 | def test_example_runs(self): 14 | """Simple integration test for example.""" 15 | run_example( 16 | num_points_to_sample=1, 17 | verbose=False, 18 | testapp=self.testapp, 19 | optimizer_info={ 20 | 'num_multistarts': TEST_OPTIMIZER_MULTISTARTS, 21 | 'num_random_samples': TEST_OPTIMIZER_NUM_RANDOM_SAMPLES, 22 | 'optimizer_parameters': TEST_GRADIENT_DESCENT_PARAMETERS._asdict(), 23 | }, 24 | rest_port=1337, 25 | ) 26 | -------------------------------------------------------------------------------- /pep8-blacklist.txt: -------------------------------------------------------------------------------- 1 | doc/conf.py 2 | setup.py 3 | -------------------------------------------------------------------------------- /production.ini: -------------------------------------------------------------------------------- 1 | [app:MOE] 2 | use = egg:MOE 3 | reload_templates = false 4 | debug_authorization = false 5 | debug_notfound = false 6 | debug_routematch = false 7 | debug_templates = false 8 | default_locale_name = en 9 | mongodb.url = mongodb://localhost 10 | mongodb.db_name = mydb 11 | 12 | [filter:weberror] 13 | use = egg:WebError#error_catcher 14 | debug = false 15 | ;error_log = 16 | ;show_exceptions_in_wsgi_errors = true 17 | ;smtp_server = localhost 18 | ;error_email = janitor@example.com 19 | ;smtp_username = janitor 20 | ;smtp_password = "janitor's password" 21 | ;from_address = paste@localhost 22 | ;error_subject_prefix = "Pyramid Error" 23 | ;smtp_use_tls = 24 | ;error_message = 25 | 26 | [pipeline:main] 27 | pipeline = 28 | weberror 29 | MOE 30 | 31 | [server:main] 32 | use = egg:Paste#http 33 | host = 0.0.0.0 34 | port = 6543 35 | 36 | # Begin logging configuration 37 | 38 | [loggers] 39 | keys = root, moe 40 | 41 | [handlers] 42 | keys = console, filelog 43 | 44 | [formatters] 45 | keys = generic 46 | 47 | [handler_filelog] 48 | class = FileHandler 49 | args = ('%(here)s/moe.log','a') 50 | level = DEBUG 51 | formatter = generic 52 | 53 | # used by all applications in the Pyramid process that ask for a logger 54 | [logger_root] 55 | level = WARN 56 | handlers = console, filelog 57 | 58 | [logger_moe] 59 | level = INFO 60 | handlers = 61 | qualname = moe 62 | 63 | [handler_console] 64 | class = StreamHandler 65 | args = (sys.stderr,) 66 | level = NOTSET 67 | formatter = generic 68 | 69 | [formatter_generic] 70 | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s 71 | 72 | # End logging configuration 73 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # If you add/remove requirements here, update ``requires`` in ``setup.py`` (root directory) 2 | pyramid==1.5.1 3 | pyramid_mako==1.0.2 4 | WebError==0.10.3 5 | pytest==2.6.3 6 | webtest==2.0.15 7 | simplejson==3.5.3 8 | numpy==1.8.1 9 | scipy==0.14.0 10 | colander==1.0b1 11 | sphinx==1.2.2 12 | breathe==1.2.0 13 | sphinxcontrib-httpdomain==1.2.1 14 | sphinx_rtd_theme==0.1.6 15 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | match = ^test 3 | nocapture = 1 4 | cover-package = moe 5 | with-coverage = 1 6 | cover-erase = 1 7 | 8 | [compile_catalog] 9 | directory = moe/locale 10 | domain = MOE 11 | statistics = true 12 | 13 | [extract_messages] 14 | add_comments = TRANSLATORS: 15 | output_file = moe/locale/MOE.pot 16 | width = 80 17 | 18 | [init_catalog] 19 | domain = MOE 20 | input_file = moe/locale/MOE.pot 21 | output_dir = moe/locale 22 | 23 | [update_catalog] 24 | domain = MOE 25 | input_file = moe/locale/MOE.pot 26 | output_dir = moe/locale 27 | previous = true 28 | --------------------------------------------------------------------------------