Source code for metaheuristic_designer.operators.extended_operator

"""
Base operator for algorithms that split the genotype into solution and parameters.
"""

from __future__ import annotations
from typing import Optional
import numpy as np

from ..initializer import Initializer
from ..population import Population
from ..operator import Operator
from ..encodings import ParameterExtendingEncoding
from .masked_operator import MaskedOperator


[docs] class ExtendedOperator(Operator): """Operator that handles a genotype split into solution and extra parameters. A mask is built from the encoding to separate the solution part from the parameter blocks. The solution is processed by `base_operator`, while each parameter block can be mutated/adapted by its own operator. Parameters ---------- base_operator : Operator Operator applied to the solution part. param_operators : dict Mapping from parameter names to their mutation operators. encoding : ParameterExtendingEncoding The encoding that defines the genotype layout. name : str, optional Display name; defaults to the base operator's name. **kwargs Forwarded to :class:`Operator`. """ def __init__(self, base_operator: Operator, param_operators: dict, encoding: ParameterExtendingEncoding, name: str = None, **kwargs): if not isinstance(encoding, ParameterExtendingEncoding): raise TypeError("The encoding must inherit from ParameterExtendingEncoding.") if name is None: name = f"{base_operator.name}" mask = np.zeros(encoding.dimension + encoding.nparams) counter = encoding.dimension for idx, (_, param_num) in enumerate(encoding.param_sizes): mask[counter : counter + param_num] = idx + 1 counter = counter + param_num operator_list = [base_operator] + [param_operators[param_name] for idx, (param_name, _) in enumerate(encoding.param_sizes)] self.main_operator = MaskedOperator(operator_list, mask=mask) self.mask = mask self.base_operator = base_operator self.param_operators = param_operators self.param_encoding = encoding super().__init__(name=name, encoding=encoding, **kwargs)
[docs] def gather_params(self) -> dict: """Collect parameters from the base operator and all parameter operators. Returns ------- dict Flat dictionary with dotted keys. """ all_params = self.get_params() for op in self.param_operators.values(): all_params.update(op.gather_params()) return all_params
[docs] def evolve(self, population: Population, initializer: Optional[Initializer] = None) -> Population: """Apply the main masked operator (solution + parameter mutations). Parameters ---------- population : Population The current population. initializer : Initializer, optional The population initializer. Returns ------- Population The evolved population. """ return self.main_operator.evolve(population, initializer=initializer)
[docs] def step(self, progress: float): """Update schedulable parameters and propagate to sub-operators. Parameters ---------- progress : float Current progress of the algorithm (0-1). """ super().step(progress) self.base_operator.step(progress) for _, op in self.param_operators.items(): if isinstance(op, Operator): op.step(progress)