# Source code for deap.tools.mutation

import math
import random

from itertools import repeat

try:
from collections.abc import Sequence
except ImportError:
from collections import Sequence

######################################
# GA Mutations                       #
######################################

[docs]def mutGaussian(individual, mu, sigma, indpb):
"""This function applies a gaussian mutation of mean *mu* and standard
deviation *sigma* on the input individual. This mutation expects a
:term:sequence individual composed of real valued attributes.
The *indpb* argument is the probability of each attribute to be mutated.

:param individual: Individual to be mutated.
:param mu: Mean or :term:python:sequence of means for the
:param sigma: Standard deviation or :term:python:sequence of
standard deviations for the gaussian addition mutation.
:param indpb: Independent probability for each attribute to be mutated.
:returns: A tuple of one individual.

This function uses the :func:~random.random and :func:~random.gauss
functions from the python base :mod:random module.
"""
size = len(individual)
if not isinstance(mu, Sequence):
mu = repeat(mu, size)
elif len(mu) < size:
raise IndexError("mu must be at least the size of individual: %d < %d" % (len(mu), size))
if not isinstance(sigma, Sequence):
sigma = repeat(sigma, size)
elif len(sigma) < size:
raise IndexError("sigma must be at least the size of individual: %d < %d" % (len(sigma), size))

for i, m, s in zip(range(size), mu, sigma):
if random.random() < indpb:
individual[i] += random.gauss(m, s)

return individual,

[docs]def mutPolynomialBounded(individual, eta, low, up, indpb):
"""Polynomial mutation as implemented in original NSGA-II algorithm in
C by Deb.

:param individual: :term:Sequence <sequence> individual to be mutated.
:param eta: Crowding degree of the mutation. A high eta will produce
a mutant resembling its parent, while a small eta will
produce a solution much more different.
:param low: A value or a :term:python:sequence of values that
is the lower bound of the search space.
:param up: A value or a :term:python:sequence of values that
is the upper bound of the search space.
:param indpb: Independent probability for each attribute to be mutated.
:returns: A tuple of one individual.
"""
size = len(individual)
if not isinstance(low, Sequence):
low = repeat(low, size)
elif len(low) < size:
raise IndexError("low must be at least the size of individual: %d < %d" % (len(low), size))
if not isinstance(up, Sequence):
up = repeat(up, size)
elif len(up) < size:
raise IndexError("up must be at least the size of individual: %d < %d" % (len(up), size))

for i, xl, xu in zip(range(size), low, up):
if random.random() <= indpb:
x = individual[i]
delta_1 = (x - xl) / (xu - xl)
delta_2 = (xu - x) / (xu - xl)
rand = random.random()
mut_pow = 1.0 / (eta + 1.)

if rand < 0.5:
xy = 1.0 - delta_1
val = 2.0 * rand + (1.0 - 2.0 * rand) * xy ** (eta + 1)
delta_q = val ** mut_pow - 1.0
else:
xy = 1.0 - delta_2
val = 2.0 * (1.0 - rand) + 2.0 * (rand - 0.5) * xy ** (eta + 1)
delta_q = 1.0 - val ** mut_pow

x = x + delta_q * (xu - xl)
x = min(max(x, xl), xu)
individual[i] = x
return individual,

[docs]def mutShuffleIndexes(individual, indpb):
"""Shuffle the attributes of the input individual and return the mutant.
The *individual* is expected to be a :term:sequence. The *indpb* argument is the
probability of each attribute to be moved. Usually this mutation is applied on
vector of indices.

:param individual: Individual to be mutated.
:param indpb: Independent probability for each attribute to be exchanged to
another position.
:returns: A tuple of one individual.

This function uses the :func:~random.random and :func:~random.randint
functions from the python base :mod:random module.
"""
size = len(individual)
for i in range(size):
if random.random() < indpb:
swap_indx = random.randint(0, size - 2)
if swap_indx >= i:
swap_indx += 1
individual[i], individual[swap_indx] = \
individual[swap_indx], individual[i]

return individual,

