Source code for metaheuristic_designer.operators.masked_operator
"""
Operator that applies different operators to disjoint slices of the genotype.
"""
from __future__ import annotations
from typing import Iterable, Optional
from copy import copy
import numpy as np
from ..initializer import Initializer
from ..population import Population
from ..operator import Operator
from ..utils import MaskLike
[docs]
class MaskedOperator(Operator):
"""Operator that partitions the genotype and applies different operators.
A mask (integer array of length `vec_size`) specifies which operator
(index into `op_list`) handles each gene. This is used internally
by :class:`ExtendedOperator` to separate the solution from auxiliary
parameters.
Parameters
----------
op_list : list of Operator
Operators to apply, one per mask index.
mask : array of int
Array of length `vec_size` assigning each gene to an operator.
name : str, optional
Display name; defaults to ``"Split (op_names)"``.
**kwargs
Forwarded to :class:`Operator`.
"""
def __init__(self, op_list: Iterable[Operator], mask: MaskLike, name: str = None, **kwargs):
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"Split ({joined_names})"
self.op_list = op_list
super().__init__(name, mask=mask, **kwargs)
[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 the appropriate operator to each slice of the genotype.
Parameters
----------
population : Population
The current population.
initializer : Initializer, optional
The population initializer.
Returns
-------
Population
The modified population.
"""
new_population = copy(population)
# In masked_operator.py, inside the loop over op_list:
for idx_op, op in enumerate(self.op_list):
split_mask = self.params.mask == idx_op
if np.any(split_mask):
split_population = new_population.take_slice(split_mask)
split_population = op.evolve(split_population, initializer)
new_population = new_population.apply_slice(split_population, split_mask)
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