Skip to content

Commit

Permalink
Refactor crossover and mutation operators
Browse files Browse the repository at this point in the history
  • Loading branch information
ajnebro committed Apr 8, 2020
1 parent aa48ee3 commit 14f46d2
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 13 deletions.
3 changes: 2 additions & 1 deletion jmetal/algorithm/singleobjective/evolution_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from jmetal.util.evaluator import Evaluator, SequentialEvaluator
from jmetal.util.generator import Generator, RandomGenerator
from jmetal.util.termination_criterion import TerminationCriterion
from jmetal.util.constraint_handling import overall_constraint_violation_degree

S = TypeVar('S')
R = TypeVar('R')
Expand Down Expand Up @@ -78,7 +79,7 @@ def replacement(self, population: List[S], offspring_population: List[S]) -> Lis
else:
population_pool.extend(offspring_population)

population_pool.sort(key=lambda s: s.objectives[0])
population_pool.sort(key=lambda s: (overall_constraint_violation_degree(s), s.objectives[0]))

new_population = []
for i in range(self.mu):
Expand Down
11 changes: 4 additions & 7 deletions jmetal/operator/crossover.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@


class NullCrossover(Crossover[Solution, Solution]):

def __init__(self):
super(NullCrossover, self).__init__(probability=0.0)

Expand All @@ -38,7 +37,6 @@ def get_name(self):


class PMXCrossover(Crossover[PermutationSolution, PermutationSolution]):

def __init__(self, probability: float):
super(PMXCrossover, self).__init__(probability=probability)

Expand Down Expand Up @@ -97,7 +95,6 @@ def get_name(self):


class CXCrossover(Crossover[PermutationSolution, PermutationSolution]):

def __init__(self, probability: float):
super(CXCrossover, self).__init__(probability=probability)

Expand Down Expand Up @@ -148,8 +145,8 @@ def __init__(self, probability: float, distribution_index: float = 20.0):
raise Exception("The distribution index is negative: " + str(distribution_index))

def execute(self, parents: List[FloatSolution]) -> List[FloatSolution]:
Check.that(type(parents[0]) is FloatSolution, "Solution type invalid: " + str(type(parents[0])))
Check.that(type(parents[1]) is FloatSolution, "Solution type invalid")
Check.that(issubclass(type(parents[0]), FloatSolution), "Solution type invalid: " + str(type(parents[0])))
Check.that(issubclass(type(parents[1]), FloatSolution), "Solution type invalid")
Check.that(len(parents) == 2, 'The number of parents is not two: {}'.format(len(parents)))

offspring = [copy.deepcopy(parents[0]), copy.deepcopy(parents[1])]
Expand Down Expand Up @@ -229,8 +226,8 @@ def __init__(self, probability: float, distribution_index: float = 20.0):
self.distribution_index = distribution_index

def execute(self, parents: List[IntegerSolution]) -> List[IntegerSolution]:
Check.that(type(parents[0]) is IntegerSolution, "Solution type invalid")
Check.that(type(parents[1]) is IntegerSolution, "Solution type invalid")
Check.that(issubclass(type(parents[0]), IntegerSolution), "Solution type invalid")
Check.that(issubclass(type(parents[1]), IntegerSolution), "Solution type invalid")
Check.that(len(parents) == 2, 'The number of parents is not two: {}'.format(len(parents)))

offspring = [copy.deepcopy(parents[0]), copy.deepcopy(parents[1])]
Expand Down
4 changes: 2 additions & 2 deletions jmetal/operator/mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __init__(self, probability: float, distribution_index: float = 0.20):
self.distribution_index = distribution_index

def execute(self, solution: FloatSolution) -> FloatSolution:
Check.that(type(solution) is FloatSolution, "Solution type invalid")
Check.that(issubclass(type(solution), FloatSolution), "Solution type invalid")
for i in range(solution.number_of_variables):
rand = random.random()

