pybop.optimisers#

Submodules#

Classes#

AdamWImpl

AdamW optimiser (adaptive moment estimation with weight decay), as described in [1].

CuckooSearchImpl

Cuckoo Search (CS) optimisation algorithm, inspired by the brood parasitism

GradientDescentImpl

Gradient descent method with a fixed, per-dimension learning rate.

IRPropPlusImpl

The iRprop+ algorithm adjusts step sizes based on the sign of the gradient

RandomSearchImpl

Random Search (RS) optimisation algorithm.

SimulatedAnnealingImpl

Simulated Annealing optimiser, implementing the classic temperature-based

Package Contents#

class pybop.optimisers.AdamWImpl(x0: numpy.ndarray, sigma0: list[float] | None, boundaries: pints.Boundaries | None)[source]#

Bases: pints.Optimiser

AdamW optimiser (adaptive moment estimation with weight decay), as described in [1].

This method is an extension of the Adam optimiser that introduces weight decay, which helps to regularise the weights and prevent overfitting.

This class reimplements the Pints’ Adam Optimiser, but with the weight decay functionality mentioned above. Original creation and credit is attributed to Pints.

Pseudo-code is given below. Here the value of the j-th parameter at iteration i is given as p_j[i] and the corresponding derivative is denoted g_j[i]:

m_j[i] = beta1 * m_j[i - 1] + (1 - beta1) * g_j[i]
v_j[i] = beta2 * v_j[i - 1] + (1 - beta2) * g_j[i]**2

m_j' = m_j[i] / (1 - beta1**(1 + i))
v_j' = v_j[i] / (1 - beta2**(1 + i))

p_j[i] = p_j[i - 1] - alpha * (m_j' / (sqrt(v_j') + eps) + lam * p_j[i - 1])

The initial values of the moments are m_j[0] = v_j[0] = 0, after which they decay with rates beta1 and beta2. The default values for these are, beta1 = 0.9 and beta2 = 0.999.

The terms m_j' and v_j' are “initialisation bias corrected” versions of m_j and v_j (see section 2 of the paper).

The parameter alpha is a step size, which is set as min(sigma0) in this implementation.

The parameter lam is the weight decay rate, which is set to 0.01 by default in this implementation.

Finally, eps is a small constant used to avoid division by zero, set to ``eps = np.finfo(float).eps in this implementation.

This is an unbounded method: Any boundaries will be ignored.

References

ask()[source]#

Returns a list of next points in the parameter-space to evaluate from the optimiser.

f_best()[source]#

Returns the best score found so far.

f_guessed()[source]#

Returns the score of the last guessed point.

n_hyper_parameters()[source]#

The number of hyper-parameters used by this optimiser.

name()[source]#

Returns the name of the optimiser.

needs_sensitivities()[source]#

Returns False if this optimiser does not require gradient, and True otherwise.

running()[source]#

Returns True if the optimisation is in progress.

tell(reply)[source]#

Receives a list of function values from the cost function from points previously specified by self.ask(), and updates the optimiser state accordingly.

x_best()[source]#

Returns the best parameter values found so far.

x_guessed()[source]#

Returns the last guessed parameter values.

_alpha#
_b1 = 0.9#
_b1t = 1#
_b2 = 0.999#
_b2t = 1#
_current#
_current_df = None#
_current_f#
_eps#
_f_best#
_lam = 0.01#
_m#
_proposed#
_ready_for_tell = False#
_running = False#
_v#
_x_best#
property b1#
property b2#
boundaries = None#
property lam#
class pybop.optimisers.CuckooSearchImpl(x0: numpy.ndarray, sigma0: list[float] | None, boundaries: pints.Boundaries | None)[source]#

Bases: pints.PopulationBasedOptimiser

Cuckoo Search (CS) optimisation algorithm, inspired by the brood parasitism of some cuckoo species. This algorithm was introduced by Yang and Deb in 2009.

The algorithm uses a population of host nests (solutions), where each cuckoo (new solution) tries to replace a worse nest in the population. The quality or fitness of the nests is determined by the cost function. A fraction of the worst nests is abandoned at each generation, and new ones are built randomly.

The pseudo-code for the Cuckoo Search is as follows:

  1. Initialise population of n host nests

  2. While (t < max_generations):
    1. Get a cuckoo randomly by Lévy flights

    2. Evaluate its quality/fitness F

    3. Choose a nest among n (say, j) randomly

    4. If (F > fitness of j):
      1. Replace j with the new solution

    5. Abandon a fraction (pa) of the worst nests and build new ones

    6. Keep the best solutions/nests

    7. Rank the solutions and find the current best

  3. End While

This implementation also uses a decreasing step size for the Lévy flights, calculated as sigma = sigma0 / sqrt(iterations), where sigma0 is the initial step size and iterations is the current iteration number.

Parameters: - pa: Probability of discovering alien eggs/solutions (abandoning rate)

References: - X. -S. Yang and Suash Deb, “Cuckoo Search via Lévy flights,”

