445 lines
17 KiB
Python
445 lines
17 KiB
Python
from __future__ import division
|
|
import random
|
|
import warnings
|
|
|
|
from collections import Sequence
|
|
from itertools import repeat
|
|
|
|
######################################
|
|
# GA Crossovers #
|
|
######################################
|
|
|
|
def cxOnePoint(ind1, ind2):
|
|
"""Executes a one point crossover on the input :term:`sequence` individuals.
|
|
The two individuals are modified in place. The resulting individuals will
|
|
respectively have the length of the other.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:returns: A tuple of two individuals.
|
|
|
|
This function uses the :func:`~random.randint` function from the
|
|
python base :mod:`random` module.
|
|
"""
|
|
size = min(len(ind1), len(ind2))
|
|
cxpoint = random.randint(1, size - 1)
|
|
ind1[cxpoint:], ind2[cxpoint:] = ind2[cxpoint:], ind1[cxpoint:]
|
|
|
|
return ind1, ind2
|
|
|
|
def cxTwoPoint(ind1, ind2):
|
|
"""Executes a two-point crossover on the input :term:`sequence`
|
|
individuals. The two individuals are modified in place and both keep
|
|
their original length.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:returns: A tuple of two individuals.
|
|
|
|
This function uses the :func:`~random.randint` function from the Python
|
|
base :mod:`random` module.
|
|
"""
|
|
size = min(len(ind1), len(ind2))
|
|
cxpoint1 = random.randint(1, size)
|
|
cxpoint2 = random.randint(1, size - 1)
|
|
if cxpoint2 >= cxpoint1:
|
|
cxpoint2 += 1
|
|
else: # Swap the two cx points
|
|
cxpoint1, cxpoint2 = cxpoint2, cxpoint1
|
|
|
|
ind1[cxpoint1:cxpoint2], ind2[cxpoint1:cxpoint2] \
|
|
= ind2[cxpoint1:cxpoint2], ind1[cxpoint1:cxpoint2]
|
|
|
|
return ind1, ind2
|
|
|
|
def cxTwoPoints(ind1, ind2):
|
|
"""
|
|
.. deprecated:: 1.0
|
|
The function has been renamed. Use :func:`~deap.tools.cxTwoPoint` instead.
|
|
"""
|
|
warnings.warn("tools.cxTwoPoints has been renamed. Use cxTwoPoint instead.",
|
|
FutureWarning)
|
|
return cxTwoPoint(ind1, ind2)
|
|
|
|
def cxUniform(ind1, ind2, indpb):
|
|
"""Executes a uniform crossover that modify in place the two
|
|
:term:`sequence` individuals. The attributes are swapped accordingto the
|
|
*indpb* probability.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:param indpb: Independent probabily for each attribute to be exchanged.
|
|
:returns: A tuple of two individuals.
|
|
|
|
This function uses the :func:`~random.random` function from the python base
|
|
:mod:`random` module.
|
|
"""
|
|
size = min(len(ind1), len(ind2))
|
|
for i in xrange(size):
|
|
if random.random() < indpb:
|
|
ind1[i], ind2[i] = ind2[i], ind1[i]
|
|
|
|
return ind1, ind2
|
|
|
|
def cxPartialyMatched(ind1, ind2):
|
|
"""Executes a partially matched crossover (PMX) on the input individuals.
|
|
The two individuals are modified in place. This crossover expects
|
|
:term:`sequence` individuals of indices, the result for any other type of
|
|
individuals is unpredictable.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:returns: A tuple of two individuals.
|
|
|
|
Moreover, this crossover generates two children by matching
|
|
pairs of values in a certain range of the two parents and swapping the values
|
|
of those indexes. For more details see [Goldberg1985]_.
|
|
|
|
This function uses the :func:`~random.randint` function from the python base
|
|
:mod:`random` module.
|
|
|
|
.. [Goldberg1985] Goldberg and Lingel, "Alleles, loci, and the traveling
|
|
salesman problem", 1985.
|
|
"""
|
|
size = min(len(ind1), len(ind2))
|
|
p1, p2 = [0]*size, [0]*size
|
|
|
|
# Initialize the position of each indices in the individuals
|
|
for i in xrange(size):
|
|
p1[ind1[i]] = i
|
|
p2[ind2[i]] = i
|
|
# Choose crossover points
|
|
cxpoint1 = random.randint(0, size)
|
|
cxpoint2 = random.randint(0, size - 1)
|
|
if cxpoint2 >= cxpoint1:
|
|
cxpoint2 += 1
|
|
else: # Swap the two cx points
|
|
cxpoint1, cxpoint2 = cxpoint2, cxpoint1
|
|
|
|
# Apply crossover between cx points
|
|
for i in xrange(cxpoint1, cxpoint2):
|
|
# Keep track of the selected values
|
|
temp1 = ind1[i]
|
|
temp2 = ind2[i]
|
|
# Swap the matched value
|
|
ind1[i], ind1[p1[temp2]] = temp2, temp1
|
|
ind2[i], ind2[p2[temp1]] = temp1, temp2
|
|
# Position bookkeeping
|
|
p1[temp1], p1[temp2] = p1[temp2], p1[temp1]
|
|
p2[temp1], p2[temp2] = p2[temp2], p2[temp1]
|
|
|
|
return ind1, ind2
|
|
|
|
def cxUniformPartialyMatched(ind1, ind2, indpb):
|
|
"""Executes a uniform partially matched crossover (UPMX) on the input
|
|
individuals. The two individuals are modified in place. This crossover
|
|
expects :term:`sequence` individuals of indices, the result for any other
|
|
type of individuals is unpredictable.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:returns: A tuple of two individuals.
|
|
|
|
Moreover, this crossover generates two children by matching
|
|
pairs of values chosen at random with a probability of *indpb* in the two
|
|
parents and swapping the values of those indexes. For more details see
|
|
[Cicirello2000]_.
|
|
|
|
This function uses the :func:`~random.random` and :func:`~random.randint`
|
|
functions from the python base :mod:`random` module.
|
|
|
|
.. [Cicirello2000] Cicirello and Smith, "Modeling GA performance for
|
|
control parameter optimization", 2000.
|
|
"""
|
|
size = min(len(ind1), len(ind2))
|
|
p1, p2 = [0]*size, [0]*size
|
|
|
|
# Initialize the position of each indices in the individuals
|
|
for i in xrange(size):
|
|
p1[ind1[i]] = i
|
|
p2[ind2[i]] = i
|
|
|
|
for i in xrange(size):
|
|
if random.random() < indpb:
|
|
# Keep track of the selected values
|
|
temp1 = ind1[i]
|
|
temp2 = ind2[i]
|
|
# Swap the matched value
|
|
ind1[i], ind1[p1[temp2]] = temp2, temp1
|
|
ind2[i], ind2[p2[temp1]] = temp1, temp2
|
|
# Position bookkeeping
|
|
p1[temp1], p1[temp2] = p1[temp2], p1[temp1]
|
|
p2[temp1], p2[temp2] = p2[temp2], p2[temp1]
|
|
|
|
return ind1, ind2
|
|
|
|
def cxOrdered(ind1, ind2):
|
|
"""Executes an ordered crossover (OX) on the input
|
|
individuals. The two individuals are modified in place. This crossover
|
|
expects :term:`sequence` individuals of indices, the result for any other
|
|
type of individuals is unpredictable.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:returns: A tuple of two individuals.
|
|
|
|
Moreover, this crossover generates holes in the input
|
|
individuals. A hole is created when an attribute of an individual is
|
|
between the two crossover points of the other individual. Then it rotates
|
|
the element so that all holes are between the crossover points and fills
|
|
them with the removed elements in order. For more details see
|
|
[Goldberg1989]_.
|
|
|
|
This function uses the :func:`~random.sample` function from the python base
|
|
:mod:`random` module.
|
|
|
|
.. [Goldberg1989] Goldberg. Genetic algorithms in search,
|
|
optimization and machine learning. Addison Wesley, 1989
|
|
"""
|
|
size = min(len(ind1), len(ind2))
|
|
a, b = random.sample(xrange(size), 2)
|
|
if a > b:
|
|
a, b = b, a
|
|
|
|
holes1, holes2 = [True]*size, [True]*size
|
|
for i in range(size):
|
|
if i < a or i > b:
|
|
holes1[ind2[i]] = False
|
|
holes2[ind1[i]] = False
|
|
|
|
# We must keep the original values somewhere before scrambling everything
|
|
temp1, temp2 = ind1, ind2
|
|
k1 , k2 = b + 1, b + 1
|
|
for i in range(size):
|
|
if not holes1[temp1[(i + b + 1) % size]]:
|
|
ind1[k1 % size] = temp1[(i + b + 1) % size]
|
|
k1 += 1
|
|
|
|
if not holes2[temp2[(i + b + 1) % size]]:
|
|
ind2[k2 % size] = temp2[(i + b + 1) % size]
|
|
k2 += 1
|
|
|
|
# Swap the content between a and b (included)
|
|
for i in range(a, b + 1):
|
|
ind1[i], ind2[i] = ind2[i], ind1[i]
|
|
|
|
return ind1, ind2
|
|
|
|
def cxBlend(ind1, ind2, alpha):
|
|
"""Executes a blend crossover that modify in-place the input individuals.
|
|
The blend crossover expects :term:`sequence` individuals of floating point
|
|
numbers.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:param alpha: Extent of the interval in which the new values can be drawn
|
|
for each attribute on both side of the parents' attributes.
|
|
:returns: A tuple of two individuals.
|
|
|
|
This function uses the :func:`~random.random` function from the python base
|
|
:mod:`random` module.
|
|
"""
|
|
for i, (x1, x2) in enumerate(zip(ind1, ind2)):
|
|
gamma = (1. + 2. * alpha) * random.random() - alpha
|
|
ind1[i] = (1. - gamma) * x1 + gamma * x2
|
|
ind2[i] = gamma * x1 + (1. - gamma) * x2
|
|
|
|
return ind1, ind2
|
|
|
|
def cxSimulatedBinary(ind1, ind2, eta):
|
|
"""Executes a simulated binary crossover that modify in-place the input
|
|
individuals. The simulated binary crossover expects :term:`sequence`
|
|
individuals of floating point numbers.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:param eta: Crowding degree of the crossover. A high eta will produce
|
|
children resembling to their parents, while a small eta will
|
|
produce solutions much more different.
|
|
:returns: A tuple of two individuals.
|
|
|
|
This function uses the :func:`~random.random` function from the python base
|
|
:mod:`random` module.
|
|
"""
|
|
for i, (x1, x2) in enumerate(zip(ind1, ind2)):
|
|
rand = random.random()
|
|
if rand <= 0.5:
|
|
beta = 2. * rand
|
|
else:
|
|
beta = 1. / (2. * (1. - rand))
|
|
beta **= 1. / (eta + 1.)
|
|
ind1[i] = 0.5 * (((1 + beta) * x1) + ((1 - beta) * x2))
|
|
ind2[i] = 0.5 * (((1 - beta) * x1) + ((1 + beta) * x2))
|
|
|
|
return ind1, ind2
|
|
|
|
|
|
def cxSimulatedBinaryBounded(ind1, ind2, eta, low, up):
|
|
"""Executes a simulated binary crossover that modify in-place the input
|
|
individuals. The simulated binary crossover expects :term:`sequence`
|
|
individuals of floating point numbers.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:param eta: Crowding degree of the crossover. A high eta will produce
|
|
children resembling to their parents, while a small eta will
|
|
produce solutions much more different.
|
|
:param low: A value or an :term:`python:sequence` of values that is the lower
|
|
bound of the search space.
|
|
:param up: A value or an :term:`python:sequence` of values that is the upper
|
|
bound of the search space.
|
|
:returns: A tuple of two individuals.
|
|
|
|
This function uses the :func:`~random.random` function from the python base
|
|
:mod:`random` module.
|
|
|
|
.. note::
|
|
This implementation is similar to the one implemented in the
|
|
original NSGA-II C code presented by Deb.
|
|
"""
|
|
size = min(len(ind1), len(ind2))
|
|
if not isinstance(low, Sequence):
|
|
low = repeat(low, size)
|
|
elif len(low) < size:
|
|
raise IndexError("low must be at least the size of the shorter 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 the shorter individual: %d < %d" % (len(up), size))
|
|
|
|
for i, xl, xu in zip(xrange(size), low, up):
|
|
if random.random() <= 0.5:
|
|
# This epsilon should probably be changed for 0 since
|
|
# floating point arithmetic in Python is safer
|
|
if abs(ind1[i] - ind2[i]) > 1e-14:
|
|
x1 = min(ind1[i], ind2[i])
|
|
x2 = max(ind1[i], ind2[i])
|
|
rand = random.random()
|
|
|
|
beta = 1.0 + (2.0 * (x1 - xl) / (x2 - x1))
|
|
alpha = 2.0 - beta**-(eta + 1)
|
|
if rand <= 1.0 / alpha:
|
|
beta_q = (rand * alpha)**(1.0 / (eta + 1))
|
|
else:
|
|
beta_q = (1.0 / (2.0 - rand * alpha))**(1.0 / (eta + 1))
|
|
|
|
c1 = 0.5 * (x1 + x2 - beta_q * (x2 - x1))
|
|
|
|
beta = 1.0 + (2.0 * (xu - x2) / (x2 - x1))
|
|
alpha = 2.0 - beta**-(eta + 1)
|
|
if rand <= 1.0 / alpha:
|
|
beta_q = (rand * alpha)**(1.0 / (eta + 1))
|
|
else:
|
|
beta_q = (1.0 / (2.0 - rand * alpha))**(1.0 / (eta + 1))
|
|
c2 = 0.5 * (x1 + x2 + beta_q * (x2 - x1))
|
|
|
|
c1 = min(max(c1, xl), xu)
|
|
c2 = min(max(c2, xl), xu)
|
|
|
|
if random.random() <= 0.5:
|
|
ind1[i] = c2
|
|
ind2[i] = c1
|
|
else:
|
|
ind1[i] = c1
|
|
ind2[i] = c2
|
|
|
|
return ind1, ind2
|
|
|
|
|
|
######################################
|
|
# Messy Crossovers #
|
|
######################################
|
|
|
|
def cxMessyOnePoint(ind1, ind2):
|
|
"""Executes a one point crossover on :term:`sequence` individual.
|
|
The crossover will in most cases change the individuals size. The two
|
|
individuals are modified in place.
|
|
|
|
:param ind1: The first individual participating in the crossover.
|
|
:param ind2: The second individual participating in the crossover.
|
|
:returns: A tuple of two individuals.
|
|
|
|
This function uses the :func:`~random.randint` function from the python base
|
|
:mod:`random` module.
|
|
"""
|
|
cxpoint1 = random.randint(0, len(ind1))
|
|
cxpoint2 = random.randint(0, len(ind2))
|
|
ind1[cxpoint1:], ind2[cxpoint2:] = ind2[cxpoint2:], ind1[cxpoint1:]
|
|
|
|
return ind1, ind2
|
|
|
|
######################################
|
|
# ES Crossovers #
|
|
######################################
|
|
|
|
def cxESBlend(ind1, ind2, alpha):
|
|
"""Executes a blend crossover on both, the individual and the strategy. The
|
|
individuals shall be a :term:`sequence` and must have a :term:`sequence`
|
|
:attr:`strategy` attribute. Adjustement of the minimal strategy shall be done
|
|
after the call to this function, consider using a decorator.
|
|
|
|
:param ind1: The first evolution strategy participating in the crossover.
|
|
:param ind2: The second evolution strategy participating in the crossover.
|
|
:param alpha: Extent of the interval in which the new values can be drawn
|
|
for each attribute on both side of the parents' attributes.
|
|
:returns: A tuple of two evolution strategies.
|
|
|
|
This function uses the :func:`~random.random` function from the python base
|
|
:mod:`random` module.
|
|
"""
|
|
for i, (x1, s1, x2, s2) in enumerate(zip(ind1, ind1.strategy,
|
|
ind2, ind2.strategy)):
|
|
# Blend the values
|
|
gamma = (1. + 2. * alpha) * random.random() - alpha
|
|
ind1[i] = (1. - gamma) * x1 + gamma * x2
|
|
ind2[i] = gamma * x1 + (1. - gamma) * x2
|
|
# Blend the strategies
|
|
gamma = (1. + 2. * alpha) * random.random() - alpha
|
|
ind1.strategy[i] = (1. - gamma) * s1 + gamma * s2
|
|
ind2.strategy[i] = gamma * s1 + (1. - gamma) * s2
|
|
|
|
return ind1, ind2
|
|
|
|
def cxESTwoPoint(ind1, ind2):
|
|
"""Executes a classical two points crossover on both the individuals and their
|
|
strategy. The individuals shall be a :term:`sequence` and must have a
|
|
:term:`sequence` :attr:`strategy` attribute. The crossover points for the
|
|
individual and the strategy are the same.
|
|
|
|
:param ind1: The first evolution strategy participating in the crossover.
|
|
:param ind2: The second evolution strategy participating in the crossover.
|
|
:returns: A tuple of two evolution strategies.
|
|
|
|
This function uses the :func:`~random.randint` function from the python base
|
|
:mod:`random` module.
|
|
"""
|
|
size = min(len(ind1), len(ind2))
|
|
|
|
pt1 = random.randint(1, size)
|
|
pt2 = random.randint(1, size - 1)
|
|
if pt2 >= pt1:
|
|
pt2 += 1
|
|
else: # Swap the two cx points
|
|
pt1, pt2 = pt2, pt1
|
|
|
|
ind1[pt1:pt2], ind2[pt1:pt2] = ind2[pt1:pt2], ind1[pt1:pt2]
|
|
ind1.strategy[pt1:pt2], ind2.strategy[pt1:pt2] = \
|
|
ind2.strategy[pt1:pt2], ind1.strategy[pt1:pt2]
|
|
|
|
return ind1, ind2
|
|
|
|
def cxESTwoPoints(ind1, ind2):
|
|
"""
|
|
.. deprecated:: 1.0
|
|
The function has been renamed. Use :func:`cxESTwoPoint` instead.
|
|
"""
|
|
return cxESTwoPoints(ind1, ind2)
|
|
|
|
# List of exported function names.
|
|
__all__ = ['cxOnePoint', 'cxTwoPoint', 'cxUniform', 'cxPartialyMatched',
|
|
'cxUniformPartialyMatched', 'cxOrdered', 'cxBlend',
|
|
'cxSimulatedBinary','cxSimulatedBinaryBounded', 'cxMessyOnePoint',
|
|
'cxESBlend', 'cxESTwoPoint']
|
|
|
|
# Deprecated functions
|
|
__all__.extend(['cxTwoPoints', 'cxESTwoPoints']) |