├── VERSION.txt ├── gp ├── tests │ ├── __init__.py │ ├── util.py │ ├── test_kernels.py │ ├── test_gaussian_kernel.py │ ├── test_periodic_kernel.py │ └── test_gp.py ├── ext │ ├── __init__.py │ ├── gaussian_c.pyx │ ├── gp_c.pyx │ └── periodic_c.pyx ├── kernels │ ├── __init__.py │ ├── base.py │ ├── gaussian.py │ └── periodic.py ├── __init__.py └── gp.py ├── requirements.txt ├── docs ├── source │ ├── gp.kernels.Kernel.rst │ ├── gp.kernels.GaussianKernel.rst │ ├── gp.kernels.PeriodicKernel.rst │ ├── gp.gp.rst │ ├── gp.kernels.rst │ ├── index.rst │ └── conf.py ├── Makefile └── make.bat ├── .gitignore ├── MANIFEST.in ├── .coveragerc ├── README.md ├── .travis.yml ├── Makefile ├── COPYING.txt ├── setup.py └── CHANGELOG.md /VERSION.txt: -------------------------------------------------------------------------------- 1 | 1.0.5 2 | -------------------------------------------------------------------------------- /gp/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | sympy>=0.7.2 2 | numpy>=1.7.1 3 | matplotlib>=1.2.1 4 | cython>=0.19.2 5 | -------------------------------------------------------------------------------- /docs/source/gp.kernels.Kernel.rst: -------------------------------------------------------------------------------- 1 | Kernel base class 2 | ================= 3 | 4 | .. autoclass:: gp.kernels.base.Kernel 5 | -------------------------------------------------------------------------------- /gp/ext/__init__.py: -------------------------------------------------------------------------------- 1 | import gaussian_c 2 | import periodic_c 3 | import gp_c 4 | 5 | __all__ = ["gaussian_c", "periodic_c", "gp_c"] 6 | -------------------------------------------------------------------------------- /docs/source/gp.kernels.GaussianKernel.rst: -------------------------------------------------------------------------------- 1 | Gaussian kernel 2 | =============== 3 | 4 | .. autoclass:: gp.kernels.gaussian.GaussianKernel 5 | -------------------------------------------------------------------------------- /docs/source/gp.kernels.PeriodicKernel.rst: -------------------------------------------------------------------------------- 1 | Periodic kernel 2 | =============== 3 | 4 | .. autoclass:: gp.kernels.periodic.PeriodicKernel 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | MANIFEST 2 | build 3 | dist 4 | docs/_build 5 | gp/ext/*.c 6 | gp/ext/*.so 7 | .coverage 8 | htmlcov 9 | gaussian_processes.egg-info 10 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include CHANGELOG.md 3 | include COPYING.txt 4 | include VERSION.txt 5 | recursive-include gp *.py 6 | recursive-include gp *.pyx 7 | -------------------------------------------------------------------------------- /docs/source/gp.gp.rst: -------------------------------------------------------------------------------- 1 | Gaussian process object 2 | ======================= 3 | 4 | .. autoclass:: gp.gp.GP 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /gp/kernels/__init__.py: -------------------------------------------------------------------------------- 1 | from base import Kernel 2 | from periodic import PeriodicKernel 3 | from gaussian import GaussianKernel 4 | 5 | __all__ = ['Kernel', 'PeriodicKernel', 'GaussianKernel'] 6 | -------------------------------------------------------------------------------- /docs/source/gp.kernels.rst: -------------------------------------------------------------------------------- 1 | Kernels 2 | ======= 3 | 4 | .. module:: gp.kernels 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | 9 | gp.kernels.GaussianKernel 10 | gp.kernels.PeriodicKernel 11 | gp.kernels.Kernel 12 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = true 3 | source = 4 | gp/ 5 | 6 | [report] 7 | exclude_lines = 8 | pragma: no cover 9 | raise NotImplementedError 10 | if __name__ == .__main__.: 11 | except KeyboardInterrupt 12 | show_missing = true 13 | -------------------------------------------------------------------------------- /gp/__init__.py: -------------------------------------------------------------------------------- 1 | from . import ext 2 | __all__ = ["ext"] 3 | 4 | from .gp import GP 5 | __all__.append("GP") 6 | 7 | from .kernels import * 8 | __all__.extend(kernels.__all__) 9 | 10 | import logging 11 | FORMAT = '%(levelname)s -- %(processName)s/%(filename)s -- %(message)s' 12 | logging.basicConfig(format=FORMAT) 13 | logger = logging.getLogger("gp") 14 | logger.setLevel("INFO") 15 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. src documentation master file, created by 2 | sphinx-quickstart on Sat Jun 29 16:21:04 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | .. include:: ../../README.rst 7 | 8 | Documentation 9 | ------------- 10 | 11 | .. toctree:: 12 | :maxdepth: 4 13 | 14 | gp.gp 15 | gp.kernels 16 | 17 | Much of this code is based off of [RW06]_. 18 | 19 | .. rubric:: Bibliography 20 | 21 | .. [RW06] Rasmussen, C. E., & Williams, C. K. I. (2006). Gaussian 22 | processes for machine learning. MIT Press. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gaussian Processes 2 | 3 | [![Build Status](https://travis-ci.org/jhamrick/gaussian_processes.png?branch=master)](https://travis-ci.org/jhamrick/gaussian_processes) 4 | [![Coverage Status](https://coveralls.io/repos/jhamrick/gaussian_processes/badge.png?branch=master)](https://coveralls.io/r/jhamrick/gaussian_processes?branch=master) 5 | 6 | `gaussian_processes` is a Python package for using and analyzing [Gaussian Processes](http://en.wikipedia.org/wiki/Gaussian_process). 7 | 8 | * [Documentation](http://jhamrick.github.io/gaussian_processes/) 9 | * [GitHub Repository](https://github.com/jhamrick/gaussian_processes) 10 | * [PyPI](https://pypi.python.org/pypi/gaussian_processes) 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | virtualenv: 5 | system_site_packages: true 6 | 7 | # Using apt doesn't help for Python 3.3, because the packages on Precise 8 | # are compiled for 3.2. This uses Miniconda to install numpy & pandas. 9 | # Cribbed from https://gist.github.com/dan-blanchard/7045057 10 | 11 | # Setup anaconda 12 | before_install: 13 | - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then wget http://repo.continuum.io/miniconda/Miniconda-2.2.2-Linux-x86_64.sh -O miniconda.sh; else wget http://repo.continuum.io/miniconda/Miniconda3-2.2.2-Linux-x86_64.sh -O miniconda.sh; fi 14 | - chmod +x miniconda.sh 15 | - ./miniconda.sh -b 16 | - export PATH=/home/travis/anaconda/bin:$PATH 17 | # Install packages 18 | install: 19 | - conda install --yes pip python=$TRAVIS_PYTHON_VERSION numpy scipy sympy matplotlib cython 20 | - pip install -r requirements.txt --use-mirrors 21 | - pip install pytest pytest-cov coveralls 22 | - python setup.py build_ext --inplace 23 | 24 | script: 25 | - xvfb-run --server-args="-screen 0 1024x768x24" py.test --cov gp 26 | 27 | after_success: 28 | - coveralls 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PYCMD=python setup.py 2 | GH_PAGES_SOURCES=gp docs VERSION.txt README.md Makefile setup.py 3 | 4 | all: 5 | $(PYCMD) bdist 6 | 7 | source: 8 | $(PYCMD) sdist 9 | 10 | upload: 11 | $(PYCMD) sdist upload 12 | 13 | install: 14 | $(PYCMD) install 15 | 16 | clean: 17 | $(PYCMD) clean --all 18 | rm -rf dist 19 | rm -f MANIFEST 20 | rm -f README 21 | rm -f *.pyc 22 | rm -f gp/ext/*.c 23 | rm -f gp/ext/*.so 24 | rm -rf *.egg-info 25 | rm -f .coverage 26 | rm -rf htmlcov 27 | rm -rf build 28 | 29 | cython: 30 | $(PYCMD) build_ext --inplace 31 | 32 | test: 33 | py.test --cov gp 34 | 35 | gh-pages: 36 | make clean || true 37 | git checkout gh-pages 38 | rm -rf _sources _static _modules 39 | git checkout master $(GH_PAGES_SOURCES) 40 | git reset HEAD 41 | pandoc --from markdown --to rst -o README.rst README.md 42 | python setup.py build_ext --inplace 43 | make -C docs html 44 | mv -fv docs/_build/html/* . 45 | rm -rf $(GH_PAGES_SOURCES) README.rst build 46 | git add -A 47 | git ci -m "Generated gh-pages for `git log master -1 --pretty=short --abbrev-commit`" && git push origin gh-pages 48 | git checkout master 49 | -------------------------------------------------------------------------------- /COPYING.txt: -------------------------------------------------------------------------------- 1 | This software is licensed under the terms of the MIT License as 2 | follows: 3 | 4 | Copyright (c) 2013 Jessica B. Hamrick 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /gp/tests/util.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | opt = { 5 | 'n_big_test_iters': 100, 6 | 'n_small_test_iters': 10, 7 | 'pct_allowed_failures': 5, 8 | 'error_threshold': 1e-5, 9 | 'dtheta': 1e-5, 10 | 'dtype': np.float64, 11 | 'eps': np.finfo(np.float64).eps, 12 | } 13 | 14 | 15 | def rand_params(*args): 16 | params = [] 17 | for param in args: 18 | if param == 'h': 19 | params.append(np.random.uniform(0, 2)) 20 | elif param == 'w': 21 | params.append(np.random.uniform(np.pi / 32., np.pi / 2.)) 22 | elif param == 'p': 23 | params.append(np.random.uniform(0.33, 3)) 24 | elif param == 's': 25 | params.append(np.random.uniform(0, 0.5)) 26 | else: # pragma: no cover 27 | pass 28 | return tuple(params) 29 | 30 | 31 | def approx_deriv(y0, y1, dx): 32 | dy = (y1 - y0) / 2. 33 | return dy / dx 34 | 35 | 36 | def make_xy(): 37 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16).astype(opt['dtype']) 38 | y = np.sin(x) 39 | return x, y 40 | 41 | 42 | def make_xo(): 43 | xo = np.linspace(-2 * np.pi, 2 * np.pi, 32).astype(opt['dtype']) 44 | return xo 45 | 46 | 47 | def seed(): 48 | np.random.seed(2348) 49 | 50 | 51 | def allclose(x, y): 52 | return np.allclose(x, y, rtol=opt['error_threshold']) 53 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | from distutils.extension import Extension 5 | from Cython.Build import cythonize 6 | import numpy as np 7 | 8 | extensions = [ 9 | Extension( 10 | "gp.ext.gaussian_c", ["gp/ext/gaussian_c.pyx"], 11 | include_dirs=[np.get_include()], 12 | libraries=["m"] 13 | ), 14 | Extension( 15 | "gp.ext.periodic_c", ["gp/ext/periodic_c.pyx"], 16 | include_dirs=[np.get_include()], 17 | libraries=["m"] 18 | ), 19 | Extension( 20 | "gp.ext.gp_c", ["gp/ext/gp_c.pyx"], 21 | include_dirs=[np.get_include()], 22 | libraries=["m"] 23 | ) 24 | ] 25 | 26 | setup( 27 | name='gaussian_processes', 28 | version=open('VERSION.txt').read().strip(), 29 | description='Python library for gaussian processes', 30 | author='Jessica B. Hamrick', 31 | author_email='jhamrick@berkeley.edu', 32 | url='https://github.com/jhamrick/gaussian_processes', 33 | packages=['gp', 'gp.kernels', 'gp.ext', 'gp.tests'], 34 | ext_modules=cythonize(extensions), 35 | keywords='gp kernel statistics', 36 | classifiers=[ 37 | "Development Status :: 3 - Alpha", 38 | "Intended Audience :: Science/Research", 39 | "License :: OSI Approved :: MIT License", 40 | "Programming Language :: Python :: 2.7", 41 | "Topic :: Scientific/Engineering :: Mathematics" 42 | ], 43 | install_requires=[ 44 | 'numpy', 45 | 'sympy' 46 | ] 47 | ) 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 1.0.5 4 | 5 | * Don't throw an error when setting s parameter 6 | * Return zero likelihood if determinant is close to zero 7 | 8 | ## Version 1.0.4 9 | 10 | * Use a Cholesky solver rather than computing inverses 11 | * Specify C-contiguous arrays and disable wraparound in Cython code 12 | * Add `__copy__`, `__deepcopy__`, `__getstate__`, and `__setstate__` 13 | methods to gp and kernels 14 | * Add methods for getting/setting kernel parameters by name 15 | 16 | ## Version 1.0.3 17 | 18 | * Fix bugs in documentation generation 19 | * Remove `fit_MLII` method, because it was not robust 20 | 21 | ## Version 1.0.2 22 | 23 | * Miscellaneous bugfixes 24 | 25 | ## Version 1.0.1 26 | 27 | * Fix bugs in compiling documentation 28 | 29 | ## Version 1.0.0 30 | 31 | * Use Cython instead of Numba 32 | * Create `gp.ext` module to hold Cython code 33 | * Improve test coverage 34 | * Enforce usage of 1-dimensional arrays 35 | 36 | ## Version 0.1.3 37 | 38 | * Fix pip installion bug 39 | * Add a few new methods to `GP`: 40 | * `dm_dtheta` -- compute the derivative of the mean of the 41 | gaussian process with respect to its parameters 42 | * `plot` -- use pyplot to plot the predictive mean and variance of 43 | the gaussian process 44 | * Switch to semantic versioning 45 | 46 | ## Version 0.01.2 47 | 48 | * Add Sphinx support 49 | * Write better documentation 50 | 51 | ## Version 0.01.1 52 | 53 | * More robust tests 54 | 55 | ## Version 0.01 56 | 57 | * Basic GP functionality 58 | * Gaussian kernel 59 | * Periodic kernel 60 | -------------------------------------------------------------------------------- /gp/kernels/base.py: -------------------------------------------------------------------------------- 1 | __all__ = ['Kernel'] 2 | 3 | from functools import wraps 4 | from copy import copy 5 | 6 | 7 | class Kernel(object): 8 | 9 | def __getstate__(self): 10 | state = {'params': self.params} 11 | return state 12 | 13 | def __setstate__(self, state): 14 | self.params = state['params'] 15 | 16 | def __copy__(self): 17 | return type(self)(*self.params) 18 | 19 | def __deepcopy__(self, memo): 20 | return type(self)(*self.params) 21 | 22 | def copy(self): 23 | """ 24 | Create a copy of the kernel. 25 | 26 | Returns 27 | ------- 28 | kernel : :class:`~gp.kernels.base.Kernel` 29 | New kernel function object of type ``type(self)``. 30 | 31 | """ 32 | return copy(self) 33 | 34 | def sym_K(self): 35 | """ 36 | Symbolic kernel function. 37 | 38 | Returns 39 | ------- 40 | K : sympy.Expr 41 | A sympy expression representing the symbolic form of the 42 | kernel function. 43 | 44 | """ 45 | raise NotImplementedError 46 | 47 | @property 48 | def params(self): 49 | """ 50 | Kernel parameters. 51 | 52 | Returns 53 | ------- 54 | params : tuple 55 | 56 | """ 57 | raise NotImplementedError 58 | 59 | def K(self, x1, x2, out=None): 60 | r""" 61 | Kernel function evaluated at `x1` and `x2`. 62 | 63 | Parameters 64 | ---------- 65 | x1 : numpy.ndarray with ``dtype='f8'`` 66 | :math:`n`-length vector 67 | x2 : numpy.ndarray with ``dtype='f8'`` 68 | :math:`m`-length vector 69 | 70 | Returns 71 | ------- 72 | K : numpy.ndarray 73 | :math:`n\times m` array 74 | 75 | """ 76 | raise NotImplementedError 77 | 78 | @wraps(K) 79 | def __call__(self, x1, x2, out=None): 80 | return self.K(x1, x2, out=out) 81 | 82 | def jacobian(self, x1, x2, out=None): 83 | r""" 84 | Jacobian of the kernel function evaluated at `x1` and `x2`. 85 | 86 | Parameters 87 | ---------- 88 | x1 : numpy.ndarray with ``dtype='f8'`` 89 | :math:`n`-length vector 90 | x2 : numpy.ndarray with ``dtype='f8'`` 91 | :math:`m`-length vector 92 | 93 | Returns 94 | ------- 95 | J : numpy.ndarray 96 | :math:`n_p\times n\times m` array, where :math:`n_p` is the 97 | number of kernel parameters. See `params`. 98 | 99 | """ 100 | raise NotImplementedError 101 | 102 | def hessian(self, x1, x2, out=None): 103 | r""" 104 | Hessian of the kernel function evaluated at `x1` and `x2`. 105 | 106 | Parameters 107 | ---------- 108 | x1 : numpy.ndarray with ``dtype='f8'`` 109 | :math:`n`-length vector 110 | x2 : numpy.ndarray with ``dtype='f8'`` 111 | :math:`m`-length vector 112 | 113 | Returns 114 | ------- 115 | H : numpy.ndarray 116 | :math:`n_p\times n_p\times n\times m` array, where 117 | :math:`n_p` is the number of kernel parameters. See 118 | `params`. 119 | 120 | """ 121 | raise NotImplementedError 122 | -------------------------------------------------------------------------------- /gp/tests/test_kernels.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | 4 | from .util import opt, approx_deriv, allclose 5 | 6 | EPS = np.finfo(float).eps 7 | DTHETA = opt['dtheta'] 8 | 9 | 10 | ###################################################################### 11 | 12 | 13 | def check_params(kernel, params): 14 | k = kernel(*params) 15 | assert (k.params == np.array(params)).all() 16 | k.params = params 17 | assert (k.params == np.array(params)).all() 18 | 19 | 20 | def check_invalid_params(kernel, good_params, bad_params): 21 | with pytest.raises(ValueError): 22 | kernel(*bad_params) 23 | 24 | k = kernel(*good_params) 25 | with pytest.raises(ValueError): 26 | k.params = bad_params 27 | 28 | 29 | def check_jacobian(k, x): 30 | kernel = type(k) 31 | params = k.params.copy() 32 | jac1 = k.jacobian(x, x) 33 | jac2 = np.empty_like(jac1) 34 | k.jacobian(x, x, out=jac2) 35 | 36 | approx_jac = np.empty(jac1.shape) 37 | for i in xrange(len(params)): 38 | p0 = list(params) 39 | p0[i] -= DTHETA 40 | p1 = list(params) 41 | p1[i] += DTHETA 42 | k0 = kernel(*p0)(x, x) 43 | k1 = kernel(*p1)(x, x) 44 | approx_jac[i] = approx_deriv(k0, k1, DTHETA) 45 | 46 | assert allclose(jac1, approx_jac) 47 | assert allclose(jac2, approx_jac) 48 | assert allclose(jac1, jac2) 49 | 50 | 51 | def check_dK_dtheta(k, x, p, i): 52 | kernel = type(k) 53 | params = k.params.copy() 54 | f = getattr(k, "dK_d%s" % p) 55 | dK_dtheta1 = f(x, x) 56 | dK_dtheta2 = np.empty_like(dK_dtheta1) 57 | f(x, x, out=dK_dtheta2) 58 | 59 | params0 = list(params) 60 | params0[i] -= DTHETA 61 | params1 = list(params) 62 | params1[i] += DTHETA 63 | k0 = kernel(*params0)(x, x) 64 | k1 = kernel(*params1)(x, x) 65 | approx_dK_dtheta = approx_deriv(k0, k1, DTHETA) 66 | 67 | assert allclose(dK_dtheta1, approx_dK_dtheta) 68 | assert allclose(dK_dtheta2, approx_dK_dtheta) 69 | assert allclose(dK_dtheta1, dK_dtheta2) 70 | 71 | 72 | def check_hessian(k, x): 73 | kernel = type(k) 74 | params = k.params.copy() 75 | hess1 = k.hessian(x, x) 76 | hess2 = np.empty_like(hess1) 77 | k.hessian(x, x, out=hess2) 78 | 79 | approx_hess = np.empty(hess1.shape) 80 | for i in xrange(len(params)): 81 | p0 = list(params) 82 | p1 = list(params) 83 | p0[i] -= DTHETA 84 | p1[i] += DTHETA 85 | jac0 = kernel(*p0).jacobian(x, x) 86 | jac1 = kernel(*p1).jacobian(x, x) 87 | approx_hess[:, i] = approx_deriv(jac0, jac1, DTHETA) 88 | 89 | assert allclose(hess1, approx_hess) 90 | assert allclose(hess2, approx_hess) 91 | assert allclose(hess1, hess2) 92 | 93 | 94 | def check_d2K_dtheta2(k, x, p1, p2, i): 95 | kernel = type(k) 96 | params = k.params.copy() 97 | f = getattr(k, "d2K_d%sd%s" % (p1, p2)) 98 | d2K_dtheta21 = f(x, x) 99 | d2K_dtheta22 = np.empty_like(d2K_dtheta21) 100 | f(x, x, out=d2K_dtheta22) 101 | 102 | params0 = list(params) 103 | params1 = list(params) 104 | params0[i] -= DTHETA 105 | params1[i] += DTHETA 106 | dK_dtheta0 = getattr(kernel(*params0), "dK_d%s" % p1)(x, x) 107 | dK_dtheta1 = getattr(kernel(*params1), "dK_d%s" % p1)(x, x) 108 | approx_d2K_dtheta2 = approx_deriv(dK_dtheta0, dK_dtheta1, DTHETA) 109 | 110 | assert allclose(d2K_dtheta21, approx_d2K_dtheta2) 111 | assert allclose(d2K_dtheta22, approx_d2K_dtheta2) 112 | assert allclose(d2K_dtheta21, d2K_dtheta22) 113 | -------------------------------------------------------------------------------- /gp/kernels/gaussian.py: -------------------------------------------------------------------------------- 1 | __all__ = ['GaussianKernel'] 2 | 3 | import numpy as np 4 | import sympy as sym 5 | 6 | from functools import wraps 7 | from gp.ext import gaussian_c 8 | from . import Kernel 9 | 10 | DTYPE = np.float64 11 | EPS = np.finfo(DTYPE).eps 12 | 13 | 14 | class GaussianKernel(Kernel): 15 | r""" 16 | Gaussian kernel function. 17 | 18 | Parameters 19 | ---------- 20 | h : float 21 | Output scale kernel parameter 22 | w : float 23 | Input scale kernel parameter 24 | 25 | Notes 26 | ----- 27 | The Gaussian kernel is defined as: 28 | 29 | .. math:: K(x_1, x_2) = \frac{h^2}{\sqrt{2\pi w^2}}\exp\left(-\frac{(x_1-x_2)^2}{2w^2}\right), 30 | 31 | where :math:`w` is the input scale parameter (equivalent to the 32 | standard deviation of the Gaussian) and :math:`h` is the output 33 | scale parameter. 34 | 35 | """ 36 | 37 | def __init__(self, h, w): 38 | self.h = None #: Output scale kernel parameter 39 | self.w = None #: Input scale kernel parameter 40 | 41 | self.set_param('h', h) 42 | self.set_param('w', w) 43 | 44 | @property 45 | def params(self): 46 | r""" 47 | Kernel parameters. 48 | 49 | Returns 50 | ------- 51 | params : numpy.ndarray ``(h, w)`` 52 | 53 | """ 54 | return np.array([self.h, self.w], dtype=DTYPE) 55 | 56 | @params.setter 57 | def params(self, val): 58 | self.set_param('h', val[0]) 59 | self.set_param('w', val[1]) 60 | 61 | def set_param(self, name, val): 62 | if name == 'h': 63 | if val < EPS: 64 | raise ValueError("invalid value for h: %s" % val) 65 | self.h = DTYPE(val) 66 | 67 | elif name == 'w': 68 | if val < EPS: 69 | raise ValueError("invalid value for w: %s" % val) 70 | self.w = DTYPE(val) 71 | 72 | else: 73 | raise ValueError("unknown parameter: %s" % name) 74 | 75 | @property 76 | @wraps(Kernel.sym_K) 77 | def sym_K(self): 78 | h = sym.Symbol('h') 79 | w = sym.Symbol('w') 80 | d = sym.Symbol('d') 81 | 82 | h2 = h ** 2 83 | w2 = w ** 2 84 | d2 = d ** 2 85 | 86 | f = h2 * (1. / sym.sqrt(2 * sym.pi * w2)) * sym.exp(-d2 / (2.0 * w2)) 87 | return f 88 | 89 | @wraps(Kernel.K) 90 | def K(self, x1, x2, out=None): 91 | if out is None: 92 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 93 | gaussian_c.K(out, x1, x2, self.h, self.w) 94 | return out 95 | 96 | @wraps(Kernel.jacobian) 97 | def jacobian(self, x1, x2, out=None): 98 | if out is None: 99 | out = np.empty((2, x1.size, x2.size), dtype=DTYPE) 100 | gaussian_c.jacobian(out, x1, x2, self.h, self.w) 101 | return out 102 | 103 | @wraps(Kernel.hessian) 104 | def hessian(self, x1, x2, out=None): 105 | if out is None: 106 | out = np.empty((2, 2, x1.size, x2.size), dtype=DTYPE) 107 | gaussian_c.hessian(out, x1, x2, self.h, self.w) 108 | return out 109 | 110 | def dK_dh(self, x1, x2, out=None): 111 | if out is None: 112 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 113 | gaussian_c.dK_dh(out, x1, x2, self.h, self.w) 114 | return out 115 | 116 | def dK_dw(self, x1, x2, out=None): 117 | if out is None: 118 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 119 | gaussian_c.dK_dw(out, x1, x2, self.h, self.w) 120 | return out 121 | 122 | def d2K_dhdh(self, x1, x2, out=None): 123 | if out is None: 124 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 125 | gaussian_c.d2K_dhdh(out, x1, x2, self.h, self.w) 126 | return out 127 | 128 | def d2K_dhdw(self, x1, x2, out=None): 129 | if out is None: 130 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 131 | gaussian_c.d2K_dhdw(out, x1, x2, self.h, self.w) 132 | return out 133 | 134 | def d2K_dwdh(self, x1, x2, out=None): 135 | if out is None: 136 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 137 | gaussian_c.d2K_dwdh(out, x1, x2, self.h, self.w) 138 | return out 139 | 140 | def d2K_dwdw(self, x1, x2, out=None): 141 | if out is None: 142 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 143 | gaussian_c.d2K_dwdw(out, x1, x2, self.h, self.w) 144 | return out 145 | -------------------------------------------------------------------------------- /gp/ext/gaussian_c.pyx: -------------------------------------------------------------------------------- 1 | # cython: boundscheck=False 2 | # cython: wraparound=False 3 | 4 | from __future__ import division 5 | 6 | import numpy as np 7 | cimport numpy as np 8 | 9 | from libc.math cimport log, exp, sqrt, M_PI 10 | 11 | DTYPE = np.float64 12 | ctypedef np.float64_t DTYPE_t 13 | 14 | cdef DTYPE_t SQRT_2_DIV_PI = sqrt(2.0 / M_PI) 15 | cdef DTYPE_t MIN = log(np.exp2(DTYPE(np.finfo(DTYPE).minexp + 4))) 16 | 17 | 18 | def K(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w): 19 | cdef int x1_s = x1.size 20 | cdef int x2_s = x2.size 21 | cdef int i, j 22 | cdef DTYPE_t h2, w2, c1, c2, e 23 | 24 | h2 = h ** 2 25 | w2 = w ** 2 26 | 27 | c1 = -0.5/w2 28 | c2 = 0.5*SQRT_2_DIV_PI*h2/w 29 | 30 | for i in xrange(x1_s): 31 | for j in xrange(x2_s): 32 | e = c1*(x1[i] - x2[j])**2 33 | if e < MIN: 34 | out[i, j] = 0 35 | else: 36 | out[i, j] = c2*exp(e) 37 | 38 | 39 | def jacobian(np.ndarray[DTYPE_t, mode='c', ndim=3] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w): 40 | dK_dh(out[0], x1, x2, h, w) 41 | dK_dw(out[1], x1, x2, h, w) 42 | 43 | 44 | def hessian(np.ndarray[DTYPE_t, mode='c', ndim=4] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w): 45 | d2K_dhdh(out[0, 0], x1, x2, h, w) 46 | d2K_dhdw(out[0, 1], x1, x2, h, w) 47 | d2K_dwdh(out[1, 0], x1, x2, h, w) 48 | d2K_dwdw(out[1, 1], x1, x2, h, w) 49 | 50 | 51 | def dK_dh(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w): 52 | cdef int x1_s = x1.size 53 | cdef int x2_s = x2.size 54 | cdef int i, j 55 | cdef DTYPE_t h2, w2, c1, c2, e 56 | 57 | h2 = h ** 2 58 | w2 = w ** 2 59 | 60 | c1 = -0.5/w2 61 | c2 = SQRT_2_DIV_PI*h/w 62 | 63 | for i in xrange(x1_s): 64 | for j in xrange(x2_s): 65 | e = c1*(x1[i] - x2[j])**2 66 | if e < MIN: 67 | out[i, j] = 0 68 | else: 69 | out[i, j] = c2*exp(e) 70 | 71 | 72 | def dK_dw(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w): 73 | cdef int x1_s = x1.size 74 | cdef int x2_s = x2.size 75 | cdef int i, j 76 | cdef DTYPE_t h2, w2, c1, c2, c3, d2, e 77 | 78 | h2 = h ** 2 79 | w2 = w ** 2 80 | 81 | c1 = -0.5/w2 82 | c2 = 0.5*SQRT_2_DIV_PI*h2/w2 83 | c3 = 0.5*SQRT_2_DIV_PI*h2/w**4 84 | 85 | for i in xrange(x1_s): 86 | for j in xrange(x2_s): 87 | d2 = (x1[i] - x2[j])**2 88 | e = c1*d2 89 | if e < MIN: 90 | out[i, j] = 0 91 | else: 92 | out[i, j] = exp(e)*(c3*d2 - c2) 93 | 94 | 95 | def d2K_dhdh(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w): 96 | cdef int x1_s = x1.size 97 | cdef int x2_s = x2.size 98 | cdef int i, j 99 | cdef DTYPE_t h2, w2, c1, c2, e 100 | 101 | h2 = h ** 2 102 | w2 = w ** 2 103 | 104 | c1 = -0.5/w2 105 | c2 = SQRT_2_DIV_PI/w 106 | 107 | for i in xrange(x1_s): 108 | for j in xrange(x2_s): 109 | e = c1*(x1[i] - x2[j])**2 110 | if e < MIN: 111 | out[i, j] = 0 112 | else: 113 | out[i, j] = c2*exp(e) 114 | 115 | 116 | def d2K_dhdw(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w): 117 | cdef int x1_s = x1.size 118 | cdef int x2_s = x2.size 119 | cdef int i, j 120 | cdef DTYPE_t h2, w2, c1, c2, c3, d2, e 121 | 122 | h2 = h ** 2 123 | w2 = w ** 2 124 | 125 | c1 = -0.5/w2 126 | c2 = SQRT_2_DIV_PI*h/w2 127 | c3 = SQRT_2_DIV_PI*h/w**4 128 | 129 | for i in xrange(x1_s): 130 | for j in xrange(x2_s): 131 | d2 = (x1[i] - x2[j])**2 132 | e = c1*d2 133 | if e < MIN: 134 | out[i, j] = 0 135 | else: 136 | out[i, j] = exp(e)*(c3*d2 - c2) 137 | 138 | 139 | def d2K_dwdh(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w): 140 | d2K_dhdw(out, x1, x2, h, w) 141 | 142 | 143 | def d2K_dwdw(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w): 144 | cdef int x1_s = x1.size 145 | cdef int x2_s = x2.size 146 | cdef int i, j 147 | cdef DTYPE_t h2, w2, c1, c2, c3, c4, d2, e 148 | 149 | h2 = h ** 2 150 | w2 = w ** 2 151 | 152 | c1 = -0.5/w2 153 | c2 = SQRT_2_DIV_PI*h2/w**3 154 | c3 = 2.5*SQRT_2_DIV_PI*h2/w**5 155 | c4 = 0.5*SQRT_2_DIV_PI*h2/w**7 156 | 157 | for i in xrange(x1_s): 158 | for j in xrange(x2_s): 159 | d2 = (x1[i] - x2[j])**2 160 | e = c1*d2 161 | if e < MIN: 162 | out[i, j] = 0 163 | else: 164 | out[i, j] = exp(e)*(c4*d2**2 - c3*d2 + c2) 165 | -------------------------------------------------------------------------------- /gp/tests/test_gaussian_kernel.py: -------------------------------------------------------------------------------- 1 | import scipy.stats 2 | import numpy as np 3 | from copy import copy, deepcopy 4 | import pickle 5 | 6 | from .. import GaussianKernel 7 | from .util import opt, rand_params, seed, allclose 8 | from .test_kernels import check_jacobian, check_hessian 9 | from .test_kernels import check_params, check_invalid_params 10 | from .test_kernels import check_dK_dtheta, check_d2K_dtheta2 11 | 12 | seed() 13 | 14 | EPS = np.finfo(float).eps 15 | N_BIG = opt['n_big_test_iters'] 16 | N_SMALL = opt['n_small_test_iters'] 17 | DTHETA = opt['dtheta'] 18 | 19 | 20 | def make_random_kernel(): 21 | h, w = rand_params('h', 'w') 22 | kernel = GaussianKernel(h, w) 23 | return kernel 24 | 25 | ###################################################################### 26 | 27 | 28 | def test_kernel_params(): 29 | seed() 30 | for i in xrange(N_BIG): 31 | params = rand_params('h', 'w') 32 | yield check_params, GaussianKernel, params 33 | 34 | good_params = rand_params('h', 'w') 35 | bad_params = list(good_params) 36 | bad_params[0] = 0 37 | yield check_invalid_params, GaussianKernel, good_params, bad_params 38 | 39 | bad_params = list(good_params) 40 | bad_params[1] = 0 41 | yield check_invalid_params, GaussianKernel, good_params, bad_params 42 | 43 | 44 | def test_K(): 45 | seed() 46 | x = np.linspace(-2, 2, 10) 47 | dx = x[:, None] - x[None, :] 48 | 49 | def check_K(kernel): 50 | K1 = kernel(x, x) 51 | K2 = np.empty_like(K1) 52 | kernel(x, x, out=K2) 53 | h, w = kernel.params 54 | pdx = np.empty(dx.shape) 55 | for i, v in enumerate(dx.flat): 56 | pdx.flat[i] = scipy.stats.norm.pdf(v, loc=0, scale=w) 57 | pdx *= h ** 2 58 | assert allclose(pdx, K1) 59 | assert allclose(pdx, K2) 60 | assert allclose(K1, K2) 61 | 62 | for i in xrange(N_BIG): 63 | kernel = make_random_kernel() 64 | yield check_K, kernel 65 | 66 | 67 | def test_sym_K(): 68 | x = np.linspace(-2, 2, 3) 69 | dx = x[:, None] - x[None, :] 70 | 71 | def check_sym_K(params): 72 | kernel = GaussianKernel(*params) 73 | K = kernel(x, x) 74 | sym_K = kernel.sym_K 75 | Ks = np.empty_like(K) 76 | for i in xrange(x.size): 77 | for j in xrange(x.size): 78 | Ks[i, j] = sym_K.evalf(subs={ 79 | 'd': dx[i, j], 80 | 'h': params[0], 81 | 'w': params[1] 82 | }) 83 | assert allclose(Ks, K) 84 | 85 | yield (check_sym_K, (1, 1)) 86 | yield (check_sym_K, (1, 2)) 87 | yield (check_sym_K, (2, 1)) 88 | yield (check_sym_K, (0.5, 0.5)) 89 | 90 | 91 | def test_jacobian(): 92 | seed() 93 | x = np.linspace(-2, 2, 10) 94 | for i in xrange(N_SMALL): 95 | kernel = make_random_kernel() 96 | yield (check_jacobian, kernel, x) 97 | 98 | 99 | def test_hessian(): 100 | seed() 101 | x = np.linspace(-2, 2, 10) 102 | for i in xrange(N_SMALL): 103 | kernel = make_random_kernel() 104 | yield (check_hessian, kernel, x) 105 | 106 | 107 | def test_dK_dh(): 108 | seed() 109 | x = np.linspace(-2, 2, 10) 110 | for i in xrange(N_SMALL): 111 | kernel = make_random_kernel() 112 | yield (check_dK_dtheta, kernel, x, 'h', 0) 113 | 114 | 115 | def test_dK_dw(): 116 | seed() 117 | x = np.linspace(-2, 2, 10) 118 | for i in xrange(N_SMALL): 119 | kernel = make_random_kernel() 120 | yield (check_dK_dtheta, kernel, x, 'w', 1) 121 | 122 | 123 | def test_d2K_dhdh(): 124 | seed() 125 | x = np.linspace(-2, 2, 10) 126 | for i in xrange(N_SMALL): 127 | kernel = make_random_kernel() 128 | yield (check_d2K_dtheta2, kernel, x, 'h', 'h', 0) 129 | 130 | 131 | def test_d2K_dhdw(): 132 | seed() 133 | x = np.linspace(-2, 2, 10) 134 | for i in xrange(N_SMALL): 135 | kernel = make_random_kernel() 136 | yield (check_d2K_dtheta2, kernel, x, 'h', 'w', 1) 137 | 138 | 139 | def test_d2K_dwdh(): 140 | seed() 141 | x = np.linspace(-2, 2, 10) 142 | for i in xrange(N_SMALL): 143 | kernel = make_random_kernel() 144 | yield (check_d2K_dtheta2, kernel, x, 'w', 'h', 0) 145 | 146 | 147 | def test_d2K_dwdw(): 148 | seed() 149 | x = np.linspace(-2, 2, 10) 150 | for i in xrange(N_SMALL): 151 | kernel = make_random_kernel() 152 | yield (check_d2K_dtheta2, kernel, x, 'w', 'w', 1) 153 | 154 | 155 | def test_copy_method(): 156 | kernel1 = make_random_kernel() 157 | kernel2 = kernel1.copy() 158 | 159 | assert kernel1.h == kernel2.h 160 | assert kernel1.w == kernel2.w 161 | 162 | 163 | def test_copy(): 164 | kernel1 = make_random_kernel() 165 | kernel2 = copy(kernel1) 166 | 167 | assert kernel1.h == kernel2.h 168 | assert kernel1.w == kernel2.w 169 | 170 | 171 | def test_deepcopy(): 172 | kernel1 = make_random_kernel() 173 | kernel2 = deepcopy(kernel1) 174 | 175 | assert kernel1.h == kernel2.h 176 | assert kernel1.w == kernel2.w 177 | assert kernel1.h is not kernel2.h 178 | assert kernel1.w is not kernel2.w 179 | 180 | 181 | def test_pickle(): 182 | kernel1 = make_random_kernel() 183 | state = pickle.dumps(kernel1) 184 | kernel2 = pickle.loads(state) 185 | 186 | assert kernel1.h == kernel2.h 187 | assert kernel1.w == kernel2.w 188 | assert kernel1.h is not kernel2.h 189 | assert kernel1.w is not kernel2.w 190 | 191 | -------------------------------------------------------------------------------- /gp/ext/gp_c.pyx: -------------------------------------------------------------------------------- 1 | # cython: boundscheck=False 2 | # cython: wraparound=False 3 | 4 | from __future__ import division 5 | 6 | import numpy as np 7 | cimport numpy as np 8 | 9 | from libc.math cimport log, exp, sqrt, M_PI, INFINITY 10 | 11 | DTYPE = np.float64 12 | ctypedef np.float64_t DTYPE_t 13 | 14 | cdef DTYPE_t MIN = log(np.exp2(DTYPE(np.finfo(DTYPE).minexp + 4))) 15 | 16 | 17 | def log_lh(np.ndarray[DTYPE_t, mode='c', ndim=1] y, np.ndarray[DTYPE_t, mode='c', ndim=2] K, np.ndarray[DTYPE_t, mode='c', ndim=1] Kiy): 18 | cdef int sign 19 | cdef DTYPE_t logdet, data_fit, complexity_penalty, constant, llh 20 | 21 | sign, logdet = np.linalg.slogdet(K) 22 | if sign != 1 or logdet < MIN: 23 | llh = -INFINITY 24 | 25 | else: 26 | data_fit = -0.5 * DTYPE(np.dot(y, Kiy)) 27 | complexity_penalty = -0.5 * logdet 28 | constant = -0.5 * y.size * log(2 * M_PI) 29 | llh = data_fit + complexity_penalty + constant 30 | 31 | return llh 32 | 33 | 34 | def dloglh_dtheta(np.ndarray[DTYPE_t, mode='c', ndim=1] y, np.ndarray[DTYPE_t, mode='c', ndim=2] Ki, np.ndarray[DTYPE_t, mode='c', ndim=3] Kj, np.ndarray[DTYPE_t, mode='c', ndim=1] Kiy, DTYPE_t s, np.ndarray[DTYPE_t, mode='c', ndim=1] dloglh): 35 | cdef int n = Kj.shape[0] 36 | cdef int m = Kj.shape[1] 37 | cdef int i 38 | cdef np.ndarray[DTYPE_t, mode='c', ndim=2] k = np.empty((m, m), dtype=DTYPE) 39 | cdef DTYPE_t t0, t1 40 | 41 | for i in xrange(n+1): 42 | if i < n: 43 | k[:] = np.dot(Ki, Kj[i]) 44 | else: 45 | k[:] = np.dot(Ki, np.eye(m) * 2 * s) 46 | 47 | t0 = 0.5 * np.dot(y, np.dot(k, Kiy)) 48 | t1 = -0.5 * np.trace(k) 49 | dloglh[i] = t0 + t1 50 | 51 | 52 | def dlh_dtheta(np.ndarray[DTYPE_t, mode='c', ndim=1] y, np.ndarray[DTYPE_t, mode='c', ndim=2] Ki, np.ndarray[DTYPE_t, mode='c', ndim=3] Kj, np.ndarray[DTYPE_t, mode='c', ndim=1] Kiy, DTYPE_t s, DTYPE_t lh, np.ndarray[DTYPE_t, mode='c', ndim=1] dlh): 53 | cdef int n = Kj.shape[0] 54 | cdef int m = Kj.shape[1] 55 | cdef int i 56 | cdef np.ndarray[DTYPE_t, mode='c', ndim=2] k = np.empty((m, m), dtype=DTYPE) 57 | cdef DTYPE_t t0, t1 58 | 59 | for i in xrange(n+1): 60 | if i < n: 61 | k[:] = np.dot(Ki, Kj[i]) 62 | else: 63 | k[:] = np.dot(Ki, np.eye(m) * 2 * s) 64 | 65 | t0 = np.dot(y, np.dot(k, Kiy)) 66 | t1 = np.trace(k) 67 | dlh[i] = 0.5 * lh * (t0 - t1) 68 | 69 | 70 | def d2lh_dtheta2(np.ndarray[DTYPE_t, mode='c', ndim=1] y, np.ndarray[DTYPE_t, mode='c', ndim=2] Ki, np.ndarray[DTYPE_t, mode='c', ndim=3] Kj, np.ndarray[DTYPE_t, mode='c', ndim=4] Kh, np.ndarray[DTYPE_t, mode='c', ndim=1] Kiy, DTYPE_t s, DTYPE_t lh, np.ndarray[DTYPE_t, mode='c', ndim=1] dlh, np.ndarray[DTYPE_t, mode='c', ndim=2] d2lh): 71 | cdef int n = Kj.shape[0] 72 | cdef int m = Kj.shape[1] 73 | cdef int i, j 74 | cdef np.ndarray[DTYPE_t, mode='c', ndim=3] dK = np.empty((n+1, m, m), dtype=DTYPE) 75 | cdef np.ndarray[DTYPE_t, mode='c', ndim=3] dKi = np.empty((n+1, m, m), dtype=DTYPE) 76 | cdef np.ndarray[DTYPE_t, mode='c', ndim=2] d2k = np.empty((m, m), dtype=DTYPE) 77 | cdef np.ndarray[DTYPE_t, mode='c', ndim=2] KidK_i = np.empty((m, m), dtype=DTYPE) 78 | cdef np.ndarray[DTYPE_t, mode='c', ndim=2] dKi_jdK_i = np.empty((m, m), dtype=DTYPE) 79 | cdef DTYPE_t ydKi_iy, ydKi_iy_tr, t0, t1, t1a, t1b, t1c 80 | 81 | for i in xrange(n+1): 82 | # first kernel derivatives 83 | if i < n: 84 | dK[i] = Kj[i] 85 | else: 86 | dK[i] = np.eye(m) * 2 * s 87 | 88 | dKi[i] = np.dot(-Ki, np.dot(dK[i], Ki)) 89 | 90 | for i in xrange(n+1): 91 | KidK_i[:] = np.dot(Ki, dK[i]) 92 | ydKi_iy = np.dot(y, np.dot(KidK_i, Kiy)) 93 | ydKi_iy_tr = ydKi_iy - np.trace(KidK_i) 94 | 95 | for j in xrange(n+1): 96 | # second kernel derivatives 97 | if j < n and i < n: 98 | d2k[:] = Kh[i, j] 99 | elif j == n and i == n: 100 | d2k[:] = np.eye(m) * 2 101 | else: 102 | d2k.fill(0) 103 | 104 | dKi_jdK_i[:] = np.dot(dKi[j], dK[i]) 105 | t0 = dlh[j] * ydKi_iy_tr 106 | 107 | t1a = np.dot(y, np.dot(dKi_jdK_i, Kiy)) 108 | t1b = np.dot(Kiy, np.dot(d2k, Kiy)) 109 | t1c = np.dot(Kiy, np.dot(dK[i], np.dot(dKi[j], y))) 110 | t1 = lh * (t1a + t1b + t1c - np.trace(dKi_jdK_i + np.dot(Ki, d2k))) 111 | d2lh[i, j] = 0.5 * (t0 + t1) 112 | 113 | 114 | def dm_dtheta(np.ndarray[DTYPE_t, mode='c', ndim=1] y, np.ndarray[DTYPE_t, mode='c', ndim=2] Ki, np.ndarray[DTYPE_t, mode='c', ndim=3] Kj, np.ndarray[DTYPE_t, mode='c', ndim=3] Kjxo, np.ndarray[DTYPE_t, mode='c', ndim=2] Kxox, DTYPE_t s, np.ndarray[DTYPE_t, mode='c', ndim=2] dm): 115 | cdef int n = Kj.shape[0] 116 | cdef int m = Kj.shape[1] 117 | cdef int m2 = Kjxo.shape[1] 118 | cdef int i 119 | cdef np.ndarray[DTYPE_t, mode='c', ndim=2] dKxox_dtheta = np.empty((m2, m), dtype=DTYPE) 120 | cdef np.ndarray[DTYPE_t, mode='c', ndim=2] dKxx_dtheta = np.empty((m, m), dtype=DTYPE) 121 | 122 | for i in xrange(n+1): 123 | if i < n: 124 | dKxox_dtheta[:] = Kjxo[i] 125 | dKxx_dtheta[:] = Kj[i] 126 | else: 127 | dKxox_dtheta.fill(0) 128 | dKxx_dtheta[:] = np.eye(m) * 2 * s 129 | 130 | dm[i] = np.dot(dKxox_dtheta, np.dot(Ki, y)) 131 | dm[i] -= np.dot(Kxox, np.dot(np.dot(Ki, np.dot(dKxx_dtheta, Ki)), y)) 132 | -------------------------------------------------------------------------------- /gp/kernels/periodic.py: -------------------------------------------------------------------------------- 1 | __all__ = ['PeriodicKernel'] 2 | 3 | import numpy as np 4 | import sympy as sym 5 | 6 | from functools import wraps 7 | from gp.ext import periodic_c 8 | from . import Kernel 9 | 10 | DTYPE = np.float64 11 | EPS = np.finfo(DTYPE).eps 12 | 13 | 14 | class PeriodicKernel(Kernel): 15 | r""" 16 | Periodic kernel function. 17 | 18 | Parameters 19 | ---------- 20 | h : float 21 | Output scale kernel parameter 22 | w : float 23 | Input scale kernel parameter 24 | p : float 25 | Period kernel parameter 26 | 27 | Notes 28 | ----- 29 | The periodic kernel is defined by Equation 4.31 of [RW06]_: 30 | 31 | .. math:: K(x_1, x_2) = h^2\exp\left(\frac{-2\sin^2\left(\frac{x_1-x_2}{2p}\right)}{w^2}\right) 32 | 33 | where :math:`w` is the input scale parameter (equivalent to the 34 | standard deviation of the Gaussian), :math:`h` is the output 35 | scale parameter, and :math:`p` is the period kernel parameter. 36 | 37 | """ 38 | 39 | def __init__(self, h, w, p): 40 | self.h = None #: Output scale kernel parameter 41 | self.w = None #: Input scale kernel parameter 42 | self.p = None #: Period kernel parameter 43 | 44 | self.set_param('h', h) 45 | self.set_param('w', w) 46 | self.set_param('p', p) 47 | 48 | @property 49 | def params(self): 50 | r""" 51 | Kernel parameters. 52 | 53 | Returns 54 | ------- 55 | params : numpy.ndarray ``(h, w, p)`` 56 | 57 | """ 58 | return np.array([self.h, self.w, self.p], dtype=DTYPE) 59 | 60 | @params.setter 61 | def params(self, val): 62 | self.set_param('h', val[0]) 63 | self.set_param('w', val[1]) 64 | self.set_param('p', val[2]) 65 | 66 | def set_param(self, name, val): 67 | if name == 'h': 68 | if val < EPS: 69 | raise ValueError("invalid value for h: %s" % val) 70 | self.h = DTYPE(val) 71 | 72 | elif name == 'w': 73 | if val < EPS: 74 | raise ValueError("invalid value for w: %s" % val) 75 | self.w = DTYPE(val) 76 | 77 | elif name == 'p': 78 | if val < EPS: 79 | raise ValueError("invalid value for p: %s" % val) 80 | self.p = DTYPE(val) 81 | 82 | else: 83 | raise ValueError("unknown parameter: %s" % name) 84 | 85 | @property 86 | @wraps(Kernel.sym_K) 87 | def sym_K(self): 88 | h = sym.Symbol('h') 89 | w = sym.Symbol('w') 90 | p = sym.Symbol('p') 91 | d = sym.Symbol('d') 92 | 93 | h2 = h ** 2 94 | w2 = w ** 2 95 | 96 | f = h2 * sym.exp(-2. * (sym.sin(d / (2. * p)) ** 2) / w2) 97 | return f 98 | 99 | @wraps(Kernel.K) 100 | def K(self, x1, x2, out=None): 101 | if out is None: 102 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 103 | periodic_c.K(out, x1, x2, self.h, self.w, self.p) 104 | return out 105 | 106 | @wraps(Kernel.jacobian) 107 | def jacobian(self, x1, x2, out=None): 108 | if out is None: 109 | out = np.empty((3, x1.size, x2.size), dtype=DTYPE) 110 | periodic_c.jacobian(out, x1, x2, self.h, self.w, self.p) 111 | return out 112 | 113 | @wraps(Kernel.hessian) 114 | def hessian(self, x1, x2, out=None): 115 | if out is None: 116 | out = np.empty((3, 3, x1.size, x2.size), dtype=DTYPE) 117 | periodic_c.hessian(out, x1, x2, self.h, self.w, self.p) 118 | return out 119 | 120 | def dK_dh(self, x1, x2, out=None): 121 | if out is None: 122 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 123 | periodic_c.dK_dh(out, x1, x2, self.h, self.w, self.p) 124 | return out 125 | 126 | def dK_dw(self, x1, x2, out=None): 127 | if out is None: 128 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 129 | periodic_c.dK_dw(out, x1, x2, self.h, self.w, self.p) 130 | return out 131 | 132 | def dK_dp(self, x1, x2, out=None): 133 | if out is None: 134 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 135 | periodic_c.dK_dp(out, x1, x2, self.h, self.w, self.p) 136 | return out 137 | 138 | def d2K_dhdh(self, x1, x2, out=None): 139 | if out is None: 140 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 141 | periodic_c.d2K_dhdh(out, x1, x2, self.h, self.w, self.p) 142 | return out 143 | 144 | def d2K_dhdw(self, x1, x2, out=None): 145 | if out is None: 146 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 147 | periodic_c.d2K_dhdw(out, x1, x2, self.h, self.w, self.p) 148 | return out 149 | 150 | def d2K_dhdp(self, x1, x2, out=None): 151 | if out is None: 152 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 153 | periodic_c.d2K_dhdp(out, x1, x2, self.h, self.w, self.p) 154 | return out 155 | 156 | def d2K_dwdh(self, x1, x2, out=None): 157 | if out is None: 158 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 159 | periodic_c.d2K_dwdh(out, x1, x2, self.h, self.w, self.p) 160 | return out 161 | 162 | def d2K_dwdw(self, x1, x2, out=None): 163 | if out is None: 164 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 165 | periodic_c.d2K_dwdw(out, x1, x2, self.h, self.w, self.p) 166 | return out 167 | 168 | def d2K_dwdp(self, x1, x2, out=None): 169 | if out is None: 170 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 171 | periodic_c.d2K_dwdp(out, x1, x2, self.h, self.w, self.p) 172 | return out 173 | 174 | def d2K_dpdh(self, x1, x2, out=None): 175 | if out is None: 176 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 177 | periodic_c.d2K_dpdh(out, x1, x2, self.h, self.w, self.p) 178 | return out 179 | 180 | def d2K_dpdw(self, x1, x2, out=None): 181 | if out is None: 182 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 183 | periodic_c.d2K_dpdw(out, x1, x2, self.h, self.w, self.p) 184 | return out 185 | 186 | def d2K_dpdp(self, x1, x2, out=None): 187 | if out is None: 188 | out = np.empty((x1.size, x2.size), dtype=DTYPE) 189 | periodic_c.d2K_dpdp(out, x1, x2, self.h, self.w, self.p) 190 | return out 191 | -------------------------------------------------------------------------------- /gp/tests/test_periodic_kernel.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pickle 3 | from copy import copy, deepcopy 4 | 5 | from .. import PeriodicKernel 6 | from .util import opt, rand_params, seed, allclose 7 | from .test_kernels import check_jacobian, check_hessian 8 | from .test_kernels import check_params, check_invalid_params 9 | from .test_kernels import check_dK_dtheta, check_d2K_dtheta2 10 | 11 | seed() 12 | 13 | EPS = opt['eps'] 14 | N_BIG = opt['n_big_test_iters'] 15 | N_SMALL = opt['n_small_test_iters'] 16 | DTHETA = opt['dtheta'] 17 | 18 | 19 | def make_random_kernel(): 20 | h, w, p = rand_params('h', 'w', 'p') 21 | kernel = PeriodicKernel(h, w, p) 22 | return kernel 23 | 24 | ###################################################################### 25 | 26 | 27 | def test_kernel_params(): 28 | seed() 29 | for i in xrange(N_BIG): 30 | params = rand_params('h', 'w', 'p') 31 | yield check_params, PeriodicKernel, params 32 | 33 | good_params = rand_params('h', 'w', 'p') 34 | bad_params = list(good_params) 35 | bad_params[0] = 0 36 | yield check_invalid_params, PeriodicKernel, good_params, bad_params 37 | 38 | bad_params = list(good_params) 39 | bad_params[1] = 0 40 | yield check_invalid_params, PeriodicKernel, good_params, bad_params 41 | 42 | bad_params = list(good_params) 43 | bad_params[2] = 0 44 | yield check_invalid_params, PeriodicKernel, good_params, bad_params 45 | 46 | 47 | def test_K(): 48 | seed() 49 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 50 | dx = x[:, None] - x[None, :] 51 | 52 | def check_K(kernel): 53 | K1 = kernel(x, x) 54 | K2 = np.empty_like(K1) 55 | kernel(x, x, out=K2) 56 | h, w, p = kernel.params 57 | pdx = (h ** 2) * np.exp(-2. * (np.sin(dx / (2. * p)) ** 2) / (w ** 2)) 58 | assert allclose(pdx, K1) 59 | assert allclose(pdx, K2) 60 | assert allclose(K1, K2) 61 | 62 | for i in xrange(N_BIG): 63 | kernel = make_random_kernel() 64 | yield check_K, kernel 65 | 66 | 67 | def test_sym_K(): 68 | x = np.linspace(-2, 2, 3) 69 | dx = x[:, None] - x[None, :] 70 | 71 | def check_sym_K(params): 72 | kernel = PeriodicKernel(*params) 73 | K = kernel(x, x) 74 | sym_K = kernel.sym_K 75 | Ks = np.empty_like(K) 76 | for i in xrange(x.size): 77 | for j in xrange(x.size): 78 | Ks[i, j] = sym_K.evalf(subs={ 79 | 'd': dx[i, j], 80 | 'h': params[0], 81 | 'w': params[1], 82 | 'p': params[2] 83 | }) 84 | 85 | assert allclose(Ks, K) 86 | 87 | yield (check_sym_K, (1, 1, 1)) 88 | yield (check_sym_K, (1, 2, 2)) 89 | yield (check_sym_K, (2, 1, 0.5)) 90 | yield (check_sym_K, (0.5, 0.5, 1)) 91 | 92 | 93 | def test_jacobian(): 94 | seed() 95 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 96 | for i in xrange(N_SMALL): 97 | kernel = make_random_kernel() 98 | yield (check_jacobian, kernel, x) 99 | 100 | 101 | def test_hessian(): 102 | seed() 103 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 104 | for i in xrange(N_SMALL): 105 | kernel = make_random_kernel() 106 | yield (check_hessian, kernel, x) 107 | 108 | 109 | def test_dK_dh(): 110 | seed() 111 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 112 | for i in xrange(N_SMALL): 113 | kernel = make_random_kernel() 114 | yield (check_dK_dtheta, kernel, x, 'h', 0) 115 | 116 | 117 | def test_dK_dw(): 118 | seed() 119 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 120 | for i in xrange(N_SMALL): 121 | kernel = make_random_kernel() 122 | yield (check_dK_dtheta, kernel, x, 'w', 1) 123 | 124 | 125 | def test_dK_dp(): 126 | seed() 127 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 128 | for i in xrange(N_SMALL): 129 | kernel = make_random_kernel() 130 | yield (check_dK_dtheta, kernel, x, 'p', 2) 131 | 132 | 133 | def test_d2K_dhdh(): 134 | seed() 135 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 136 | for i in xrange(N_SMALL): 137 | kernel = make_random_kernel() 138 | yield (check_d2K_dtheta2, kernel, x, 'h', 'h', 0) 139 | 140 | 141 | def test_d2K_dhdw(): 142 | seed() 143 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 144 | for i in xrange(N_SMALL): 145 | kernel = make_random_kernel() 146 | yield (check_d2K_dtheta2, kernel, x, 'h', 'w', 1) 147 | 148 | 149 | def test_d2K_dhdp(): 150 | seed() 151 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 152 | for i in xrange(N_SMALL): 153 | kernel = make_random_kernel() 154 | yield (check_d2K_dtheta2, kernel, x, 'h', 'p', 2) 155 | 156 | 157 | def test_d2K_dwdh(): 158 | seed() 159 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 160 | for i in xrange(N_SMALL): 161 | kernel = make_random_kernel() 162 | yield (check_d2K_dtheta2, kernel, x, 'w', 'h', 0) 163 | 164 | 165 | def test_d2K_dwdw(): 166 | seed() 167 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 168 | for i in xrange(N_SMALL): 169 | kernel = make_random_kernel() 170 | yield (check_d2K_dtheta2, kernel, x, 'w', 'w', 1) 171 | 172 | 173 | def test_d2K_dwdp(): 174 | seed() 175 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 176 | for i in xrange(N_SMALL): 177 | kernel = make_random_kernel() 178 | yield (check_d2K_dtheta2, kernel, x, 'w', 'p', 2) 179 | 180 | 181 | def test_d2K_dpdh(): 182 | seed() 183 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 184 | for i in xrange(N_SMALL): 185 | kernel = make_random_kernel() 186 | yield (check_d2K_dtheta2, kernel, x, 'p', 'h', 0) 187 | 188 | 189 | def test_d2K_dpdw(): 190 | seed() 191 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 192 | for i in xrange(N_SMALL): 193 | kernel = make_random_kernel() 194 | yield (check_d2K_dtheta2, kernel, x, 'p', 'w', 1) 195 | 196 | 197 | def test_d2K_dpdp(): 198 | seed() 199 | x = np.linspace(-2 * np.pi, 2 * np.pi, 16) 200 | for i in xrange(N_SMALL): 201 | kernel = make_random_kernel() 202 | yield (check_d2K_dtheta2, kernel, x, 'p', 'p', 2) 203 | 204 | 205 | 206 | def test_copy_method(): 207 | kernel1 = make_random_kernel() 208 | kernel2 = kernel1.copy() 209 | 210 | assert kernel1.h == kernel2.h 211 | assert kernel1.w == kernel2.w 212 | assert kernel1.p == kernel2.p 213 | 214 | 215 | def test_copy(): 216 | kernel1 = make_random_kernel() 217 | kernel2 = copy(kernel1) 218 | 219 | assert kernel1.h == kernel2.h 220 | assert kernel1.w == kernel2.w 221 | assert kernel1.p == kernel2.p 222 | 223 | 224 | def test_deepcopy(): 225 | kernel1 = make_random_kernel() 226 | kernel2 = deepcopy(kernel1) 227 | 228 | assert kernel1.h == kernel2.h 229 | assert kernel1.w == kernel2.w 230 | assert kernel1.p == kernel2.p 231 | assert kernel1.h is not kernel2.h 232 | assert kernel1.w is not kernel2.w 233 | assert kernel1.p is not kernel2.p 234 | 235 | 236 | def test_pickle(): 237 | kernel1 = make_random_kernel() 238 | state = pickle.dumps(kernel1) 239 | kernel2 = pickle.loads(state) 240 | 241 | assert kernel1.h == kernel2.h 242 | assert kernel1.w == kernel2.w 243 | assert kernel1.p == kernel2.p 244 | assert kernel1.h is not kernel2.h 245 | assert kernel1.w is not kernel2.w 246 | assert kernel1.p is not kernel2.p 247 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/src.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/src.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/src" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/src" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\src.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\src.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /gp/ext/periodic_c.pyx: -------------------------------------------------------------------------------- 1 | # cython: boundscheck=False 2 | # cython: wraparound=False 3 | 4 | from __future__ import division 5 | 6 | import numpy as np 7 | cimport numpy as np 8 | 9 | from libc.math cimport log, exp, sqrt, cos, sin, M_PI 10 | 11 | DTYPE = np.float64 12 | ctypedef np.float64_t DTYPE_t 13 | 14 | cdef DTYPE_t SQRT_2_DIV_PI = sqrt(2.0 / M_PI) 15 | cdef DTYPE_t MIN = log(np.exp2(DTYPE(np.finfo(DTYPE).minexp + 4))) 16 | 17 | 18 | def K(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 19 | cdef int x1_s = x1.size 20 | cdef int x2_s = x2.size 21 | cdef int i, j 22 | cdef DTYPE_t hw, w2, d 23 | 24 | h2 = h ** 2 25 | w2 = w ** 2 26 | 27 | for i in xrange(x1_s): 28 | for j in xrange(x2_s): 29 | d = x1[i] - x2[j] 30 | out[i, j] = h2*exp(-2.0*sin(0.5*d/p)**2/w2) 31 | 32 | 33 | def jacobian(np.ndarray[DTYPE_t, mode='c', ndim=3] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 34 | dK_dh(out[0], x1, x2, h, w, p) 35 | dK_dw(out[1], x1, x2, h, w, p) 36 | dK_dp(out[2], x1, x2, h, w, p) 37 | 38 | 39 | def hessian(np.ndarray[DTYPE_t, mode='c', ndim=4] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 40 | d2K_dhdh(out[0, 0], x1, x2, h, w, p) 41 | d2K_dhdw(out[0, 1], x1, x2, h, w, p) 42 | d2K_dhdp(out[0, 2], x1, x2, h, w, p) 43 | 44 | d2K_dwdh(out[1, 0], x1, x2, h, w, p) 45 | d2K_dwdw(out[1, 1], x1, x2, h, w, p) 46 | d2K_dwdp(out[1, 2], x1, x2, h, w, p) 47 | 48 | d2K_dpdh(out[2, 0], x1, x2, h, w, p) 49 | d2K_dpdw(out[2, 1], x1, x2, h, w, p) 50 | d2K_dpdp(out[2, 2], x1, x2, h, w, p) 51 | 52 | 53 | def dK_dh(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 54 | cdef int x1_s = x1.size 55 | cdef int x2_s = x2.size 56 | cdef int i, j 57 | cdef DTYPE_t hw, w2, d 58 | 59 | h2 = h ** 2 60 | w2 = w ** 2 61 | 62 | for i in xrange(x1_s): 63 | for j in xrange(x2_s): 64 | d = x1[i] - x2[j] 65 | out[i, j] = 2.0*h*exp(-2.0*sin(0.5*d/p)**2/w2) 66 | 67 | 68 | def dK_dw(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 69 | cdef int x1_s = x1.size 70 | cdef int x2_s = x2.size 71 | cdef int i, j 72 | cdef DTYPE_t hw, w2, d 73 | 74 | h2 = h ** 2 75 | w2 = w ** 2 76 | 77 | for i in xrange(x1_s): 78 | for j in xrange(x2_s): 79 | d = x1[i] - x2[j] 80 | out[i, j] = 4.0*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)**2/w**3 81 | 82 | 83 | def dK_dp(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 84 | cdef int x1_s = x1.size 85 | cdef int x2_s = x2.size 86 | cdef int i, j 87 | cdef DTYPE_t hw, w2, p2, d 88 | 89 | h2 = h ** 2 90 | w2 = w ** 2 91 | p2 = p ** 2 92 | 93 | for i in xrange(x1_s): 94 | for j in xrange(x2_s): 95 | d = x1[i] - x2[j] 96 | out[i, j] = 2.0*d*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)*cos(0.5*d/p)/(p2*w2) 97 | 98 | 99 | def d2K_dhdh(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 100 | cdef int x1_s = x1.size 101 | cdef int x2_s = x2.size 102 | cdef int i, j 103 | cdef DTYPE_t hw, w2, d 104 | 105 | h2 = h ** 2 106 | w2 = w ** 2 107 | 108 | for i in xrange(x1_s): 109 | for j in xrange(x2_s): 110 | d = x1[i] - x2[j] 111 | out[i, j] = 2.0*exp(-2.0*sin(0.5*d/p)**2/w2) 112 | 113 | 114 | def d2K_dhdw(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 115 | cdef int x1_s = x1.size 116 | cdef int x2_s = x2.size 117 | cdef int i, j 118 | cdef DTYPE_t hw, w2, d 119 | 120 | h2 = h ** 2 121 | w2 = w ** 2 122 | 123 | for i in xrange(x1_s): 124 | for j in xrange(x2_s): 125 | d = x1[i] - x2[j] 126 | out[i, j] = 8.0*h*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)**2/w**3 127 | 128 | 129 | def d2K_dhdp(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 130 | cdef int x1_s = x1.size 131 | cdef int x2_s = x2.size 132 | cdef int i, j 133 | cdef DTYPE_t hw, w2, p2, d 134 | 135 | h2 = h ** 2 136 | w2 = w ** 2 137 | p2 = p ** 2 138 | 139 | for i in xrange(x1_s): 140 | for j in xrange(x2_s): 141 | d = x1[i] - x2[j] 142 | out[i, j] = 4.0*d*h*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)*cos(0.5*d/p)/(p2*w2) 143 | 144 | 145 | def d2K_dwdh(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 146 | cdef int x1_s = x1.size 147 | cdef int x2_s = x2.size 148 | cdef int i, j 149 | cdef DTYPE_t hw, w2, d 150 | 151 | h2 = h ** 2 152 | w2 = w ** 2 153 | 154 | for i in xrange(x1_s): 155 | for j in xrange(x2_s): 156 | d = x1[i] - x2[j] 157 | out[i, j] = 8.0*h*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)**2/w**3 158 | 159 | 160 | def d2K_dwdw(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 161 | cdef int x1_s = x1.size 162 | cdef int x2_s = x2.size 163 | cdef int i, j 164 | cdef DTYPE_t hw, w2, d 165 | 166 | h2 = h ** 2 167 | w2 = w ** 2 168 | 169 | for i in xrange(x1_s): 170 | for j in xrange(x2_s): 171 | d = x1[i] - x2[j] 172 | out[i, j] = -12.0*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)**2/w**4 + 16.0*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)**4/w**6 173 | 174 | 175 | def d2K_dwdp(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 176 | cdef int x1_s = x1.size 177 | cdef int x2_s = x2.size 178 | cdef int i, j 179 | cdef DTYPE_t hw, w2, p2, d 180 | 181 | h2 = h ** 2 182 | w2 = w ** 2 183 | p2 = p ** 2 184 | 185 | for i in xrange(x1_s): 186 | for j in xrange(x2_s): 187 | d = x1[i] - x2[j] 188 | out[i, j] = -4.0*d*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)*cos(0.5*d/p)/(p2*w**3) + 8.0*d*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)**3*cos(0.5*d/p)/(p2*w**5) 189 | 190 | 191 | def d2K_dpdh(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 192 | cdef int x1_s = x1.size 193 | cdef int x2_s = x2.size 194 | cdef int i, j 195 | cdef DTYPE_t hw, w2, p2, d 196 | 197 | h2 = h ** 2 198 | w2 = w ** 2 199 | p2 = p ** 2 200 | 201 | for i in xrange(x1_s): 202 | for j in xrange(x2_s): 203 | d = x1[i] - x2[j] 204 | out[i, j] = 4.0*d*h*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)*cos(0.5*d/p)/(p2*w2) 205 | 206 | 207 | def d2K_dpdw(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 208 | cdef int x1_s = x1.size 209 | cdef int x2_s = x2.size 210 | cdef int i, j 211 | cdef DTYPE_t hw, w2, p2, d 212 | 213 | h2 = h ** 2 214 | w2 = w ** 2 215 | p2 = p ** 2 216 | 217 | for i in xrange(x1_s): 218 | for j in xrange(x2_s): 219 | d = x1[i] - x2[j] 220 | out[i, j] = -4.0*d*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)*cos(0.5*d/p)/(p2*w**3) + 8.0*d*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)**3*cos(0.5*d/p)/(p2*w**5) 221 | 222 | 223 | def d2K_dpdp(np.ndarray[DTYPE_t, mode='c', ndim=2] out, np.ndarray[DTYPE_t, mode='c', ndim=1] x1, np.ndarray[DTYPE_t, mode='c', ndim=1] x2, DTYPE_t h, DTYPE_t w, DTYPE_t p): 224 | cdef int x1_s = x1.size 225 | cdef int x2_s = x2.size 226 | cdef int i, j 227 | cdef DTYPE_t hw, w2, d 228 | 229 | h2 = h ** 2 230 | w2 = w ** 2 231 | 232 | for i in xrange(x1_s): 233 | for j in xrange(x2_s): 234 | d = x1[i] - x2[j] 235 | out[i, j] = d**2*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)**2/(p**4*w2) - 1.0*d**2*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*cos(0.5*d/p)**2/(p**4*w2) + 4.0*d**2*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)**2*cos(0.5*d/p)**2/(p**4*w**4) - 4.0*d*h2*exp(-2.0*sin(0.5*d/p)**2/w2)*sin(0.5*d/p)*cos(0.5*d/p)/(p**3*w2) 236 | 237 | 238 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # gaussian_processes documentation build configuration file, created by 4 | # sphinx-quickstart on Sat Jun 29 16:21:04 2013. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | sys.path.insert(0, os.path.abspath('../../')) 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | 23 | # If your documentation needs a minimal Sphinx version, state it here. 24 | #needs_sphinx = '1.0' 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be extensions 27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 28 | extensions = ['sphinx.ext.autodoc', 29 | 'sphinx.ext.viewcode', 30 | 'sphinx.ext.mathjax', 31 | 'numpydoc', 32 | 'sphinx.ext.autosummary'] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix of source filenames. 38 | source_suffix = '.rst' 39 | 40 | # The encoding of source files. 41 | #source_encoding = 'utf-8-sig' 42 | 43 | # The master toctree document. 44 | master_doc = 'index' 45 | 46 | # General information about the project. 47 | project = u'gaussian_processes' 48 | copyright = u'2013, Jessica B. Hamrick' 49 | 50 | # The version info for the project you're documenting, acts as replacement for 51 | # |version| and |release|, also used in various other places throughout the 52 | # built documents. 53 | # 54 | # The short X.Y version. 55 | version = open("../../VERSION.txt").read().strip() 56 | # The full version, including alpha/beta/rc tags. 57 | release = open("../../VERSION.txt").read().strip() 58 | 59 | # The language for content autogenerated by Sphinx. Refer to documentation 60 | # for a list of supported languages. 61 | #language = None 62 | 63 | # There are two options for replacing |today|: either, you set today to some 64 | # non-false value, then it is used: 65 | #today = '' 66 | # Else, today_fmt is used as the format for a strftime call. 67 | #today_fmt = '%B %d, %Y' 68 | 69 | # List of patterns, relative to source directory, that match files and 70 | # directories to ignore when looking for source files. 71 | exclude_patterns = ['_build'] 72 | 73 | # The reST default role (used for this markup: `text`) to use for all documents. 74 | default_role = 'py:obj' 75 | 76 | # If true, '()' will be appended to :func: etc. cross-reference text. 77 | add_function_parentheses = False 78 | 79 | # If true, the current module name will be prepended to all description 80 | # unit titles (such as .. function::). 81 | #add_module_names = True 82 | 83 | # If true, sectionauthor and moduleauthor directives will be shown in the 84 | # output. They are ignored by default. 85 | #show_authors = False 86 | 87 | # The name of the Pygments (syntax highlighting) style to use. 88 | pygments_style = 'sphinx' 89 | 90 | # A list of ignored prefixes for module index sorting. 91 | #modindex_common_prefix = [] 92 | 93 | # If true, keep warnings as "system message" paragraphs in the built documents. 94 | #keep_warnings = False 95 | 96 | 97 | # -- Options for HTML output --------------------------------------------------- 98 | 99 | # The theme to use for HTML and HTML Help pages. See the documentation for 100 | # a list of builtin themes. 101 | html_theme = 'default' 102 | 103 | # Theme options are theme-specific and customize the look and feel of a theme 104 | # further. For a list of options available for each theme, see the 105 | # documentation. 106 | #html_theme_options = {} 107 | 108 | # Add any paths that contain custom themes here, relative to this directory. 109 | #html_theme_path = [] 110 | 111 | # The name for this set of Sphinx documents. If None, it defaults to 112 | # " v documentation". 113 | #html_title = None 114 | 115 | # A shorter title for the navigation bar. Default is the same as html_title. 116 | #html_short_title = None 117 | 118 | # The name of an image file (relative to this directory) to place at the top 119 | # of the sidebar. 120 | #html_logo = None 121 | 122 | # The name of an image file (within the static path) to use as favicon of the 123 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 124 | # pixels large. 125 | #html_favicon = None 126 | 127 | # Add any paths that contain custom static files (such as style sheets) here, 128 | # relative to this directory. They are copied after the builtin static files, 129 | # so a file named "default.css" will overwrite the builtin "default.css". 130 | html_static_path = ['_static'] 131 | 132 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 133 | # using the given strftime format. 134 | #html_last_updated_fmt = '%b %d, %Y' 135 | 136 | # If true, SmartyPants will be used to convert quotes and dashes to 137 | # typographically correct entities. 138 | #html_use_smartypants = True 139 | 140 | # Custom sidebar templates, maps document names to template names. 141 | #html_sidebars = {} 142 | 143 | # Additional templates that should be rendered to pages, maps page names to 144 | # template names. 145 | #html_additional_pages = {} 146 | 147 | # If false, no module index is generated. 148 | #html_domain_indices = True 149 | 150 | # If false, no index is generated. 151 | #html_use_index = True 152 | 153 | # If true, the index is split into individual pages for each letter. 154 | #html_split_index = False 155 | 156 | # If true, links to the reST sources are added to the pages. 157 | #html_show_sourcelink = True 158 | 159 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 160 | #html_show_sphinx = True 161 | 162 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 163 | #html_show_copyright = True 164 | 165 | # If true, an OpenSearch description file will be output, and all pages will 166 | # contain a tag referring to it. The value of this option must be the 167 | # base URL from which the finished HTML is served. 168 | #html_use_opensearch = '' 169 | 170 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 171 | #html_file_suffix = None 172 | 173 | # Output file base name for HTML help builder. 174 | htmlhelp_basename = 'gaussian_processes' 175 | 176 | 177 | # -- Options for LaTeX output -------------------------------------------------- 178 | 179 | latex_elements = { 180 | # The paper size ('letterpaper' or 'a4paper'). 181 | #'papersize': 'letterpaper', 182 | 183 | # The font size ('10pt', '11pt' or '12pt'). 184 | #'pointsize': '10pt', 185 | 186 | # Additional stuff for the LaTeX preamble. 187 | #'preamble': '', 188 | } 189 | 190 | # Grouping the document tree into LaTeX files. List of tuples 191 | # (source start file, target name, title, author, documentclass [howto/manual]). 192 | latex_documents = [ 193 | ('index', 'gaussian_processes.tex', u'gaussian_processes Documentation', 194 | u'Jessica B. Hamrick', 'manual'), 195 | ] 196 | 197 | # The name of an image file (relative to this directory) to place at the top of 198 | # the title page. 199 | #latex_logo = None 200 | 201 | # For "manual" documents, if this is true, then toplevel headings are parts, 202 | # not chapters. 203 | #latex_use_parts = False 204 | 205 | # If true, show page references after internal links. 206 | #latex_show_pagerefs = False 207 | 208 | # If true, show URL addresses after external links. 209 | #latex_show_urls = False 210 | 211 | # Documents to append as an appendix to all manuals. 212 | #latex_appendices = [] 213 | 214 | # If false, no module index is generated. 215 | #latex_domain_indices = True 216 | 217 | 218 | # -- Options for manual page output -------------------------------------------- 219 | 220 | # One entry per manual page. List of tuples 221 | # (source start file, name, description, authors, manual section). 222 | man_pages = [ 223 | ('index', 'gaussian_processes', u'gaussian_processes Documentation', 224 | [u'Jessica B. Hamrick'], 1) 225 | ] 226 | 227 | # If true, show URL addresses after external links. 228 | #man_show_urls = False 229 | 230 | 231 | # -- Options for Texinfo output ------------------------------------------------ 232 | 233 | # Grouping the document tree into Texinfo files. List of tuples 234 | # (source start file, target name, title, author, 235 | # dir menu entry, description, category) 236 | texinfo_documents = [ 237 | ('index', 'gaussian_processes', u'gaussian_processes Documentation', 238 | u'Jessica B. Hamrick', 'gaussian_processes', 'One line description of project.', 239 | 'Miscellaneous'), 240 | ] 241 | 242 | # Documents to append as an appendix to all manuals. 243 | #texinfo_appendices = [] 244 | 245 | # If false, no module index is generated. 246 | #texinfo_domain_indices = True 247 | 248 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 249 | #texinfo_show_urls = 'footnote' 250 | 251 | # If true, do not generate a @detailmenu in the "Top" node's menu. 252 | #texinfo_no_detailmenu = False 253 | 254 | 255 | # -- Options for Epub output --------------------------------------------------- 256 | 257 | # Bibliographic Dublin Core info. 258 | epub_title = u'gaussian_processes' 259 | epub_author = u'Jessica B. Hamrick' 260 | epub_publisher = u'Jessica B. Hamrick' 261 | epub_copyright = u'2013, Jessica B. Hamrick' 262 | 263 | # The language of the text. It defaults to the language option 264 | # or en if the language is not set. 265 | #epub_language = '' 266 | 267 | # The scheme of the identifier. Typical schemes are ISBN or URL. 268 | #epub_scheme = '' 269 | 270 | # The unique identifier of the text. This can be a ISBN number 271 | # or the project homepage. 272 | #epub_identifier = '' 273 | 274 | # A unique identification for the text. 275 | #epub_uid = '' 276 | 277 | # A tuple containing the cover image and cover page html template filenames. 278 | #epub_cover = () 279 | 280 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 281 | #epub_guide = () 282 | 283 | # HTML files that should be inserted before the pages created by sphinx. 284 | # The format is a list of tuples containing the path and title. 285 | #epub_pre_files = [] 286 | 287 | # HTML files shat should be inserted after the pages created by sphinx. 288 | # The format is a list of tuples containing the path and title. 289 | #epub_post_files = [] 290 | 291 | # A list of files that should not be packed into the epub file. 292 | #epub_exclude_files = [] 293 | 294 | # The depth of the table of contents in toc.ncx. 295 | #epub_tocdepth = 3 296 | 297 | # Allow duplicate toc entries. 298 | #epub_tocdup = True 299 | 300 | # Fix unsupported image types using the PIL. 301 | #epub_fix_images = False 302 | 303 | # Scale large images. 304 | #epub_max_image_width = 0 305 | 306 | # If 'no', URL addresses will not be shown. 307 | #epub_show_urls = 'inline' 308 | 309 | # If false, no index is generated. 310 | #epub_use_index = True 311 | 312 | # -- Options for autodoc --------------------------------------------- 313 | 314 | autoclass_content = 'class' 315 | autodoc_member_order = 'alphabetical' 316 | autodoc_default_flags = ['members', 317 | 'undoc-members', 318 | 'inherited-members', 319 | 'show-inheritance'] 320 | 321 | # -- Options for autosummary------------------------------------------ 322 | 323 | autosummary_generate = False 324 | 325 | 326 | # -- Options for numpydoc -------------------------------------------- 327 | 328 | numpydoc_show_class_members = False 329 | -------------------------------------------------------------------------------- /gp/tests/test_gp.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import pickle 5 | 6 | from numpy import dot 7 | from copy import copy, deepcopy 8 | 9 | from .. import GP 10 | from .. import GaussianKernel as kernel 11 | from .util import opt, rand_params, seed, allclose 12 | from .util import approx_deriv, make_xy, make_xo 13 | 14 | EPS = opt['eps'] 15 | N_BIG = opt['n_big_test_iters'] 16 | N_SMALL = opt['n_small_test_iters'] 17 | DTHETA = opt['dtheta'] 18 | PFAIL = opt['pct_allowed_failures'] 19 | DTYPE = opt['dtype'] 20 | 21 | ###################################################################### 22 | 23 | 24 | def make_gp(): 25 | x, y = make_xy() 26 | gp = GP(kernel(1, 1), x, y, s=1) 27 | return gp 28 | 29 | 30 | def make_random_gp(): 31 | x, y = make_xy() 32 | h, w, s = rand_params('h', 'w', 's') 33 | gp = GP(kernel(h, w), x, y, s=s) 34 | return gp 35 | 36 | 37 | def count_failures(check, n): 38 | seed() 39 | 40 | params = [] 41 | failures = [] 42 | for i in xrange(n): 43 | gp = make_random_gp() 44 | try: 45 | check(gp) 46 | except AssertionError as err: # pragma: no cover 47 | params.append(tuple(gp.params)) 48 | failures.append(err.msg) 49 | 50 | pfail = 100 * len(failures) / n 51 | msg = "%s failed %d/%d (%.1f%%) times" % ( 52 | check.__name__, len(failures), n, pfail) 53 | print zip(params, failures) 54 | assert pfail < PFAIL, msg 55 | 56 | ################################################################## 57 | 58 | 59 | def test_mean(): 60 | def check_mean(gp): 61 | gp.s = 0 62 | assert allclose(gp.mean(gp.x), gp.y) 63 | 64 | count_failures(check_mean, N_BIG) 65 | 66 | 67 | def test_inv(): 68 | def check_inv(gp): 69 | I = dot(gp.Kxx, gp.inv_Kxx) 70 | assert allclose(I, np.eye(I.shape[0])) 71 | 72 | count_failures(check_inv, N_SMALL) 73 | 74 | 75 | def test_dloglh(): 76 | def check_dloglh(gp): 77 | params = gp.params 78 | jac = gp.dloglh_dtheta 79 | 80 | approx_jac = np.empty(jac.shape) 81 | for i in xrange(len(params)): 82 | p0 = list(params) 83 | p0[i] -= DTHETA 84 | gp0 = gp.copy() 85 | gp0.params = p0 86 | 87 | p1 = list(params) 88 | p1[i] += DTHETA 89 | gp1 = gp.copy() 90 | gp1.params = p1 91 | 92 | approx_jac[i] = approx_deriv( 93 | gp0.log_lh, gp1.log_lh, DTHETA) 94 | 95 | assert allclose(jac, approx_jac) 96 | 97 | count_failures(check_dloglh, N_BIG) 98 | 99 | 100 | def test_dlh(): 101 | def check_dlh(gp): 102 | params = gp.params 103 | jac = gp.dlh_dtheta 104 | 105 | approx_jac = np.empty(jac.shape) 106 | for i in xrange(len(params)): 107 | p0 = list(params) 108 | p0[i] -= DTHETA 109 | gp0 = gp.copy() 110 | gp0.params = p0 111 | 112 | p1 = list(params) 113 | p1[i] += DTHETA 114 | gp1 = gp.copy() 115 | gp1.params = p1 116 | 117 | approx_jac[i] = approx_deriv( 118 | gp0.lh, gp1.lh, DTHETA) 119 | 120 | assert allclose(jac, approx_jac) 121 | 122 | count_failures(check_dlh, N_BIG) 123 | 124 | 125 | def test_d2lh(): 126 | def check_d2lh(gp): 127 | params = gp.params 128 | hess = gp.d2lh_dtheta2 129 | 130 | approx_hess = np.empty(hess.shape) 131 | for i in xrange(len(params)): 132 | p0 = list(params) 133 | p0[i] -= DTHETA 134 | gp0 = gp.copy() 135 | gp0.params = p0 136 | 137 | p1 = list(params) 138 | p1[i] += DTHETA 139 | gp1 = gp.copy() 140 | gp1.params = p1 141 | 142 | approx_hess[:, i] = approx_deriv( 143 | gp0.dlh_dtheta, gp1.dlh_dtheta, DTHETA) 144 | 145 | assert allclose(hess, approx_hess) 146 | 147 | count_failures(check_d2lh, N_BIG) 148 | 149 | 150 | def test_dm(): 151 | xo = make_xo() 152 | 153 | def check_dm(gp): 154 | params = gp.params 155 | jac = gp.dm_dtheta(xo) 156 | 157 | approx_jac = np.empty(jac.shape) 158 | for i in xrange(len(params)): 159 | p0 = list(params) 160 | p0[i] -= DTHETA 161 | gp0 = gp.copy() 162 | gp0.params = p0 163 | 164 | p1 = list(params) 165 | p1[i] += DTHETA 166 | gp1 = gp.copy() 167 | gp1.params = p1 168 | 169 | approx_jac[i] = approx_deriv( 170 | gp0.mean(xo), gp1.mean(xo), DTHETA) 171 | 172 | assert allclose(jac, approx_jac) 173 | 174 | count_failures(check_dm, N_BIG) 175 | 176 | 177 | def test_dtypes(): 178 | gp = make_gp() 179 | xo = make_xo() 180 | 181 | def check_dtype(x): 182 | assert x.dtype == DTYPE 183 | 184 | def check_type(x): 185 | assert type(x) == DTYPE 186 | 187 | yield check_dtype, gp.x 188 | yield check_dtype, gp.y 189 | yield check_type, gp.s 190 | yield check_dtype, gp.params 191 | yield check_dtype, gp.Kxx 192 | yield check_dtype, gp.Kxx_J 193 | yield check_dtype, gp.Kxx_H 194 | yield check_dtype, gp.Lxx 195 | yield check_dtype, gp.inv_Kxx 196 | yield check_dtype, gp.inv_Kxx_y 197 | yield check_type, gp.log_lh 198 | yield check_type, gp.lh 199 | yield check_dtype, gp.dloglh_dtheta 200 | yield check_dtype, gp.dlh_dtheta 201 | yield check_dtype, gp.d2lh_dtheta2 202 | yield check_dtype, gp.Kxoxo(xo) 203 | yield check_dtype, gp.Kxxo(xo) 204 | yield check_dtype, gp.Kxox(xo) 205 | yield check_dtype, gp.mean(xo) 206 | yield check_dtype, gp.cov(xo) 207 | yield check_dtype, gp.dm_dtheta(xo) 208 | 209 | 210 | def test_shapes(): 211 | gp = make_gp() 212 | xo = make_xo() 213 | n = gp.x.size 214 | m = xo.size 215 | n_p = gp.params.size 216 | 217 | def check_prop_ndim(x, ndim): 218 | assert getattr(gp, x).ndim == ndim 219 | 220 | def check_prop_shape(x, shape): 221 | assert getattr(gp, x).shape == shape 222 | 223 | def check_func_shape(x, shape): 224 | assert getattr(gp, x)(xo).shape == shape 225 | 226 | yield check_prop_ndim, 'x', 1 227 | yield check_prop_shape, 'y', (n,) 228 | yield check_prop_shape, 'Kxx', (n, n) 229 | yield check_prop_shape, 'Kxx_J', (n_p - 1, n, n) 230 | yield check_prop_shape, 'Kxx_H', (n_p - 1, n_p - 1, n, n) 231 | yield check_prop_shape, 'Lxx', (n, n) 232 | yield check_prop_shape, 'inv_Kxx', (n, n) 233 | yield check_prop_shape, 'inv_Kxx_y', (n,) 234 | yield check_prop_shape, 'dloglh_dtheta', (n_p,) 235 | yield check_prop_shape, 'dlh_dtheta', (n_p,) 236 | yield check_prop_shape, 'd2lh_dtheta2', (n_p, n_p) 237 | yield check_func_shape, 'Kxoxo', (m, m) 238 | yield check_func_shape, 'Kxxo', (n, m) 239 | yield check_func_shape, 'Kxox', (m, n) 240 | yield check_func_shape, 'mean', (m,) 241 | yield check_func_shape, 'cov', (m, m) 242 | yield check_func_shape, 'dm_dtheta', (n_p, m) 243 | 244 | 245 | def test_memoprop_del(): 246 | gp = make_gp() 247 | 248 | def check_del(prop): 249 | getattr(gp, prop) 250 | assert prop in gp._memoized 251 | delattr(gp, prop) 252 | assert prop not in gp._memoized 253 | 254 | yield check_del, "Kxx" 255 | yield check_del, "Kxx_J" 256 | yield check_del, "Kxx_H" 257 | yield check_del, "Lxx" 258 | yield check_del, "inv_Kxx" 259 | yield check_del, "inv_Kxx_y" 260 | yield check_del, "log_lh" 261 | yield check_del, "lh" 262 | yield check_del, "dloglh_dtheta" 263 | yield check_del, "dlh_dtheta" 264 | yield check_del, "d2lh_dtheta2" 265 | 266 | 267 | def test_reset_memoized(): 268 | gp = make_gp() 269 | 270 | def check_memoized(prop, val): 271 | gp.Kxx 272 | assert gp._memoized != {} 273 | setattr(gp, prop, val) 274 | assert gp._memoized == {} 275 | 276 | yield check_memoized, "x", gp.x.copy() + 1 277 | yield check_memoized, "y", gp.y.copy() + 1 278 | yield check_memoized, "s", gp.s + 1 279 | yield check_memoized, "params", gp.params + 1 280 | 281 | 282 | def test_set_y(): 283 | gp = make_gp() 284 | y = gp.y.copy() 285 | with pytest.raises(ValueError): 286 | gp.y = y[:, None] 287 | 288 | 289 | def test_params(): 290 | x, y = make_xy() 291 | h, w = rand_params('h', 'w') 292 | GP(kernel(h, w), x, y, s=0) 293 | 294 | with pytest.raises(ValueError): 295 | GP(kernel(h, w), x, y, s=-1) 296 | 297 | 298 | def test_invalid_params(): 299 | h = 0.53356762 300 | w = 2.14797803 301 | s = 0 302 | 303 | x = np.array([0.0, 0.3490658503988659, 0.6981317007977318, 304 | 1.0471975511965976, 1.3962634015954636, 1.7453292519943295, 305 | 2.0943951023931953, 0.41968261, 0.97349106, 1.51630532, 306 | 1.77356282, 2.07011378, 2.87018553, 3.70955074, 3.96680824, 307 | 4.50962249, 4.80617345, 5.06343095, 5.6062452]) 308 | 309 | y = np.array([-5.297411814764175e-16, 2.2887507861169e-16, 310 | 1.1824308893126911e-15, 1.9743321560961036e-15, 311 | 3.387047586844716e-15, 3.2612801348363973e-15, 312 | 2.248201624865942e-15, -3.061735126365188e-05, 313 | 2.1539042816804896e-05, -3.900581031467468e-05, 314 | 4.603140942399664e-05, 0.00014852070373963522, 315 | -0.011659908151004955, -0.001060998167383152, 316 | -0.0002808538329216448, -8.057870658869265e-06, 317 | -7.668984947838558e-07, -7.910215881378919e-08, 318 | -3.2649468298271893e-10]) 319 | 320 | gp = GP(kernel(h, w), x, y, s=s) 321 | 322 | with pytest.raises(np.linalg.LinAlgError): 323 | gp.Lxx 324 | with pytest.raises(np.linalg.LinAlgError): 325 | gp.inv_Kxx 326 | with pytest.raises(np.linalg.LinAlgError): 327 | gp.inv_Kxx_y 328 | 329 | assert gp.log_lh == -np.inf 330 | assert gp.lh == 0 331 | assert np.isnan(gp.dloglh_dtheta).all() 332 | assert np.isnan(gp.dlh_dtheta).all() 333 | assert np.isnan(gp.d2lh_dtheta2).all() 334 | 335 | 336 | def test_plot(): 337 | gp = make_gp() 338 | 339 | fig, ax = plt.subplots() 340 | gp.plot(ax=ax) 341 | 342 | fig, ax = plt.subplots() 343 | gp.plot() 344 | 345 | fig, ax = plt.subplots() 346 | gp.plot(xlim=[0, 1]) 347 | 348 | plt.close('all') 349 | 350 | 351 | def test_copy_method(): 352 | gp1 = make_gp() 353 | gp2 = gp1.copy(deep=False) 354 | 355 | assert gp1 is not gp2 356 | assert gp1._x is gp2._x 357 | assert gp1._y is gp2._y 358 | assert gp1._s is gp2._s 359 | assert gp1.K is gp2.K 360 | 361 | 362 | def test_copy(): 363 | gp1 = make_gp() 364 | gp2 = copy(gp1) 365 | 366 | assert gp1 is not gp2 367 | assert gp1._x is gp2._x 368 | assert gp1._y is gp2._y 369 | assert gp1._s is gp2._s 370 | assert gp1.K is gp2.K 371 | 372 | 373 | def test_deepcopy_method(): 374 | gp1 = make_gp() 375 | gp2 = gp1.copy(deep=True) 376 | 377 | assert gp1 is not gp2 378 | assert gp1._x is not gp2._x 379 | assert gp1._y is not gp2._y 380 | assert gp1._s is not gp2._s 381 | assert gp1.K is not gp2.K 382 | assert gp1.K.params is not gp2.K.params 383 | 384 | assert (gp1._x == gp2._x).all() 385 | assert (gp1._y == gp2._y).all() 386 | assert gp1._s == gp2._s 387 | assert (gp1.K.params == gp2.K.params).all() 388 | 389 | 390 | def test_deepcopy(): 391 | gp1 = make_gp() 392 | gp2 = deepcopy(gp1) 393 | 394 | assert gp1 is not gp2 395 | assert gp1._x is not gp2._x 396 | assert gp1._y is not gp2._y 397 | assert gp1._s is not gp2._s 398 | assert gp1.K is not gp2.K 399 | assert gp1.K.params is not gp2.K.params 400 | 401 | assert (gp1._x == gp2._x).all() 402 | assert (gp1._y == gp2._y).all() 403 | assert gp1._s == gp2._s 404 | assert (gp1.K.params == gp2.K.params).all() 405 | 406 | 407 | def test_pickle(): 408 | gp1 = make_gp() 409 | state = pickle.dumps(gp1) 410 | gp2 = pickle.loads(state) 411 | 412 | assert gp1 is not gp2 413 | assert gp1._x is not gp2._x 414 | assert gp1._y is not gp2._y 415 | assert gp1._s is not gp2._s 416 | assert gp1.K is not gp2.K 417 | assert gp1.K.params is not gp2.K.params 418 | 419 | assert (gp1._x == gp2._x).all() 420 | assert (gp1._y == gp2._y).all() 421 | assert gp1._s == gp2._s 422 | assert (gp1.K.params == gp2.K.params).all() 423 | 424 | 425 | def test_set_params(): 426 | gp = make_gp() 427 | gp.set_param('h', 1) 428 | gp.set_param('w', 0.2) 429 | gp.set_param('s', 0.01) 430 | 431 | with pytest.raises(AttributeError): 432 | gp.set_param('p', 1.1) 433 | -------------------------------------------------------------------------------- /gp/gp.py: -------------------------------------------------------------------------------- 1 | __all__ = ["GP"] 2 | 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | import scipy.optimize as optim 6 | import logging 7 | 8 | from scipy.linalg import cholesky, cho_solve 9 | from copy import copy, deepcopy 10 | 11 | from .ext import gp_c 12 | 13 | logger = logging.getLogger("gp.gp") 14 | 15 | DTYPE = np.float64 16 | EPS = np.finfo(DTYPE).eps 17 | MIN = np.log(np.exp2(DTYPE(np.finfo(DTYPE).minexp + 4))) 18 | 19 | 20 | def memoprop(f): 21 | """ 22 | Memoized property. 23 | 24 | When the property is accessed for the first time, the return value 25 | is stored and that value is given on subsequent calls. The memoized 26 | value can be cleared by calling 'del prop', where prop is the name 27 | of the property. 28 | 29 | """ 30 | fname = f.__name__ 31 | 32 | def fget(self): 33 | if fname not in self._memoized: 34 | self._memoized[fname] = f(self) 35 | return self._memoized[fname] 36 | 37 | def fdel(self): 38 | del self._memoized[fname] 39 | 40 | prop = property(fget=fget, fdel=fdel, doc=f.__doc__) 41 | return prop 42 | 43 | 44 | class GP(object): 45 | r""" 46 | Gaussian Process object. 47 | 48 | Parameters 49 | ---------- 50 | K : :class:`~gp.kernels.base.Kernel` 51 | Kernel object 52 | x : numpy.ndarray 53 | :math:`n` array of input locations 54 | y : numpy.ndarray 55 | :math:`n` array of input observations 56 | s : number (default=0) 57 | Standard deviation of observation noise 58 | 59 | """ 60 | 61 | def __init__(self, K, x, y, s=0): 62 | r""" 63 | Initialize the GP. 64 | 65 | """ 66 | #: Kernel for the gaussian process, of type 67 | #: :class:`~gp.kernels.base.Kernel` 68 | self.K = K 69 | self._x = None 70 | self._y = None 71 | self._s = None 72 | self._memoized = {} 73 | 74 | self.x = x 75 | self.y = y 76 | self.s = s 77 | 78 | def __getstate__(self): 79 | state = {} 80 | state['K'] = self.K 81 | state['_x'] = self._x 82 | state['_y'] = self._y 83 | state['_s'] = self._s 84 | state['_memoized'] = self._memoized 85 | return state 86 | 87 | def __setstate__(self, state): 88 | self.K = state['K'] 89 | self._x = state['_x'] 90 | self._y = state['_y'] 91 | self._s = state['_s'] 92 | self._memoized = state['_memoized'] 93 | 94 | def __copy__(self): 95 | state = self.__getstate__() 96 | cls = type(self) 97 | gp = cls.__new__(cls) 98 | gp.__setstate__(state) 99 | return gp 100 | 101 | def __deepcopy__(self, memo): 102 | state = deepcopy(self.__getstate__(), memo) 103 | cls = type(self) 104 | gp = cls.__new__(cls) 105 | gp.__setstate__(state) 106 | return gp 107 | 108 | def copy(self, deep=True): 109 | """ 110 | Create a copy of the gaussian process object. 111 | 112 | Parameters 113 | ---------- 114 | deep : bool (default=True) 115 | Whether to return a deep or shallow copy 116 | 117 | Returns 118 | ------- 119 | gp : :class:`~gp.gp.GP` 120 | New gaussian process object 121 | 122 | """ 123 | if deep: 124 | gp = deepcopy(self) 125 | else: 126 | gp = copy(self) 127 | return gp 128 | 129 | @property 130 | def x(self): 131 | r""" 132 | Vector of input locations. 133 | 134 | Returns 135 | ------- 136 | x : numpy.ndarray 137 | :math:`n` array, where :math:`n` is the number of 138 | locations. 139 | 140 | """ 141 | return self._x 142 | 143 | @x.setter 144 | def x(self, val): 145 | if np.any(val != self._x): 146 | self._memoized = {} 147 | self._x = np.array(val, copy=True, dtype=DTYPE) 148 | self._x.flags.writeable = False 149 | else: # pragma: no cover 150 | pass 151 | 152 | @property 153 | def y(self): 154 | r""" 155 | Vector of input observations. 156 | 157 | Returns 158 | ------- 159 | y : numpy.ndarray 160 | :math:`n` array, where :math:`n` is the number of 161 | observations. 162 | 163 | """ 164 | return self._y 165 | 166 | @y.setter 167 | def y(self, val): 168 | if np.any(val != self._y): 169 | self._memoized = {} 170 | self._y = np.array(val, copy=True, dtype=DTYPE) 171 | self._y.flags.writeable = False 172 | if self.y.shape != self.x.shape: 173 | raise ValueError("invalid shape for y: %s" % str(self.y.shape)) 174 | else: # pragma: no cover 175 | pass 176 | 177 | @property 178 | def s(self): 179 | r""" 180 | Standard deviation of the observation noise for the gaussian 181 | process. 182 | 183 | Returns 184 | ------- 185 | s : numpy.float64 186 | 187 | """ 188 | return self._s 189 | 190 | @s.setter 191 | def s(self, val): 192 | if val < 0: 193 | raise ValueError("invalid value for s: %s" % val) 194 | 195 | if val != self._s: 196 | self._memoized = {} 197 | self._s = DTYPE(val) 198 | 199 | @property 200 | def params(self): 201 | r""" 202 | Gaussian process parameters. 203 | 204 | Returns 205 | ------- 206 | params : numpy.ndarray 207 | Consists of the kernel's parameters, `self.K.params`, and the 208 | observation noise parameter, :math:`s`, in that order. 209 | 210 | """ 211 | _params = np.empty(self.K.params.size + 1) 212 | _params[:-1] = self.K.params 213 | _params[-1] = self._s 214 | return _params 215 | 216 | @params.setter 217 | def params(self, val): 218 | if np.any(self.params != val): 219 | self._memoized = {} 220 | self.K.params = val[:-1] 221 | self.s = val[-1] 222 | else: # pragma: no cover 223 | pass 224 | 225 | def get_param(self, name): 226 | if name == 's': 227 | return self.s 228 | else: 229 | return getattr(self.K, name) 230 | 231 | def set_param(self, name, val): 232 | if name == 's': 233 | self.s = val 234 | else: 235 | p = getattr(self.K, name) 236 | if p != val: 237 | self._memoized = {} 238 | self.K.set_param(name, val) 239 | else: # pragma: no cover 240 | pass 241 | 242 | @memoprop 243 | def Kxx(self): 244 | r""" 245 | Kernel covariance matrix :math:`\mathbf{K}_{xx}`. 246 | 247 | Returns 248 | ------- 249 | Kxx : numpy.ndarray 250 | :math:`n\times n` covariance matrix 251 | 252 | Notes 253 | ----- 254 | The entry at index :math:`(i, j)` is defined as: 255 | 256 | .. math:: K_{x_ix_j} = K(x_i, x_j) + s^2\delta(x_i-x_j), 257 | 258 | where :math:`K(\cdot{})` is the kernel function, :math:`s` is the 259 | standard deviation of the observation noise, and :math:`\delta` 260 | is the Dirac delta function. 261 | 262 | """ 263 | x, s = self._x, self._s 264 | K = self.K(x, x) 265 | K += np.eye(x.size, dtype=DTYPE) * (s ** 2) 266 | return K 267 | 268 | @memoprop 269 | def Kxx_J(self): 270 | x = self._x 271 | return self.K.jacobian(x, x) 272 | 273 | @memoprop 274 | def Kxx_H(self): 275 | x = self._x 276 | return self.K.hessian(x, x) 277 | 278 | @memoprop 279 | def Lxx(self): 280 | r""" 281 | Cholesky decomposition of the kernel covariance matrix. 282 | 283 | Returns 284 | ------- 285 | Lxx : numpy.ndarray 286 | :math:`n\times n` lower triangular matrix 287 | 288 | Notes 289 | ----- 290 | The value is :math:`\mathbf{L}_{xx}`, such that 291 | :math:`\mathbf{K}_{xx} = \mathbf{L}_{xx}\mathbf{L}_{xx}^\top`. 292 | 293 | """ 294 | return cholesky(self.Kxx, lower=True, overwrite_a=False, check_finite=True) 295 | 296 | @memoprop 297 | def inv_Kxx(self): 298 | r""" 299 | Inverse kernel covariance matrix, :math:`\mathbf{K}_{xx}^{-1}`. 300 | 301 | Note that this inverse is provided mostly just for 302 | reference. If you actually need to use it, use the Cholesky 303 | decomposition (`self.Lxx`) instead. 304 | 305 | Returns 306 | ------- 307 | inv_Kxx : numpy.ndarray 308 | :math:`n\times n` matrix 309 | 310 | """ 311 | inv_Lxx = np.linalg.inv(self.Lxx) 312 | return np.dot(inv_Lxx.T, inv_Lxx) 313 | 314 | @memoprop 315 | def inv_Kxx_y(self): 316 | r""" 317 | Dot product of the inverse kernel covariance matrix and of 318 | observation vector. 319 | 320 | This uses scipy's cholesky solver to compute the solution. 321 | 322 | Returns 323 | ------- 324 | inv_Kxx_y : numpy.ndarray 325 | :math:`n` array 326 | 327 | Notes 328 | ----- 329 | This is defined as :math:`\mathbf{K}_{xx}^{-1}\mathbf{y}`. 330 | 331 | """ 332 | inv_Kxx_y = cho_solve( 333 | (self.Lxx, True), self._y, 334 | overwrite_b=False, check_finite=True) 335 | return inv_Kxx_y 336 | 337 | @memoprop 338 | def log_lh(self): 339 | r""" 340 | Marginal log likelihood. 341 | 342 | Returns 343 | ------- 344 | log_lh : numpy.float64 345 | Marginal log likelihood 346 | 347 | Notes 348 | ----- 349 | This is the log likelihood of observations :math:`\mathbf{y}` 350 | given locations :math:`\mathbf{x}` and kernel parameters 351 | :math:`\theta`. It is defined by Eq. 5.8 of [RW06]_: 352 | 353 | .. math:: 354 | 355 | \log{p(\mathbf{y} | \mathbf{x}, \mathbf{\theta})} = -\frac{1}{2}\mathbf{y}^\top \mathbf{K}_{xx}^{-1}\mathbf{y} - \frac{1}{2}\log{\left|\mathbf{K}_{xx}\right|}-\frac{d}{2}\log{2\pi}, 356 | 357 | where :math:`d` is the dimensionality of :math:`\mathbf{x}`. 358 | 359 | """ 360 | y = self._y 361 | K = self.Kxx 362 | try: 363 | Kiy = self.inv_Kxx_y 364 | except np.linalg.LinAlgError: 365 | return -np.inf 366 | 367 | return DTYPE(gp_c.log_lh(y, K, Kiy)) 368 | 369 | @memoprop 370 | def lh(self): 371 | r""" 372 | Marginal likelihood. 373 | 374 | Returns 375 | ------- 376 | lh : numpy.float64 377 | Marginal likelihood 378 | 379 | Notes 380 | ----- 381 | This is the likelihood of observations :math:`\mathbf{y}` given 382 | locations :math:`\mathbf{x}` and kernel parameters 383 | :math:`\theta`. It is defined as: 384 | 385 | .. math:: 386 | 387 | p(\mathbf{y} | \mathbf{x}, \mathbf{\theta}) = \left(2\pi\right)^{-\frac{d}{2}}\left|\mathbf{K}_{xx}\right|^{-\frac{1}{2}}\exp\left(-\frac{1}{2}\mathbf{y}^\top\mathbf{K}_{xx}^{-1}\mathbf{y}\right) 388 | 389 | where :math:`d` is the dimensionality of :math:`\mathbf{x}`. 390 | 391 | """ 392 | llh = self.log_lh 393 | if llh < MIN: 394 | return 0 395 | else: 396 | return np.exp(self.log_lh) 397 | 398 | @memoprop 399 | def dloglh_dtheta(self): 400 | r""" 401 | Derivative of the marginal log likelihood. 402 | 403 | Returns 404 | ------- 405 | dloglh_dtheta : numpy.ndarray 406 | :math:`n_\theta`-length vector of derivatives, where 407 | :math:`n_\theta` is the number of parameters (equivalent to 408 | ``len(self.params)``). 409 | 410 | Notes 411 | ----- 412 | This is a vector of first partial derivatives of the log 413 | likelihood with respect to its parameters :math:`\theta`. It is 414 | defined by Equation 5.9 of [RW06]_: 415 | 416 | .. math:: 417 | 418 | \frac{\partial}{\partial\theta_i}\log{p(\mathbf{y}|\mathbf{x},\theta)}=\frac{1}{2}\mathbf{y}^\top\mathbf{K}_{xx}^{-1}\frac{\partial\mathbf{K}_{xx}}{\partial\theta_i}\mathbf{K}_{xx}^{-1}\mathbf{y}-\frac{1}{2}\mathbf{tr}\left(\mathbf{K}_{xx}^{-1}\frac{\partial\mathbf{K}_{xx}}{\partial\theta_i}\right) 419 | 420 | """ 421 | 422 | y = self._y 423 | dloglh = np.empty(len(self.params)) 424 | try: 425 | Ki = self.inv_Kxx 426 | except np.linalg.LinAlgError: 427 | dloglh.fill(np.nan) 428 | return dloglh 429 | 430 | Kj = self.Kxx_J 431 | Kiy = self.inv_Kxx_y 432 | gp_c.dloglh_dtheta(y, Ki, Kj, Kiy, self._s, dloglh) 433 | return dloglh 434 | 435 | @memoprop 436 | def dlh_dtheta(self): 437 | r""" 438 | Derivative of the marginal likelihood. 439 | 440 | Returns 441 | ------- 442 | dlh_dtheta : numpy.ndarray 443 | :math:`n_\theta`-length vector of derivatives, where 444 | :math:`n_\theta` is the number of parameters (equivalent to 445 | ``len(self.params)``). 446 | 447 | Notes 448 | ----- 449 | This is a vector of first partial derivatives of the likelihood 450 | with respect to its parameters :math:`\theta`. 451 | 452 | """ 453 | 454 | y = self._y 455 | dlh = np.empty(len(self.params)) 456 | try: 457 | Ki = self.inv_Kxx 458 | except np.linalg.LinAlgError: 459 | dlh.fill(np.nan) 460 | return dlh 461 | 462 | Kj = self.Kxx_J 463 | Kiy = self.inv_Kxx_y 464 | lh = self.lh 465 | gp_c.dlh_dtheta(y, Ki, Kj, Kiy, self._s, lh, dlh) 466 | return dlh 467 | 468 | @memoprop 469 | def d2lh_dtheta2(self): 470 | r""" 471 | Second derivative of the marginal likelihood. 472 | 473 | Returns 474 | ------- 475 | d2lh_dtheta2 : numpy.ndarray 476 | :math:`n_\theta`-length vector of derivatives, where 477 | :math:`n_\theta` is the number of parameters (equivalent to 478 | ``len(self.params)``). 479 | 480 | Notes 481 | ----- 482 | This is a matrix of second partial derivatives of the likelihood 483 | with respect to its parameters :math:`\theta`. 484 | 485 | """ 486 | 487 | y = self._y 488 | d2lh = np.empty((len(self.params), len(self.params))) 489 | try: 490 | Ki = self.inv_Kxx 491 | except np.linalg.LinAlgError: 492 | d2lh.fill(np.nan) 493 | return d2lh 494 | 495 | Kj = self.Kxx_J 496 | Kh = self.Kxx_H 497 | Kiy = self.inv_Kxx_y 498 | lh = self.lh 499 | dlh = self.dlh_dtheta 500 | 501 | gp_c.d2lh_dtheta2(y, Ki, Kj, Kh, Kiy, self._s, lh, dlh, d2lh) 502 | return d2lh 503 | 504 | def Kxoxo(self, xo): 505 | r""" 506 | Kernel covariance matrix of new sample locations. 507 | 508 | Parameters 509 | ---------- 510 | xo : numpy.ndarray 511 | :math:`m` array of new sample locations 512 | 513 | Returns 514 | ------- 515 | Kxoxo : numpy.ndarray 516 | :math:`m\times m` covariance matrix 517 | 518 | Notes 519 | ----- 520 | This is defined as :math:`K(\mathbf{x^*}, \mathbf{x^*})`, where 521 | :math:`\mathbf{x^*}` are the new locations. 522 | 523 | """ 524 | return self.K(xo, xo) 525 | 526 | def Kxxo(self, xo): 527 | r""" 528 | Kernel covariance matrix between given locations and new sample 529 | locations. 530 | 531 | Parameters 532 | ---------- 533 | xo : numpy.ndarray 534 | :math:`m` array of new sample locations 535 | 536 | Returns 537 | ------- 538 | Kxxo : numpy.ndarray 539 | :math:`n\times m` covariance matrix 540 | 541 | Notes 542 | ----- 543 | This is defined as :math:`K(\mathbf{x},\mathbf{x^*})`, where 544 | :math:`\mathbf{x}` are the given locations and 545 | :math:`\mathbf{x^*}` are the new sample locations. 546 | 547 | """ 548 | return self.K(self._x, xo) 549 | 550 | def Kxox(self, xo): 551 | r""" 552 | Kernel covariance matrix between new sample locations and given 553 | locations. 554 | 555 | Parameters 556 | ---------- 557 | xo : numpy.ndarray 558 | :math:`m` array of new sample locations 559 | 560 | Returns 561 | ------- 562 | Kxox : numpy.ndarray 563 | :math:`m\times n` covariance matrix 564 | 565 | Notes 566 | ----- 567 | This is defined as :math:`K(\mathbf{x^*},\mathbf{x})`, where 568 | :math:`\mathbf{x^*}` are the new sample locations and 569 | :math:`\mathbf{x}` are the given locations 570 | 571 | """ 572 | return self.K(xo, self._x) 573 | 574 | def mean(self, xo): 575 | r""" 576 | Predictive mean of the gaussian process. 577 | 578 | Parameters 579 | ---------- 580 | xo : numpy.ndarray 581 | :math:`m` array of new sample locations 582 | 583 | Returns 584 | ------- 585 | mean : numpy.ndarray 586 | :math:`m` array of predictive means 587 | 588 | Notes 589 | ----- 590 | This is defined by Equation 2.23 of [RW06]_: 591 | 592 | .. math:: 593 | 594 | \mathbf{m}(\mathbf{x^*})=K(\mathbf{x^*}, \mathbf{x})\mathbf{K}_{xx}^{-1}\mathbf{y} 595 | 596 | """ 597 | return np.dot(self.Kxox(xo), self.inv_Kxx_y) 598 | 599 | def cov(self, xo): 600 | r""" 601 | Predictive covariance of the gaussian process. 602 | 603 | Parameters 604 | ---------- 605 | xo : numpy.ndarray 606 | :math:`m` array of new sample locations 607 | 608 | Returns 609 | ------- 610 | cov : numpy.ndarray 611 | :math:`m\times m` array of predictive covariances 612 | 613 | Notes 614 | ----- 615 | This is defined by Eq. 2.24 of [RW06]_: 616 | 617 | .. math:: 618 | 619 | \mathbf{C}=K(\mathbf{x^*}, \mathbf{x^*}) - K(\mathbf{x^*}, \mathbf{x})\mathbf{K}_{xx}^{-1}K(\mathbf{x}, \mathbf{x^*}) 620 | 621 | """ 622 | Kxoxo = self.Kxoxo(xo) 623 | Kxox = self.Kxox(xo) 624 | Kxxo = self.Kxxo(xo) 625 | return Kxoxo - np.dot(Kxox, np.dot(self.inv_Kxx, Kxxo)) 626 | 627 | def dm_dtheta(self, xo): 628 | r""" 629 | Derivative of the mean of the gaussian process with respect to 630 | its parameters, and evaluated at `xo`. 631 | 632 | Parameters 633 | ---------- 634 | xo : numpy.ndarray 635 | :math:`m` array of new sample locations 636 | 637 | Returns 638 | ------- 639 | dm_dtheta : numpy.ndarray 640 | :math:`n_p\times m` array, where :math:`n_p` is the 641 | number of parameters (see `params`). 642 | 643 | Notes 644 | ----- 645 | The analytic form is: 646 | 647 | .. math:: 648 | 649 | \frac{\partial}{\partial \theta_i}m(\mathbf{x^*})=\frac{\partial K(\mathbf{x^*}, \mathbf{x})}{\partial \theta_i}\mathbf{K}_{xx}^{-1}\mathbf{y} - K(\mathbf{x^*}, \mathbf{x})\mathbf{K}_{xx}^{-1}\frac{\partial \mathbf{K}_{xx}}{\partial \theta_i}\mathbf{K}_{xx}^{-1}\mathbf{y} 650 | 651 | """ 652 | 653 | y = self._y 654 | Ki = self.inv_Kxx 655 | Kj = self.Kxx_J 656 | Kjxo = self.K.jacobian(xo, self._x) 657 | Kxox = self.Kxox(xo) 658 | 659 | dm = np.empty((len(self.params), xo.size)) 660 | gp_c.dm_dtheta(y, Ki, Kj, Kjxo, Kxox, self._s, dm) 661 | 662 | return dm 663 | 664 | def plot(self, ax=None, xlim=None, color='k', markercolor='r'): 665 | """ 666 | Plot the predictive mean and variance of the gaussian process. 667 | 668 | Parameters 669 | ---------- 670 | ax : `matplotlib.pyplot.axes.Axes` (optional) 671 | The axes on which to draw the graph. Defaults to 672 | ``plt.gca()`` if not given. 673 | xlim : (lower x limit, upper x limit) (optional) 674 | The limits of the x-axis. Defaults to the minimum and 675 | maximum of `x` if not given. 676 | color : str (optional) 677 | The line color to use. The default is 'k' (black). 678 | markercolor : str (optional) 679 | The marker color to use. The default is 'r' (red). 680 | 681 | """ 682 | 683 | x, y = self._x, self._y 684 | 685 | if ax is None: 686 | ax = plt.gca() 687 | if xlim is None: 688 | xlim = (x.min(), x.max()) 689 | 690 | X = np.linspace(xlim[0], xlim[1], 1000) 691 | mean = self.mean(X) 692 | cov = self.cov(X) 693 | std = np.sqrt(np.diag(cov)) 694 | upper = mean + std 695 | lower = mean - std 696 | 697 | ax.fill_between(X, lower, upper, color=color, alpha=0.3) 698 | ax.plot(X, mean, lw=2, color=color) 699 | ax.plot(x, y, 'o', ms=5, color=markercolor) 700 | ax.set_xlim(*xlim) 701 | --------------------------------------------------------------------------------