2009 World Congress on Nature & Biologically Inspired Computing (NaBIC), Coimbatore, India, 2009, pp. 210-214, https://doi.org/10.1109/NABIC.2009.5393690.

  • S. Walton, O. Hassan, K. Morgan, M.R. Brown, Modified cuckoo search: A new gradient free optimisation algorithm, Chaos, Solitons & Fractals, Volume 44, Issue 9, 2011, Pages 710-718, ISSN 0960-0779, https://doi.org/10.1016/j.chaos.2011.06.004.

_suggested_population_size()[source]#

Inherited from Pints:PopulationBasedOptimiser. Returns a suggested population size, based on the dimension of the parameter space.

abandon_nests(idx)[source]#

Updates the nests to abandon the worst performers and reinitialise.

ask()[source]#

Returns a list of next points in the parameter-space to evaluate from the optimiser.

clip_nests(x)[source]#

Clip the input array to the boundaries if available.

f_best()[source]#

Returns the best score found so far.

levy_flight(alpha, size)[source]#

Generate step sizes via the Mantegna’s algorithm for Levy flights

name()[source]#

Returns the name of the optimiser.

running()[source]#

Returns True if the optimisation is in progress.

tell(replies)[source]#

Receives a list of function values from the cost function from points previously specified by self.ask(), and updates the optimiser state accordingly.

x_best()[source]#

Returns the best parameter values found so far.

_dim#
_f_best#
_finite_boundaries#
_fitness#
_iterations = 0#
_pa = 0.25#
_ready_for_tell = False#
_running = False#
_x_best#
beta = 1.5#
property pa#
step_size#
class pybop.optimisers.GradientDescentImpl(x0: numpy.ndarray, sigma0: list[float] | None, boundaries: pints.Boundaries | None)[source]#

Bases: pints.Optimiser

Gradient descent method with a fixed, per-dimension learning rate.

Gradient descent updates the current position in the direction of the steepest descent, as determined by the negative of the gradient of the function.

The update rule for each iteration is given by:

\[x_{t+1} = x_t - \eta * \nabla f(x_t)\]
where:
  • \(x_t\) are the current parameter values at iteration t,

  • \(\nabla f(x_t)\) is the gradient of the function at \(x_t\),

  • \(\eta\) is the learning rate, which controls the step size.

This class reimplements the Pints’ Gradient Descent, but with multidimensional, fixed learning rates. Original creation and credit is attributed to Pints.

Parameters:
  • x0 (array-like) – Initial starting point for the optimisation. This should be a 1D array representing the starting parameter values for the function being optimised.

  • sigma0 (float or array-like, optional) – Initial learning rate or rates for each dimension. If a scalar is provided, the same learning rate is applied across all dimensions. If an array is provided, each dimension will have its own learning rate (default: 0.02).

  • boundaries (pybop.Boundaries, optional) – Boundaries for the parameters. This optimiser ignores boundaries and operates as an unbounded method (default: None).

_x_best#

The best parameter values (solution) found so far.

Type:

array-like

_f_best#

The best function value (objective value) found so far.

Type:

float

_current#

The current parameter values at the latest iteration.

Type:

array-like

_eta#

The current learning rate(s). Can be a scalar or per-dimension array.

Type:

array-like

_running#

Indicates whether the optimisation process is running.

Type:

bool

_ready_for_tell#

Indicates whether the optimiser is ready to receive feedback from the objective function.

Type:

bool

ask()[source]#

Proposes the next point for evaluation.

f_best()[source]#

Returns the best objective value found.

learning_rate()[source]#

Returns the learning rate(s).

n_hyper_parameters()[source]#

Returns the number of hyper-parameters (learning rate).

name()[source]#

Returns the name of the optimiser.

needs_sensitivities()[source]#

Indicates this optimiser requires gradient information.

running()[source]#

Returns whether the optimiser is running.

set_hyper_parameters(x)[source]#

Sets hyper-parameters (learning rate).

set_learning_rate(eta)[source]#

Sets the learning rate. Supports per-dimension rates.

Parameters:

eta (float or array-like) – New learning rate(s).

tell(reply)[source]#

Updates optimiser with function evaluation results.

x_best()[source]#

Returns the best solution found.

_eta#
_f_best#
_ready_for_tell = False#
_running = False#
class pybop.optimisers.IRPropPlusImpl(x0: numpy.ndarray, sigma0: list[float] | None, boundaries: pints.Boundaries | None)[source]#

Bases: pints.Optimiser

The iRprop+ algorithm adjusts step sizes based on the sign of the gradient in each dimension, increasing step sizes when the sign remains consistent and decreasing when the sign changes. This implementation includes weight (parameter) backtracking that reverts the previous step in the event of a gradient sign changing.

References: - [1] Igel and Hüsken (2003): Empirical Evaluation of Improved Rprop Algorithms. - [2] Riedmiller and Braun (1993): A Direct Adaptive Method for Faster Backpropagation. - [3] Igel and Husk (2003): Improving the Rprop Learning Algorithm.