[docs]def mutFlipBit(individual, indpb):
"""Flip the value of the attributes of the input individual and return the
mutant. The *individual* is expected to be a :term:sequence and the values of the
attributes shall stay valid after the not operator is called on them.
The *indpb* argument is the probability of each attribute to be
flipped. This mutation is usually applied on boolean individuals.

:param individual: Individual to be mutated.
:param indpb: Independent probability for each attribute to be flipped.
:returns: A tuple of one individual.

This function uses the :func:~random.random function from the python base
:mod:random module.
"""
for i in range(len(individual)):
if random.random() < indpb:
individual[i] = type(individual[i])(not individual[i])

return individual,

[docs]def mutUniformInt(individual, low, up, indpb):
"""Mutate an individual by replacing attributes, with probability *indpb*,
by a integer uniformly drawn between *low* and *up* inclusively.

:param individual: :term:Sequence <sequence> individual to be mutated.
:param low: The lower bound or a :term:python:sequence of
of lower bounds of the range from which to draw the new
integer.
:param up: The upper bound or a :term:python:sequence of
of upper bounds of the range from which to draw the new
integer.
:param indpb: Independent probability for each attribute to be mutated.
:returns: A tuple of one individual.
"""
size = len(individual)
if not isinstance(low, Sequence):
low = repeat(low, size)
elif len(low) < size:
raise IndexError("low must be at least the size of individual: %d < %d" % (len(low), size))
if not isinstance(up, Sequence):
up = repeat(up, size)
elif len(up) < size:
raise IndexError("up must be at least the size of individual: %d < %d" % (len(up), size))

for i, xl, xu in zip(range(size), low, up):
if random.random() < indpb:
individual[i] = random.randint(xl, xu)

return individual,

def mutInversion(individual):
"""Select two points (indices) in the individual, reverse the order of the
attributes between these points [low, high] and return the mutated individual.
This implementation allows for the length of the inversion to be 0 and 1,
which would cause no change. This mutation is useful in situations where the

:param individual: Individual to be mutated.
:returns: A tuple of one individual.

This function uses the :func:~random.random function from the python base
:mod:random module.
"""
size = len(individual)
if size == 0:
return individual,

index_one = random.randrange(size)
index_two = random.randrange(size)
start_index = min(index_one, index_two)
end_index = max(index_one, index_two)

# Reverse the contents of the individual between the indices
individual[start_index:end_index] = individual[start_index:end_index][::-1]

return individual,

######################################
# ES Mutations                       #
######################################

[docs]def mutESLogNormal(individual, c, indpb):
r"""Mutate an evolution strategy according to its :attr:strategy
attribute as described in [Beyer2002]_. First the strategy is mutated
according to an extended log normal rule, :math:\\boldsymbol{\sigma}_t =
\\exp(\\tau_0 \mathcal{N}_0(0, 1)) \\left[ \\sigma_{t-1, 1}\\exp(\\tau
\mathcal{N}_1(0, 1)), \ldots, \\sigma_{t-1, n} \\exp(\\tau
\mathcal{N}_n(0, 1))\\right], with :math:\\tau_0 =
\\frac{c}{\\sqrt{2n}} and :math:\\tau = \\frac{c}{\\sqrt{2\\sqrt{n}}},
the the individual is mutated by a normal distribution of mean 0 and
standard deviation of :math:\\boldsymbol{\sigma}_{t} (its current
strategy) then . A recommended choice is c=1 when using a :math:(10,
100) evolution strategy [Beyer2002]_ [Schwefel1995]_.

:param individual: :term:Sequence <sequence> individual to be mutated.
:param c: The learning parameter.
:param indpb: Independent probability for each attribute to be mutated.
:returns: A tuple of one individual.

.. [Beyer2002] Beyer and Schwefel, 2002, Evolution strategies - A
Comprehensive Introduction

.. [Schwefel1995] Schwefel, 1995, Evolution and Optimum Seeking.
Wiley, New York, NY
"""
size = len(individual)
t = c / math.sqrt(2. * math.sqrt(size))
t0 = c / math.sqrt(2. * size)
n = random.gauss(0, 1)
t0_n = t0 * n

for indx in range(size):
if random.random() < indpb:
individual.strategy[indx] *= math.exp(t0_n + t * random.gauss(0, 1))
individual[indx] += individual.strategy[indx] * random.gauss(0, 1)

return individual,

__all__ = ['mutGaussian', 'mutPolynomialBounded', 'mutShuffleIndexes',
'mutFlipBit', 'mutUniformInt', 'mutInversion', 'mutESLogNormal']