177 lines
7.1 KiB
Python
177 lines
7.1 KiB
Python
![]() |
from __future__ import division
|
||
|
import random
|
||
|
|
||
|
from functools import partial
|
||
|
from operator import attrgetter
|
||
|
|
||
|
######################################
|
||
|
# Selections #
|
||
|
######################################
|
||
|
|
||
|
def selRandom(individuals, k):
|
||
|
"""Select *k* individuals at random from the input *individuals* with
|
||
|
replacement. The list returned contains references to the input
|
||
|
*individuals*.
|
||
|
|
||
|
:param individuals: A list of individuals to select from.
|
||
|
:param k: The number of individuals to select.
|
||
|
:returns: A list of selected individuals.
|
||
|
|
||
|
This function uses the :func:`~random.choice` function from the
|
||
|
python base :mod:`random` module.
|
||
|
"""
|
||
|
return [random.choice(individuals) for i in xrange(k)]
|
||
|
|
||
|
|
||
|
def selBest(individuals, k):
|
||
|
"""Select the *k* best individuals among the input *individuals*. The
|
||
|
list returned contains references to the input *individuals*.
|
||
|
|
||
|
:param individuals: A list of individuals to select from.
|
||
|
:param k: The number of individuals to select.
|
||
|
:returns: A list containing the k best individuals.
|
||
|
"""
|
||
|
return sorted(individuals, key=attrgetter("fitness"), reverse=True)[:k]
|
||
|
|
||
|
|
||
|
def selWorst(individuals, k):
|
||
|
"""Select the *k* worst individuals among the input *individuals*. The
|
||
|
list returned contains references to the input *individuals*.
|
||
|
|
||
|
:param individuals: A list of individuals to select from.
|
||
|
:param k: The number of individuals to select.
|
||
|
:returns: A list containing the k worst individuals.
|
||
|
"""
|
||
|
return sorted(individuals, key=attrgetter("fitness"))[:k]
|
||
|
|
||
|
|
||
|
def selTournament(individuals, k, tournsize):
|
||
|
"""Select *k* individuals from the input *individuals* using *k*
|
||
|
tournaments of *tournsize* individuals. The list returned contains
|
||
|
references to the input *individuals*.
|
||
|
|
||
|
:param individuals: A list of individuals to select from.
|
||
|
:param k: The number of individuals to select.
|
||
|
:param tournsize: The number of individuals participating in each tournament.
|
||
|
:returns: A list of selected individuals.
|
||
|
|
||
|
This function uses the :func:`~random.choice` function from the python base
|
||
|
:mod:`random` module.
|
||
|
"""
|
||
|
chosen = []
|
||
|
for i in xrange(k):
|
||
|
aspirants = selRandom(individuals, tournsize)
|
||
|
chosen.append(max(aspirants, key=attrgetter("fitness")))
|
||
|
return chosen
|
||
|
|
||
|
def selRoulette(individuals, k):
|
||
|
"""Select *k* individuals from the input *individuals* using *k*
|
||
|
spins of a roulette. The selection is made by looking only at the first
|
||
|
objective of each individual. The list returned contains references to
|
||
|
the input *individuals*.
|
||
|
|
||
|
:param individuals: A list of individuals to select from.
|
||
|
:param k: The number of individuals to select.
|
||
|
:returns: A list of selected individuals.
|
||
|
|
||
|
This function uses the :func:`~random.random` function from the python base
|
||
|
:mod:`random` module.
|
||
|
|
||
|
.. warning::
|
||
|
The roulette selection by definition cannot be used for minimization
|
||
|
or when the fitness can be smaller or equal to 0.
|
||
|
"""
|
||
|
s_inds = sorted(individuals, key=attrgetter("fitness"), reverse=True)
|
||
|
sum_fits = sum(ind.fitness.values[0] for ind in individuals)
|
||
|
|
||
|
chosen = []
|
||
|
for i in xrange(k):
|
||
|
u = random.random() * sum_fits
|
||
|
sum_ = 0
|
||
|
for ind in s_inds:
|
||
|
sum_ += ind.fitness.values[0]
|
||
|
if sum_ > u:
|
||
|
chosen.append(ind)
|
||
|
break
|
||
|
|
||
|
return chosen
|
||
|
|
||
|
|
||
|
def selDoubleTournament(individuals, k, fitness_size, parsimony_size, fitness_first):
|
||
|
"""Tournament selection which use the size of the individuals in order
|
||
|
to discriminate good solutions. This kind of tournament is obviously
|
||
|
useless with fixed-length representation, but has been shown to
|
||
|
significantly reduce excessive growth of individuals, especially in GP,
|
||
|
where it can be used as a bloat control technique (see
|
||
|
[Luke2002fighting]_). This selection operator implements the double
|
||
|
tournament technique presented in this paper.
|
||
|
|
||
|
The core principle is to use a normal tournament selection, but using a
|
||
|
special sample function to select aspirants, which is another tournament
|
||
|
based on the size of the individuals. To ensure that the selection
|
||
|
pressure is not too high, the size of the size tournament (the number
|
||
|
of candidates evaluated) can be a real number between 1 and 2. In this
|
||
|
case, the smaller individual among two will be selected with a probability
|
||
|
*size_tourn_size*/2. For instance, if *size_tourn_size* is set to 1.4,
|
||
|
then the smaller individual will have a 0.7 probability to be selected.
|
||
|
|
||
|
.. note::
|
||
|
In GP, it has been shown that this operator produces better results
|
||
|
when it is combined with some kind of a depth limit.
|
||
|
|
||
|
:param individuals: A list of individuals to select from.
|
||
|
:param k: The number of individuals to select.
|
||
|
:param fitness_size: The number of individuals participating in each \
|
||
|
fitness tournament
|
||
|
:param parsimony_size: The number of individuals participating in each \
|
||
|
size tournament. This value has to be a real number\
|
||
|
in the range [1,2], see above for details.
|
||
|
:param fitness_first: Set this to True if the first tournament done should \
|
||
|
be the fitness one (i.e. the fitness tournament producing aspirants for \
|
||
|
the size tournament). Setting it to False will behaves as the opposite \
|
||
|
(size tournament feeding fitness tournaments with candidates). It has been \
|
||
|
shown that this parameter does not have a significant effect in most cases\
|
||
|
(see [Luke2002fighting]_).
|
||
|
:returns: A list of selected individuals.
|
||
|
|
||
|
.. [Luke2002fighting] Luke and Panait, 2002, Fighting bloat with
|
||
|
nonparametric parsimony pressure
|
||
|
"""
|
||
|
assert (1 <= parsimony_size <= 2), "Parsimony tournament size has to be in the range [1, 2]."
|
||
|
|
||
|
def _sizeTournament(individuals, k, select):
|
||
|
chosen = []
|
||
|
for i in xrange(k):
|
||
|
# Select two individuals from the population
|
||
|
# The first individual has to be the shortest
|
||
|
prob = parsimony_size / 2.
|
||
|
ind1, ind2 = select(individuals, k=2)
|
||
|
|
||
|
if len(ind1) > len(ind2):
|
||
|
ind1, ind2 = ind2, ind1
|
||
|
elif len(ind1) == len(ind2):
|
||
|
# random selection in case of a tie
|
||
|
prob = 0.5
|
||
|
|
||
|
# Since size1 <= size2 then ind1 is selected
|
||
|
# with a probability prob
|
||
|
chosen.append(ind1 if random.random() < prob else ind2)
|
||
|
|
||
|
return chosen
|
||
|
|
||
|
def _fitTournament(individuals, k, select):
|
||
|
chosen = []
|
||
|
for i in xrange(k):
|
||
|
aspirants = select(individuals, k=fitness_size)
|
||
|
chosen.append(max(aspirants, key=attrgetter("fitness")))
|
||
|
return chosen
|
||
|
|
||
|
if fitness_first:
|
||
|
tfit = partial(_fitTournament, select=selRandom)
|
||
|
return _sizeTournament(individuals, k, tfit)
|
||
|
else:
|
||
|
tsize = partial(_sizeTournament, select=selRandom)
|
||
|
return _fitTournament(individuals, k, tsize)
|
||
|
|
||
|
__all__ = ['selRandom', 'selBest', 'selWorst', 'selRoulette',
|
||
|
'selTournament', 'selDoubleTournament']
|