Parameters:
  • x0 (array-like) – Initial starting point for the optimisation.

  • sigma0 (float or array-like, optional) – Initial step size(s). If a scalar is provided, it is applied to all dimensions. Default is 0.05.

  • boundaries (pints.Boundaries, optional) – Boundary constraints for the optimisation. If None, no boundaries are applied.

eta_min#

Factor by which the step size is reduced when the gradient sign changes. Default is 0.5.

Type:

float

eta_max#

Factor by which the step size is increased when the gradient sign remains consistent. Default is 1.2.

Type:

float

step_min#

Minimum allowable step size. Default is 1e-3 * min(sigma0).

Type:

float

step_max#

Maximum allowable step size. Default is None (unlimited).

Type:

float or None

ask()[source]#

Proposes the next point for evaluation.

Returns:

A list containing the proposed point.

Return type:

list

f_best()[source]#

Returns the best function value found so far.

name()[source]#

Returns the name of the optimiser.

needs_sensitivities()[source]#

Indicates that this optimiser requires gradient information.

running()[source]#

Returns the state of the optimiser

tell(reply)[source]#

Updates the optimiser with the function value and gradient at the proposed point.

Parameters:

reply (list) – A list containing a tuple of (function value, gradient) at the proposed point.

Raises:

RuntimeError – If ask() was not called before tell().

x_best()[source]#

Returns the best position found so far.

_boundaries#
_f_best#
_f_current#
_gradient_previous = None#
_proposed#
_ready_for_tell = False#
_running = False#
_step_sizes#
_update_previous#
_x_best#
_x_current#
eta_max = 1.2#
eta_min = 0.5#
step_max: float | None = None#
step_min#
class pybop.optimisers.RandomSearchImpl(x0: numpy.ndarray, sigma0: list[float] | None, boundaries: pints.Boundaries | None)[source]#

Bases: pints.PopulationBasedOptimiser

Random Search (RS) optimisation algorithm. This algorithm explores the parameter space by randomly sampling points.

The algorithm does the following: 1. Initialise a population of solutions. 2. At each iteration, generate n number of random positions within boundaries. 3. Evaluate the quality/fitness of the positions. 4. Replace the best position with improved position if found.

Parameters:
  • population_size – Number of solutions to evaluate per iteration.

  • optional – Number of solutions to evaluate per iteration.

References: The Random Search algorithm implemented in this work is based on principles outlined in “Introduction to Stochastic Search and Optimization: Estimation, Simulation, and Control” by Spall, J. C. (2003).

The implementation inherits from the PINTS PopulationOptimiser.

_suggested_population_size()[source]#

Returns a suggested population size based on the dimension of the parameter space.

ask()[source]#

Returns a list of positions to evaluate in the optimiser-space.

clip_candidates(x)[source]#

Clip the input array to the boundaries if available.

f_best()[source]#

Returns the best score found so far.

name()[source]#

Returns the name of the optimiser.

running()[source]#

Returns True if the optimisation is in progress.

tell(replies)[source]#

Receives a list of cost function values from points previously specified by self.ask(), and updates the optimiser state accordingly.

x_best()[source]#

Returns the best parameter values found so far.

_dim#
_f_best#
_lower = None#
_ready_for_tell = False#
_running = False#
_upper = None#
_x_best#
class pybop.optimisers.SimulatedAnnealingImpl(x0: numpy.ndarray, sigma0: list[float] | None, boundaries: pints.Boundaries | None)[source]#

Bases: pints.Optimiser

Simulated Annealing optimiser, implementing the classic temperature-based probabilistic optimisation method.

This method uses a temperature schedule to control the probability of accepting worse solutions as it explores the parameter space. As the temperature decreases, the algorithm becomes more selective, eventually converging to a local or global optimum.

The probability of accepting a worse solution is given by:

.. math::

P(accept) = exp(-(f_{ ext{new}} - fold)/T)

The temperature decreases according to the cooling schedule:

.. math::

T = T0 * lpha^k

where: - :math: T0 is the initial temperature - :math: lpha is the cooling rate (between 0 and 1) - :math: k is the iteration number

Parameters:
  • x0 (numpy array) – Initial position

  • sigma0 (float) – Initial step size

  • boundaries (dict, optional) – Optional boundaries for parameters

ask()[source]#

Returns a list of next points in the parameter-space to evaluate from the optimiser.

f_best()[source]#

Returns the best score found.

n_hyper_parameters()[source]#

Returns the number of hyper-parameters for this optimiser.

name()[source]#

Returns the name of this optimiser.

needs_sensitivities()[source]#

Returns whether this method needs sensitivities.

running()[source]#

Returns whether the optimisation is still running.

tell(reply)[source]#

Receives a list of function values from the cost function from points previously specified by self.ask(), and updates the optimiser state accordingly.

x_best()[source]#

Returns the best position found.

_current#
_current_f#
_f_best#
_iterations = 0#
_proposed#
_ready_for_tell = False#
_running = False#
_temperature = 1.0#
_temperature_decay = 0.95#
_x_best#
property cooling_rate#
property temperature#