Example #1
0
    def __init__(self,
                 problem: FloatProblem,
                 swarm_size: int,
                 uniform_mutation: UniformMutation,
                 non_uniform_mutation: NonUniformMutation,
                 leaders: Optional[BoundedArchive],
                 epsilon: float,
                 termination_criterion: TerminationCriterion,
                 swarm_generator: Generator = store.default_generator,
                 swarm_evaluator: Evaluator = store.default_evaluator):
        """ This class implements the OMOPSO algorithm as described in

        todo Update this reference
        * SMPSO: A new PSO-based metaheuristic for multi-objective optimization

        The implementation of OMOPSO provided in jMetalPy follows the algorithm template described in the algorithm
        templates section of the documentation.

        :param problem: The problem to solve.
        :param swarm_size: Size of the swarm.
        :param leaders: Archive for leaders.
        """
        super(OMOPSO, self).__init__(problem=problem, swarm_size=swarm_size)
        self.swarm_generator = swarm_generator
        self.swarm_evaluator = swarm_evaluator

        self.termination_criterion = termination_criterion
        self.observable.register(termination_criterion)

        self.uniform_mutation = uniform_mutation
        self.non_uniform_mutation = non_uniform_mutation

        self.leaders = leaders

        self.epsilon = epsilon
        self.epsilon_archive = NonDominatedSolutionsArchive(
            EpsilonDominanceComparator(epsilon))

        self.c1_min = 1.5
        self.c1_max = 2.0
        self.c2_min = 1.5
        self.c2_max = 2.0
        self.r1_min = 0.0
        self.r1_max = 1.0
        self.r2_min = 0.0
        self.r2_max = 1.0
        self.weight_min = 0.1
        self.weight_max = 0.5
        self.change_velocity1 = -1
        self.change_velocity2 = -1

        self.dominance_comparator = DominanceComparator()

        self.speed = numpy.zeros(
            (self.swarm_size, self.problem.number_of_variables), dtype=float)
Example #2
0
    def configurar_SPEA2(self):

        algorithm = SPEA2(
                problem=self.problema,
                population_size=self.maxima_poblacion,
                offspring_population_size=self.maxima_poblacion,
                mutation=PolynomialMutation(probability=self.probabilidad, distribution_index=0.20),
                crossover=SBXCrossover(probability=self.probabilidad, distribution_index=20),
                termination_criterion=StoppingByEvaluations(max_evaluations=self.evaluaciones),
                dominance_comparator=DominanceComparator())
        return algorithm
Example #3
0
 def __init__(self,
              problem: Problem[S],
              mutation: Mutation,
              termination_criterion: TerminationCriterion,
              comparator=DominanceComparator()):
     super(LocalSearch, self).__init__()
     self.comparator = comparator
     self.problem = problem
     self.mutation = mutation
     self.termination_criterion = termination_criterion
     self.observable.register(termination_criterion)
Example #4
0
    def __init__(
        self,
        problem: FloatProblem,
        swarm_size: int,
        mutation: Mutation,
        leaders: Optional[BoundedArchive],
        termination_criterion: TerminationCriterion = store.
        default_termination_criteria,
        swarm_generator: Generator = store.default_generator,
        swarm_evaluator: Evaluator = store.default_evaluator,
    ):
        """This class implements the SMPSO algorithm as described in

        * SMPSO: A new PSO-based metaheuristic for multi-objective optimization
        * MCDM 2009. DOI: `<http://dx.doi.org/10.1109/MCDM.2009.4938830/>`_.

        The implementation of SMPSO provided in jMetalPy follows the algorithm template described in the algorithm
        templates section of the documentation.

        :param problem: The problem to solve.
        :param swarm_size: Size of the swarm.
        :param max_evaluations: Maximum number of evaluations/iterations.
        :param mutation: Mutation operator (see :py:mod:`jmetal.operator.mutation`).
        :param leaders: Archive for leaders.
        """
        super(SMPSO, self).__init__(problem=problem, swarm_size=swarm_size)
        self.swarm_generator = swarm_generator
        self.swarm_evaluator = swarm_evaluator
        self.termination_criterion = termination_criterion
        self.observable.register(termination_criterion)
        self.mutation_operator = mutation
        self.leaders = leaders

        self.c1_min = 1.5
        self.c1_max = 2.5
        self.c2_min = 1.5
        self.c2_max = 2.5
        self.r1_min = 0.0
        self.r1_max = 1.0
        self.r2_min = 0.0
        self.r2_max = 1.0
        self.min_weight = 0.1
        self.max_weight = 0.1
        self.change_velocity1 = -1
        self.change_velocity2 = -1

        self.dominance_comparator = DominanceComparator()

        self.speed = numpy.zeros(
            (self.swarm_size, self.problem.number_of_variables), dtype=float)
        self.delta_max, self.delta_min = (
            numpy.empty(problem.number_of_variables),
            numpy.empty(problem.number_of_variables),
        )
