예제 #1
0
    def update_external_archive(self):
        feasible_solutions = []
        for solution in self.solutions:
            if is_feasible(solution):
                feasible_solutions.append(copy.deepcopy(solution))

        if len(feasible_solutions) > 0:
            feasible_solutions = feasible_solutions + self.archive
            ranking = FastNonDominatedRanking()
            ranking.compute_ranking(feasible_solutions)

            first_rank_solutions = ranking.get_subfront(0)
            if len(first_rank_solutions) <= self.population_size:
                self.archive = []
                for solution in first_rank_solutions:
                    self.archive.append(copy.deepcopy(solution))
            else:
                crowding_distance = CrowdingDistance()
                while len(first_rank_solutions) > self.population_size:
                    crowding_distance.compute_density_estimator(
                        first_rank_solutions)
                    first_rank_solutions = sorted(
                        first_rank_solutions,
                        key=lambda x: x.attributes['crowding_distance'],
                        reverse=True)
                    first_rank_solutions.pop()

                self.archive = []
                for solution in first_rank_solutions:
                    self.archive.append(copy.deepcopy(solution))
예제 #2
0
    def execute(self, front: List[S]) -> List[S]:
        if front is None:
            raise Exception('The front is null')
        elif len(front) == 0:
            raise Exception('The front is empty')

        ranking = FastNonDominatedRanking(self.dominance_comparator)
        crowding_distance = CrowdingDistance()
        ranking.compute_ranking(front)

        ranking_index = 0
        new_solution_list = []

        while len(new_solution_list) < self.max_population_size:
            if len(ranking.get_subfront(ranking_index)) < (self.max_population_size - len(new_solution_list)):
                new_solution_list = new_solution_list + ranking.get_subfront(ranking_index)
                ranking_index += 1
            else:
                subfront = ranking.get_subfront(ranking_index)
                crowding_distance.compute_density_estimator(subfront)
                sorted_subfront = sorted(subfront, key=lambda x: x.attributes['crowding_distance'], reverse=True)
                for i in range((self.max_population_size - len(new_solution_list))):
                    new_solution_list.append(sorted_subfront[i])

        return new_solution_list
