Skip to content

Commit

Permalink
Updated docs for optimiser and CMA-ES. Added proper exception for CMA…
Browse files Browse the repository at this point in the history
…-ES not supporting 1-d.
  • Loading branch information
MichaelClerx committed Dec 18, 2024
1 parent 9843b64 commit 4398aa8
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 14 deletions.
30 changes: 18 additions & 12 deletions pints/_optimisers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,23 @@ class Optimiser(pints.Loggable, pints.TunableMethod):
Parameters
----------
x0
A starting point for searches in the parameter space. This value may be
used directly (for example as the initial position of a particle in
A starting point for searches in the parameter space. This must be a
1-dimensional vector, and its length will determine the dimensionality
of the search space. The initial position ``x0`` may may be used
directly (for example as the initial position of a particle in
:class:`PSO`) or indirectly (for example as the center of a
distribution in :class:`XNES`).
sigma0
An optional initial standard deviation around ``x0``. Can be specified
either as a scalar value (one standard deviation for all coordinates)
or as an array with one entry per dimension. Not all methods will use
this information.
this information, and some methods will only use part of the provided
information (e.g. ``CMA-ES`` will only use the smallest value in
``sigma0``). If no value for ``sigma0`` is provided, a guess will be
made. If a :meth:`range<Boundaries.range>` can be obtained from
provided boundaries, then 1/6th of this range will be used. If not, the
value will be set as ``abs(x0[i]) / 3`` for any ``i`` where
``x0[i] != 0`` or as ``1`` where ``x0[i] == 0``.
boundaries
An optional set of boundaries on the parameter space.
Expand Down Expand Up @@ -340,17 +348,14 @@ class OptimisationController(object):
An :class:`pints.ErrorMeasure` or a :class:`pints.LogPDF` that
evaluates points in the parameter space.
x0
The starting point for searches in the parameter space. This value may
be used directly (for example as the initial position of a particle in
:class:`PSO`) or indirectly (for example as the center of a
distribution in :class:`XNES`).
The starting point for searches in the parameter space. For details,
see :class:`Optimiser`.
sigma0
An optional initial standard deviation around ``x0``. Can be specified
either as a scalar value (one standard deviation for all coordinates)
or as an array with one entry per dimension. Not all methods will use
this information.
An optional initial standard deviation around ``x0``. For details, see
:class:`Optimiser`.
boundaries
An optional set of boundaries on the parameter space.
An optional set of boundaries on the parameter space. For details, see
:class:`Optimiser`.
transformation
An optional :class:`pints.Transformation` to allow the optimiser to
search in a transformed parameter space. If used, points shown or
Expand All @@ -359,6 +364,7 @@ class OptimisationController(object):
method
The class of :class:`pints.Optimiser` to use for the optimisation.
If no method is specified, :class:`CMAES` is used.
"""

def __init__(
Expand Down
23 changes: 23 additions & 0 deletions pints/_optimisers/_cmaes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ class CMAES(pints.PopulationBasedOptimiser):
CMA-ES stands for Covariance Matrix Adaptation Evolution Strategy, and is
designed for non-linear derivative-free optimization problems.
To initialise, set an initial position ``x0``, an initial covariance
``sigma0``, and optionally a set of :class:`pints.Boundaries`.
CMA-ES is designed for multivariate optimisation, so ``x0`` should have a
dimension of 2 or greater.
The initial covariance ``sigma0`` should be a scalar (one standard
deviation for all parameters), but for compatibility with other optimisers
an array is also accepted: in this case the smallest value in ``sigma0``
will be used. If no ``sigma0`` is given a guess will be made based on the
boundaries (if provided) or ``x0``, see :meth:`Optimiser` for details. The
method's authors suggest choosing ``sigma0`` such that the optimum is
expected to be within ``3 * sigma0`` from the initial position ``x0``.
:class:`Rectangular boundaries<pints.RectangularBoundaries>` are supported
natively. Other boundary shapes are supported by passing ``nan`` to ``cma``
for any point requested outside of the boundaries.
Extends :class:`PopulationBasedOptimiser`.
References
Expand All @@ -38,6 +56,11 @@ class CMAES(pints.PopulationBasedOptimiser):
def __init__(self, x0, sigma0=None, boundaries=None):
super(CMAES, self).__init__(x0, sigma0, boundaries)

# 1-D is not supported
if len(x0) < 2:
raise ValueError(
'1-dimensional optimisation is not supported by CMA-ES.')

# Set initial state
self._running = False
self._ready_for_tell = False
Expand Down
29 changes: 29 additions & 0 deletions pints/tests/test_mcmc_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,35 @@ def test_log_pdf_storage_in_memory_single(self):
likelihoods = [self.log_likelihood(x) for x in chain]
self.assertTrue(np.all(evals[i] == likelihoods))

# Test with a sensitivity-using method
mcmc = pints.MCMCController(self.log_posterior, n_chains, xs)
mcmc.set_max_iterations(n_iterations)
mcmc.set_log_to_screen(False)
mcmc.set_log_pdf_storage(True)
chains = mcmc.run()

# Test shape of returned array
evals = mcmc.log_pdfs()
self.assertEqual(len(evals.shape), 3)
self.assertEqual(evals.shape[0], n_chains)
self.assertEqual(evals.shape[1], n_iterations)
self.assertEqual(evals.shape[2], 3)

# Test returned values
for i, chain in enumerate(chains):
posteriors = [self.log_posterior(x) for x in chain]
self.assertTrue(np.all(evals[i, :, 0] == posteriors))

likelihoods = [self.log_likelihood(x) for x in chain]
self.assertTrue(np.all(evals[i, :, 1] == likelihoods))

priors = [self.log_prior(x) for x in chain]
self.assertTrue(np.all(evals[i, :, 2] == priors))





# Test disabling again
mcmc = pints.MCMCController(self.log_posterior, n_chains, xs)
mcmc.set_max_iterations(n_iterations)
Expand Down
18 changes: 16 additions & 2 deletions pints/tests/test_opt_cmaes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# copyright notice and full license details.
#
import unittest
import warnings

import numpy as np

import pints
Expand Down Expand Up @@ -124,8 +126,15 @@ def test_ask_tell(self):
self.assertEqual(opt.f_guessed(), np.inf)

# Test deprecated xbest and fbest
self.assertEqual(list(opt.xbest()), list(x))
self.assertEqual(opt.fbest(), np.inf)
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
self.assertEqual(list(opt.xbest()), list(x))
self.assertEqual(len(w), 1)
self.assertIn('xbest` is deprecated', str(w[-1].message))
with warnings.catch_warnings(record=True) as w:
self.assertEqual(opt.fbest(), np.inf)
self.assertEqual(len(w), 1)
self.assertIn('fbest` is deprecated', str(w[-1].message))

# Tell before ask
self.assertRaisesRegex(
Expand Down Expand Up @@ -154,6 +163,11 @@ def test_name(self):
opt = method(np.array([0, 1.01]))
self.assertIn('CMA-ES', opt.name())

def test_one_dimensional(self):
# Tests 1-d is not supported
self.assertRaisesRegex(
ValueError, '1-dimensional optimisation is', pints.CMAES, [1])


if __name__ == '__main__':
print('Add -v for more debug output')
Expand Down

0 comments on commit 4398aa8

Please sign in to comment.