Example #5
0
 def configurar_NSGAIII(self):
     
     algorithm = NSGAIII(
             problem=self.problema,
             reference_directions = UniformReferenceDirectionFactory(5, n_points=91),
             population_size=self.maxima_poblacion,
             mutation=PolynomialMutation(probability = self.probabilidad , distribution_index=0.20),
             crossover=SBXCrossover(probability= self.probabilidad, distribution_index=20),
             termination_criterion=StoppingByEvaluations(max_evaluations=self.evaluaciones),
             dominance_comparator=DominanceComparator())
     return algorithm
Example #6
0
    def execute(self, solution_list: List[S]) -> S:
        if solution_list is None:
            raise Exception("The solution list is null")
        elif len(solution_list) == 0:
            raise Exception("The solution is empty")

        result = solution_list[0]
        for solution in solution_list[1:]:
            if DominanceComparator().compare(solution, result) < 0:
                result = solution

        return result
Example #7
0
    def execute(self, individuals: List[Solution]) -> List[Solution]:
        assert len(individuals) == 2

        sol_a, sol_b = individuals
        better_sol, worse_sol = sol_a, sol_b

        if DominanceComparator().compare(sol_a, sol_b):
            better_sol, worse_sol = sol_b, sol_a

        worse_sol.transfer_energy_to(better_sol,
                                     self.exchange_factor * worse_sol.energy)

        return [sol_a, sol_b]
Example #8
0
    def execute(self, front: List[S]) -> S:
        if front is None:
            raise Exception('The front is null')
        elif len(front) == 0:
            raise Exception('The front is empty')

        result = front[0]

        for solution in front[1:]:
            if DominanceComparator().compare(solution, result) < 0:
                result = solution

        return result
Example #9
0
    def __init__(self,
                 problem: DynamicProblem,
                 population_size: int,
                 cr: float,
                 f: float,
                 termination_criterion: TerminationCriterion,
                 k: float = 0.5,
                 population_generator: Generator = store.default_generator,
                 population_evaluator: Evaluator = store.default_evaluator,
                 dominance_comparator: Comparator = DominanceComparator()):
        super(DynamicGDE3, self).__init__(
            problem, population_size, cr, f, termination_criterion, k,
            population_generator, population_evaluator, dominance_comparator)

        self.completed_iterations = 0
Example #10
0
    def compute_ranking(self, solution_list: List[S]):
        # number of solutions dominating solution ith
        dominate_me = [0 for i in range(len(solution_list))]

        # list of solutions dominated by solution ith
        i_dominate = [[] for i in range(len(solution_list))]

        # front[i] contains the list of solutions belonging to front i
        front = [[] for i in range(len(solution_list) + 1)]

        for p in range(len(solution_list) - 1):
            for q in range(p + 1, len(solution_list)):
                dominance_test_result = DominanceComparator().compare(
                    solution_list[p], solution_list[q])
                if dominance_test_result is -1:
                    i_dominate[p].append(q)
                    dominate_me[q] += 1
                elif dominance_test_result is 1:
                    i_dominate[q].append(p)
                    dominate_me[p] += 1

        for i in range(len(solution_list)):
            if dominate_me[i] is 0:
                front[0].append(i)
                solution_list[i].attributes["dominance_ranking"] = 0

        i = 0
        while len(front[i]) != 0:
            i += 1
            for p in front[i - 1]:
                if p <= len(i_dominate):
                    for q in i_dominate[p]:
                        index = q
                        dominate_me[index] -= 1
                        if dominate_me[index] is 0:
                            front[i].append(index)
                            solution_list[index].attributes[
                                "dominance_ranking"] = i

        self.ranked_sublists = [[]] * i
        for j in range(i):
            Q = [0] * len(front[j])
            for k in range(len(front[j])):
                Q[k] = solution_list[front[j][k]]
            self.ranked_sublists[j] = Q

        return self.ranked_sublists