Expand Down Expand Up @@ -98,7 +98,7 @@ def __init__(self, probability: float, distribution_index: float = 0.20):
self.distribution_index = distribution_index

def execute(self, solution: IntegerSolution) -> IntegerSolution:
Check.that(type(solution) is IntegerSolution, "Solution type invalid")
Check.that(issubclass(type(solution, IntegerSolution)), "Solution type invalid")

for i in range(solution.number_of_variables):
if random.random() <= self.probability:
Expand Down
24 changes: 23 additions & 1 deletion jmetal/operator/test/test_crossover.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
from typing import List
from unittest import mock

from jmetal.core.operator import Crossover
Expand Down Expand Up @@ -294,6 +295,27 @@ def test_should_execute_return_the_parents_if_the_crossover_probability_is_zero(
self.assertEqual(solution1.variables, offspring[0].variables)
self.assertEqual(solution2.variables, offspring[1].variables)

def test_should_execute_work_with_a_solution_subclass_of_float_solution(self):
class NewFloatSolution(FloatSolution):
def __init__(self, lower_bound: List[float], upper_bound: List[float], number_of_objectives: int,
number_of_constraints: int = 0):
super(NewFloatSolution, self).__init__(lower_bound, upper_bound, number_of_objectives,
number_of_constraints)

solution1 = NewFloatSolution([1, 2], [2, 4], 2, 2)
solution2 = NewFloatSolution([1, 2], [2, 4], 2, 2)

solution1.variables = [1.5, 2.7]
solution2.variables = [1.7, 3.6]

crossover: SBXCrossover = SBXCrossover(0.0, 20.0)
offspring = crossover.execute([solution1, solution2])

self.assertEqual(2, len(offspring))
self.assertEqual(solution1.variables, offspring[0].variables)
self.assertEqual(solution2.variables, offspring[1].variables)


def test_should_execute_produce_valid_solutions_when_crossing_two_single_variable_solutions(self):
pass

Expand All @@ -308,7 +330,7 @@ def test_should_constructor_raise_an_exception_if_the_parameter_list_is_Empty(se
CompositeCrossover([])

def test_should_constructor_create_a_valid_operator_when_adding_a_single_crossover_operator(self):
crossover: Crossover = SBXCrossover(0.9, 20.0)
crossover: Crossover = SBXCrossover(0.9, 20.0)

operator = CompositeCrossover([crossover])
self.assertIsNotNone(operator)
Expand Down
18 changes: 16 additions & 2 deletions jmetal/operator/test/test_mutation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
from typing import List

from jmetal.core.operator import Mutation
from jmetal.core.solution import BinarySolution, FloatSolution, IntegerSolution, CompositeSolution
Expand Down Expand Up @@ -46,8 +47,21 @@ def test_should_the_solution_change__if_the_probability_is_one(self):
operator = PolynomialMutation(1.0)
solution = FloatSolution([-5, -5, -5], [5, 5, 5], 2)
solution.variables = [1.0, 2.0, 3.0]
FloatSolution.lower_bound = [-5, -5, -5]
FloatSolution.upper_bound = [5, 5, 5]

mutated_solution = operator.execute(solution)

self.assertNotEqual([1.0, 2.0, 3.0], mutated_solution.variables)

def test_should_execute_work_with_a_solution_subclass_of_float_solution(self):
class NewFloatSolution(FloatSolution):
def __init__(self, lower_bound: List[float], upper_bound: List[float], number_of_objectives: int,
number_of_constraints: int = 0):
super(NewFloatSolution, self).__init__(lower_bound, upper_bound, number_of_objectives,
number_of_constraints)

operator = PolynomialMutation(1.0)
solution = NewFloatSolution([-5, -5, -5], [5, 5, 5], 2)
solution.variables = [1.0, 2.0, 3.0]

mutated_solution = operator.execute(solution)

Expand Down

0 comments on commit 14f46d2

Please sign in to comment.