Source code for pymoo.algorithms.soo.nonconvex.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)
            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 ): """ Parameters ---------- n_elites : int Number of elite individuals n_offsprings : int Number of offsprings to be generated through mating of an elite and a non-elite individual n_mutants : int Number of mutations to be introduced each generation bias : float Bias of an offspring inheriting the allele of its elite parent eliminate_duplicates : bool or class The duplicate elimination is more important if a decoding is used. The duplicate check has to be performed on the decoded variable and not on the real values. Therefore, we recommend passing a DuplicateElimination object. If eliminate_duplicates is simply set to `True`, then duplicates are filtered out whenever the objective values are equal. """ 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__)