예제 #3
0
    def __init__(
        self,
        problem: Problem,
        population_size: int,
        neighborhood: Neighborhood,
        archive: BoundedArchive,
        mutation: Mutation,
        crossover: Crossover,
        selection: Selection = BinaryTournamentSelection(
            MultiComparator([
                FastNonDominatedRanking.get_comparator(),
                CrowdingDistance.get_comparator()
            ])),
        termination_criterion: TerminationCriterion = store.
        default_termination_criteria,
        population_generator: Generator = store.default_generator,
        population_evaluator: Evaluator = store.default_evaluator,
        dominance_comparator: Comparator = store.default_comparator,
    ):
        """
        MOCEll implementation as described in:

        :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(MOCell, self).__init__(
            problem=problem,
            population_size=population_size,
            offspring_population_size=1,
            mutation=mutation,
            crossover=crossover,
            selection=selection,
            termination_criterion=termination_criterion,
            population_evaluator=population_evaluator,
            population_generator=population_generator,
        )
        self.dominance_comparator = dominance_comparator
        self.neighborhood = neighborhood
        self.archive = archive
        self.current_individual = 0
        self.current_neighbors = []

        self.comparator = MultiComparator([
            FastNonDominatedRanking.get_comparator(),
            CrowdingDistance.get_comparator()
        ])
예제 #4
0
 def __init__(
     self,
     problem: DynamicProblem[S],
     population_size: int,
     offspring_population_size: int,
     mutation: Mutation,
     crossover: Crossover,
     selection: Selection = BinaryTournamentSelection(
         MultiComparator([
             FastNonDominatedRanking.get_comparator(),
             CrowdingDistance.get_comparator()
         ])),
     termination_criterion: TerminationCriterion = store.
     default_termination_criteria,
     population_generator: Generator = store.default_generator,
     population_evaluator: Evaluator = store.default_evaluator,
     dominance_comparator: DominanceComparator = DominanceComparator()):
     super(DynamicNSGAII, self).__init__(
         problem=problem,
         population_size=population_size,
         offspring_population_size=offspring_population_size,
         mutation=mutation,
         crossover=crossover,
         selection=selection,
         population_evaluator=population_evaluator,
         population_generator=population_generator,
         termination_criterion=termination_criterion,
         dominance_comparator=dominance_comparator)
     self.completed_iterations = 0
     self.start_computing_time = 0
     self.total_computing_time = 0
예제 #5
0
    def replacement(self, population: List[S], offspring_population: List[S]) -> List[List[S]]:
        result = self.dominance_comparator.compare(population[self.current_individual], offspring_population[0])

        if result == 1:  # the offspring individual dominates the current one
            population[self.current_individual] = offspring_population[0]
            self.archive.add(offspring_population[0])
        elif result == 0:  # the offspring and current individuals are non-dominated
            new_individual = offspring_population[0]

            self.current_neighbors.append(new_individual)

            ranking: Ranking = FastNonDominatedRanking()
            ranking.compute_ranking(self.current_neighbors)

            density_estimator: DensityEstimator = CrowdingDistance()
            for i in range(ranking.get_number_of_subfronts()):
                density_estimator.compute_density_estimator(ranking.get_subfront(i))

            self.current_neighbors.sort(key=cmp_to_key(self.comparator.compare))
            worst_solution = self.current_neighbors[-1]

            self.archive.add(new_individual)
            if worst_solution != new_individual:
                population[self.current_individual] = new_individual

        return population
예제 #6
0
    def __init__(
        self,
        problem: Problem,
        population_size: int,
        mutation: Mutation,
        crossover: Crossover,
        number_of_cores: int,
        client,
        selection: Selection = BinaryTournamentSelection(
            MultiComparator([
                FastNonDominatedRanking.get_comparator(),
                CrowdingDistance.get_comparator()
            ])),
        termination_criterion: TerminationCriterion = store.
        default_termination_criteria,
        dominance_comparator: DominanceComparator = DominanceComparator()):
        super(DistributedNSGAII, self).__init__()
        self.problem = problem
        self.population_size = population_size
        self.mutation_operator = mutation
        self.crossover_operator = crossover
        self.selection_operator = selection
        self.dominance_comparator = dominance_comparator

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

        self.number_of_cores = number_of_cores
        self.client = client
예제 #7
0
    def __init__(
        self,
        problem: Problem,
        dominance_comparator: Comparator = store.default_comparator,
        max_evaluations: int = 250,
        individual_population_size: int = 100,
        report_interval: int = 100,
        dataDirectory: str = "./decmopy/decmo2/weigths",
    ):
        super().__init__()
        self.problem = problem
        self.population_size = individual_population_size
        self.max_evaluations = max_evaluations
        self.report_interval = report_interval
        self.dataDirectory = dataDirectory
        self.mix_interval = 1
        """Replacement"""
        ranking = FastNonDominatedRanking(dominance_comparator)
        density_estimator = CrowdingDistance()
        self.r = RankingAndDensityEstimatorReplacement(
            ranking, density_estimator, RemovalPolicyType.SEQUENTIAL)

        self.MIN_VALUES = 0
        self.MAX_VALUES = 1
        min_values: List[float] = []
        max_values: List[float] = []
        for _ in range(problem.number_of_objectives):
            min_values.append(sys.float_info.max)
            max_values.append(sys.float_info.min)
        self.extreme_values: List[List[float]] = []
        self.extreme_values.append(min_values)
        self.extreme_values.append(max_values)
예제 #8
0
 def __init__(self, maximum_size: int, reference_point: List[float]):
     super(CrowdingDistanceArchiveWithReferencePoint, self).__init__(
         maximum_size=maximum_size,
         reference_point=reference_point,
         comparator=SolutionAttributeComparator("crowding_distance",
                                                lowest_is_best=False),
         density_estimator=CrowdingDistance())
예제 #9
0
    def replacement(self, population: List[S],
                    offspring_population: List[S]) -> List[List[S]]:
        """ This method joins the current and offspring populations to produce the population of the next generation
        by applying the ranking and crowding distance selection.

        :param population: Parent population.
        :param offspring_population: Offspring population.
        :return: New population after ranking and crowding distance selection is applied.
        """
        ranking = FastNonDominatedRanking(self.dominance_comparator)
        density_estimator = CrowdingDistance()

        r = RankingAndDensityEstimatorReplacement(ranking, density_estimator,
                                                  RemovalPolicyType.ONE_SHOT)
        solutions = r.replace(population, offspring_population)

        front = self.get_result()

        if type(front) is not list:
            solutions = [front]

        for solution in front:
            print(solution.variables[0])

        for solution in front:
            print(str(front.index(solution)) + ": ",
                  sep='  ',
                  end='',
                  flush=True)
            print(solution.objectives, sep='  ', end='', flush=True)
            print()

        return solutions
예제 #10
0
    def __init__(self,
                 reference_directions,
                 problem: Problem,
                 mutation: Mutation,
                 crossover: Crossover,
                 population_size: int = None,
                 selection: Selection = BinaryTournamentSelection(
                     MultiComparator([FastNonDominatedRanking.get_comparator(),
                                      CrowdingDistance.get_comparator()])),
                 termination_criterion: TerminationCriterion = store.default_termination_criteria,
                 population_generator: Generator = store.default_generator,
                 population_evaluator: Evaluator = store.default_evaluator,
                 dominance_comparator: Comparator = store.default_comparator):
        self.reference_directions = reference_directions.compute()
        if not population_size:
            population_size = len(self.reference_directions)
        if self.reference_directions.shape[1] != problem.number_of_objectives:
            raise Exception('Dimensionality of reference points must be equal to the number of objectives')

        super(NSGAIII, self).__init__(
            problem=problem,
            population_size=population_size,
            offspring_population_size=population_size,
            mutation=mutation,
            crossover=crossover,
            selection=selection,
            termination_criterion=termination_criterion,
            population_evaluator=population_evaluator,
            population_generator=population_generator,
            dominance_comparator=dominance_comparator
        )

        self.extreme_points = None
        self.ideal_point = np.full(self.problem.number_of_objectives, np.inf)
        self.worst_point = np.full(self.problem.number_of_objectives, -np.inf)
예제 #11
0
    def __init__(self,
                 problem: Problem,
                 population_size: int,
                 offspring_population_size: int,
                 mutation: Mutation,
                 crossover: Crossover,
                 selection: Selection = BinaryTournamentSelection(
                     MultiComparator([
                         FastNonDominatedRanking.get_comparator(),
                         CrowdingDistance.get_comparator()
                     ])),
                 termination_criterion: TerminationCriterion = store.
                 default_termination_criteria,
                 population_generator: Generator = store.default_generator,
                 population_evaluator: Evaluator = store.default_evaluator,
                 dominance_comparator: Comparator = store.default_comparator,
                 target_value_threshold: List[float] = None,
                 target_pattern: List[int] = None):
        """
        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.

        :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
        self.generation = 0
        self.target_pattern = target_pattern
        self.problem_solved = False
        self.target_value_threshold = target_value_threshold

        self.file_pareto_front = os.getcwd() + '/' + str(
            time.strftime("%Y_%m_%d")) + '_PARETO_'
        if not os.path.exists(self.file_pareto_front):
            os.mkdir(self.file_pareto_front)
