nevo.operators

Base Classes

Base Operator Interface

This module defines the base interface for all optimisation operators in NEVO. Each operator implements a specific search strategy (exploration or exploitation).

class Operator(name, operator_type='exploration', short_name=None, complexity=5)[source]

Bases: ABC

Abstract base class for optimisation operators.

Each operator must implement: - generate_population(): Create candidate solutions - get_parameters(): Return operator-specific parameters

Operators can be either: - EXPLORATION: Global search, diversity promotion - EXPLOITATION: Local refinement, convergence

Complexity levels (1-10): - 1-3: Simple random-based operators - 4-6: Memory-based operators - 7-10: Physics/swarm-based operators

__init__(name, operator_type='exploration', short_name=None, complexity=5)[source]

Initialize operator.

Parameters:
  • name (str) – Unique identifier for this operator

  • operator_type (str) – Either “exploration” or “exploitation”

  • short_name (str, optional) – Short name for display (defaults to first 3 letters uppercase)

  • complexity (int) – Complexity level from 1 (simple) to 10 (complex)

abstractmethod generate_population(centre, state, population_size)[source]

Generate a population of candidate solutions.

Parameters:
  • centre (np.ndarray) – Current search centre (fitness-weighted centroid)

  • state (Dict[str, Any]) – Current optimisation state (memory, best solution, etc.)

  • population_size (int) – Number of candidates to generate

Returns:

candidates – Population of shape (population_size, dimensions) All values should be in [-1, 1] (v-space)

Return type:

np.ndarray

get_parameters()[source]

Get operator-specific parameters.

Returns:

params – Dictionary of parameter names and values

Return type:

Dict[str, Any]

update_statistics(improved, improvement=0.0)[source]

Update operator usage statistics.

Parameters:
  • improved (bool) – Whether this operator improved the best solution

  • improvement (float) – Magnitude of improvement (if any)

reset_statistics()[source]

Reset operator statistics.

class ExplorationOperator(name, short_name=None, complexity=5)[source]

Bases: Operator

Base class for exploration operators (global search).

__init__(name, short_name=None, complexity=5)[source]

Initialize operator.

Parameters:
  • name (str) – Unique identifier for this operator

  • operator_type (str) – Either “exploration” or “exploitation”

  • short_name (str, optional) – Short name for display (defaults to first 3 letters uppercase)

  • complexity (int) – Complexity level from 1 (simple) to 10 (complex)

class ExploitationOperator(name, short_name=None, complexity=5)[source]

Bases: Operator

Base class for exploitation operators (local refinement).

__init__(name, short_name=None, complexity=5)[source]

Initialize operator.

Parameters:
  • name (str) – Unique identifier for this operator

  • operator_type (str) – Either “exploration” or “exploitation”

  • short_name (str, optional) – Short name for display (defaults to first 3 letters uppercase)

  • complexity (int) – Complexity level from 1 (simple) to 10 (complex)

Standard Operators

Standard Optimisation Operators

This module implements common metaheuristic operators adapted for neuromorphic optimisation.

class LevyFlight(alpha=0.3, beta=1.5, gamma=0.1)[source]

Bases: ExplorationOperator

Lévy Flight Operator

Heavy-tailed random walk for escaping local minima. Uses Mantegna’s algorithm to generate Lévy-distributed steps.

Best used when: stuck in local optima, need global exploration.

The update rule is:

\[x_{\text{new}} = x_c + \alpha L_{\beta} + \gamma (x_{\text{best}} - x_c)\]

where \(\alpha\) is the step size, \(L_{\beta}\) is a Lévy-distributed step (Mantegna’s algorithm, exponent \(\beta\)), and \(\gamma\) controls the direction bias towards \(x_{\text{best}}\).

