Source code for jmetal.operator.mutation

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 UniformMutation(Mutation[FloatSolution]): def __init__(self, probability: float, perturbation: float = 0.5): super(UniformMutation, self).__init__(probability=probability) self.perturbation = perturbation
[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: tmp = (random.random() - 0.5) * self.perturbation tmp += solution.variables[i] if tmp < solution.lower_bound[i]: tmp = solution.lower_bound[i] elif tmp > solution.upper_bound[i]: tmp = solution.upper_bound[i] solution.variables[i] = tmp return solution
[docs] def get_name(self): return "Uniform mutation"
[docs] class NonUniformMutation(Mutation[FloatSolution]): def __init__(self, probability: float, perturbation: float = 0.5, max_iterations: int = 0.5): super(NonUniformMutation, self).__init__(probability=probability) self.perturbation = perturbation self.max_iterations = max_iterations self.current_iteration = 0
[docs] def execute(self, solution: FloatSolution) -> FloatSolution: Check.that(type(solution) is FloatSolution, "Solution type invalid") for i in range(len(solution.variables)): if random.random() <= self.probability: rand = random.random() if rand <= 0.5: tmp = self.__delta(solution.upper_bound[i] - solution.variables[i], self.perturbation) else: tmp = self.__delta(solution.lower_bound[i] - solution.variables[i], self.perturbation) tmp += solution.variables[i] if tmp < solution.lower_bound[i]: tmp = solution.lower_bound[i] elif tmp > solution.upper_bound[i]: tmp = solution.upper_bound[i] solution.variables[i] = tmp return solution
[docs] def set_current_iteration(self, current_iteration: int): self.current_iteration = current_iteration
def __delta(self, y: float, b_mutation_parameter: float): return y * ( 1.0 - pow( random.random(), pow((1.0 - 1.0 * self.current_iteration / self.max_iterations), b_mutation_parameter) ) )
[docs] def get_name(self): return "Uniform 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"