Source code for pyec.distribution.ec.es

from pyec.distribution.convolution import Convolution
from pyec.distribution import Gaussian as SimpleGaussian
from pyec.distribution import BernoulliTernary as SimpleBernoulli
from pyec.distribution import FixedCube
from pyec.distribution.bayes.mutators import *
from pyec.distribution.bayes.sample import DAGSampler
from pyec.distribution.ec.mutators import *
from pyec.distribution.ec.selectors import *
from pyec.config import Config, ConfigBuilder

import logging
log = logging.getLogger(__file__)  
      
class SimpleExtension(PopulationDistribution):
   def __init__(self, toExtend, extension):
      self.toExtend = toExtend
      self.extension = extension
      self.config = self.toExtend.config
    
   def __call__(self):
      return append(self.toExtend.__call__(), self.extension, axis=0)

   def batch(self, popSize):
      return [self.__call__() for i in xrange(popSize)]

   def update(self, generation, population):
      self.toExtend.update(generation, population)

[docs]class ESConfigurator(ConfigBuilder): """ A :class:`ConfigBuilder` for a standard (mu/rho +, lambda)--ES. By default: * mu = 10 * lambda = 50 * rho = 1 (no crossover) * "Plus" style selection is used. * If rho > 1, dominant crossover is used. * Adaptive mutation is used. """ def __init__(self, *args): super(ESConfigurator, self).__init__(EvolutionStrategy) self.cfg.crossover = "dominant" self.cfg.selection = "plus" self.cfg.mu = 10 self.cfg.lmbda = 50 self.cfg.rho = 1 self.cfg.space = "real" self.cfg.mutation = "es" self.cfg.cmaCumulation = .025 self.cfg.cmaCorrelation = .025 self.cfg.cmaDamping = .00005
[docs]class EvolutionStrategy(Convolution): """ Implements a configurable Evolution Strategy. See <http://en.wikipedia.org/wiki/Evolution_strategy> for details and references. Config parameters: * mu -- The number of parents to create the next generation * rho -- The number of parents for crossover * selection -- The type of selection, either "plus" or "comma" * crossover -- The type of crossover, either "dominant" or "intermediate" * mutation -- Either "cma" or "es"; "es" is default. * dim -- The dimension of the binary or real space being optimized * space -- Either "real" or "binary"; the type of vector space being optimized * bounded -- Whether the search domain is constrained. * populationSize -- The size of the population for each generation. The parameter "lambda" is determined by the populationSize and mu, along with the choice of selection (plus or comma). In real space, initial distribution is either a :class:`FixedCube` if the search is constrained, or a :class:`Gaussian` if not. In binary space, the initial distribution is a random :class:`Bernoulli`. Standard mutation ("es") adapts the mutation parameters for each member of the population. If mutation is "cma", then the algorithm of Hansen and Ostermeier (1996) is used. Note that the 1996 algorithm for CMA differs from the modern version (2001) and maintains separate mutation parameters for each solution. Adaptive parameters are not implemented for binary spaces. Extra parameters for CMA: * cmaCumulation (.025) * cmaCorrelation (.025) * cmaDamping (.00005) """ unsorted = False def __init__(self, config): """ Config options: mu - number of parents rho - number of parents for crossover selection - (plus, comma) crossover - (dominant, intermediate) """ self.config = config self.selectors = [] self.selectors.append(EvolutionStrategySelection(config)) self.selector = Convolution(self.selectors) self.mutators = [] if config.rho > 1: if config.crossover == 'dominant': crosser = DominantCrosser(config) elif config.crossover == 'intermediate': crosser = IntermediateCrosser(config) else: raise Exception, "Unknown crossover method" order = config.rho self.mutators.append(Crossover(self.selector, crosser, order)) if config.space == 'real': if hasattr(config, 'mutation') and config.mutation == 'cma': self.mutators.append(CorrelatedEndogeneousGaussian(config)) if config.bounded: initial = SimpleExtension(FixedCube(config), self.buildRotation(config)) else: initial = SimpleExtension(SimpleGaussian(config), self.buildRotation(config)) else: self.mutators.append(EndogeneousGaussian(config)) if config.bounded: initial = SimpleExtension(FixedCube(config), ones(config.dim)) else: initial = SimpleExtension(SimpleGaussian(config), ones(config.dim)) elif config.space == 'binary': bitFlip = 0.05 if hasattr(config, 'bitFlipProbs'): bitFlip = config.bitFlipProbs self.mutators.append(Bernoulli(bitFlip)) initial = SimpleBernoulli(config) else: raise Exception, "Unknown space" self.mutator = Convolution(self.mutators) super(EvolutionStrategy, self).__init__([self.selector, self.mutator], initial) def convert(self, x): return x[:self.config.dim] def buildRotation(self, config): ret = [] for i in xrange(config.dim): for j in xrange(config.dim): if i == j: ret.append(config.varInit) elif j > i: ret.append(0.0) ret = append(array(ret), zeros(config.dim), axis=0) ret = append(array(ret), ones(config.dim), axis=0) ret = append(array(ret), zeros(config.dim), axis=0) return ret @classmethod def configurator(cls): return ESConfigurator(cls)