Example #11
0
    def __init__(self,
                 problem: FloatProblem,
                 swarm_size: int,
                 max_evaluations: int,
                 mutation: Mutation[FloatSolution],
                 leaders: BoundedArchive[FloatSolution],
                 observable: Observable = DefaultObservable(),
                 evaluator: Evaluator[FloatSolution] = SequentialEvaluator[
                     FloatSolution]()):
        super(SMPSO, self).__init__()
        self.problem = problem
        self.swarm_size = swarm_size
        self.max_evaluations = max_evaluations
        self.mutation: Mutation[FloatSolution] = mutation
        self.leaders = leaders
        self.observable = observable
        self.evaluator = evaluator

        self.evaluations = 0

        self.c1_min = 1.5
        self.c1_max = 2.5
        self.c2_min = 1.5
        self.c2_max = 2.5

        self.min_weight = 0.1
        self.max_weight = 0.1

        self.change_velocity1 = -1
        self.change_velocity2 = -1

        self.dominance_comparator = DominanceComparator()

        self.speed = numpy.zeros(
            (self.swarm_size, self.problem.number_of_variables), dtype=float)
        self.delta_max = numpy.empty(problem.number_of_variables)
        self.delta_min = numpy.empty(problem.number_of_variables)
        for i in range(problem.number_of_variables):
            self.delta_max[i] = (self.problem.upper_bound[i] -
                                 self.problem.lower_bound[i]) / 2.0

        self.delta_min = -1.0 * self.delta_max
Example #12
0
    def __init__(self,
                 problem: Problem,
                 population_size: int,
                 offspring_population_size: int,
                 mutation: Mutation,
                 crossover: Crossover,
                 selection: Selection,
                 termination_criterion: TerminationCriterion,
                 population_generator: Generator = store.default_generator,
                 population_evaluator: Evaluator = store.default_evaluator,
                 dominance_comparator: Comparator = DominanceComparator()):
        """  NSGA-II implementation as described in

        * K. Deb, A. Pratap, S. Agarwal and T. Meyarivan, "A fast and elitist
          multiobjective genetic algorithm: NSGA-II," in IEEE Transactions on Evolutionary Computation,
          vol. 6, no. 2, pp. 182-197, Apr 2002. doi: 10.1109/4235.996017

        NSGA-II is a genetic algorithm (GA), i.e. it belongs to the evolutionary algorithms (EAs)
        family. The implementation of NSGA-II provided in jMetalPy follows the evolutionary
        algorithm template described in the algorithm module (:py:mod:`jmetal.core.algorithm`).

        .. note:: A steady-state version of this algorithm can be run by setting the offspring size to 1 and the mating pool size to 2.

        :param problem: The problem to solve.
        :param population_size: Size of the population.
        :param mutation: Mutation operator (see :py:mod:`jmetal.operator.mutation`).
        :param crossover: Crossover operator (see :py:mod:`jmetal.operator.crossover`).
        :param selection: Selection operator (see :py:mod:`jmetal.operator.selection`).
        """
        super(NSGAII, self).__init__(
            problem=problem,
            population_size=population_size,
            offspring_population_size=offspring_population_size,
            mutation=mutation,
            crossover=crossover,
            selection=selection,
            termination_criterion=termination_criterion,
            population_evaluator=population_evaluator,
            population_generator=population_generator)
        self.dominance_comparator = dominance_comparator
Example #13
0
    def __init__(self,
                 problem: Problem,
                 population_size: int,
                 cr: float,
                 f: float,
                 termination_criterion: TerminationCriterion,
                 k: float = 0.5,
                 population_generator: Generator = store.default_generator,
                 population_evaluator: Evaluator = store.default_evaluator,
                 dominance_comparator: Comparator = DominanceComparator()):
        super(GDE3, self).__init__(problem=problem,
                                   population_size=population_size,
                                   offspring_population_size=population_size)
        self.dominance_comparator = dominance_comparator
        self.selection_operator = DifferentialEvolutionSelection()
        self.crossover_operator = DifferentialEvolutionCrossover(cr, f, k)

        self.population_generator = population_generator
        self.population_evaluator = population_evaluator

        self.termination_criterion = termination_criterion
        self.observable.register(termination_criterion)
