-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmutation.py
216 lines (176 loc) · 8.49 KB
/
mutation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
from __future__ import division
import math
import random
from itertools import repeat
from collections import Sequence
######################################
# GA Mutations #
######################################
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
gaussian addition mutation.
: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(xrange(size), mu, sigma):
if random.random() < indpb:
individual[i] += random.gauss(m, s)
return individual,
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.
: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(xrange(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,
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 xrange(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,
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 xrange(len(individual)):
if random.random() < indpb:
individual[i] = type(individual[i])(not individual[i])
return individual,
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 wich to draw the new
integer.
:param up: The upper bound or a :term:`python:sequence` of
of upper bounds of the range from wich 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(xrange(size), low, up):
if random.random() < indpb:
individual[i] = random.randint(xl, xu)
return individual,
######################################
# ES Mutations #
######################################
def mutESLogNormal(individual, c, indpb):
"""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 xrange(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', 'mutESLogNormal']