Source code for metaheuristic_designer.operators.composite_operator

"""
Operator that applies a sequence of operators one after another.
"""

from __future__ import annotations
from typing import Iterable, Optional
from copy import copy

from metaheuristic_designer.initializer import Initializer
from metaheuristic_designer.population import Population

from ..encoding import Encoding
from ..utils import RNGLike
from ..operator import Operator


[docs] class CompositeOperator(Operator): """Operator that sequentially applies a list of operators. Each operator in `op_list` receives the population returned by the previous one. This is the canonical way to chain crossover and mutation, or to build more complex pipelines. Parameters ---------- op_list : list of Operator The operators to apply in order. name : str, optional Display name; defaults to ``"Sequence (op_names)"``. encoding : Encoding, optional Encoding applied to the genotype. random_state : RNGLike, optional Random number generator (shared with sub-operators). """ def __init__(self, op_list: Iterable[Operator], name: str = None, encoding: Optional[Encoding] = None, random_state: Optional[RNGLike] = None): if name is None: op_names = [] for op in op_list: if not isinstance(op, Operator): op_names.append("lambda_func") else: op_names.append(op.name) joined_names = ", ".join(op_names) name = f"Sequence ({joined_names})" # We need to define the op_list before the constructor since it's used in the update method self.op_list = op_list super().__init__(name=name, encoding=encoding, random_state=random_state)
[docs] def gather_params(self) -> dict: """Collect parameters from this operator and all sub-operators. Returns ------- dict Flat dictionary with dotted keys. """ all_params = self.get_params() for op in self.op_list: all_params.update(op.gather_params()) return all_params
[docs] def evolve(self, population: Population, initializer: Optional[Initializer] = None) -> Population: """Apply each operator in sequence. Parameters ---------- population : Population The current population. initializer : Initializer, optional The population initializer. Returns ------- Population The population after all operators have been applied. """ new_population = copy(population) for op in self.op_list: new_population = op.evolve(new_population, initializer) return new_population
[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) for op in self.op_list: if isinstance(op, Operator): op.step(progress)
[docs] def get_state(self) -> dict: data = super().get_state() data["op_list"] = [] for op in self.op_list: if isinstance(op, Operator): data["op_list"].append(op.get_state()) else: data["op_list"].append("lambda_func") return data