if __name__ == "__main__":
    problem = Srinivas()
    problem.reference_front = read_solutions(
        filename="resources/reference_front/Srinivas.pf")

    max_evaluations = 25000
    algorithm = NSGAII(
        problem=problem,
        population_size=100,
        offspring_population_size=100,
        mutation=PolynomialMutation(probability=1.0 /
                                    problem.number_of_variables,
                                    distribution_index=20),
        crossover=SBXCrossover(probability=1.0, distribution_index=20),
        termination_criterion=StoppingByEvaluations(
            max_evaluations=max_evaluations),
        dominance_comparator=DominanceComparator(),
    )

    algorithm.run()
    front = algorithm.get_result()

    # Save results to file
    print_function_values_to_file(front, "FUN." + algorithm.label)
    print_variables_to_file(front, "VAR." + algorithm.label)

    print(f"Algorithm: {algorithm.get_name()}")
    print(f"Problem: {problem.get_name()}")
    print(f"Computing time: {algorithm.total_computing_time}")
Example #15
0
 def __init__(self, comparator: Comparator = DominanceComparator()):
     super(BinaryTournamentSelection, self).__init__()
     self.comparator = comparator
Example #16
0
 def __init__(self, max_population_size: int, dominance_comparator: Comparator = DominanceComparator()):
     super(RankingAndCrowdingDistanceSelection, self).__init__()
     self.max_population_size = max_population_size
     self.dominance_comparator = dominance_comparator
Example #17
0
 def setUp(self):
     self.comparator = DominanceComparator()
Example #18
0
 def __init__(self, comparator: Comparator = DominanceComparator()):
     super(StrengthRanking, self).__init__(comparator)
Example #19
0
 def __init__(self, comparator: Comparator = DominanceComparator()):
     super(FastNonDominatedRanking, self).__init__(comparator)
Example #20
0
 def default_comparator(self):
     return DominanceComparator()
Example #21
0
 def __init__(self, comparator: Comparator = DominanceComparator()):
     super(Ranking, self).__init__()
     self.number_of_comparisons = 0
     self.ranked_sublists = []
     self.comparator = comparator
