Source code for metaheuristic_designer.operators.operator_functions.utils

from copy import copy
from dataclasses import dataclass, field
from typing import Optional
import numpy as np
from ...utils import ScalarLike, VectorLike
from ...population import Population
from ...initializer import Initializer


[docs] def dummy_op( population_matrix: np.ndarray, fitness_array: np.ndarray, random_state: Optional[np.random.Generator] = None, f: ScalarLike = 0 ) -> np.ndarray: """Return a matrix of constant value *f* with the same shape as the input. This operator is intended **only for debugging and testing**. It ignores the population contents and produces an array where every gene equals *f*. Parameters ---------- population_matrix : np.ndarray 2-D array of shape ``(N, M)`` (ignored, but its shape is used). _fitness_array : np.ndarray Fitness values (unused, kept for interface compatibility). random_state : np.random.Generator, optional Random number generator (unused). f : ScalarLike, optional Value to fill the array with. Default is ``0``. Returns ------- np.ndarray A ``(N, M)`` array filled with the constant *f*. """ return np.full_like(population_matrix, f)
[docs] def add_const( population_matrix: np.ndarray, fitness_array: np.ndarray, random_state: Optional[np.random.Generator] = None, f: VectorLike | ScalarLike = 0 ) -> np.ndarray: """Add a constant (or vector) to every gene of the population. Useful as a trivial baseline operator: it simply returns ``population_matrix + f``, where *f* can be a scalar (added to all genes) or a vector (added per gene). Parameters ---------- population_matrix : np.ndarray 2-D array of shape ``(N, M)``. fitness_array : np.ndarray Fitness values (unused). random_state : np.random.Generator, optional Random number generator (unused). f : VectorLike or ScalarLike, optional Value(s) to add. A scalar is broadcast to every gene; a 1-D array of length *M* is added per gene. Default is ``0``. Returns ------- np.ndarray ``population_matrix + f``. """ return population_matrix + f
# ------------------------------------------------------------------ # Wrapper dataclasses – they adapt your plain functions into the # Operator interface used by the GA loop. # ------------------------------------------------------------------
[docs] @dataclass class OperatorFnDef: """Bridge a matrix-to-matrix operator function into an :class:`Operator`. This wrapper accepts a callable that operates on a genotype matrix, fitness array, and random state, and turns it into an object that can be used directly on a :class:`Population`. It merges user-supplied keyword arguments with stored defaults and forced parameters, then invokes the underlying function and updates the population's genotype. Parameters ---------- operator_fn : callable Function with signature ``(population_matrix, fitness_array, random_state, **kwargs) -> np.ndarray``. params : dict, optional Default keyword arguments for the operator. forced_params : dict, optional Keyword arguments that **always** override user-supplied ones. """ operator_fn: callable params: dict = field(default_factory=dict) forced_params: dict = field(default_factory=dict) def __call__(self, population: Population, initializer: Initializer, random_state: Optional[np.random.Generator] = None, **kwargs) -> Population: """Execute the wrapped operator and return a new population. Parameters ---------- population : Population The current population (its genotype matrix and fitness are used). initializer : Initializer Population initializer (forwarded to the operator if needed). random_state : np.random.Generator, optional Random number generator. **kwargs Additional keyword arguments passed to the operator function. They are combined with, but overridden by, :attr:`forced_params`. Returns ------- Population A new population with the genotype updated by the operator. """ modified_kwargs = {} modified_kwargs.update(self.params) modified_kwargs.update(kwargs) modified_kwargs.update(self.forced_params) return population.update_genotype( self.operator_fn(copy(population.genotype_matrix), population.fitness, random_state=random_state, **modified_kwargs) )
[docs] @dataclass class OperatorRandomDef: """Bridge a random-style operator function into an :class:`Operator`. This wrapper is intended for operators that **replace** the genotype with entirely new random values (e.g., uniform sampling, initializer-based reset). It passes the population's genotype matrix, the initializer, and the random state to the underlying function. Parameters ---------- operator_fn : callable Function with signature ``(population_matrix, initializer, random_state, **kwargs) -> np.ndarray``. params : dict, optional Default keyword arguments. forced_params : dict, optional Keyword arguments that override any user-supplied ones. """ operator_fn: callable params: dict = field(default_factory=dict) forced_params: dict = field(default_factory=dict) def __call__(self, population: Population, initializer: Initializer, random_state: Optional[np.random.Generator] = None, **kwargs) -> Population: """Execute the random operator and return a new population. Parameters ---------- population : Population The current population (its genotype matrix is used as a shape reference). initializer : Initializer Population initializer, forwarded to the operator. random_state : np.random.Generator, optional Random number generator. **kwargs Additional keyword arguments for the operator function. Returns ------- Population A new population where the genotype has been replaced by the operator's output. """ modified_kwargs = {} modified_kwargs.update(self.params) modified_kwargs.update(kwargs) modified_kwargs.update(self.forced_params) return population.update_genotype(self.operator_fn(population.genotype_matrix, initializer, random_state=random_state, **modified_kwargs))
[docs] @dataclass class ObtainStatisticDef: """Wrap a statistic‑computing function into an :class:`Operator`. This adapter is used for functions that compute a single summary vector (e.g., population mean, median, standard deviation) and store it as the new genotype (usually a single-row population). Parameters ---------- operator_fn : callable Function with signature ``(population_matrix, random_state, **kwargs) -> np.ndarray``. params : dict, optional Default keyword arguments. forced_params : dict, optional Keyword arguments that override user-supplied ones. """ operator_fn: callable params: dict = field(default_factory=dict) forced_params: dict = field(default_factory=dict) def __call__(self, population: Population, initializer: Initializer, random_state: Optional[np.random.Generator] = None, **kwargs) -> Population: """Compute a statistic and replace the population’s genotype. Parameters ---------- population : Population The current population (its genotype matrix is analysed). initializer : Initializer Not used by this wrapper (included for interface compatibility). random_state : np.random.Generator, optional Random number generator. **kwargs Additional keyword arguments for the operator function. Returns ------- Population A population whose genotype has been replaced by the computed statistic. """ modified_kwargs = {} modified_kwargs.update(self.params) modified_kwargs.update(kwargs) modified_kwargs.update(self.forced_params) return population.update_genotype(self.operator_fn(population.genotype_matrix, random_state, **modified_kwargs))
[docs] @dataclass class OperatorSwarmDef: """Bridge a swarm operator function into an :class:`Operator`. This wrapper is designed for operators that directly receive the whole :class:`Population` object and the initializer, and are responsible for returning an updated :class:`Population` **themselves** (e.g., PSO operators that need access to historical bests). Parameters ---------- operator_fn : callable Function with signature ``(population, initializer, random_state, **kwargs) -> Population``. params : dict, optional Default keyword arguments. forced_params : dict, optional Keyword arguments that override user-supplied ones. """ operator_fn: callable params: dict = field(default_factory=dict) forced_params: dict = field(default_factory=dict) def __call__(self, population: Population, initializer: Initializer, random_state: np.random.Generator, **kwargs) -> Population: """Execute the swarm operator and return the new population. Parameters ---------- population : Population The current population, passed directly to the operator. initializer : Initializer Population initializer, passed to the operator. random_state : np.random.Generator Random number generator (required for swarm operators). **kwargs Additional keyword arguments for the operator function. Returns ------- Population The population returned by the operator (may be the same object or a new one, depending on the operator). """ modified_kwargs = {} modified_kwargs.update(self.params) modified_kwargs.update(kwargs) modified_kwargs.update(self.forced_params) return self.operator_fn(population, initializer, random_state=random_state, **modified_kwargs)