Source code for pymoo.algorithms.soo.nonconvex.brkga

"""Biased Random Key Genetic Algorithm (BRKGA)."""

import numpy as np

from pymoo.algorithms.base.genetic import GeneticAlgorithm
from pymoo.algorithms.soo.nonconvex.ga import FitnessSurvival
from pymoo.core.duplicate import DefaultDuplicateElimination, DuplicateElimination
from pymoo.core.population import Population
from pymoo.core.selection import Selection
from pymoo.core.survival import Survival
from pymoo.docs import parse_doc_string
from pymoo.operators.crossover.binx import BinomialCrossover
from pymoo.operators.mutation.nom import NoMutation
from pymoo.operators.sampling.rnd import FloatRandomSampling
from pymoo.termination.default import DefaultSingleObjectiveTermination
from pymoo.util.display.single import SingleObjectiveOutput
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting


# =========================================================================================================
# Implementation
# =========================================================================================================


class EliteSurvival(Survival):
    def __init__(self, n_elites, eliminate_duplicates=None):
        super().__init__(False)
        self.n_elites = n_elites
        self.eliminate_duplicates = eliminate_duplicates

    def _do(self, problem, pop, n_survive=None, algorithm=None, **kwargs):

        if isinstance(self.eliminate_duplicates, bool) and self.eliminate_duplicates:
            pop = DefaultDuplicateElimination(func=lambda p: p.get("F")).do(pop)

        elif isinstance(self.eliminate_duplicates, DuplicateElimination):
            _, no_candidates, candidates = DefaultDuplicateElimination(
                func=lambda pop: pop.get("F")
            ).do(pop, return_indices=True)
            _, _, is_duplicate = self.eliminate_duplicates.do(
                pop[candidates],
                pop[no_candidates],
                return_indices=True,
                to_itself=False,
            )
            elim = set(np.array(candidates)[is_duplicate])
            pop = pop[[k for k in range(len(pop)) if k not in elim]]

        if problem.n_obj == 1:
            pop = FitnessSurvival().do(problem, pop, n_survive=len(pop))
            elites = pop[: self.n_elites]
            non_elites = pop[self.n_elites :]
        else:
            I = NonDominatedSorting().do(pop.get("F"), only_non_dominated_front=True)  # noqa: E741
            elites = pop[I]
            non_elites = pop[[k for k in range(len(pop)) if k not in I]]

        elites.set("type", ["elite"] * len(elites))
        non_elites.set("type", ["non_elite"] * len(non_elites))

        return pop


class EliteBiasedSelection(Selection):
    def _do(self, problem, pop, n_select, n_parents, **kwargs):
        random_state = kwargs.get("random_state")
        _type = pop.get("type")
        elites = np.where(_type == "elite")[0].astype(int)
        non_elites = np.where(_type == "non_elite")[0].astype(int)

        # if through duplicate elimination no non-elites exist
        if len(non_elites) == 0:
            non_elites = elites

        # do the mating selection - always one elite and one non-elites
        s_elite = random_state.choice(elites, size=n_select)
        s_non_elite = random_state.choice(non_elites, size=n_select)

        return np.column_stack([s_elite, s_non_elite])


[docs] class BRKGA(GeneticAlgorithm): def __init__( self, n_elites=200, n_offsprings=700, n_mutants=100, bias=0.7, sampling=FloatRandomSampling(), survival=None, output=SingleObjectiveOutput(), eliminate_duplicates=False, **kwargs, ): """Biased Random Key Genetic Algorithm. Args: n_elites: Number of elite individuals. n_offsprings: Number of offsprings generated through mating. n_mutants: Number of mutations introduced each generation. bias: Bias of an offspring inheriting from its elite parent. sampling: Sampling strategy for initial population. survival: Survival selection strategy. output: Output display configuration. eliminate_duplicates: If True, duplicates are filtered by objective values. If a DuplicateElimination object, uses it for custom duplicate checking. **kwargs: Additional keyword arguments. """ if survival is None: survival = EliteSurvival( n_elites, eliminate_duplicates=eliminate_duplicates ) super().__init__( pop_size=n_elites + n_offsprings + n_mutants, n_offsprings=n_offsprings, sampling=sampling, selection=EliteBiasedSelection(), crossover=BinomialCrossover(bias, prob=1.0, n_offsprings=1), mutation=NoMutation(), survival=survival, output=output, eliminate_duplicates=True, advance_after_initial_infill=True, **kwargs, ) self.n_elites = n_elites self.n_mutants = n_mutants self.bias = bias self.termination = DefaultSingleObjectiveTermination() def _infill(self): pop = self.pop # actually do the mating given the elite selection and biased crossover off = self.mating.do( self.problem, pop, n_offsprings=self.n_offsprings, algorithm=self, random_state=self.random_state, ) # create the mutants randomly to fill the population with mutants = FloatRandomSampling().do( self.problem, self.n_mutants, algorithm=self, random_state=self.random_state ) # evaluate all the new solutions return Population.merge(off, mutants) def _advance(self, infills=None, **kwargs): pop = self.pop # get all the elites from the current population elites = np.where(pop.get("type") == "elite")[0] # finally merge everything together and sort by fitness pop = Population.merge(pop[elites], infills) # the do survival selection - set the elites for the next round self.pop = self.survival.do( self.problem, pop, n_survive=len(pop), algorithm=self )
parse_doc_string(BRKGA.__init__)