Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-organisation/tidying of functional tests submodule #1386

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions pints/functionaltests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
#
# "Functional test" module for PINTS.
#
# This file is part of PINTS (https://github.com/pints-team/pints/) which is
# released under the BSD 3-clause license. See accompanying LICENSE.md for
# copyright notice and full license details.
#

from .differential_evolution import test_differential_evolution_on_banana
from .differential_evolution import test_differential_evolution_on_two_dim_gaussian
# Import all problem classes straight into this module, so that they can be
# addressed as e.g. pints.functionaltests.RunMcmcMethodOnAnnulus.
from ._problems import ( # noqa
RunMcmcMethodOnAnnulus,
RunMcmcMethodOnBanana,
RunMcmcMethodOnCone,
RunMcmcMethodOnCorrelatedGaussian,
RunMcmcMethodOnHighDimensionalGaussian,
RunMcmcMethodOnMultimodalGaussian,
RunMcmcMethodOnTwoDimGaussian,
)

# Import all test modules (not methods!) directly into this method, so that
# they can be addressed as e.g.
# pints.functionaltests.dram_acmc.two_dim_gaussian().
from . import ( # noqa
differential_evolution_mcmc,
dram_acmc,
dream_mcmc,
emcee_hammer_mcmc,
haario_acmc,
haario_bardenet_acmc,
hamiltonian_mcmc,
mala_mcmc,
metropolis_random_walk_mcmc,
monomial_gamma_hamiltonian_mcmc,
no_u_turn_mcmc,
population_mcmc,
relativistic_mcmc,
slice_stepout_mcmc,
)


from .haario_bardenet_acmc import test_haario_bardenet_acmc_on_annulus
from .haario_bardenet_acmc import test_haario_bardenet_acmc_on_banana
from .haario_bardenet_acmc import test_haario_bardenet_acmc_on_correlated_gaussian
from .haario_bardenet_acmc import test_haario_bardenet_acmc_on_two_dim_gaussian
# Test discovery methods
from ._discovery import tests
40 changes: 40 additions & 0 deletions pints/functionaltests/_discovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#
# Functional test discovery methods for PINTS.
#
# This file is part of PINTS (https://github.com/pints-team/pints/) which is
# released under the BSD 3-clause license. See accompanying LICENSE.md for
# copyright notice and full license details.
#
import inspect

import pints.functionaltests as ft


def tests(method=None):
"""
Returns a list of all functional tests, each represented as a tuple
``(method, test)`` where ``method`` is the PINTS class being tested and
``test`` is a callable that returns the test results.
If the optional argument ``method`` is given, only tests for this method
are returned.
"""
# Get all modules imported into this module
modules = [getattr(ft, x) for x in dir(ft) if not x.startswith('_')]
modules = [x for x in modules if inspect.ismodule(x)]

# Look for (explicitly defined) tests
tests = []
for module in modules:
try:
m_method = module._method
m_tests = module._functional_tests
except AttributeError:
continue

if method is None or method == m_method:
for test in m_tests:
tests.append((m_method, test))

return tests

94 changes: 76 additions & 18 deletions pints/functionaltests/_problems.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,44 @@
#
# Shared problems used in functional testing.
#
# This file is part of PINTS (https://github.com/pints-team/pints/) which is
# released under the BSD 3-clause license. See accompanying LICENSE.md for
# copyright notice and full license details.
#

import numpy as np

import pints
import pints.toy


class RunMcmcMethodOnProblem(object):
"""
Base class for tests that run an MCMC method on a log-PDF.
Parameters
----------
log_pdf : pints.LogPDF
The PDF to sample. Will be passed to a :class:`pints.MCMCController`.
x0
One or more starting points to be passed to the
:class:`pints.MCMCController`.
sigma0
One or more ``sigma0`` parameters to be passed to the
:class:`pints.MCMCController`.
method : pints.MCMCSampler
The method to test. Will be passed to the
:class:`pints.MCMCController`.
n_chains : int
The number of chains to run. Will be passed to the
:class:`pints.MCMCController`.
n_iterations : int
The number of iterations to run
n_warmup : int
The number of iterations to discard
method_hyper_parameters : list
A list of hyperparameter values.
"""

