Source code for metaheuristic_designer.strategies.hybrid.memetic

"""
Hybrid search strategy based on memetic theory from biology.
"""

from copy import copy
from typing import Optional, Tuple

from ...population import Population
from ...objective_function import ObjectiveFunc
from ...parent_selection import ParentSelection
from ...utils import MaskLike, RNGLike
from ...search_strategy import SearchStrategy


[docs] class MemeticStrategy(SearchStrategy): """Strategy that combines a main search strategy with a local search procedure that improves solutions after they are evolved. Parameters ---------- main_strategy : SearchStrategy Main search strategy used in the optimization algorithm. local_search_heuristic : SearchStrategy Local search procedure used to improve solutions after evolution. local_search_depth : int, optional Number of times to repeat the local search procedure per iteration, by default 1 keep_improved_solutions : str, optional Whether to keep the improved solutions for the next iteration (Lamarkian memetic algorithms) or to just update the fitness keeping the original solution values (Baldwinian memetic algorithms), by default True improvement_selection : ParentSelection, optional Selection method with which to pick the solutions that will be improved with local search, by default None rng : Operator[RNGLike], optional Random number generator, by default None """ def __init__( self, main_strategy: SearchStrategy, local_search_heuristic: SearchStrategy, local_search_depth: int = 1, local_search_frequency: int = 1, keep_improved_solutions: bool = True, improvement_selection: ParentSelection = None, rng: Optional[RNGLike] = None, ): self.main_strategy = main_strategy self.local_search_heuristic = local_search_heuristic self.improvement_selection = improvement_selection self.keep_improved_solutions = keep_improved_solutions self.local_search_counter = 0 super().__init__( main_strategy.initializer, rng=rng, # Forced kwargs local_search_depth=local_search_depth, local_search_frequency=local_search_frequency, ) def _do_local_search(self, offspring: Population, objfunc: ObjectiveFunc) -> Tuple[Population, MaskLike]: """Apply the local search procedure to a set of solutions. Parameters ---------- offspring : Population Population of solutions to improve. Returns ------- Tuple[Population, MaskLike] A pair of the improved complete population and mask indicating which solutions were chosen. """ selected_to_improve = self.improvement_selection(offspring) chosen_idx = self.improvement_selection.last_selection_idx prev_population = selected_to_improve for _ in range(self.params.local_search_depth): population = self.local_search_heuristic.parent_sel.select(prev_population) population = self.local_search_heuristic.operator.evolve(population) population = objfunc.repair_population(population) population = objfunc.calculate_fitness(population) improved_offspring = self.local_search_heuristic.survivor_sel.select(prev_population, population) # Assign improved individuals to the population offspring = offspring.apply_selection(improved_offspring, chosen_idx) prev_population = improved_offspring return offspring, chosen_idx
[docs] def update(self, progress: float): super().update(progress) self.main_strategy.update(progress) self.local_search_heuristic.update(progress) self.improvement_selection.update(progress)
[docs] def step(self, prev_population: Population, objfunc: ObjectiveFunc) -> Population: population = self.main_strategy.parent_sel.select(prev_population) # implicit copy population = self.main_strategy.operator.evolve(population) # Do local search on perturbed individuals population_memetic = copy(population) self.local_search_counter += 1 if self.local_search_counter >= self.params.local_search_frequency: population_memetic, chosen_idx = self._do_local_search(population_memetic, objfunc) if not self.keep_improved_solutions: fitness_obtained = population_memetic.fitness population_memetic = population population_memetic.fitness[chosen_idx] = fitness_obtained[chosen_idx] self.local_search_counter = 0 population_memetic = objfunc.repair_population(population_memetic) population_memetic = objfunc.calculate_fitness(population_memetic) population = self.main_strategy.survivor_sel.select(population=prev_population, offspring=population_memetic) return population
[docs] def get_state(self) -> dict: data = { "main_strategy": self.main_strategy.get_state(), "local_search_heuristic": self.local_search_heuristic.get_state(), "improvement_selection": self.improvement_selection.get_state(), } return data