from numpy import *
import copy, traceback, sys
from pyec.distribution.basic import PopulationDistribution
from pyec.util.partitions import Point, Partition, ScoreTree, Segment
from pyec.util.TernaryString import TernaryString
import logging
logger = logging.getLogger(__file__)
[docs]class Selection(PopulationDistribution):
"""A selection method"""
pass
[docs]class BestSelection(Selection):
def __init__(self, config):
super(Selection, self).__init__(config)
self.best = None
self.score = None
def __call__(self):
return self.best
def update(self, generation, population):
for x, s in population:
if s >= self.score:
self.best = x
self.score = s
[docs]class EvolutionStrategySelection(Selection):
def __init__(self, config):
super(EvolutionStrategySelection, self).__init__(config)
self.total = 0
self.mu = config.mu
self.plus = config.selection == 'plus' # plus or comma selection, for ES
def __call__(self):
idx = random.randint(0,self.mu-1)
return self.population[idx]
def batch(self, popSize):
if self.plus:
return self.population \
+ [self.__call__() for i in xrange(popSize - self.mu)]
else:
return [self.__call__() for i in xrange(popSize)]
[docs] def update(self, generation, population):
"""the population is ordered by score, take the first mu as parents"""
self.population = [x for x,s in population][:self.mu]
[docs]class Proportional(Selection):
def __init__(self, config):
super(Proportional, self).__init__(config)
self.total = 0
self.matchedPopulation = []
def __call__(self):
rnd = random.random_sample() * self.total
for x,amt in self.matchedPopulation:
if amt >= rnd:
return x
return self.matchedPopulation[-1][0]
def batch(self, popSize):
return [self.__call__() for i in xrange(popSize)]
def update(self, generation, population):
self.population = copy.deepcopy(population)
self.total = sum([s for x,s in population])
self.matchedPopulation = []
amt = 0
for x,s in population:
amt += s
self.matchedPopulation.append((x,amt))
return self.population
[docs]class Tournament(Selection):
def __init__(self, config):
super(Tournament, self).__init__(config)
self.pressure = config.selectionPressure
self.total = 0
self.matchedPopulation = []
def __call__(self):
rnd = random.random_sample() * self.total
for x,amt in self.matchedPopulation:
if amt >= rnd:
return x
return self.matchedPopulation[-1][0]
def batch(self, popSize):
return [self.__call__() for i in xrange(popSize)]
def update(self, generation, population):
self.population = copy.deepcopy(population)
self.matchedPopulation = []
amt = self.pressure
self.total = 0
for x,s in population:
self.matchedPopulation.append((x,amt))
self.total += amt
amt *= (1 - self.pressure)
return self.population
[docs]class Ranker(object):
def __call__(self, rank, popSize):
pass
[docs]class LinearRanker(Ranker):
def __init__(self, pressure):
"""pressure between 1.0 and 2.0"""
self.pressure = pressure
def __call__(self, rank, popSize):
return 2 - self.pressure + (2 * (self.pressure-1))* ((rank-1.0)/(popSize - 1.0))
[docs]class NonlinearRanker(Ranker):
def __init__(self, pressure, popSize):
self.pressure = pressure
self.coeffs = [self.pressure for i in xrange(popSize)]
self.coeffs[0] -= popSize
self.root = roots(self.coeffs)[0].real
def __call__(self, rank, popSize):
""" root is root of (pressure * sum_k=0^(popSize-1) x^k) - popSize * x ^(popSize - 1)"""
return self.root ** (rank - 1.0)
[docs]class Ranking(Selection):
"""Takes a ranking function which weights individuals according to rank
Rank is 1 for lowest, K for highest in population of size K"""
def __init__(self, config):
super(Ranking, self).__init__(config)
self.ranker = config.ranker
self.total = 0
self.matchedPopulation = []
def __call__(self):
rnd = random.random_sample() * self.total
for x,amt in self.matchedPopulation:
if amt >= rnd:
return x
return self.matchedPopulation[-1][0]
def density(self, idx):
return self.ranker(idx, len(self.matchedPopulation)) / self.total
def batch(self, popSize):
return [self.__call__() for i in xrange(popSize)]
def update(self, generation, population):
self.population = population # copy.deepcopy(population)
self.matchedPopulation = []
amt = 0
idx = len(population)
for x,s in population:
amt += self.ranker(idx, len(population))
self.matchedPopulation.append((x,amt))
idx -= 1
self.total = amt
return self.population
[docs]class Elitist(Selection):
def __init__(self, config):
super(Elitist, self).__init__(config)
self.maxScore = -1e100
self.maxOrg = None
self.population = None
def batch(self, popSize):
return self.population
def update(self, generation, population):
if population[0][1] > self.maxScore or self.maxOrg is None:
self.maxScore = population[0][1]
self.maxOrg = population[0][0]
self.population = copy.deepcopy(population)
else:
self.population = [(self.maxOrg, self.maxScore)]
self.population.extend(population)
self.population = self.population[:-1]
[docs]class Annealing(Selection):
def __init__(self, config):
super(Annealing, self).__init__(config)
self.n = 0
self.ids = []
self.segment = None
self.activeField = config.activeField
config.taylorCenter = 1.0
if not hasattr(config, 'taylorDepth'):
config.taylorDepth = 0
def __call__(self, **kwargs):
# handle initial case
id = None
area = 1.0
if self.n == 0:
center = self.config.initialDistribution()
else:
# select a mixture point
try:
point, area = self.sample()
center = getattr(point, self.activeField)
id = point.id
self.ids.append(id)
except Exception, msg:
traceback.print_exc(file=sys.stdout)
center = self.config.initialDistribution()
if self.config.passArea:
return center, area
else:
return center
def batch(self, m):
self.ids = []
ret = [self() for i in xrange(m)]
self.config.primaryPopulation = self.ids
return ret
def update(self, n, population):
rerun = self.n == n
self.n = n
if hasattr(self.config, 'anneal') and not self.config.anneal:
self.temp = 1.0
else:
self.temp = log(n)
if self.segment is None:
self.segment = Segment.objects.get(name=self.config.segment)
if not rerun:
if hasattr(self.config.fitness, 'train'):
self.config.fitness.train(self, n)
[docs]class ProportionalAnnealing(Annealing):
def __init__(self, config):
if not hasattr(config, 'taylorDepth'):
config.taylorDepth = 10
super(ProportionalAnnealing, self).__init__(config)
def sample(self):
return Point.objects.sampleProportional(self.segment, self.temp, self.config)
#print "sampled: ", ret.binary, ret.score
#return ret
def update(self, n, population):
rerun = n == self.n
super(ProportionalAnnealing, self).update(n, population)
if not rerun and .5 * floor(2*self.temp) > self.config.taylorCenter:
ScoreTree.objects.resetTaylor(self.segment, .5 * floor(2 *self.temp), self.config)
[docs]class TournamentAnnealing(Annealing):
def sample(self):
return Point.objects.sampleTournament(self.segment, self.temp, self.config)
class TournamentNonAnnealing(TournamentAnnealing):
def update(self, n, population):
super(TournamentNonAnnealing, self).update(n, population)
# now the population serves as the starting point
self.temp = 1.0
class TournamentAnnealingSecondary(Annealing):
def sample(self):
if self.index >= len(self.config.primaryPopulation):
self.index = 0
lastId = self.config.primaryPopulation[self.index]
self.index += 1
return Point.objects.sampleTournamentSecondary(self.segment, lastId, self.temp, self.config)
def batch(self, m):
ret = [self() for i in xrange(m)]
return ret
def update(self, n, population):
super(TournamentAnnealingSecondary, self).update(n, population)
# now the population serves as the starting point
self.index = 0