def __init__(self, log_pdf, x0, sigma0, method, n_chains, n_iterations,
n_warmup, method_hyper_parameters):
Expand All @@ -26,9 +55,11 @@ def __init__(self, log_pdf, x0, sigma0, method, n_chains, n_iterations,
def estimate_kld(self):
"""
Estimates the Kullback-Leibler divergence.
Raises an ``AttributeError`` if the underlying LogPDF does not have a
method ``kl_divergence()``.
"""
chains = np.vstack(self.chains)

return np.mean(self.log_pdf.kl_divergence(chains))

def estimate_mean_ess(self):
Expand All @@ -47,15 +78,21 @@ def estimate_mean_ess(self):

def estimate_distance(self):
"""
Estimates a measure of distance for the `pints.AnnulusLogPDF` class.
Estimates a measure of distance between the sampled chains and the true
distribution.
Raises an ``AttributeError`` if the underlying LogPDF does not have a
method ``distance()``.
"""
return self.log_pdf.distance(np.vstack(self.chains))


class RunMcmcMethodOnTwoDimGaussian(RunMcmcMethodOnProblem):
"""
Tests a given MCMC method on a standard 2 dimensional Gaussian
distribution.
Tests a given MCMC method on a two-dimensional Gaussian distribution with
means ``[0, 0]`` and sigma ``[1, 1]``.
For constructor arguments, see :class:`RunMcmcMethodOnProblem`.
"""

def __init__(self, method, n_chains, n_iterations, n_warmup,
Expand All @@ -75,7 +112,11 @@ def __init__(self, method, n_chains, n_iterations, n_warmup,

class RunMcmcMethodOnBanana(RunMcmcMethodOnProblem):
"""
Tests a given MCMC method on `pints.toy.TwistedGaussianLogPDF`.
Tests a given MCMC method on a two-dimensional
:class:`pints.toy.TwistedGaussianLogPDF` distribution with means
``[0, 0]``.
For constructor arguments, see :class:`RunMcmcMethodOnProblem`.
"""
def __init__(self, method, n_chains, n_iterations, n_warmup,
method_hyper_parameters=None):
Expand All @@ -91,6 +132,7 @@ def __init__(self, method, n_chains, n_iterations, n_warmup,
n_warmup, method_hyper_parameters)


'''
class RunMcmcMethodOnSimpleEggBox(RunMcmcMethodOnProblem):
"""
Tests a given MCMC method on `pints.toy.SimpleEggBoxLogPDF`.
Expand All @@ -105,11 +147,15 @@ def __init__(self, method, n_chains, n_iterations, n_warmup,
super().__init__(log_pdf, x0, sigma0, method, n_chains, n_iterations,
n_warmup, method_hyper_parameters)
'''


class RunMcmcMethodOnHighDimensionalGaussian(RunMcmcMethodOnProblem):
"""
Tests a given MCMC method on `pints.toy.HighDimensionalGaussianLogPDF`.
Tests a given MCMC method on a 20-dimensional
:class:`pints.toy.HighDimensionalGaussianLogPDF` centered at the origin.
For constructor arguments, see :class:`RunMcmcMethodOnProblem`.
"""
def __init__(self, method, n_chains, n_iterations, n_warmup,
method_hyper_parameters=None):
Expand All @@ -123,13 +169,14 @@ def __init__(self, method, n_chains, n_iterations, n_warmup,

class RunMcmcMethodOnCorrelatedGaussian(RunMcmcMethodOnProblem):
"""
Tests a given MCMC method on `pints.toy.HighDimensionalGaussianLogPDF`
but using a 6-dimensional problem with higher correlation.
Tests a given MCMC method on a 6-dimensional, highly correlated
:class:`pints.toy.HighDimensionalGaussianLogPDF` centered at the origin.
For constructor arguments, see :class:`RunMcmcMethodOnProblem`.
"""
def __init__(self, method, n_chains, n_iterations, n_warmup,
method_hyper_parameters=None):
log_pdf = pints.toy.HighDimensionalGaussianLogPDF(
dimension=6, rho=0.8)
log_pdf = pints.toy.HighDimensionalGaussianLogPDF(dimension=6, rho=0.8)
x0 = np.random.uniform(-4, 4, size=(n_chains, 6))
sigma0 = None

Expand All @@ -139,7 +186,11 @@ def __init__(self, method, n_chains, n_iterations, n_warmup,

class RunMcmcMethodOnAnnulus(RunMcmcMethodOnProblem):
"""
Tests a given MCMC method on `pints.AnnulusLogPDF`.
Tests a given MCMC method on a two-dimensional
:class:`pints.toy.AnnulusLogPDF` distribution, with its highest values at
any point ``x`` with ``np.linalg.norm(x) == 10``.
For constructor arguments, see :class:`RunMcmcMethodOnProblem`.
"""
def __init__(self, method, n_chains, n_iterations, n_warmup,
method_hyper_parameters=None):
Expand All @@ -153,17 +204,21 @@ def __init__(self, method, n_chains, n_iterations, n_warmup,

class RunMcmcMethodOnMultimodalGaussian(RunMcmcMethodOnProblem):
"""
Tests a given MCMC method on `MultimodalGaussianLogPDF`.
Tests a given MCMC method on a two-dimensional
:class:`pints.toy.MultimodalGaussianLogPDF` with modes at ``[0, 0]``,
``[5, 10]``, and ``[10, 0]``.
For constructor arguments, see :class:`RunMcmcMethodOnProblem`.
"""
def __init__(self, method, n_chains, n_iterations, n_warmup,
method_hyper_parameters=None):
modes = [[0, 0],
[5, 10],
[10, 0]]
covariances = [[[1, 0], [0, 1]],
[[2, 0.8], [0.8, 3]],
[[1, -0.5], [-0.5, 1]]]
log_pdf = pints.toy.MultimodalGaussianLogPDF(modes=[[0, 0],
[5, 10],
[10, 0]],
covariances=covariances)
log_pdf = pints.toy.MultimodalGaussianLogPDF(modes, covariances)
x0 = log_pdf.sample(n_chains)
sigma0 = None

Expand All @@ -173,7 +228,10 @@ def __init__(self, method, n_chains, n_iterations, n_warmup,

class RunMcmcMethodOnCone(RunMcmcMethodOnProblem):
"""
Tests a given MCMC method on `MultimodalGaussianLogPDF`.
Tests a given MCMC method on a two-dimensional
:class:`pints.toy,ConeLogPDF` centered at ``[0, 0]``.
For constructor arguments, see :class:`RunMcmcMethodOnProblem`.
"""
def __init__(self, method, n_chains, n_iterations, n_warmup,
method_hyper_parameters=None):
Expand Down
79 changes: 0 additions & 79 deletions pints/functionaltests/differential_evolution.py

This file was deleted.

Loading