Example #22
0
    def run(self) -> List[S]:
        pool_1_size = self.population_size
        pool_2_size = self.population_size

        selection_operator_1 = BinaryTournamentSelection()
        crossover_operator_1 = IntegerSBXCrossover(1.0, 20.0)
        mutation_operator_1 = IntegerPolynomialMutation(
            1.0 / self.problem.number_of_variables, 20.0)
        selection_operator_2 = DifferentialEvolutionSelection()
        crossover_operator_2 = DifferentialEvolutionCrossover(0.2, 0.5, 0.5)

        dominance = DominanceComparator()

        max_iterations = self.max_iterations
        iterations = 0

        parent_1: List[IntegerSolution] = [None, None]

        generational_hv: List[float] = []

        current_gen = 0
        """Create the initial subpopulation pools and evaluate them"""
        pool_1: List[IntegerSolution] = []
        for i in range(pool_1_size):
            pool_1.append(self.problem.create_solution())
            pool_1[i] = self.problem.evaluate(pool_1[i])

        pool_2: List[IntegerSolution] = []
        for i in range(pool_2_size):
            pool_2.append(self.problem.create_solution())
            pool_2[i] = self.problem.evaluate(pool_2[i])

        evaluations = pool_1_size + pool_2_size

        mix = self.mix_interval

        problem = self.problem

        h = HyperVolume(reference_point=[1] *
                        self.problem.number_of_objectives)

        initial_population = True
        """The main evolutionary cycle"""
        while iterations < max_iterations:
            combi: List[IntegerSolution] = []
            if not initial_population:
                offspring_pop_1: List[IntegerSolution] = []
                offspring_pop_2: List[IntegerSolution] = []
                """Evolve pool 1"""
                for i in range(pool_1_size):
                    parent_1[0] = selection_operator_1.execute(pool_1)
                    parent_1[1] = selection_operator_1.execute(pool_1)

                    child_1: IntegerSolution = crossover_operator_1.execute(
                        parent_1)[0]
                    child_1 = mutation_operator_1.execute(child_1)

                    child_1 = problem.evaluate(child_1)
                    evaluations += 1

                    offspring_pop_1.append(child_1)
                """Evolve pool 2"""
                for i in range(pool_2_size):
                    parent_2: List[
                        IntegerSolution] = selection_operator_2.execute(pool_2)

                    crossover_operator_2.current_individual = pool_2[i]
                    child_2 = crossover_operator_2.execute(parent_2)
                    child_2 = problem.evaluate(child_2[0])

                    evaluations += 1

                    result = dominance.compare(pool_2[i], child_2)

                    if result == -1:
                        offspring_pop_2.append(pool_2[i])
                    elif result == 1:
                        offspring_pop_2.append(child_2)
                    else:
                        offspring_pop_2.append(child_2)
                        offspring_pop_2.append(pool_2[i])

                ind_1 = pool_1[random.randint(0, pool_1_size - 1)]
                ind_2 = pool_2[random.randint(0, pool_2_size - 1)]

                offspring_pop_1.append(ind_1)
                offspring_pop_2.append(ind_2)

                offspring_pop_1.extend(pool_1)
                pool_1 = self.r.replace(offspring_pop_1[:pool_1_size],
                                        offspring_pop_1[pool_1_size:])

                pool_2 = self.r.replace(offspring_pop_2[:pool_2_size],
                                        offspring_pop_2[pool_2_size:])

                mix -= 1
                if mix == 0:
                    """Time to perform fitness sharing"""
                    mix = self.mix_interval
                    combi = combi + pool_1 + pool_2
                    # print("Combi size: ", len(combi))
                    """pool1size/10"""

                    combi = self.r.replace(
                        combi[:int(pool_1_size / 10)],
                        combi[int(pool_1_size / 10):len(combi)],
                    )
                    """
                    print(
                        "Sizes: ",
                        len(pool_1) + len(combi),
                        len(pool_2) + len(combi),
                        "\n",
                    )
                    """
                    pool_1 = self.r.replace(pool_1, combi)

                    pool_2 = self.r.replace(pool_2, combi)

            if initial_population:
                initial_population = False

            iterations += 1
            print("Iterations: ", str(iterations))
            """
            hval_1 = h.compute([s.objectives for s in pool_1])
            hval_2 = h.compute([s.objectives for s in pool_2])
            print("hval_1: ", str(hval_1))
            print("hval_2: ", str(hval_2), "\n")
            """

            new_gen = int(evaluations / self.report_interval)
            if new_gen > current_gen:
                combi = combi + pool_1 + pool_2

                combi = self.r.replace(combi[:(2 * pool_1_size)],
                                       combi[(2 * pool_1_size):])

                hval = h.compute([s.objectives for s in combi])
                for i in range(current_gen, new_gen, 1):
                    generational_hv.append(hval)

                current_gen = new_gen
        """#Write runtime generational HV to file"""
        """Return the first non dominated front"""
        combi_ini: List[IntegerSolution] = []
        combi_ini.extend(pool_1)
        combi_ini.extend(pool_2)
        combi_ini = self.r.replace(
            combi_ini[:pool_1_size + pool_2_size],
            combi_ini[pool_1_size + pool_2_size:],
        )
        return combi_ini
Example #23
0
 def __init__(self):
     super(NonDominatedSolutionListArchive, self).__init__()
     self.comparator = DominanceComparator()
 def __init__(self, dominance_comparator: Comparator = DominanceComparator()):
     super(NonDominatedSolutionsArchive, self).__init__()
     self.comparator = dominance_comparator
