import random
from jmetal.core.operator import Mutation
from jmetal.core.solution import (
BinarySolution,
CompositeSolution,
FloatSolution,
IntegerSolution,
PermutationSolution,
Solution,
)
from jmetal.util.ckecking import Check
"""
.. module:: mutation
:platform: Unix, Windows
:synopsis: Module implementing mutation operators.
.. moduleauthor:: Antonio J. Nebro <antonio@lcc.uma.es>, Antonio Benítez-Hidalgo <antonio.b@uma.es>
"""
[docs]
class NullMutation(Mutation[Solution]):
def __init__(self):
super(NullMutation, self).__init__(probability=0)
[docs]
def execute(self, solution: Solution) -> Solution:
return solution
[docs]
def get_name(self):
return "Null mutation"
[docs]
class BitFlipMutation(Mutation[BinarySolution]):
def __init__(self, probability: float):
super(BitFlipMutation, self).__init__(probability=probability)
[docs]
def execute(self, solution: BinarySolution) -> BinarySolution:
Check.that(type(solution) is BinarySolution, "Solution type invalid")
for i in range(len(solution.variables)):
for j in range(len(solution.variables[i])):
rand = random.random()
if rand <= self.probability:
solution.variables[i][j] = True if solution.variables[i][j] is False else False
return solution
[docs]
def get_name(self):
return "BitFlip mutation"
[docs]
class PolynomialMutation(Mutation[FloatSolution]):
def __init__(self, probability: float, distribution_index: float = 0.20):
super(PolynomialMutation, self).__init__(probability=probability)
self.distribution_index = distribution_index
[docs]
def execute(self, solution: FloatSolution) -> FloatSolution:
Check.that(issubclass(type(solution), FloatSolution), "Solution type invalid")
for i in range(len(solution.variables)):
rand = random.random()
if rand <= self.probability:
y = solution.variables[i]
yl, yu = solution.lower_bound[i], solution.upper_bound[i]
if yl == yu:
y = yl
else:
delta1 = (y - yl) / (yu - yl)
delta2 = (yu - y) / (yu - yl)
rnd = random.random()
mut_pow = 1.0 / (self.distribution_index + 1.0)
if rnd <= 0.5:
xy = 1.0 - delta1
val = 2.0 * rnd + (1.0 - 2.0 * rnd) * (pow(xy, self.distribution_index + 1.0))
deltaq = pow(val, mut_pow) - 1.0
else:
xy = 1.0 - delta2
val = 2.0 * (1.0 - rnd) + 2.0 * (rnd - 0.5) * (pow(xy, self.distribution_index + 1.0))
deltaq = 1.0 - pow(val, mut_pow)
y += deltaq * (yu - yl)
if y < solution.lower_bound[i]:
y = solution.lower_bound[i]
if y > solution.upper_bound[i]:
y = solution.upper_bound[i]
solution.variables[i] = y
return solution
[docs]
def get_name(self):
return "Polynomial mutation"
[docs]
class IntegerPolynomialMutation(Mutation[IntegerSolution]):
def __init__(self, probability: float, distribution_index: float = 0.20):
super(IntegerPolynomialMutation, self).__init__(probability=probability)
self.distribution_index = distribution_index
[docs]
def execute(self, solution: IntegerSolution) -> IntegerSolution:
Check.that(issubclass(type(solution), IntegerSolution), "Solution type invalid")
for i in range(len(solution.variables)):
if random.random() <= self.probability:
y = solution.variables[i]
yl, yu = solution.lower_bound[i], solution.upper_bound[i]
if yl == yu:
y = yl
else:
delta1 = (y - yl) / (yu - yl)
delta2 = (yu - y) / (yu - yl)
mut_pow = 1.0 / (self.distribution_index + 1.0)
rnd = random.random()
if rnd <= 0.5:
xy = 1.0 - delta1
val = 2.0 * rnd + (1.0 - 2.0 * rnd) * (xy ** (self.distribution_index + 1.0))
deltaq = val**mut_pow - 1.0
else:
xy = 1.0 - delta2
val = 2.0 * (1.0 - rnd) + 2.0 * (rnd - 0.5) * (xy ** (self.distribution_index + 1.0))
deltaq = 1.0 - val**mut_pow
y += deltaq * (yu - yl)
if y < solution.lower_bound[i]:
y = solution.lower_bound[i]
if y > solution.upper_bound[i]:
y = solution.upper_bound[i]
solution.variables[i] = int(round(y))
return solution
[docs]
def get_name(self):
return "Polynomial mutation (Integer)"
[docs]
class SimpleRandomMutation(Mutation[FloatSolution]):
def __init__(self, probability: float):
super(SimpleRandomMutation, self).__init__(probability=probability)
[docs]
def execute(self, solution: FloatSolution) -> FloatSolution:
Check.that(type(solution) is FloatSolution, "Solution type invalid")
for i in range(len(solution.variables)):
rand = random.random()
if rand <= self.probability:
solution.variables[i] = (
solution.lower_bound[i] + (solution.upper_bound[i] - solution.lower_bound[i]) * random.random()
)
return solution
[docs]
def get_name(self):
return "Simple random_search mutation"
[docs]
class PermutationSwapMutation(Mutation[PermutationSolution]):
[docs]
def execute(self, solution: PermutationSolution) -> PermutationSolution:
Check.that(type(solution) is PermutationSolution, "Solution type invalid")
rand = random.random()
if rand <= self.probability:
pos_one, pos_two = random.sample(range(len(solution.variables)), 2)
solution.variables[pos_one], solution.variables[pos_two] = (
solution.variables[pos_two],
solution.variables[pos_one],
)
return solution
[docs]
def get_name(self):
return "Permutation Swap mutation"
[docs]
class CompositeMutation(Mutation[Solution]):
def __init__(self, mutation_operator_list: [Mutation]):
super(CompositeMutation, self).__init__(probability=1.0)
Check.is_not_none(mutation_operator_list)
Check.collection_is_not_empty(mutation_operator_list)
self.mutation_operators_list = []
for operator in mutation_operator_list:
Check.that(issubclass(operator.__class__, Mutation), "Object is not a subclass of Mutation")
self.mutation_operators_list.append(operator)
[docs]
def execute(self, solution: CompositeSolution) -> CompositeSolution:
Check.is_not_none(solution)
mutated_solution_components = []
for i in range(len(solution.variables)):
mutated_solution_components.append(self.mutation_operators_list[i].execute(solution.variables[i]))
return CompositeSolution(mutated_solution_components)
[docs]
def get_name(self) -> str:
return "Composite mutation operator"
[docs]
class ScrambleMutation(Mutation[PermutationSolution]):
[docs]
def execute(self, solution: PermutationSolution) -> PermutationSolution:
rand = random.random()
if rand <= self.probability:
point1 = random.randint(0, len(solution.variables))
point2 = random.randint(0, len(solution.variables) - 1)
if point2 >= point1:
point2 += 1
else:
point1, point2 = point2, point1
if point2 - point1 >= 20:
point2 = point1 + 20
values = solution.variables[point1:point2]
solution.variables[point1:point2] = random.sample(values, len(values))
return solution
[docs]
def get_name(self):
return "Scramble"