__init__(alpha=0.3, beta=1.5, gamma=0.1)[source]
Parameters:
  • alpha (float) – Step size scaling factor (0.1-0.5 recommended)

  • beta (float) – Lévy exponent (1.5 is standard)

  • gamma (float) – Direction bias factor towards global best (0.05-0.2 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates using Lévy flight.

Return type:

ndarray

class DifferentialEvolution(F=0.8, CR=0.9)[source]

Bases: ExplorationOperator

Differential Evolution Operator (DE/rand/1/bin)

Uses memory diversity to generate directed exploration. Creates new solutions by combining existing memory solutions.

Best used when: memory has diversity, exploring promising regions.

The update rule (DE/rand/1/bin) is:

\[x_{\text{new}} = x_a + F (x_b - x_c)\]

where \(x_a, x_b, x_c\) are distinct memory solutions, \(F\) is the mutation factor, and binomial crossover is applied with probability \(CR\).

__init__(F=0.8, CR=0.9)[source]
Parameters:
  • F (float) – Mutation factor (0.5-1.0 recommended)

  • CR (float) – Crossover probability (0.7-1.0 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates using DE mutation.

Return type:

ndarray

class ParticleSwarm(w=0.7, c1=1.5, c2=1.5)[source]

Bases: ExploitationOperator

Particle Swarm Optimisation Operator

Velocity-based exploitation with attraction to personal and global bests. Maintains velocity state for smooth convergence.

Best used when: improving and converging, exploitation phase.

The update equations are:

\[\begin{split}v_i &= w v_i + c_1 r_1 (p_{\text{best}} - x_i) + c_2 r_2 (g_{\text{best}} - x_i) \\ x_{\text{new}} &= x_i + v_i\end{split}\]

where \(w\) is the inertia weight and \(c_1, c_2\) are the cognitive and social coefficients.

__init__(w=0.7, c1=1.5, c2=1.5)[source]
Parameters:
  • w (float) – Inertia weight (0.4-0.9 recommended)

  • c1 (float) – Cognitive (personal best) coefficient

  • c2 (float) – Social (global best) coefficient

generate_population(centre, state, population_size)[source]

Generate candidates using PSO dynamics.

Return type:

ndarray

class SpiralOptimisation(r_base=0.995)[source]

Bases: ExploitationOperator

Spiral Optimisation Operator

Anisotropic convergence using logarithmic spirals in 2D planes. Each plane has independent rotation and convergence parameters.

Best used when: highly converged, fine-tuning phase

The update rule is:

\[\begin{split}x_{new} = x_{best} + r^{\theta} \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} (x_i - x_{best})\end{split}\]

where \(r\) is the convergence rate and \(\theta\) is the rotation angle.

__init__(r_base=0.995)[source]
Parameters:

r_base (float) – Base convergence rate (0.9-0.99 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates using anisotropic spiral.

Return type:

ndarray

class RandomSearch(scale=0.5, distribution='uniform')[source]

Bases: ExplorationOperator

Random Search Operator

Simple uniform random sampling around the centre. Useful for initial exploration or when other operators stagnate.

Best used when: no prior information, need baseline exploration.

The update rule is:

\[x_{\text{new}} = x_c + \delta\]

where \(\delta\) is sampled from a uniform or Gaussian distribution with scale parameter \(s\).

__init__(scale=0.5, distribution='uniform')[source]
Parameters:
  • scale (float) – Perturbation scale (0.1-1.0 recommended)

  • distribution (str) – Distribution type: ‘uniform’ or ‘gaussian’

generate_population(centre, state, population_size)[source]

Generate candidates using random perturbations.

Return type:

ndarray

class LocalRandomWalk(probability=0.75, scale=0.1)[source]

Bases: ExploitationOperator

Local Random Walk Operator (from Cuckoo Search)

Small-scale local exploration with probability-based activation. Generates subtle perturbations for fine-tuning solutions.

Best used when: near optimum, need local refinement.

The update rule is:

\[x_{\text{new}} = x_c + s \, \Delta\]

where \(s\) is the step size scale and \(\Delta\) is a difference vector from two randomly selected memory solutions.

__init__(probability=0.75, scale=0.1)[source]
Parameters:
  • probability (float) – Probability of applying walk to each dimension (0.5-0.9 recommended)

  • scale (float) – Step size scale (0.05-0.2 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates using local random walk.

Return type:

ndarray

class GravitationalSearch(gravity=1.0, alpha=0.02)[source]

Bases: ExplorationOperator

Gravitational Search Algorithm Operator

Mass-based attraction dynamics where better solutions have more mass. Solutions are attracted towards heavier (better) solutions.

Best used when: need directed exploration, moderate diversity.

The update rule is:

\[x_{\text{new}} = x_i + a_i\]

where \(a_i\) is the acceleration from gravitational forces of all other solutions, weighted by their masses (derived from fitness).

__init__(gravity=1.0, alpha=0.02)[source]
Parameters:
  • gravity (float) – Gravitational constant (0.5-2.0 recommended)

  • alpha (float) – Gravity decay rate (0.01-0.05 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates using gravitational dynamics.

Return type:

ndarray

class FireflyAlgorithm(alpha=0.2, beta=1.0, gamma=1.0)[source]

Bases: ExplorationOperator

Firefly Algorithm Operator

Light-based attraction where brighter (better) fireflies attract others. Combines attraction with randomisation for balanced exploration.

Best used when: need attraction-based exploration, moderate convergence.

The update rule is:

\[x_{\text{new}} = x_i + \beta(r)(x_j - x_i) + \alpha \epsilon\]

where \(\beta(r) = \beta_0 e^{-\gamma r^2}\) is the distance-based attractiveness, \(\alpha\) is the randomisation parameter, and \(\epsilon\) is a random perturbation.

__init__(alpha=0.2, beta=1.0, gamma=1.0)[source]
Parameters:
  • alpha (float) – Randomisation parameter (0.1-0.5 recommended)

  • beta (float) – Attractiveness at distance zero (0.5-2.0 recommended)

  • gamma (float) – Light absorption coefficient (0.5-2.0 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates using firefly dynamics.

Return type:

ndarray

class CentralForce(gravity=2.0, alpha=2.0)[source]

Bases: ExplorationOperator

Central Force Optimisation Operator

Physics-inspired operator using gravitational attraction towards the global best solution with inverse-square law dynamics.

Best used when: need strong directional bias, exploitation-exploration balance

The mathematical expression for this operator is given by: x_{new} = x_{i} + F_{c}

where: - ( x_{new} ) is the new candidate solution. - ( x_{i} ) is the current position of the solution. - ( F_{c} ) is the central force vector directed towards the global best solution, calculated using an inverse power law based on distance.

__init__(gravity=2.0, alpha=2.0)[source]
Parameters:
  • gravity (float) – Gravitational constant (1.0-3.0 recommended)

  • alpha (float) – Distance exponent (1.5-2.5 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates using central force dynamics.

Return type:

ndarray

class GeneticCrossover(crossover='blend', alpha=0.5)[source]

Bases: ExplorationOperator

Genetic Algorithm Crossover Operator

Recombination of memory solutions using various crossover strategies. Creates new solutions by combining genetic material from parents.

Best used when: memory has good diversity, want to combine good features

The mathematical expression for this operator is given by: x_{new} = Crossover(x_{parent1}, x_{parent2})

where: - ( x_{new} ) is the new candidate solution. - ( x_{parent1}, x_{parent2} ) are two parent solutions selected from memory.

__init__(crossover='blend', alpha=0.5)[source]
Parameters:
  • crossover (str) – Crossover type: ‘blend’, ‘single_point’, ‘two_point’, ‘uniform’

  • alpha (float) – Blend crossover extension factor (0.3-0.7 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates using genetic crossover.

Return type:

ndarray

class GeneticMutation(mutation_rate=0.2, scale=0.3, distribution='gaussian')[source]

Bases: ExploitationOperator

Genetic Algorithm Mutation Operator

Random perturbation of solutions with configurable mutation rate. Introduces diversity by modifying individual genes.

Best used when: need local refinement, fine-tuning solutions

The mathematical expression for this operator is given by: x_{new} = x_{i} + m

where: - ( x_{new} ) is the new candidate solution. - ( x_{i} ) is the current position of the solution. - ( m ) is a mutation vector where each gene is altered with a certain probability according to the mutation rate and distribution.

Note: With high mutation rates (>0.5) and large scales (>0.5), this operator behaves more like exploration. Default parameters favour exploitation.

__init__(mutation_rate=0.2, scale=0.3, distribution='gaussian')[source]
Parameters:
  • mutation_rate (float) – Probability of mutating each gene (0.1-0.4 recommended)

  • scale (float) – Mutation magnitude (0.1-0.5 recommended)

  • distribution (str) – Mutation distribution: ‘gaussian’ or ‘uniform’

generate_population(centre, state, population_size)[source]

Generate candidates using genetic mutation.

Return type:

ndarray

class SimulatedAnnealing(initial_temp=1.0, cooling_rate=0.995)[source]

Bases: ExploitationOperator

Simulated Annealing Operator

Temperature-based local search with decreasing randomness. Allows uphill moves early, becomes greedy over time.

Best used when: need controlled exploitation, avoiding local minima

The mathematical expression for this operator is given by: x_{new} = x_{centre} + T cdot delta + b

where: - ( x_{new} ) is the new candidate solution. - ( x_{centre} ) is the current centre solution. - ( T ) is the current temperature controlling perturbation scale. - ( delta ) is a random perturbation vector. - ( b ) is a bias vector towards the global best solution, increasing as temperature decreases.

__init__(initial_temp=1.0, cooling_rate=0.995)[source]
Parameters:
  • initial_temp (float) – Initial temperature (0.5-2.0 recommended)

  • cooling_rate (float) – Temperature decay per call (0.99-0.999 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates using temperature-scaled perturbations.

Return type:

ndarray

reset_temperature()[source]

Reset temperature to initial value.

class TabuSearch(tabu_tenure=10, neighbourhood_size=0.2)[source]

Bases: ExploitationOperator

Tabu Search Operator

Memory-based local search that avoids recently visited regions. Maintains a tabu list to prevent cycling.

Best used when: stuck in local optima, need diversification

The mathematical expression for this operator is given by: x_{new} = x_{centre} + delta

where: - ( x_{new} ) is the new candidate solution. - ( x_{centre} ) is the current centre solution. - ( delta ) is a random perturbation vector. - The candidate solutions are checked against a tabu list to avoid revisiting recent solutions.

__init__(tabu_tenure=10, neighbourhood_size=0.2)[source]
Parameters:
  • tabu_tenure (int) – Number of iterations to keep moves in tabu list (5-20 recommended)

  • neighbourhood_size (float) – Size of local neighbourhood (0.1-0.3 recommended)

generate_population(centre, state, population_size)[source]

Generate candidates avoiding tabu regions.

Return type:

ndarray

class NeuromorphicExplorationEnsemble(n_neurons=150, tau_synapse=0.005, max_rates=(100, 200), intercepts=(-1.0, 1.0), use_numpy_fallback=False)[source]

Bases: ExplorationOperator

Nengo LIF-based exploration using fast spiking neural populations.

This operator builds Nengo neural networks inside the optimiser’s model with LIF neurons, fast synaptic filtering, and NEF decoding.

IMPORTANT: Unlike other operators, this creates Nengo Ensembles that are integrated into the main Nengo simulation loop.

__init__(n_neurons=150, tau_synapse=0.005, max_rates=(100, 200), intercepts=(-1.0, 1.0), use_numpy_fallback=False)[source]

Initialize operator.

Parameters:
  • name (str) – Unique identifier for this operator

  • operator_type (str) – Either “exploration” or “exploitation”

  • short_name (str, optional) – Short name for display (defaults to first 3 letters uppercase)

  • complexity (int) – Complexity level from 1 (simple) to 10 (complex)

build_network(model, state_ensemble, dimension)[source]

Build Nengo LIF ensemble for exploration with spike decoding.

Return type:

Ensemble

generate_population(centre, state, population_size)[source]

Generate exploration candidates from Nengo spike decoding.

Return type:

ndarray

class NeuromorphicExploitationEnsemble(n_neurons=200, tau_synapse=0.02, max_rates=(50, 100), intercepts=(-0.5, 0.5), trust_radius=0.2, use_numpy_fallback=False)[source]

Bases: ExploitationOperator

Nengo LIF-based exploitation using slow spiking neural populations.

This operator builds Nengo neural networks inside the optimiser’s model with LIF neurons, slow synaptic filtering for attractor dynamics.

IMPORTANT: Unlike other operators, this creates Nengo Ensembles that are integrated into the main Nengo simulation loop.

__init__(n_neurons=200, tau_synapse=0.02, max_rates=(50, 100), intercepts=(-0.5, 0.5), trust_radius=0.2, use_numpy_fallback=False)[source]

Initialize operator.

Parameters:
  • name (str) – Unique identifier for this operator

  • operator_type (str) – Either “exploration” or “exploitation”

  • short_name (str, optional) – Short name for display (defaults to first 3 letters uppercase)

  • complexity (int) – Complexity level from 1 (simple) to 10 (complex)

build_network(model, state_ensemble, dimension)[source]

Build Nengo LIF ensemble for exploitation with spike decoding.

Return type:

Ensemble

generate_population(centre, state, population_size)[source]

Generate exploitation candidates from Nengo spike decoding.

Return type:

ndarray

class CoordinateDescent(k_frac=None, scale=0.1)[source]

Bases: ExploitationOperator

Coordinate Descent Operator

Perturbs a randomly chosen sparse subset of dimensions per candidate, keeping all other coordinates fixed at the global best value.

Particularly effective for separable and block-separable high-dimensional functions where variable interactions are limited.

Best used when: D > 50, function is separable or near-separable.

The update rule is:

\[x_k^{\text{new}} = x_k^{\text{best}} + \sigma \epsilon_k, \quad k \in \mathcal{K}\]

where \(\mathcal{K}\) is a random subset of \(k\) coordinates, \(\sigma\) is the step size, and \(\epsilon_k \sim \mathcal{N}(0,1)\).

__init__(k_frac=None, scale=0.1)[source]
Parameters:
  • k_frac (float or None) – Fraction of dimensions to perturb per candidate. None uses 1 / sqrt(D), capped at 0.5. Range: (0, 1].

  • scale (float) – Perturbation magnitude (0.05-0.3 recommended).

generate_population(centre, state, population_size)[source]

Generate candidates by perturbing a sparse subset of coordinates.

Return type:

ndarray

class RandomEmbedding(effective_dim=10, scale=0.3, refresh_every=100)[source]

Bases: ExplorationOperator

Random Embedding Operator

Embeds the search in a low-dimensional subspace using a random orthogonal projection. Exploits the observation that many real-world high-dimensional problems have low effective dimensionality.

Based on the REMEDA principle: a random linear embedding of dimension \(d_{\text{eff}} \ll D\) preserves near-optimal solutions when the intrinsic dimension of the problem is low.

Best used when: D > 100, problem has low intrinsic dimensionality.

The update rule is:

\[x_{\text{new}} = x_c + A z, \quad z \sim \mathcal{N}(0, \sigma^2 I_{d})\]

where \(A \in \mathbb{R}^{D \times d}\) is a random orthonormal matrix and \(d = d_{\text{eff}}\) is the effective dimensionality.

__init__(effective_dim=10, scale=0.3, refresh_every=100)[source]
Parameters:
  • effective_dim (int) – Low-dimensional subspace size (5–20 recommended for high-D problems).

  • scale (float) – Step size in the embedded subspace (0.1-0.5 recommended).

  • refresh_every (int) – Re-draw the random projection after this many generate_population calls, so the search periodically explores different subspaces.

generate_population(centre, state, population_size)[source]

Generate candidates via random projection into a low-D subspace.

Return type:

ndarray

class CovarianceAdaptation(elite_frac=0.3, scale=0.5, max_full_cov_dim=50)[source]

Bases: ExploitationOperator

Covariance Adaptation Operator (simplified CMA-ES style)

Estimates the covariance of the elite memory solutions and samples new candidates from the induced Gaussian distribution. Automatically adapts the step distribution to the local problem geometry.

For D max_full_cov_dim a full covariance matrix is used (accurate but O(D²) memory). For D > max_full_cov_dim only the diagonal variance is estimated (linear memory, still captures per-dimension scaling).

Best used when: memory is well populated, problem has non-separable structure.

The update rule is:

\[x_{\text{new}} \sim \mathcal{N}(\mu_{\text{elite}},\, \sigma^2 C_{\text{elite}})\]

where \(\mu_{\text{elite}}\) and \(C_{\text{elite}}\) are the mean and (optionally diagonal) covariance of the top-elite_frac memory solutions.

__init__(elite_frac=0.3, scale=0.5, max_full_cov_dim=50)[source]
Parameters:
  • elite_frac (float) – Fraction of valid memory used as elite set (0.2–0.5 recommended).

  • scale (float) – Global scaling factor for the covariance (0.2–1.0 recommended).

  • max_full_cov_dim (int) – Dimensions above this threshold use diagonal-only covariance.

generate_population(centre, state, population_size)[source]

Generate candidates by sampling from the elite covariance distribution.

Return type:

ndarray

Registry

Operator Registry

Central registry for all available operators.

get_operator(name, **kwargs)[source]

Get an operator instance by name.

Parameters:
  • name (str) – Operator name (must be in OPERATOR_REGISTRY)

  • **kwargs – Operator-specific parameters

Returns:

operator – Instantiated operator

Return type:

Operator

list_operators()[source]

List all available operators with descriptions.

Returns:

operators – Dictionary mapping operator names to their docstring summaries

Return type:

Dict[str, str]