예제 #12
0
파일: moga.py 프로젝트: ufvceiec/MOGA
    def replacement(self, population: List[S], offspring_population: List[S]) -> List[List[S]]:


        ranking = FastNonDominatedRanking(self.dominance_comparator)
        density_estimator = CrowdingDistance()
        r = RankingAndDensityEstimatorReplacement(ranking, density_estimator, RemovalPolicyType.ONE_SHOT)
        solutions = r.replace(population, offspring_population)

        return solutions
예제 #13
0
    def replacement(self, population: List[S],
                    offspring_population: List[S]) -> List[List[S]]:
        """ This method joins the current and offspring populations to produce the population of the next generation
        by applying the ranking and crowding distance selection.
        :param population: Parent population.
        :param offspring_population: Offspring population.
        :return: New population after ranking and crowding distance selection is applied.
        """
        ranking = FastNonDominatedRanking(self.dominance_comparator)
        density_estimator = CrowdingDistance()
        r = RankingAndDensityEstimatorReplacement(ranking, density_estimator,
                                                  RemovalPolicyType.ONE_SHOT)
        solutions = r.replace(population, offspring_population)

        return solutions
예제 #14
0
    def __init__(self,
                 problem: MultiTransfer.MultiTransferProblem,
                 population_size: int,
                 offspring_population_size: int,
                 mutation: Mutation,
                 crossover: Crossover,
                 selection: Selection = BinaryTournamentSelection(
                     MultiComparator([FastNonDominatedRanking.get_comparator(),
                                      CrowdingDistance.get_comparator()])),
                 termination_criterion: TerminationCriterion = store.default_termination_criteria,
                 population_generator: Generator = store.default_generator,
                 population_evaluator: Evaluator = store.default_evaluator,
                 dominance_comparator: Comparator = store.default_comparator):
        """
        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.

        :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_MEDA, 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
예제 #15
0
    def __init__(
        self,
        problem: Problem,
        dominance_comparator: Comparator = store.default_comparator,
        max_iterations: int = 250,
        individual_population_size: int = 100,
        report_interval: int = 100,
    ):
        super().__init__()

        self.problem = problem
        self.population_size = individual_population_size
        self.max_iterations = max_iterations
        self.report_interval = report_interval
        self.mix_interval = self.population_size / 10
        """Replacement"""
        ranking = FastNonDominatedRanking(dominance_comparator)
        density_estimator = CrowdingDistance()

        self.r = RankingAndDensityEstimatorReplacement(
            ranking, density_estimator, RemovalPolicyType.SEQUENTIAL)
예제 #16
0
    def construct(self, hyperparameters: Mapping, scenario: Mapping,
                  warm_startup_info: Mapping) -> None:

        # Constructing meta-heuristics initialization arguments (components + simple hyperparameters)
        init_args = dict(copy(hyperparameters))

        if "crossover_type" in init_args:
            import jmetal.operator.crossover as crossover
            crossover_class = JMetalPyWrapper._get_class_from_module(
                name=init_args.pop("crossover_type"), module=crossover)
            init_args["crossover"] = crossover_class(
                init_args.pop("crossover_probability"))

        if "mutation_type" in init_args:
            import jmetal.operator.mutation as mutation
            mutation_class = JMetalPyWrapper._get_class_from_module(
                name=init_args.pop("mutation_type"), module=mutation)
            init_args["mutation"] = mutation_class(
                init_args.pop("mutation_probability"))

        if "selection_type" in init_args:
            selection_type = init_args.pop('selection_type')
            if selection_type == 'ReuletteWheelSelection':
                from jmetal.util.comparator import MultiComparator
                from jmetal.util.density_estimator import CrowdingDistance
                from jmetal.util.ranking import FastNonDominatedRanking
                init_args["selection"] = MultiComparator([
                    FastNonDominatedRanking.get_comparator(),
                    CrowdingDistance.get_comparator()
                ])
            else:
                import jmetal.operator.selection as selection
                selection_class = JMetalPyWrapper._get_class_from_module(
                    name=selection_type, module=selection)
                init_args["selection"] = selection_class()

        # Add all non-component Metaheuristic parameters
        if "offspring_population_size" in init_args:
            offsp_population = init_args.pop("offspring_population_size")
            # offspring_population_size should be even
            offsp_population += offsp_population % 2
            init_args['offspring_population_size'] = offsp_population

        # elitist should be bool
        if "elitist" in init_args:
            init_args['elitist'] = True if init_args.pop(
                'elitist') == "True" else False

        if "population_size" in init_args:
            init_args["population_size"] = init_args.pop("population_size")

        termination_class = JMetalPyWrapper._get_class_from_module(
            name=scenario["Budget"]["Type"], module=termination)
        init_args["termination_criterion"] = termination_class(
            scenario["Budget"]["Amount"])

        # making dict hashable enables using LRU cache
        problem_init_params = HashableDict(
            scenario['problem_initialization_parameters'])
        problem = JMetalPyWrapper._get_problem(
            problem_name=scenario['Problem'], init_params=problem_init_params)
        init_args["problem"] = problem
        # Attach initial solutions.
        self.load_initial_solutions(warm_startup_info, problem)

        llh_name = init_args.pop("low level heuristic")
        if llh_name == "jMetalPy.SimulatedAnnealing":
            init_args["solution_generator"] = self._solution_generator
        else:
            init_args['population_generator'] = self._solution_generator

        # Instantiate Metaheuristic object
        self._llh_algorithm = JMetalPyWrapper._get_algorithm_class(llh_name)(
            **init_args)
예제 #17
0
 def __init__(self, maximum_size: int):
     super(CrowdingDistanceArchive, self).__init__(
         maximum_size=maximum_size,
         comparator=SolutionAttributeComparator("crowding_distance",
                                                lowest_is_best=False),
         density_estimator=CrowdingDistance())
from jmetal.util.termination_criterion import StoppingByEvaluations

if __name__ == '__main__':
    problem = TSP(instance='../../resources/TSP_instances/kroA100.tsp')

    print('Cities: ', problem.number_of_variables)

    algorithm = GeneticAlgorithm(
        problem=problem,
        population_size=100,
        offspring_population_size=100,
        mutation=PermutationSwapMutation(1.0 / problem.number_of_variables),
        crossover=PMXCrossover(0.8),
        selection=BinaryTournamentSelection(
            MultiComparator([
                FastNonDominatedRanking.get_comparator(),
                CrowdingDistance.get_comparator()
            ])),
        termination_criterion=StoppingByEvaluations(max=2500000))

    algorithm.observable.register(observer=PrintObjectivesObserver(1000))

    algorithm.run()
    result = algorithm.get_result()

    print('Algorithm: {}'.format(algorithm.get_name()))
    print('Problem: {}'.format(problem.get_name()))
    print('Solution: {}'.format(result.variables))
    print('Fitness: {}'.format(result.objectives[0]))
    print('Computing time: {}'.format(algorithm.total_computing_time))
예제 #19
0
    def run(self):
        """ Execute the algorithm. """
        self.start_computing_time = time.time()

        create_solution = dask.delayed(self.problem.create_solution)
        evaluate_solution = dask.delayed(self.problem.evaluate)

        task_pool = as_completed([], with_results=True)

        for _ in range(self.number_of_cores):
            new_solution = create_solution()
            new_evaluated_solution = evaluate_solution(new_solution)
            future = self.client.compute(new_evaluated_solution)

            task_pool.add(future)

        batches = task_pool.batches()

        auxiliar_population = []
        while len(auxiliar_population) < self.population_size:
            batch = next(batches)
            for _, received_solution in batch:
                auxiliar_population.append(received_solution)

                if len(auxiliar_population) < self.population_size:
                    break

            # submit as many new tasks as we collected
            for _ in batch:
                new_solution = create_solution()
                new_evaluated_solution = evaluate_solution(new_solution)
                future = self.client.compute(new_evaluated_solution)

                task_pool.add(future)

        self.init_progress()

        # perform an algorithm step to create a new solution to be evaluated
        while not self.stopping_condition_is_met():
            batch = next(batches)

            for _, received_solution in batch:
                offspring_population = [received_solution]

                # replacement
                ranking = FastNonDominatedRanking(self.dominance_comparator)
                density_estimator = CrowdingDistance()

                r = RankingAndDensityEstimatorReplacement(
                    ranking, density_estimator, RemovalPolicyType.ONE_SHOT)
                auxiliar_population = r.replace(auxiliar_population,
                                                offspring_population)

                # selection
                mating_population = []
                for _ in range(2):
                    solution = self.selection_operator.execute(
                        auxiliar_population)
                    mating_population.append(solution)

                # Reproduction and evaluation
                new_task = self.client.submit(reproduction, mating_population,
                                              self.problem,
                                              self.crossover_operator,
                                              self.mutation_operator)
                task_pool.add(new_task)

                # update progress
                self.evaluations += 1
                self.solutions = auxiliar_population

                self.update_progress()

                if self.stopping_condition_is_met():
                    break

        self.total_computing_time = time.time() - self.start_computing_time

        # at this point, computation is done
        for future, _ in task_pool:
            future.cancel()
예제 #20
0
 def setUp(self):
     self.crowding = CrowdingDistance()
예제 #21
0
class CrowdingDistanceTestCases(unittest.TestCase):

    def setUp(self):
        self.crowding = CrowdingDistance()

    def test_should_the_crowding_distance_of_an_empty_set_do_nothing(self):
        solution_list = []
        self.crowding.compute_density_estimator(solution_list)

    def test_should_the_crowding_distance_of_single_solution_be_infinity(self):
        solution = Solution(3, 3)
        solution_list = [solution]

        self.crowding.compute_density_estimator(solution_list)
        value = solution_list[0].attributes["crowding_distance"]

        self.assertEqual(float("inf"), value)

    def test_should_the_crowding_distance_of_two_solutions_be_infinity(self):
        solution1 = Solution(3, 3)
        solution2 = Solution(3, 3)
        solution_list = [solution1, solution2]

        self.crowding.compute_density_estimator(solution_list)
        value_from_solution1 = solution_list[0].attributes["crowding_distance"]
        value_from_solution2 = solution_list[1].attributes["crowding_distance"]

        self.assertEqual(float("inf"), value_from_solution1)
        self.assertEqual(float("inf"), value_from_solution2)

    def test_should_the_crowding_distance_of_three_solutions_correctly_assigned(self):
        solution1 = Solution(2, 2)
        solution2 = Solution(2, 2)
        solution3 = Solution(2, 2)

        solution1.objectives[0] = 0.0
        solution1.objectives[1] = 1.0
        solution2.objectives[0] = 1.0
        solution2.objectives[1] = 0.0
        solution3.objectives[0] = 0.5
        solution3.objectives[1] = 0.5

        solution_list = [solution1, solution2, solution3]

        self.crowding.compute_density_estimator(solution_list)

        value_from_solution1 = solution_list[0].attributes["crowding_distance"]
        value_from_solution2 = solution_list[1].attributes["crowding_distance"]
        value_from_solution3 = solution_list[2].attributes["crowding_distance"]

        self.assertEqual(float("inf"), value_from_solution1)
        self.assertEqual(float("inf"), value_from_solution2)
        self.assertEqual(2.0, value_from_solution3)

    def test_should_the_crowding_distance_of_four_solutions_correctly_assigned(self):
        solution1 = Solution(2, 2)
        solution2 = Solution(2, 2)
        solution3 = Solution(2, 2)
        solution4 = Solution(2, 2)

        solution1.objectives[0] = 0.0
        solution1.objectives[1] = 1.0
        solution2.objectives[0] = 1.0
        solution2.objectives[1] = 0.0
        solution3.objectives[0] = 0.5
        solution3.objectives[1] = 0.5
        solution4.objectives[0] = 0.75
        solution4.objectives[1] = 0.75

        solution_list = [solution1, solution2, solution3, solution4]

        self.crowding.compute_density_estimator(solution_list)

        value_from_solution1 = solution_list[0].attributes["crowding_distance"]
        value_from_solution2 = solution_list[1].attributes["crowding_distance"]
        value_from_solution3 = solution_list[2].attributes["crowding_distance"]
        value_from_solution4 = solution_list[3].attributes["crowding_distance"]

        self.assertEqual(float("inf"), value_from_solution1)
        self.assertEqual(float("inf"), value_from_solution2)
        self.assertGreater(value_from_solution3, value_from_solution4)