Example #25
0
    def run(self) -> List[S]:
        # selection operator 1
        selection_operator_1 = BinaryTournamentSelection()
        # selection operator 2
        selection_operator_2 = DifferentialEvolutionSelection()
        # crossover operator 1
        crossover_operator_1 = SBXCrossover(1.0, 20.0)
        # crossover operator 2
        crossover_operator_2 = DifferentialEvolutionCrossover(0.2, 0.5, 0.5)
        # crossover operator 3
        crossover_operator_3 = DifferentialEvolutionCrossover(1.0, 0.5, 0.5)
        # mutation operator 1
        mutation_operator_1 = PolynomialMutation(
            1.0 / self.problem.number_of_variables, 20.0)
        # dominance comparator
        dominance = DominanceComparator()

        # array that stores the "generational" HV quality
        generational_hv: List[float] = []

        parent_1: List[FloatSolution] = [None, None]
        parent_2: List[FloatSolution] = []
        parent_3: List[FloatSolution] = []

        # initialize some local and global variables
        pool_1: List[FloatSolution] = []
        pool_2: List[FloatSolution] = []

        # size of elite subset used for fitness sharing between subpopulations
        nrOfDirectionalSolutionsToEvolve = int(self.population_size / 5)
        # subpopulation 1
        pool_1_size = int(self.population_size -
                          (nrOfDirectionalSolutionsToEvolve / 2))
        # subpopulation 2
        pool_2_size = int(self.population_size -
                          (nrOfDirectionalSolutionsToEvolve / 2))

        print(
            str(pool_1_size) + " - " + str(nrOfDirectionalSolutionsToEvolve) +
            " - " + str(self.mix_interval))

        evaluations = 0
        current_gen = 0
        directionalArchiveSize = 2 * self.population_size
        weights = self.__create_uniform_weights(
            directionalArchiveSize, self.problem.number_of_objectives)

        directionalArchive = self.__create_directional_archive(weights)
        neighbourhoods = self.__create_neighbourhoods(directionalArchive,
                                                      self.population_size)

        nrOfReplacements = 1
        iniID = 0

        # Create the initial pools
        # pool1
        pool_1: List[FloatSolution] = []
        for _ in range(pool_1_size):
            new_solution = self.problem.create_solution()
            new_solution = self.problem.evaluate(new_solution)
            evaluations += 1
            pool_1.append(new_solution)

            self.__update_extreme_values(new_solution)
            dr = directionalArchive[iniID]
            dr.curr_sol = new_solution
            iniID += 1
        # pool2
        pool_2: List[FloatSolution] = []
        for _ in range(pool_2_size):
            new_solution = self.problem.create_solution()
            new_solution = self.problem.evaluate(new_solution)
            evaluations += 1
            pool_2.append(new_solution)

            self.__update_extreme_values(new_solution)
            dr = directionalArchive[iniID]
            dr.curr_sol = new_solution
            iniID += 1
        # directional archive initialization
        pool_A: List[FloatSolution] = []
        while iniID < directionalArchiveSize:
            new_solution = self.problem.create_solution()
            new_solution = self.problem.evaluate(new_solution)
            evaluations += 1
            pool_A.append(new_solution)

            self.__update_extreme_values(new_solution)
            dr = directionalArchive[iniID]
            dr.curr_sol = new_solution
            iniID += 1

        mix = self.mix_interval
        h = HyperVolume(reference_point=[1] *
                        self.problem.number_of_objectives)

        insertionRate: List[float] = [0, 0, 0]
        bonusEvals: List[int] = [0, 0, nrOfDirectionalSolutionsToEvolve]
        testRun = True

        # record the generational HV of the initial population
        combiAll: List[FloatSolution] = []
        cGen = int(evaluations / self.report_interval)
        if cGen > 0:
            combiAll = pool_1 + pool_2 + pool_A
            combiAll = self.r.replace(
                combiAll[:pool_1_size + pool_2_size],
                combiAll[pool_1_size + pool_2_size:],
            )
            hval = h.compute([s.objectives for s in combiAll])
            for _ in range(cGen):
                generational_hv.append(hval)
            current_gen = cGen

        # the main loop of the algorithm
        while evaluations < self.max_evaluations:
            offspringPop1: List[FloatSolution] = []
            offspringPop2: List[FloatSolution] = []
            offspringPop3: List[FloatSolution] = []

            dirInsertPool1: List[FloatSolution] = []
            dirInsertPool2: List[FloatSolution] = []
            dirInsertPool3: List[FloatSolution] = []

            # evolve pool1 - using SPEA2 evolutionary model
            nfe: int = 0
            while nfe < (pool_1_size + bonusEvals[0]):
                parent_1[0] = selection_operator_1.execute(pool_1)
                parent_1[1] = selection_operator_1.execute(pool_1)

                child1a: FloatSolution = crossover_operator_1.execute(
                    parent_1)[0]
                child1a = mutation_operator_1.execute(child1a)

                child1a = self.problem.evaluate(child1a)
                evaluations += 1
                nfe += 1

                offspringPop1.append(child1a)
                dirInsertPool1.append(child1a)

            # evolve pool2 - using DEMO SP evolutionary model
            i: int = 0
            unselectedIDs: List[int] = []
            for ID in range(len(pool_2)):
                unselectedIDs.append(ID)

            nfe = 0
            while nfe < (pool_2_size + bonusEvals[1]):
                index = random.randint(0, len(unselectedIDs) - 1)
                i = unselectedIDs[index]
                unselectedIDs.pop(index)

                parent_2 = selection_operator_2.execute(pool_2)

                crossover_operator_2.current_individual = pool_2[i]
                child2 = crossover_operator_2.execute(parent_2)
                child2 = self.problem.evaluate(child2[0])

                evaluations += 1
                nfe += 1

                result = dominance.compare(pool_2[i], child2)

                if result == -1:  # solution i dominates child
                    offspringPop2.append(pool_2[i])
                elif result == 1:  # child dominates
                    offspringPop2.append(child2)
                else:  # the two solutions are non-dominated
                    offspringPop2.append(child2)
                    offspringPop2.append(pool_2[i])

                dirInsertPool2.append(child2)

                if len(unselectedIDs) == 0:
                    for ID in range(len(pool_2)):
                        unselectedIDs.append(random.randint(
                            0,
                            len(pool_2) - 1))

            # evolve pool3 - Directional Decomposition DE/rand/1/bin
            IDs = self.__compute_neighbourhood_Nfe_since_last_update(
                neighbourhoods, directionalArchive,
                nrOfDirectionalSolutionsToEvolve)

            nfe = 0
            for j in range(len(IDs)):
                if nfe < bonusEvals[2]:
                    nfe += 1
                else:
                    break

                cID = IDs[j]

                chosenSol: FloatSolution = None
                if directionalArchive[cID].curr_sol != None:
                    chosenSol = directionalArchive[cID].curr_sol
                else:
                    chosenSol = pool_1[0]
                    print("error!")

                parent_3: List[FloatSolution] = [None, None, None]

                r1 = random.randint(0, len(neighbourhoods[cID]) - 1)
                r2 = random.randint(0, len(neighbourhoods[cID]) - 1)
                r3 = random.randint(0, len(neighbourhoods[cID]) - 1)
                while r2 == r1:
                    r2 = random.randint(0, len(neighbourhoods[cID]) - 1)
                while r3 == r1 or r3 == r2:
                    r3 = random.randint(0, len(neighbourhoods[cID]) - 1)

                parent_3[0] = directionalArchive[r1].curr_sol
                parent_3[1] = directionalArchive[r2].curr_sol
                parent_3[2] = directionalArchive[r3].curr_sol

                crossover_operator_3.current_individual = chosenSol
                child3 = crossover_operator_3.execute(parent_3)[0]
                child3 = mutation_operator_1.execute(child3)

                child3 = self.problem.evaluate(child3)
                evaluations += 1

                dirInsertPool3.append(child3)

            # compute directional improvements
            # pool1
            improvements = 0
            for j in range(len(dirInsertPool1)):
                testSol = dirInsertPool1[j]
                self.__update_extreme_values(testSol)
                improvements += self.__update_neighbourhoods(
                    directionalArchive, testSol, nrOfReplacements)
            insertionRate[0] += (1.0 * improvements) / len(dirInsertPool1)

            # pool2
            improvements = 0
            for j in range(len(dirInsertPool2)):
                testSol = dirInsertPool2[j]
                self.__update_extreme_values(testSol)
                improvements += self.__update_neighbourhoods(
                    directionalArchive, testSol, nrOfReplacements)
            insertionRate[1] += (1.0 * improvements) / len(dirInsertPool2)

            # pool3
            improvements = 0
            for j in range(len(dirInsertPool3)):
                testSol = dirInsertPool3[j]
                self.__update_extreme_values(testSol)
                improvements += self.__update_neighbourhoods(
                    directionalArchive, testSol, nrOfReplacements)
            # on java, dividing a floating number by 0, returns NaN
            # on python, dividing a floating number by 0, returns an exception
            if len(dirInsertPool3) == 0:
                insertionRate[2] = None
            else:
                insertionRate[2] += (1.0 * improvements) / len(dirInsertPool3)

            for dr in directionalArchive:
                offspringPop3.append(dr.curr_sol)

            offspringPop1 = offspringPop1 + pool_1
            pool_1 = self.r.replace(offspringPop1[:pool_1_size],
                                    offspringPop1[pool_1_size:])
            pool_2 = self.r.replace(offspringPop2[:pool_2_size],
                                    offspringPop2[pool_2_size:])

            combi: List[FloatSolution] = []
            mix -= 1

            if mix == 0:
                mix = self.mix_interval
                combi = combi + pool_1 + pool_2 + offspringPop3
                print("Combi size: " + str(len(combi)))

                combi = self.r.replace(
                    combi[:nrOfDirectionalSolutionsToEvolve],
                    combi[nrOfDirectionalSolutionsToEvolve:],
                )

                insertionRate[0] /= self.mix_interval
                insertionRate[1] /= self.mix_interval
                if insertionRate[2] != None:
                    insertionRate[2] /= self.mix_interval
                """
                print(
                    "Insertion rates: "
                    + str(insertionRate[0])
                    + " - "
                    + str(insertionRate[1])
                    + " - "
                    + str(insertionRate[2])
                    + " - Test run:"
                    + str(testRun)
                )
                """
                if testRun:
                    if (insertionRate[0] > insertionRate[1]) and (
                            insertionRate[0] > insertionRate[2]):
                        print("SPEA2 win - bonus run!")
                        bonusEvals[0] = nrOfDirectionalSolutionsToEvolve
                        bonusEvals[1] = 0
                        bonusEvals[2] = 0
                    if (insertionRate[1] > insertionRate[0]) and (
                            insertionRate[1] > insertionRate[2]):
                        print("DE win - bonus run!")
                        bonusEvals[0] = 0
                        bonusEvals[1] = nrOfDirectionalSolutionsToEvolve
                        bonusEvals[2] = 0
                    if (insertionRate[2] > insertionRate[0]) and (
                            insertionRate[2] > insertionRate[1]):
                        print("Directional win - no bonus!")
                        bonusEvals[0] = 0
                        bonusEvals[1] = 0
                        bonusEvals[2] = nrOfDirectionalSolutionsToEvolve
                else:
                    print("Test run - no bonus!")
                    bonusEvals[0] = 0
                    bonusEvals[1] = 0
                    bonusEvals[2] = nrOfDirectionalSolutionsToEvolve

                testRun = not testRun

                insertionRate[0] = 0.0
                insertionRate[1] = 0.0
                insertionRate[2] = 0.0

                pool_1 = pool_1 + combi
                pool_2 = pool_2 + combi
                print("Sizes: " + str(len(pool_1)) + " " + str(len(pool_2)))

                pool_1 = self.r.replace(pool_1[:pool_1_size],
                                        pool_1[pool_1_size:])
                pool_2 = self.r.replace(pool_2[:pool_2_size],
                                        pool_2[pool_2_size:])

                self.__clear_Nfe_history(directionalArchive)

            hVal1 = h.compute([s.objectives for s in pool_1])
            hVal2 = h.compute([s.objectives for s in pool_2])
            hVal3 = h.compute([s.objectives for s in offspringPop3])

            newGen = int(evaluations / self.report_interval)

            if newGen > current_gen:
                print("Hypervolume: " + str(newGen) + " - " + str(hVal1) +
                      " - " + str(hVal2) + " - " + str(hVal3))
                combi = combi + pool_1 + pool_2 + offspringPop3
                combi = self.r.replace(combi[:self.population_size * 2],
                                       combi[self.population_size * 2:])
                hval = h.compute([s.objectives for s in combi])
                for j in range(current_gen, newGen):
                    generational_hv.append(hval)
                current_gen = newGen

        # return the final combined non-dominated set of maximum size = (populationSize * 2)
        combiAll: List[FloatSolution] = []
        combiAll = combiAll + pool_1 + pool_2 + pool_A
        combiAll = self.r.replace(combiAll[:self.population_size * 2],
                                  combiAll[self.population_size * 2:])
        return combiAll