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
from copy import copy
import numpy as np
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) -> Population:
"""Apply the appropriate operator to each slice of the genotype.
Parameters
----------
population : Population
The current population.
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)
new_population = new_population.apply_slice(split_population, split_mask)
return new_population
[docs]
def update(self, progress: float):
"""Update schedulable parameters and propagate to sub-operators.
Parameters
----------
progress : float
Current progress of the algorithm (0-1).
"""
super().update(progress)
for op in self.op_list:
if isinstance(op, Operator):
op.update(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