def __init__(self):
        """Genome Constructor"""
        self.evaluator = FunctionSlot("Evaluator")
        self.initializator = FunctionSlot("Initializator")
        self.mutator = FunctionSlot("Mutator")
        self.crossover = FunctionSlot("Crossover")

        self.internalParams = {}
        self.score = 0.0
        self.fitness = 0.0
예제 #2
0
파일: ParticleBase.py 프로젝트: DiNAi/pypso
	def __init__(self):
		""" Particle Constructor """
		self.evaluator = FunctionSlot("Evaluator")
		self.position_initializator = FunctionSlot("Position Initializator")
		self.velocity_initializator = FunctionSlot(" Velocity Initializator")
		self.position_communicator = FunctionSlot("Position Communicator")
		self.information_communicator = FunctionSlot("Information Communicator")
		
		self.allSlots = [self.evaluator, self.position_initializator,
					self.velocity_initializator, self.position_communicator, self.information_communicator]
		
		self.internalParams = {}
		self.fitness = 0.0
		self.ownBestFitness = 0.0
예제 #3
0
 def __init__(self):
     
    """Genome Constructor"""
    self.evaluator = FunctionSlot("Evaluator")
    self.initializator = FunctionSlot("Initializator")
    self.mutator = FunctionSlot("Mutator")
    self.crossover = FunctionSlot("Crossover")
    ###
    self.list_dic={}
    self.list_margin={}
    
    self.totalmoney=0
    #my_set
    self.internalParams = {}
    self.score = 0.0
    self.fitness = 0.0
예제 #4
0
 def __init__(self):
     self.selector = FunctionSlot("Selector")
     self.GAEngine = None
     self.nMigrationRate = Consts.CDefGenMigrationRate
     self.nIndividuals = Consts.CDefMigrationNIndividuals
     self.nReplacement = Consts.CDefGenMigrationReplacement
     self.networkCompression = 9
예제 #5
0
   def __init__(self, genome, seed=None, interactiveMode=True):
      """ Initializator of GSimpleGA """
      if seed: random.seed(seed)

      if type(interactiveMode) != BooleanType:
         Util.raiseException("Interactive Mode option must be True or False", TypeError)
      
      if not isinstance(genome, GenomeBase):
         Util.raiseException("The genome must be a GenomeBase subclass", TypeError)

      self.internalPop  = GPopulation(genome)
      self.nGenerations = Consts.CDefGAGenerations
      self.pMutation    = Consts.CDefGAMutationRate
      self.pCrossover   = Consts.CDefGACrossoverRate
      self.nElitismReplacement = Consts.CDefGAElitismReplacement
      self.setPopulationSize(Consts.CDefGAPopulationSize)
      self.minimax      = Consts.minimaxType["maximize"]
      self.elitism      = True

      # Adapters
      self.dbAdapter        = None
      self.migrationAdapter = None
      
      self.time_init       = None
      self.interactiveMode = interactiveMode
      self.interactiveGen  = -1
      self.GPMode = False

      self.selector            = FunctionSlot("Selector")
      self.stepCallback        = FunctionSlot("Generation Step Callback")
      self.terminationCriteria = FunctionSlot("Termination Criteria")
      self.selector.set(Consts.CDefGASelector)
      self.allSlots            = [ self.selector, self.stepCallback, self.terminationCriteria ]

      self.internalParams = {}
      ####
     
      #####
      self.currentGeneration = 0

      # GP Testing
      for classes in Consts.CDefGPGenomes:
         if  isinstance(self.internalPop.oneSelfGenome, classes):
            self.setGPMode(True)
            break
      
      logging.debug("A GA Engine was created, nGenerations=%d", self.nGenerations)
예제 #6
0
 def __init__(self, host, port, group_name):
    self.myself = None
    self.groupName = group_name
    self.selector = FunctionSlot("Selector")
    self.setMyself(host, port)
    self.GAEngine = None
    self.nMigrationRate = Consts.CDefGenMigrationRate
    self.nIndividuals = Consts.CDefMigrationNIndividuals
    self.nReplacement = Consts.CDefGenMigrationReplacement
    self.networkCompression = 9
예제 #7
0
   def __init__(self, genome):
      """ The GPopulation Class creator """

      if isinstance(genome, GPopulation):
         self.oneSelfGenome  = genome.oneSelfGenome
         self.internalPop    = []
         self.internalPopRaw = []
         self.popSize       = genome.popSize
         self.sortType      = genome.sortType
         self.sorted        = False
         self.minimax       = genome.minimax
         self.scaleMethod   = genome.scaleMethod
         self.allSlots      = [self.scaleMethod]
         self.evaluator     = FunctionSlot("Evaluator")

         self.internalParams = genome.internalParams
         self.multiProcessing = genome.multiProcessing

         self.statted = False
         self.stats   = Statistics()
         return

      logging.debug("New population instance, %s class genomes.", genome.__class__.__name__)
      self.oneSelfGenome  = genome
      self.internalPop    = []
      self.internalPopRaw = []
      self.popSize       = 0
      self.sortType      = Consts.CDefPopSortType
      self.sorted        = False
      self.minimax       = Consts.CDefPopMinimax
      self.scaleMethod   = FunctionSlot("Scale Method")
      self.scaleMethod.set(Consts.CDefPopScale)
      self.allSlots      = [self.scaleMethod]
      self.evaluator     = FunctionSlot("Evaluator")

      self.internalParams = {}
      self.multiProcessing = (False, False)

      # Statistics
      self.statted = False
      self.stats   = Statistics()
예제 #8
0
파일: Pso.py 프로젝트: DiNAi/pypso
	def __init__(self,topology,seed=None,interactiveMode=True):
		""" Initializator of PSO """
		#random seed
		random.seed(seed)
		#Pso type used by the particle
		self.psoType = Consts.CDefPsoType
		#Topology used
		self.topology = topology
		#Set the population size
		self.setSwarmSize(Consts.CDefSwarmSize)
		#Cognitive and Social Coefficients
		self.C1,self.C2 = Consts.CDefCoefficients
        #Time steps
		self.timeSteps = Consts.CDefSteps
		#Interactive Mode (True or False)
		self.interactiveMode = interactiveMode
		#Current step
		self.currentStep = 0
		#Inertia Factor Minus
		self.inertiaFactorMinus = None
		#Inertia coefficient
		self.inertiaFactor = None
		#Time initial
		self.time_init = None
	    #Optimization type
		self.minimax = Consts.minimaxType["minimize"]
		#Report file adapter 
		self.reportAdapter = None
		#Step Callback
		self.stepCallback = FunctionSlot("Step Callback")
		#Termination Criteria
		self.terminationCriteria = FunctionSlot("Termination Criteria")
		#All slots
		self.allSlots = [self.stepCallback, self.terminationCriteria]
		
		print "A PSO Engine was created, timeSteps=% d" % ( self.timeSteps, )
예제 #9
0
   def __init__(self, genome):
      """ The GPopulation Class creator """

      logging.debug("New population instance, %s class genomes.", genome.__class__.__name__)
      self.oneSelfGenome = genome
      self.internalPop   = []
      self.popSize       = 0
      self.sortType      = Consts.CDefPopSortType
      self.sorted        = False
      self.minimax       = Consts.CDefPopMinimax
      self.scaleMethod   = FunctionSlot("Scale Method")
      self.scaleMethod.set(Consts.CDefPopScale)
      self.allSlots      = [self.scaleMethod]

      # Statistics
      self.statted = False
      self.stats   = Statistics()
예제 #10
0
    def __init__(self, genome, seed=None, interactiveMode=True):
        """ Initializator of GSimpleGA """
        if seed: random.seed(seed)

        if type(interactiveMode) != BooleanType:
            Util.raiseException(
                "Interactive Mode option must be True or False", TypeError)

        if not isinstance(genome, GenomeBase):
            Util.raiseException("The genome must be a GenomeBase subclass",
                                TypeError)

        self.genome = genome
        self.internalPop = GPopulation(genome)
        self.nGenerations = Consts.CDefGAGenerations
        self.pMutation = Consts.CDefGAMutationRate
        self.pCrossover = Consts.CDefGACrossoverRate
        self.nElitismReplacement = Consts.CDefGAElitismReplacement
        self.setPopulationSize(Consts.CDefGAPopulationSize)
        self.minimax = Consts.minimaxType["maximize"]
        self.elitism = True

        # Adapters
        self.dbAdapter = None
        self.migrationAdapter = None

        self.time_init = None
        self.interactiveMode = interactiveMode
        self.interactiveGen = -1
        self.GPMode = False

        self.selector = FunctionSlot("Selector")
        self.stepCallback = FunctionSlot("Generation Step Callback")
        self.terminationCriteria = FunctionSlot("Termination Criteria")
        self.selector.set(Consts.CDefGASelector)
        self.allSlots = [
            self.selector, self.stepCallback, self.terminationCriteria
        ]

        self.internalParams = {}

        self.currentGeneration = 0

        # GP Testing
        for classes in Consts.CDefGPGenomes:
            if isinstance(self.internalPop.oneSelfGenome, classes):
                self.setGPMode(True)
                break

        logging.debug("A GA Engine was created, nGenerations=%d",
                      self.nGenerations)
예제 #11
0
    def __init__(self):
        self.myself = None
        self.selector = FunctionSlot("Selector")
        self.GAEngine = None
        self.nMigrationRate = Consts.CDefGenMigrationRate
        self.nIndividuals = Consts.CDefMigrationNIndividuals
        self.nReplacement = Consts.CDefGenMigrationReplacement

        self.comm = MPI.COMM_WORLD
        self.pid = self.comm.rank
        self.best_selector = Selectors.GRankSelector

        #now this is fixed
        if self.pid == 0:
            self.source = self.comm.size - 1
        else:
            self.source = self.comm.rank - 1
        self.dest = (self.comm.rank +1) % (self.comm.size)

        self.all_stars = None
예제 #12
0
    def __init__(self, genome):
        """ The GPopulation Class creator """

        if isinstance(genome, GPopulation):
            self.oneSelfGenome = genome.oneSelfGenome
            self.internalPop = []
            self.internalPopRaw = []
            self.popSize = genome.popSize
            self.sortType = genome.sortType
            self.sorted = False
            self.minimax = genome.minimax
            self.scaleMethod = genome.scaleMethod
            self.allSlots = [self.scaleMethod]
            self.evaluator = FunctionSlot("Evaluator")

            self.internalParams = genome.internalParams
            self.multiProcessing = genome.multiProcessing

            self.statted = False
            self.stats = Statistics()
            return

        logging.debug("New population instance, %s class genomes.",
                      genome.__class__.__name__)
        self.oneSelfGenome = genome
        self.internalPop = []
        self.internalPopRaw = []
        self.popSize = 0
        self.sortType = Consts.CDefPopSortType
        self.sorted = False
        self.minimax = Consts.CDefPopMinimax
        self.scaleMethod = FunctionSlot("Scale Method")
        self.scaleMethod.set(Consts.CDefPopScale)
        self.allSlots = [self.scaleMethod]
        self.evaluator = FunctionSlot("Evaluator")

        self.internalParams = {}
        self.multiProcessing = (False, False)

        # Statistics
        self.statted = False
        self.stats = Statistics()
예제 #13
0
class MPIMigrator(object):
    selector = None
    """ This is the function slot for the selection method
    if you want to change the default selector, you must do this: ::

    migration_scheme.selector.set(Selectors.GRouletteWheel) """

    def __init__(self):
        self.myself = None
        self.selector = FunctionSlot("Selector")
        self.GAEngine = None
        self.nMigrationRate = Consts.CDefGenMigrationRate
        self.nIndividuals = Consts.CDefMigrationNIndividuals
        self.nReplacement = Consts.CDefGenMigrationReplacement

        self.comm = MPI.COMM_WORLD
        self.pid = self.comm.rank
        self.best_selector = Selectors.GRankSelector

        #now this is fixed
        if self.pid == 0:
            self.source = self.comm.size - 1
        else:
            self.source = self.comm.rank - 1
        self.dest = (self.comm.rank +1) % (self.comm.size)

        self.all_stars = None

    def isReady(self):
        """ Returns true if is time to migrate """

        if self.GAEngine.getCurrentGeneration() == 0:
            return False

        if self.GAEngine.getCurrentGeneration() % self.nMigrationRate == 0:
            return True
        else:
            return False

    def getNumReplacement(self):
        """ Return the number of individuals that will be
        replaced in the migration process """
        return self.nReplacement

    def setNumReplacement(self, num_individuals):
        """ Return the number of individuals that will be
        replaced in the migration process

        :param num_individuals: the number of individuals to be replaced
        """
        self.nReplacement = num_individuals

    def getNumIndividuals(self):
        """ Return the number of individuals that will migrate

        :rtype: the number of individuals to be replaced
        """
        return self.nIndividuals

    def setNumIndividuals(self, num_individuals):
        """ Set the number of individuals that will migrate

        :param num_individuals: the number of individuals
        """
        self.nIndividuals = num_individuals

    def setMigrationRate(self, generations):
        """ Sets the generation frequency supposed to migrate
        and receive individuals.

        :param generations: the number of generations
        """
        self.nMigrationRate = generations

    def getMigrationRate(self):
        """ Return the the generation frequency supposed to migrate
        and receive individuals

        :rtype: the number of generations
        """
        return self.nMigrationRate

    def setGAEngine(self, ga_engine):
        """ Sets the GA Engine handler """
        self.GAEngine = ga_engine

    def start(self):
        """ Initializes the migration scheme """
        pass

    def stop(self):
        """ Stops the migration engine """
        pass

    def getGroupName(self):
        """ Gets the group name

        .. note:: all islands of evolution which are supposed to exchange
                  individuals, must have the same group name.
        """
        return self.groupName

    def setGroupName(self, name):
        """ Sets the group name

        :param name: the group name

        .. note:: all islands of evolution which are supposed to exchange
                  individuals, must have the same group name.
        """
        self.groupName = name

    def select(self):
        """ Pickes an individual from population using specific selection method

        :rtype: an individual object
        """
        if self.selector.isEmpty():
            return self.GAEngine.select(popID=self.GAEngine.currentGeneration)
        else:
            for it in self.selector.applyFunctions(self.GAEngine.internalPop,
                                                   popID=self.GAEngine.currentGeneration):
                return it

    def selectPool(self, num_individuals):
        """ Select num_individuals number of individuals and return a pool

        :param num_individuals: the number of individuals to select
        :rtype: list with individuals
        """
        pool = [self.select() for _ in xrange(num_individuals)]
        return pool

    def gather_bests(self):
        '''
        Collect all the best individuals from the various populations. The
        result is stored in process 0
        '''
        best_guy = self.best_selector(self.GAEngine.internalPop,
                                      popID=self.GAEngine.currentGeneration)

        self.all_stars = self.comm.gather(sendobj = best_guy, root = 0)


    def exchange(self):
        """ This is the main method, is where the individuals
        are exchanged """

        if not self.isReady():
            return

        pool_to_send = self.selectPool(self.getNumIndividuals())
        pool_received  = self.comm.sendrecv(sendobj=pool_to_send,
                                            dest=self.dest,
                                            sendtag=0,
                                            recvobj=None,
                                            source=self.source,
                                            recvtag=0)

        population = self.GAEngine.getPopulation()

        pool = pool_received
        for i in xrange(self.getNumReplacement()):
            if len(pool) <= 0:
                break
            choice = rand_choice(pool)
            pool.remove(choice)

            # replace the worst
            population[len(population)-1-i] = choice

        self.gather_bests()
예제 #14
0
class GenomeBase:
   """ GenomeBase Class - The base of all chromosome representation """

   evaluator = None
   """ This is the :term:`evaluation function` slot, you can add
   a function with the *set* method: ::

      genome.evaluator.set(eval_func)
   """

   initializator = None
   """ This is the initialization function of the genome, you
   can change the default initializator using the function slot: ::

      genome.initializator.set(Initializators.G1DListInitializatorAllele)

   In this example, the initializator :func:`Initializators.G1DListInitializatorAllele`
   will be used to create the initial population.
   """

   mutator = None
   """ This is the mutator function slot, you can change the default
   mutator using the slot *set* function: ::

      genome.mutator.set(Mutators.G1DListMutatorSwap)

   """

   premutator = None

   crossover = None
   """ This is the reproduction function slot, the crossover. You
   can change the default crossover method using: ::

      genome.crossover.set(Crossovers.G1DListCrossoverUniform)
   """


   def __init__(self):
      """Genome Constructor"""
      self.evaluator = FunctionSlot("Evaluator")
      self.initializator = FunctionSlot("Initializator")
      self.mutator = FunctionSlot("Mutator")
      self.premutator = FunctionSlot("PreMutator")
      self.crossover = FunctionSlot("Crossover")
 
      self.internalParams = {}
      self.score = 0.0
      self.fitness = 0.0

   def getRawScore(self):
      """ Get the Raw Score of the genome

      :rtype: genome raw score

      """
      return self.score

   def getFitnessScore(self):
      """ Get the Fitness Score of the genome

      :rtype: genome fitness score

      """
      return self.fitness

   def __repr__(self):
      """String representation of Genome"""
      allSlots =  self.allSlots = [ self.evaluator, self.initializator,
                                    self.mutator, self.crossover ]

      ret = "- GenomeBase\n"
      ret+= "\tScore:\t\t\t %.6f\n" % (self.score,)
      ret+= "\tFitness:\t\t %.6f\n\n" % (self.fitness,)
      ret+= "\tParams:\t\t %s\n\n" % (self.internalParams,)

      for slot in allSlots:
         ret+= "\t" + slot.__repr__()
      ret+="\n"

      return ret

   def setParams(self, **args):
      """ Set the internal params

      Example:
         >>> genome.setParams(rangemin=0, rangemax=100, gauss_mu=0, gauss_sigma=1)

      .. note:: All the individuals of the population shares this parameters and uses
                the same instance of this dict.

      :param args: this params will saved in every chromosome for genetic op. use

      """
      self.internalParams.update(args)
   
   def getParam(self, key, nvl=None):
      """ Gets an internal parameter

      Example:
         >>> genome.getParam("rangemax")
         100

      .. note:: All the individuals of the population shares this parameters and uses
                the same instance of this dict.

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      """
      return self.internalParams.get(key, nvl)
      
   def resetStats(self):
      """ Clear score and fitness of genome """
      self.score = 0.0
      self.fitness = 0.0
      
   def evaluate(self, **args):
      """ Called to evaluate genome

      :param args: this parameters will be passes to the evaluator

      """
      self.resetStats()
      for it in self.evaluator.applyFunctions(self, **args):
         self.score += it

   def initialize(self, **args):
      """ Called to initialize genome

      :param args: this parameters will be passed to the initializator

      """
      for it in self.initializator.applyFunctions(self, **args):
         pass

   def mutate(self, **args):
      """ Called to mutate the genome

      :param args: this parameters will be passed to the mutator
      :rtype: the number of mutations returned by mutation operator

      """
      nmuts = 0
      for it in self.mutator.applyFunctions(self, **args):
         nmuts+=it
      return nmuts

   def premutate(self, **args):
      """ Called to pre-mutate the genome

      :param args: this parameters will be passed to the premutator
      :rtype: the number of mutations returned by mutation operator
      """
      nmuts = 0
      for it in self.premutator.applyFunctions(self, **args):
         nmuts+=it
      return nmuts

   def copy(self, g):
      """ Copy the current GenomeBase to 'g'

      :param g: the destination genome      

      .. note:: If you are planning to create a new chromosome representation, you
                **must** implement this method on your class.

      """
      g.score = self.score
      g.fitness = self.fitness
      g.evaluator = self.evaluator
      g.initializator = self.initializator
      g.mutator = self.mutator
      g.premutator = self.premutator
      g.crossover = self.crossover
      #g.internalParams = self.internalParams.copy()
      g.internalParams = self.internalParams
      
   def clone(self):
      """ Clone this GenomeBase

      :rtype: the clone genome   

      .. note:: If you are planning to create a new chromosome representation, you
                **must** implement this method on your class.
      """
      newcopy = GenomeBase()
      self.copy(newcopy)
      return newcopy
예제 #15
0
class GSimpleGA:
   """ GA Engine Class - The Genetic Algorithm Core

   Example:
      >>> ga = GSimpleGA.GSimpleGA(genome)
      >>> ga.selector.set(Selectors.GRouletteWheel)
      >>> ga.setGenerations(120)
      >>> ga.terminationCriteria.set(GSimpleGA.ConvergenceCriteria)

   :param genome: the :term:`Sample Genome`
   :param interactiveMode: this flag enables the Interactive Mode, the default is True
   :param seed: the random seed value

   .. note:: if you use the same random seed, all the runs of algorithm will be the same

   """

   selector = None
   """ This is the function slot for the selection method
   if you want to change the default selector, you must do this: ::

      ga_engine.selector.set(Selectors.GRouletteWheel) """

   stepCallback = None
   """ This is the :term:`step callback function` slot,
   if you want to set the function, you must do this: ::

      def your_func(ga_engine):
         # Here you have access to the GA Engine
         return False

      ga_engine.stepCallback.set(your_func)

   now *"your_func"* will be called every generation.
   When this function returns True, the GA Engine will stop the evolution and show
   a warning, if is False, the evolution continues.
   """

   terminationCriteria = None
   """ This is the termination criteria slot, if you want to set one
   termination criteria, you must do this: ::

      ga_engine.terminationCriteria.set(GSimpleGA.ConvergenceCriteria)

   Now, when you run your GA, it will stop when the population converges.

   There are those termination criteria functions: :func:`GSimpleGA.RawScoreCriteria`, :func:`GSimpleGA.ConvergenceCriteria`, :func:`GSimpleGA.RawStatsCriteria`, :func:`GSimpleGA.FitnessStatsCriteria`

   But you can create your own termination function, this function receives
   one parameter which is the GA Engine, follows an example: ::

      def ConvergenceCriteria(ga_engine):
         pop = ga_engine.getPopulation()
         return pop[0] == pop[len(pop)-1]

   When this function returns True, the GA Engine will stop the evolution and show
   a warning, if is False, the evolution continues, this function is called every
   generation.
   """

   def __init__(self, genome, seed=None, interactiveMode=True):
      """ Initializator of GSimpleGA """
      if seed: random.seed(seed)

      if type(interactiveMode) != BooleanType:
         Util.raiseException("Interactive Mode option must be True or False", TypeError)

      if not isinstance(genome, GenomeBase):
         Util.raiseException("The genome must be a GenomeBase subclass", TypeError)

      self.internalPop  = GPopulation(genome)
      self.nGenerations = Consts.CDefGAGenerations
      self.pMutation    = Consts.CDefGAMutationRate
      self.pCrossover   = Consts.CDefGACrossoverRate
      self.nElitismReplacement = Consts.CDefGAElitismReplacement
      self.setPopulationSize(Consts.CDefGAPopulationSize)
      self.minimax      = Consts.minimaxType["maximize"]
      self.elitism      = True

      # Adapters
      self.dbAdapter        = None
      self.migrationAdapter = None

      self.time_init       = None
      self.interactiveMode = interactiveMode
      self.interactiveGen  = -1
      self.GPMode = False

      self.selector            = FunctionSlot("Selector")
      self.stepCallback        = FunctionSlot("Generation Step Callback")
      self.terminationCriteria = FunctionSlot("Termination Criteria")
      self.selector.set(Consts.CDefGASelector)
      self.allSlots            = [ self.selector, self.stepCallback, self.terminationCriteria ]

      self.internalParams = {}

      self.currentGeneration = 0

      # GP Testing
      for classes in Consts.CDefGPGenomes:
         if  isinstance(self.internalPop.oneSelfGenome, classes):
            self.setGPMode(True)
            break

      logging.debug("A GA Engine was created, nGenerations=%d", self.nGenerations)

   def setGPMode(self, bool_value):
      """ Sets the Genetic Programming mode of the GA Engine

      :param bool_value: True or False
      """
      self.GPMode = bool_value

   def getGPMode(self):
      """ Get the Genetic Programming mode of the GA Engine

      :rtype: True or False
      """
      return self.GPMode

   def __call__(self, *args, **kwargs):
      """ A method to implement a callable object

      Example:
         >>> ga_engine(freq_stats=10)

      .. versionadded:: 0.6
         The callable method.
      """
      if kwargs.get("freq_stats", None):
         return self.evolve(kwargs.get("freq_stats"))
      else:
         return self.evolve()

   def setParams(self, **args):
      """ Set the internal params

      Example:
         >>> ga.setParams(gp_terminals=['x', 'y'])


      :param args: params to save

      ..versionaddd:: 0.6
         Added the *setParams* method.
      """
      self.internalParams.update(args)

   def getParam(self, key, nvl=None):
      """ Gets an internal parameter

      Example:
         >>> ga.getParam("gp_terminals")
         ['x', 'y']

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      ..versionaddd:: 0.6
         Added the *getParam* method.
      """
      return self.internalParams.get(key, nvl)

   def setInteractiveGeneration(self, generation):
      """ Sets the generation in which the GA must enter in the
      Interactive Mode

      :param generation: the generation number, use "-1" to disable

      .. versionadded::0.6
         The *setInteractiveGeneration* method.
      """
      if generation < -1:
         Util.raiseException("Generation must be >= -1", ValueError)
      self.interactiveGen = generation

   def getInteractiveGeneration(self):
      """ returns the generation in which the GA must enter in the
      Interactive Mode

      :rtype: the generation number or -1 if not set

      .. versionadded::0.6
         The *getInteractiveGeneration* method.
      """
      return self.interactiveGen

   def setElitismReplacement(self, numreplace):
      """ Set the number of best individuals to copy to the next generation on the elitism

      :param numreplace: the number of individuals

      .. versionadded:: 0.6
         The *setElitismReplacement* method.

      """
      if numreplace < 1:
         Util.raiseException("Replacement number must be >= 1", ValueError)
      self.nElitismReplacement = numreplace


   def setInteractiveMode(self, flag=True):
      """ Enable/disable the interactive mode

      :param flag: True or False

      .. versionadded: 0.6
         The *setInteractiveMode* method.

      """
      if type(flag) != BooleanType:
         Util.raiseException("Interactive Mode option must be True or False", TypeError)
      self.interactiveMode = flag


   def __repr__(self):
      """ The string representation of the GA Engine """
      ret =  "- GSimpleGA\n"
      ret += "\tGP Mode:\t\t %s\n" % self.getGPMode()
      ret += "\tPopulation Size:\t %d\n" % (self.internalPop.popSize,)
      ret += "\tGenerations:\t\t %d\n" % (self.nGenerations,)
      ret += "\tCurrent Generation:\t %d\n" % (self.currentGeneration,)
      ret += "\tMutation Rate:\t\t %.2f\n" % (self.pMutation,)
      ret += "\tCrossover Rate:\t\t %.2f\n" % (self.pCrossover,)
      ret += "\tMinimax Type:\t\t %s\n" % (Consts.minimaxType.keys()[Consts.minimaxType.values().index(self.minimax)].capitalize(),)
      ret += "\tElitism:\t\t %s\n" % (self.elitism,)
      ret += "\tElitism Replacement:\t %d\n" % (self.nElitismReplacement,)
      ret += "\tDB Adapter:\t\t %s\n" % (self.dbAdapter,)
      for slot in self.allSlots:
         ret+= "\t" + slot.__repr__()
      ret+="\n"
      return ret

   def setMultiProcessing(self, flag=True, full_copy=False):
      """ Sets the flag to enable/disable the use of python multiprocessing module.
      Use this option when you have more than one core on your CPU and when your
      evaluation function is very slow.

      Pyevolve will automaticly check if your Python version has **multiprocessing**
      support and if you have more than one single CPU core. If you don't have support
      or have just only one core, Pyevolve will not use the **multiprocessing**
      feature.

      Pyevolve uses the **multiprocessing** to execute the evaluation function over
      the individuals, so the use of this feature will make sense if you have a
      truly slow evaluation function (which is commom in GAs).

      The parameter "full_copy" defines where the individual data should be copied back
      after the evaluation or not. This parameter is useful when you change the
      individual in the evaluation function.

      :param flag: True (default) or False
      :param full_copy: True or False (default)

      .. warning:: Use this option only when your evaluation function is slow, so you'll
                   get a good tradeoff between the process communication speed and the
                   parallel evaluation. The use of the **multiprocessing** doesn't means
                   always a better performance.

      .. note:: To enable the multiprocessing option, you **MUST** add the *__main__* check
                on your application, otherwise, it will result in errors. See more on the
                `Python Docs <http://docs.python.org/library/multiprocessing.html#multiprocessing-programming>`__
                site.

      .. versionadded:: 0.6
         The `setMultiProcessing` method.

      """
      if type(flag) != BooleanType:
         Util.raiseException("Multiprocessing option must be True or False", TypeError)

      if type(full_copy) != BooleanType:
         Util.raiseException("Multiprocessing 'full_copy' option must be True or False", TypeError)

      self.internalPop.setMultiProcessing(flag, full_copy)

   def setMigrationAdapter(self, migration_adapter=None):
      """ Sets the Migration Adapter

      .. versionadded:: 0.6
         The `setMigrationAdapter` method.
      """

      self.migrationAdapter = migration_adapter
      if self.migrationAdapter is not None:
         self.migrationAdapter.setGAEngine(self)

   def setDBAdapter(self, dbadapter=None):
      """ Sets the DB Adapter of the GA Engine

      :param dbadapter: one of the :mod:`DBAdapters` classes instance

      .. warning:: the use the of a DB Adapter can reduce the speed performance of the
                   Genetic Algorithm.
      """
      if (dbadapter is not None) and (not isinstance(dbadapter, DBBaseAdapter)):
         Util.raiseException("The DB Adapter must be a DBBaseAdapter subclass", TypeError)
      self.dbAdapter = dbadapter

   def setPopulationSize(self, size):
      """ Sets the population size, calls setPopulationSize() of GPopulation

      :param size: the population size

      .. note:: the population size must be >= 2

      """
      if size < 2:
         Util.raiseException("population size must be >= 2", ValueError)
      self.internalPop.setPopulationSize(size)

   def setSortType(self, sort_type):
      """ Sets the sort type, Consts.sortType["raw"]/Consts.sortType["scaled"]

      Example:
         >>> ga_engine.setSortType(Consts.sortType["scaled"])

      :param sort_type: the Sort Type

      """
      if sort_type not in Consts.sortType.values():
         Util.raiseException("sort type must be a Consts.sortType type", TypeError)
      self.internalPop.sortType = sort_type

   def setMutationRate(self, rate):
      """ Sets the mutation rate, between 0.0 and 1.0

      :param rate: the rate, between 0.0 and 1.0

      """
      if (rate>1.0) or (rate<0.0):
         Util.raiseException("Mutation rate must be >= 0.0 and <= 1.0", ValueError)
      self.pMutation = rate

   def setCrossoverRate(self, rate):
      """ Sets the crossover rate, between 0.0 and 1.0

      :param rate: the rate, between 0.0 and 1.0

      """
      if (rate>1.0) or (rate<0.0):
         Util.raiseException("Crossover rate must be >= 0.0 and <= 1.0", ValueError)
      self.pCrossover = rate

   def setGenerations(self, num_gens):
      """ Sets the number of generations to evolve

      :param num_gens: the number of generations

      """
      if num_gens < 1:
         Util.raiseException("Number of generations must be >= 1", ValueError)
      self.nGenerations = num_gens

   def getGenerations(self):
      """ Return the number of generations to evolve

      :rtype: the number of generations

      .. versionadded:: 0.6
         Added the *getGenerations* method
      """
      return self.nGenerations

   def getMinimax(self):
      """ Gets the minimize/maximize mode

      :rtype: the Consts.minimaxType type

      """
      return self.minimax

   def setMinimax(self, mtype):
      """ Sets the minimize/maximize mode, use Consts.minimaxType

      :param mtype: the minimax mode, from Consts.minimaxType

      """
      if mtype not in Consts.minimaxType.values():
         Util.raiseException("Minimax must be maximize or minimize", TypeError)
      self.minimax = mtype

   def getCurrentGeneration(self):
      """ Gets the current generation

      :rtype: the current generation

      """
      return self.currentGeneration

   def setElitism(self, flag):
      """ Sets the elitism option, True or False

      :param flag: True or False

      """
      if type(flag) != BooleanType:
         Util.raiseException("Elitism option must be True or False", TypeError)
      self.elitism = flag

   def getDBAdapter(self):
      """ Gets the DB Adapter of the GA Engine

      :rtype: a instance from one of the :mod:`DBAdapters` classes

      """
      return self.dbAdapter

   def bestIndividual(self):
      """ Returns the population best individual

      :rtype: the best individual

      """
      return self.internalPop.bestRaw()

   def worstIndividual(self):
      """ Returns the population worst individual

      :rtype: the best individual

      """
      return self.internalPop.worstRaw()

   def __gp_catch_functions(self, prefix):
      """ Internally used to catch functions with some specific prefix
      as non-terminals of the GP core """
      import __main__ as mod_main

      function_set = {}

      main_dict = mod_main.__dict__
      for obj, addr in main_dict.items():
         if obj[0:len(prefix)] == prefix:
            try:
               op_len = addr.func_code.co_argcount
            except:
               continue
            function_set[obj] = op_len

      if len(function_set) <= 0:
         Util.raiseException("No function set found using function prefix '%s' !" % prefix, ValueError)

      self.setParams(gp_function_set=function_set)

   def initialize(self):
      """ Initializes the GA Engine. Create and initialize population """
      self.internalPop.create(minimax=self.minimax)
      self.internalPop.initialize(ga_engine=self)
      logging.debug("The GA Engine was initialized !")

   def getPopulation(self):
      """ Return the internal population of GA Engine

      :rtype: the population (:class:`GPopulation.GPopulation`)

      """
      return self.internalPop

   def getStatistics(self):
      """ Gets the Statistics class instance of current generation

      :rtype: the statistics instance (:class:`Statistics.Statistics`)

      """
      return self.internalPop.getStatistics()

   def step(self):
      """ Just do one step in evolution, one generation """
      genomeMom = None
      genomeDad = None

      newPop = GPopulation(self.internalPop)
      logging.debug("Population was cloned.")

      size_iterate = len(self.internalPop)

      # Odd population size
      if size_iterate % 2 != 0: size_iterate -= 1

      crossover_empty = self.select(popID=self.currentGeneration).crossover.isEmpty()

      for i in xrange(0, size_iterate, 2):
         genomeMom = self.select(popID=self.currentGeneration)
         genomeDad = self.select(popID=self.currentGeneration)

         if not crossover_empty and self.pCrossover >= 1.0:
            for it in genomeMom.crossover.applyFunctions(mom=genomeMom, dad=genomeDad, count=2):
               (sister, brother) = it
         else:
            if not crossover_empty and Util.randomFlipCoin(self.pCrossover):
               for it in genomeMom.crossover.applyFunctions(mom=genomeMom, dad=genomeDad, count=2):
                  (sister, brother) = it
            else:
               sister = genomeMom.clone()
               brother = genomeDad.clone()

         sister.mutate(pmut=self.pMutation, ga_engine=self)
         brother.mutate(pmut=self.pMutation, ga_engine=self)

         newPop.internalPop.append(sister)
         newPop.internalPop.append(brother)

      if len(self.internalPop) % 2 != 0:
         genomeMom = self.select(popID=self.currentGeneration)
         genomeDad = self.select(popID=self.currentGeneration)

         if Util.randomFlipCoin(self.pCrossover):
            for it in genomeMom.crossover.applyFunctions(mom=genomeMom, dad=genomeDad, count=1):
               (sister, brother) = it
         else:
            sister = random.choice([genomeMom, genomeDad])
            sister = sister.clone()
            sister.mutate(pmut=self.pMutation, ga_engine=self)

         newPop.internalPop.append(sister)

      logging.debug("Evaluating the new created population.")
      newPop.evaluate()

      if self.elitism:
         logging.debug("Doing elitism.")
         if self.getMinimax() == Consts.minimaxType["maximize"]:
            for i in xrange(self.nElitismReplacement):
               #re-evaluate before being sure this is the best
               self.internalPop.bestRaw(i).evaluate()
               if self.internalPop.bestRaw(i).score > newPop.bestRaw(i).score:
                  newPop[len(newPop)-1-i] = self.internalPop.bestRaw(i)
         elif self.getMinimax() == Consts.minimaxType["minimize"]:
            for i in xrange(self.nElitismReplacement):
               #re-evaluate before being sure this is the best
               self.internalPop.bestRaw(i).evaluate()
               if self.internalPop.bestRaw(i).score < newPop.bestRaw(i).score:
                  newPop[len(newPop)-1-i] = self.internalPop.bestRaw(i)

      self.internalPop = newPop
      self.internalPop.sort()

      logging.debug("The generation %d was finished.", self.currentGeneration)

      self.currentGeneration += 1

      return (self.currentGeneration == self.nGenerations)

   def printStats(self):
      """ Print generation statistics

      :rtype: the printed statistics as string

      .. versionchanged:: 0.6
         The return of *printStats* method.
      """
      percent = self.currentGeneration * 100 / float(self.nGenerations)
      message = "Gen. %d (%.2f%%):" % (self.currentGeneration, percent)
      logging.info(message)
      print message,
      sys_stdout.flush()
      self.internalPop.statistics()
      stat_ret = self.internalPop.printStats()
      return message + stat_ret

   def printTimeElapsed(self):
      """ Shows the time elapsed since the begin of evolution """
      total_time = time()-self.time_init
      print "Total time elapsed: %.3f seconds." % total_time
      return total_time

   def dumpStatsDB(self):
      """ Dumps the current statistics to database adapter """
      logging.debug("Dumping stats to the DB Adapter")
      self.internalPop.statistics()
      self.dbAdapter.insert(self)

   def evolve(self, freq_stats=0):
      """ Do all the generations until the termination criteria, accepts
      the freq_stats (default is 0) to dump statistics at n-generation

      Example:
         >>> ga_engine.evolve(freq_stats=10)
         (...)

      :param freq_stats: if greater than 0, the statistics will be
                         printed every freq_stats generation.
      :rtype: returns the best individual of the evolution

      .. versionadded:: 0.6
         the return of the best individual

      """

      stopFlagCallback = False
      stopFlagTerminationCriteria = False

      self.time_init = time()

      logging.debug("Starting the DB Adapter and the Migration Adapter if any")
      if self.dbAdapter: self.dbAdapter.open(self)
      if self.migrationAdapter: self.migrationAdapter.start()


      if self.getGPMode():
         gp_function_prefix = self.getParam("gp_function_prefix")
         if gp_function_prefix is not None:
            self.__gp_catch_functions(gp_function_prefix)

      self.initialize()
      self.internalPop.evaluate()
      self.internalPop.sort()
      logging.debug("Starting loop over evolutionary algorithm.")


      def stop_evolution(s, f):
         #print signal, frame

         if s == signal.SIGINT:
            if self.internalPop.multiProcessing[0]:
               logging.debug("CTRL-C detected, finishing evolution (stopping parallel jobs).")
               self.internalPop.proc_pool.terminate()
               self.internalPop.proc_pool.join()
            else:
               logging.debug("CTRL-C detected, finishing evolution.")
            if freq_stats: print "\n\tA break was detected, you have interrupted the evolution !\n"

         if freq_stats != 0:
            self.printStats()
            self.printTimeElapsed()

         if self.dbAdapter:
            logging.debug("Closing the DB Adapter")
            if not (self.currentGeneration % self.dbAdapter.getStatsGenFreq() == 0):
               self.dumpStatsDB()
            self.dbAdapter.commitAndClose()

         if self.migrationAdapter:
            logging.debug("Closing the Migration Adapter")
            if freq_stats: print "Stopping the migration adapter... ",
            self.migrationAdapter.stop()
            if freq_stats: print "done !"

         if s == signal.SIGINT:
            print self.bestIndividual()
            exit(0)
         else:
            return self.bestIndividual()

      signal.signal(signal.SIGINT, stop_evolution)

      while True:
         if self.migrationAdapter:
            logging.debug("Migration adapter: exchange")
            self.migrationAdapter.exchange()
            self.internalPop.clearFlags()
            self.internalPop.sort()

         if not self.stepCallback.isEmpty():
            for it in self.stepCallback.applyFunctions(self):
               stopFlagCallback = it

         if not self.terminationCriteria.isEmpty():
            for it in self.terminationCriteria.applyFunctions(self):
               stopFlagTerminationCriteria = it

         if freq_stats:
            if (self.currentGeneration % freq_stats == 0) or (self.getCurrentGeneration() == 0):
               self.printStats()
               #print self.bestIndividual()

         if self.dbAdapter:
            if self.currentGeneration % self.dbAdapter.getStatsGenFreq() == 0:
               self.dumpStatsDB()

         if stopFlagTerminationCriteria:
            logging.debug("Evolution stopped by the Termination Criteria !")
            if freq_stats:
               print "\n\tEvolution stopped by Termination Criteria function !\n"
            break

         if stopFlagCallback:
            logging.debug("Evolution stopped by Step Callback function !")
            if freq_stats:
               print "\n\tEvolution stopped by Step Callback function !\n"
            break

         if self.interactiveMode:
            if sys_platform[:3] == "win":
               if msvcrt.kbhit():
                  if ord(msvcrt.getch()) == Consts.CDefESCKey:
                     print "Loading modules for Interactive Mode...",
                     logging.debug("Windows Interactive Mode key detected ! generation=%d", self.getCurrentGeneration())
                     from pyevolve import Interaction
                     print " done !"
                     interact_banner = "## Pyevolve v.%s - Interactive Mode ##\nPress CTRL-Z to quit interactive mode." % (pyevolve.__version__,)
                     session_locals = { "ga_engine"  : self,
                                        "population" : self.getPopulation(),
                                        "pyevolve"   : pyevolve,
                                        "it"         : Interaction}
                     print
                     code.interact(interact_banner, local=session_locals)

            if (self.getInteractiveGeneration() >= 0) and (self.getInteractiveGeneration() == self.getCurrentGeneration()):
                     print "Loading modules for Interactive Mode...",
                     logging.debug("Manual Interactive Mode key detected ! generation=%d", self.getCurrentGeneration())
                     from pyevolve import Interaction
                     print " done !"
                     interact_banner = "## Pyevolve v.%s - Interactive Mode ##" % (pyevolve.__version__,)
                     session_locals = { "ga_engine"  : self,
                                        "population" : self.getPopulation(),
                                        "pyevolve"   : pyevolve,
                                        "it"         : Interaction}
                     print
                     code.interact(interact_banner, local=session_locals)

         if self.step(): break
      return stop_evolution(signal.SIGUSR1, None)

   def select(self, **args):
      """ Select one individual from population

      :param args: this parameters will be sent to the selector

      """
      for it in self.selector.applyFunctions(self.internalPop, **args):
         return it
예제 #16
0
class GPopulation:
   """ GPopulation Class - The container for the population

   **Examples**
      Get the population from the :class:`GSimpleGA.GSimpleGA` (GA Engine) instance
         >>> pop = ga_engine.getPopulation()

      Get the best fitness individual
         >>> bestIndividual = pop.bestFitness()

      Get the best raw individual
         >>> bestIndividual = pop.bestRaw()

      Get the statistics from the :class:`Statistics.Statistics` instance
         >>> stats = pop.getStatistics()
         >>> print stats["rawMax"]
         10.4

      Iterate, get/set individuals
         >>> for ind in pop:
         >>>   print ind
         (...)
         
         >>> for i in xrange(len(pop)):
         >>>    print pop[i]
         (...)

         >>> pop[10] = newGenome
         >>> pop[10].fitness
         12.5

   :param genome: the :term:`Sample genome`

   """

   def __init__(self, genome):
      """ The GPopulation Class creator """

      logging.debug("New population instance, %s class genomes.", genome.__class__.__name__)
      self.oneSelfGenome = genome
      self.internalPop   = []
      self.popSize       = 0
      self.sortType      = Consts.CDefPopSortType
      self.sorted        = False
      self.minimax       = Consts.CDefPopMinimax
      self.scaleMethod   = FunctionSlot("Scale Method")
      self.scaleMethod.set(Consts.CDefPopScale)
      self.allSlots      = [self.scaleMethod]

      # Statistics
      self.statted = False
      self.stats   = Statistics()

   def setMinimax(minimax):
      """ Sets the population minimax

      Example:
         >>> pop.setMinimax(Consts.minimaxType["maximize"])
   
      :param minimax: the minimax type

      """
      self.minimax = minimax

   def __repr__(self):
      """ Returns the string representation of the population """
      ret =  "- GPopulation\n"
      ret += "\tPopulation Size:\t %d\n" % (self.popSize,)
      ret += "\tSort Type:\t\t %s\n" % (Consts.sortType.keys()[Consts.sortType.values().index(self.sortType)].capitalize(),)
      ret += "\tMinimax Type:\t\t %s\n" % (Consts.minimaxType.keys()[Consts.minimaxType.values().index(self.minimax)].capitalize(),)
      for slot in self.allSlots:
         ret+= "\t" + slot.__repr__()
      ret+="\n"
      ret+= self.stats.__repr__()
      return ret

   def __len__(self):
      """ Return the length of population """
      return len(self.internalPop)
      
   def __getitem__(self, key):
      """ Returns the specified individual from population """
      return self.internalPop[key]

   def __iter__(self):
      """ Returns the iterator of the population """
      return iter(self.internalPop)

   def __setitem__(self, key, value):
      """ Set an individual of population """
      self.internalPop[key] = value
      self.__clear_flags()

   def __clear_flags(self):
      self.sorted = False
      self.statted = False

   def getStatistics(self):
      """ Return a Statistics class for statistics

      :rtype: the :class:`Statistics.Statistics` instance

      """
      self.statistics()
      return self.stats      

   def statistics(self):
      """ Do statistical analysis of population and set 'statted' to True """
      if self.statted: return
      logging.debug("Running statistical calc.")
      raw_sum = 0

      len_pop = len(self)
      for ind in xrange(len_pop):
         raw_sum += self[ind].score

      self.stats["rawMax"] = max(self, key=key_raw_score).score
      self.stats["rawMin"] = min(self, key=key_raw_score).score
      self.stats["rawAve"] = raw_sum / float(len_pop)
      
      tmpvar = 0.0;
      for ind in xrange(len_pop):
         s = self[ind].score - self.stats["rawAve"]
         s*= s
         tmpvar += s

      tmpvar/= float((len(self) - 1))
      self.stats["rawDev"] = math_sqrt(tmpvar)
      self.stats["rawVar"] = tmpvar

      self.statted = True

   def bestFitness(self, index=0):
      """ Return the best scaled fitness individual of population

      :param index: the *index* best individual
      :rtype: the individual

      """
      self.sort()
      return self.internalPop[index]

   def bestRaw(self):
      """ Return the best raw score individual of population

      :rtype: the individual
      
      """
      if self.minimax == Consts.minimaxType["minimize"]:
         return min(self, key=key_raw_score)
      else:
         return max(self, key=key_raw_score)

   def sort(self):
      """ Sort the population """
      if self.sorted: return
      rev = (self.minimax == Consts.minimaxType["maximize"])

      if self.sortType == Consts.sortType["raw"]:
         self.internalPop.sort(cmp=cmp_individual_raw, reverse=rev)
      else:
         self.scale()
         self.internalPop.sort(cmp=cmp_individual_scaled, reverse=rev)

      self.sorted = True

   def setPopulationSize(self, size):
      """ Set the population size

      :param size: the population size

      """
      self.popSize = size

   def setSortType(self, sort_type):
      """ Sets the sort type

      Example:
         >>> pop.setSortType(Consts.sortType["scaled"])

      :param sort_type: the Sort Type

      """
      self.sortType = sort_type

   def create(self, **args):
      """ Clone the example genome to fill the population """
      self.clear()
      self.minimax = args["minimax"]
      for i in xrange(self.popSize):
         self.internalPop.append(self.oneSelfGenome.clone())
      self.__clear_flags()

   def initialize(self):
      """ Initialize all individuals of population,
      this calls the initialize() of individuals """
      for gen in self.internalPop:
         gen.initialize()
      self.__clear_flags()

   def evaluate(self, **args):
      """ Evaluate all individuals in population, calls the evaluate() method of individuals
   
      :param args: this params are passed to the evaluation function

      """
      for ind in self.internalPop:
         ind.evaluate(**args)
      self.__clear_flags()

   def scale(self, **args):
      """ Scale the population using the scaling method

      :param args: this parameter is passed to the scale method

      """
      for it in self.scaleMethod.applyFunctions(self, **args):
         pass

      fit_sum = 0
      for ind in xrange(len(self)):
         fit_sum += self[ind].fitness

      self.stats["fitMax"] = max(self, key=key_fitness_score).fitness
      self.stats["fitMin"] = min(self, key=key_fitness_score).fitness
      self.stats["fitAve"] = fit_sum / float(len(self))

      self.sorted = False

   def printStats(self):
      """ Print statistics of the current population """
      message = ""
      if self.sortType == Consts.sortType["scaled"]:
         message =  "Max/Min/Avg Fitness(Raw) [%.2f(%.2f)/%.2f(%.2f)/%.2f(%.2f)]" % (self.stats["fitMax"], self.stats["rawMax"], self.stats["fitMin"], self.stats["rawMin"], self.stats["fitAve"], self.stats["rawAve"])
      else:
         message = "Max/Min/Avg Raw [%.2f/%.2f/%.2f]" % (self.stats["rawMax"], self.stats["rawMin"], self.stats["rawAve"])
      logging.info(message)
      print message
      return message

   def copy(self, pop):
      """ Copy current population to 'pop'

      :param pop: the destination population

      .. warning:: this method do not copy the individuals, only the population logic

      """
      pop.popSize = self.popSize
      pop.sortType = self.sortType
      pop.sorted = self.sorted
      pop.statted = self.statted
      pop.minimax = self.minimax
      pop.scaleMethod = self.scaleMethod
   
   def clear(self):
      """ Remove all individuals from population """
      del self.internalPop[:]
      self.__clear_flags()
      
   def clone(self):
      """ Return a brand-new cloned population """
      newpop = GPopulation(self.oneSelfGenome.clone())
      self.copy(newpop)
      return newpop
예제 #17
0
class MigrationScheme:
   """ This is the base class for all migration schemes
   
   :param host: the source hostname
   :param port: the source host port
   :param group_name: the group name
   """

   selector = None
   """ This is the function slot for the selection method
   if you want to change the default selector, you must do this: ::

      migration_scheme.selector.set(Selectors.GRouletteWheel) """

   def __init__(self, host, port, group_name):
      self.myself = None
      self.groupName = group_name
      self.selector = FunctionSlot("Selector")
      self.setMyself(host, port)
      self.GAEngine = None
      self.nMigrationRate = Consts.CDefGenMigrationRate
      self.nIndividuals = Consts.CDefMigrationNIndividuals
      self.nReplacement = Consts.CDefGenMigrationReplacement
      self.networkCompression = 9

   def isReady(self):
      """ Returns true if is time to migrate """
      return True if self.GAEngine.getCurrentGeneration() % self.nMigrationRate == 0 else False

   def getCompressionLevel(self):
      """ Get the zlib compression level of network data
      
      The values are in the interval described on the :func:`Network.pickleAndCompress`
      """
      return self.networkCompression

   def setCompressionLevel(self, level):
      """ Set the zlib compression level of network data

      The values are in the interval described on the :func:`Network.pickleAndCompress`
      
      :param level: the zlib compression level
      """
      self.networkCompression = level

   def getNumReplacement(self):
      """ Return the number of individuals that will be
      replaced in the migration process """
      return self.nReplacement

   def setNumReplacement(self, num_individuals):
      """ Return the number of individuals that will be
      replaced in the migration process
      
      :param num_individuals: the number of individuals to be replaced
      """
      self.nReplacement = num_individuals

   def getNumIndividuals(self):
      """ Return the number of individuals that will migrate

      :rtype: the number of individuals to be replaced
      """
      return self.nIndividuals

   def setNumIndividuals(self, num_individuals):
      """ Set the number of individuals that will migrate
      
      :param num_individuals: the number of individuals
      """
      self.nIndividuals = num_individuals 
   
   def setMigrationRate(self, generations):
      """ Sets the generation frequency supposed to migrate
      and receive individuals.

      :param generations: the number of generations      
      """
      self.nMigrationRate = generations

   def getMigrationRate(self):
      """ Return the the generation frequency supposed to migrate
      and receive individuals
      
      :rtype: the number of generations
      """
      return self.nMigrationRate

   def setGAEngine(self, ga_engine):
      """ Sets the GA Engine handler """
      self.GAEngine = ga_engine

   def start(self):
      """ Initializes the migration scheme """
      pass

   def stop(self):
      """ Stops the migration engine """
      pass

   def getGroupName(self):
      """ Gets the group name
      
      .. note:: all islands of evolution which are supposed to exchange
                individuals, must have the same group name.
      """
      return self.groupName

   def setGroupName(self, name):
      """ Sets the group name
      
      :param name: the group name

      .. note:: all islands of evolution which are supposed to exchange
                individuals, must have the same group name.
      """
      self.groupName = name

   def setMyself(self, host, port):
      """ Which interface you will use to send/receive data
      
      :param host: your hostname
      :param port: your port
      """
      self.myself = (host, port)

   def select(self):
      """ Pickes an individual from population using specific selection method
      
      :rtype: an individual object
      """
      if self.selector.isEmpty():
         return self.GAEngine.select(popID=self.GAEngine.currentGeneration)
      else:
         for it in self.selector.applyFunctions(self.GAEngine.internalPop, popID=self.GAEngine.currentGeneration):
            return it

   def selectPool(self, num_individuals):
      """ Select num_individuals number of individuals and return a pool
      
      :param num_individuals: the number of individuals to select
      :rtype: list with individuals
      """
      pool = [self.select() for i in xrange(num_individuals)]
      return pool

   def exchange(self):
      """ Exchange individuals """
      pass
예제 #18
0
class GSimpleGA:
    """ GA Engine Class - The Genetic Algorithm Core

   Example:
      >>> ga = GSimpleGA.GSimpleGA(genome)
      >>> ga.selector.set(Selectors.GRouletteWheel)
      >>> ga.setGenerations(120)
      >>> ga.terminationCriteria.set(GSimpleGA.ConvergenceCriteria)

   :param genome: the :term:`Sample Genome`
   :param interactiveMode: this flag enables the Interactive Mode, the default is True
   :param seed: the random seed value

   .. note:: if you use the same random seed, all the runs of algorithm will be the same

   """

    selector = None
    """ This is the function slot for the selection method
   if you want to change the default selector, you must do this: ::

      ga_engine.selector.set(Selectors.GRouletteWheel) """

    stepCallback = None
    """ This is the :term:`step callback function` slot,
   if you want to set the function, you must do this: ::
      
      def your_func(ga_engine):
         # Here you have access to the GA Engine
         return False

      ga_engine.stepCallback.set(your_func)
      
   now *"your_func"* will be called every generation.
   When this function returns True, the GA Engine will stop the evolution and show
   a warning, if is False, the evolution continues.
   """

    terminationCriteria = None
    """ This is the termination criteria slot, if you want to set one
   termination criteria, you must do this: ::

      ga_engine.terminationCriteria.set(GSimpleGA.ConvergenceCriteria)
      
   Now, when you run your GA, it will stop when the population converges.

   There are those termination criteria functions: :func:`GSimpleGA.RawScoreCriteria`, :func:`GSimpleGA.ConvergenceCriteria`, :func:`GSimpleGA.RawStatsCriteria`, :func:`GSimpleGA.FitnessStatsCriteria`

   But you can create your own termination function, this function receives
   one parameter which is the GA Engine, follows an example: ::

      def ConvergenceCriteria(ga_engine):
         pop = ga_engine.getPopulation()
         return pop[0] == pop[len(pop)-1]

   When this function returns True, the GA Engine will stop the evolution and show
   a warning, if is False, the evolution continues, this function is called every
   generation.
   """
    def __init__(self, genome, seed=None, interactiveMode=True):
        """ Initializator of GSimpleGA """
        if seed: random.seed(seed)

        if type(interactiveMode) != BooleanType:
            Util.raiseException(
                "Interactive Mode option must be True or False", TypeError)

        if not isinstance(genome, GenomeBase):
            Util.raiseException("The genome must be a GenomeBase subclass",
                                TypeError)

        self.internalPop = GPopulation(genome)
        self.nGenerations = Consts.CDefGAGenerations
        self.pMutation = Consts.CDefGAMutationRate
        self.pCrossover = Consts.CDefGACrossoverRate
        self.nElitismReplacement = Consts.CDefGAElitismReplacement
        self.setPopulationSize(Consts.CDefGAPopulationSize)
        self.minimax = Consts.minimaxType["maximize"]
        self.elitism = True

        # Adapters
        self.dbAdapter = None
        self.migrationAdapter = None

        self.time_init = None
        self.interactiveMode = interactiveMode
        self.interactiveGen = -1
        self.GPMode = False

        self.selector = FunctionSlot("Selector")
        self.stepCallback = FunctionSlot("Generation Step Callback")
        self.terminationCriteria = FunctionSlot("Termination Criteria")
        self.selector.set(Consts.CDefGASelector)
        self.allSlots = [
            self.selector, self.stepCallback, self.terminationCriteria
        ]

        self.internalParams = {}

        self.currentGeneration = 0

        # GP Testing
        for classes in Consts.CDefGPGenomes:
            if isinstance(self.internalPop.oneSelfGenome, classes):
                self.setGPMode(True)
                break

        logging.debug("A GA Engine was created, nGenerations=%d",
                      self.nGenerations)

    def setGPMode(self, bool_value):
        """ Sets the Genetic Programming mode of the GA Engine
      
      :param bool_value: True or False
      """
        self.GPMode = bool_value

    def getGPMode(self):
        """ Get the Genetic Programming mode of the GA Engine
      
      :rtype: True or False
      """
        return self.GPMode

    def __call__(self, *args, **kwargs):
        """ A method to implement a callable object

      Example:
         >>> ga_engine(freq_stats=10)
         
      .. versionadded:: 0.6
         The callable method.
      """
        if kwargs.get("freq_stats", None):
            return self.evolve(kwargs.get("freq_stats"))
        else:
            return self.evolve()

    def setParams(self, **args):
        """ Set the internal params

      Example:
         >>> ga.setParams(gp_terminals=['x', 'y'])


      :param args: params to save

      ..versionaddd:: 0.6
         Added the *setParams* method.
      """
        self.internalParams.update(args)

    def getParam(self, key, nvl=None):
        """ Gets an internal parameter

      Example:
         >>> ga.getParam("gp_terminals")
         ['x', 'y']

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      ..versionaddd:: 0.6
         Added the *getParam* method.
      """
        return self.internalParams.get(key, nvl)

    def setInteractiveGeneration(self, generation):
        """ Sets the generation in which the GA must enter in the
      Interactive Mode
      
      :param generation: the generation number, use "-1" to disable

      .. versionadded::0.6
         The *setInteractiveGeneration* method.
      """
        if generation < -1:
            Util.raiseException("Generation must be >= -1", ValueError)
        self.interactiveGen = generation

    def getInteractiveGeneration(self):
        """ returns the generation in which the GA must enter in the
      Interactive Mode
      
      :rtype: the generation number or -1 if not set

      .. versionadded::0.6
         The *getInteractiveGeneration* method.
      """
        return self.interactiveGen

    def setElitismReplacement(self, numreplace):
        """ Set the number of best individuals to copy to the next generation on the elitism

      :param numreplace: the number of individuals
      
      .. versionadded:: 0.6
         The *setElitismReplacement* method.

      """
        if numreplace < 1:
            Util.raiseException("Replacement number must be >= 1", ValueError)
        self.nElitismReplacement = numreplace

    def setInteractiveMode(self, flag=True):
        """ Enable/disable the interactive mode
      
      :param flag: True or False

      .. versionadded: 0.6
         The *setInteractiveMode* method.
      
      """
        if type(flag) != BooleanType:
            Util.raiseException(
                "Interactive Mode option must be True or False", TypeError)
        self.interactiveMode = flag

    def __repr__(self):
        """ The string representation of the GA Engine """
        ret = "- GSimpleGA\n"
        ret += "\tGP Mode:\t\t %s\n" % self.getGPMode()
        ret += "\tPopulation Size:\t %d\n" % (self.internalPop.popSize, )
        ret += "\tGenerations:\t\t %d\n" % (self.nGenerations, )
        ret += "\tCurrent Generation:\t %d\n" % (self.currentGeneration, )
        ret += "\tMutation Rate:\t\t %.2f\n" % (self.pMutation, )
        ret += "\tCrossover Rate:\t\t %.2f\n" % (self.pCrossover, )
        ret += "\tMinimax Type:\t\t %s\n" % (Consts.minimaxType.keys()[
            Consts.minimaxType.values().index(self.minimax)].capitalize(), )
        ret += "\tElitism:\t\t %s\n" % (self.elitism, )
        ret += "\tElitism Replacement:\t %d\n" % (self.nElitismReplacement, )
        ret += "\tDB Adapter:\t\t %s\n" % (self.dbAdapter, )
        for slot in self.allSlots:
            ret += "\t" + slot.__repr__()
        ret += "\n"
        return ret

    def setMultiProcessing(self,
                           flag=True,
                           full_copy=False,
                           number_of_processes=-1):
        """ Sets the flag to enable/disable the use of python multiprocessing module.
      Use this option when you have more than one core on your CPU and when your
      evaluation function is very slow.

      Pyevolve will automaticly check if your Python version has **multiprocessing**
      support and if you have more than one single CPU core. If you don't have support
      or have just only one core, Pyevolve will not use the **multiprocessing**
      feature.

      Pyevolve uses the **multiprocessing** to execute the evaluation function over
      the individuals, so the use of this feature will make sense if you have a
      truly slow evaluation function (which is commom in GAs).      

      The parameter "full_copy" defines where the individual data should be copied back
      after the evaluation or not. This parameter is useful when you change the
      individual in the evaluation function.
      
      :param flag: True (default) or False
      :param full_copy: True or False (default)
      :param number_of_processes: None = use the default, or specify the number

      .. warning:: Use this option only when your evaluation function is slow, so you'll
                   get a good tradeoff between the process communication speed and the
                   parallel evaluation. The use of the **multiprocessing** doesn't means
                   always a better performance.

      .. note:: To enable the multiprocessing option, you **MUST** add the *__main__* check
                on your application, otherwise, it will result in errors. See more on the
                `Python Docs <http://docs.python.org/library/multiprocessing.html#multiprocessing-programming>`__
                site.

      .. versionadded:: 0.6
         The `setMultiProcessing` method.

      """
        if type(flag) != BooleanType:
            Util.raiseException("Multiprocessing option must be True or False",
                                TypeError)

        if type(full_copy) != BooleanType:
            Util.raiseException(
                "Multiprocessing 'full_copy' option must be True or False",
                TypeError)

        self.internalPop.setMultiProcessing(flag, full_copy,
                                            number_of_processes)

    def setMigrationAdapter(self, migration_adapter=None):
        """ Sets the Migration Adapter

      .. versionadded:: 0.6
         The `setMigrationAdapter` method.
      """
        if (migration_adapter is not None) and (not isinstance(
                migration_adapter, MigrationScheme)):
            Util.raiseException(
                "The Migration Adapter must be a MigrationScheme subclass",
                TypeError)

        self.migrationAdapter = migration_adapter
        if self.migrationAdapter is not None:
            self.migrationAdapter.setGAEngine(self)

    def setDBAdapter(self, dbadapter=None):
        """ Sets the DB Adapter of the GA Engine
      
      :param dbadapter: one of the :mod:`DBAdapters` classes instance

      .. warning:: the use the of a DB Adapter can reduce the speed performance of the
                   Genetic Algorithm.
      """
        if (dbadapter
                is not None) and (not isinstance(dbadapter, DBBaseAdapter)):
            Util.raiseException(
                "The DB Adapter must be a DBBaseAdapter subclass", TypeError)
        self.dbAdapter = dbadapter

    def setPopulationSize(self, size):
        """ Sets the population size, calls setPopulationSize() of GPopulation

      :param size: the population size

      .. note:: the population size must be >= 2

      """
        if size < 2:
            Util.raiseException("population size must be >= 2", ValueError)
        self.internalPop.setPopulationSize(size)

    def setSortType(self, sort_type):
        """ Sets the sort type, Consts.sortType["raw"]/Consts.sortType["scaled"]

      Example:
         >>> ga_engine.setSortType(Consts.sortType["scaled"])

      :param sort_type: the Sort Type

      """
        if sort_type not in Consts.sortType.values():
            Util.raiseException("sort type must be a Consts.sortType type",
                                TypeError)
        self.internalPop.sortType = sort_type

    def setMutationRate(self, rate):
        """ Sets the mutation rate, between 0.0 and 1.0

      :param rate: the rate, between 0.0 and 1.0

      """
        if (rate > 1.0) or (rate < 0.0):
            Util.raiseException("Mutation rate must be >= 0.0 and <= 1.0",
                                ValueError)
        self.pMutation = rate

    def setPreMutationRate(self, rate):
        """ Sets the pre-mutation rate, > 0.0.
      Premutation is done only when no cross-over occurs.
      :param rate: the rate, >= 0.0
      """
        if (rate < 0.0):
            Util.raiseException("Pre-mutation rate must be >= 0.0", ValueError)
        self.pPreMutation = rate

    def setCrossoverRate(self, rate):
        """ Sets the crossover rate, between 0.0 and 1.0

      :param rate: the rate, between 0.0 and 1.0

      """
        if (rate > 1.0) or (rate < 0.0):
            Util.raiseException("Crossover rate must be >= 0.0 and <= 1.0",
                                ValueError)
        self.pCrossover = rate

    def setGenerations(self, num_gens):
        """ Sets the number of generations to evolve

      :param num_gens: the number of generations

      """
        if num_gens < 1:
            Util.raiseException("Number of generations must be >= 1",
                                ValueError)
        self.nGenerations = num_gens

    def getGenerations(self):
        """ Return the number of generations to evolve

      :rtype: the number of generations

      .. versionadded:: 0.6
         Added the *getGenerations* method
      """
        return self.nGenerations

    def getMinimax(self):
        """ Gets the minimize/maximize mode

      :rtype: the Consts.minimaxType type

      """
        return self.minimax

    def setMinimax(self, mtype):
        """ Sets the minimize/maximize mode, use Consts.minimaxType

      :param mtype: the minimax mode, from Consts.minimaxType

      """
        if mtype not in Consts.minimaxType.values():
            Util.raiseException("Minimax must be maximize or minimize",
                                TypeError)
        self.minimax = mtype

    def getCurrentGeneration(self):
        """ Gets the current generation

      :rtype: the current generation

      """
        return self.currentGeneration

    def setElitism(self, flag):
        """ Sets the elitism option, True or False

      :param flag: True or False

      """
        if type(flag) != BooleanType:
            Util.raiseException("Elitism option must be True or False",
                                TypeError)
        self.elitism = flag

    def getDBAdapter(self):
        """ Gets the DB Adapter of the GA Engine

      :rtype: a instance from one of the :mod:`DBAdapters` classes

      """
        return self.dbAdapter

    def bestIndividual(self):
        """ Returns the population best individual

      :rtype: the best individual

      """
        return self.internalPop.bestRaw()

    def __gp_catch_functions(self, prefix):
        """ Internally used to catch functions with some specific prefix
      as non-terminals of the GP core """
        import __main__ as mod_main

        function_set = {}

        main_dict = mod_main.__dict__
        for obj, addr in main_dict.items():
            if obj[0:len(prefix)] == prefix:
                try:
                    op_len = addr.func_code.co_argcount
                except:
                    continue
                function_set[obj] = op_len

        if len(function_set) <= 0:
            Util.raiseException(
                "No function set found using function prefix '%s' !" % prefix,
                ValueError)

        self.setParams(gp_function_set=function_set)

    def initialize(self):
        """ Initializes the GA Engine. Create and initialize population """
        self.internalPop.create(minimax=self.minimax)
        self.internalPop.initialize(ga_engine=self)
        logging.debug("The GA Engine was initialized !")

    def getPopulation(self):
        """ Return the internal population of GA Engine

      :rtype: the population (:class:`GPopulation.GPopulation`)

      """
        return self.internalPop

    def getStatistics(self):
        """ Gets the Statistics class instance of current generation

      :rtype: the statistics instance (:class:`Statistics.Statistics`)

      """
        return self.internalPop.getStatistics()

    def clear(self):
        """ Petrowski's Clearing Method """

    def step(self):
        """ Just do one step in evolution, one generation """
        genomeMom = None
        genomeDad = None

        newPop = GPopulation(self.internalPop)
        logging.debug("Population was cloned.")

        size_iterate = len(self.internalPop)

        # Odd population size
        if size_iterate % 2 != 0: size_iterate -= 1

        #Check on the crossover function by picking a random individual - is it empty?
        crossover_empty = self.select(
            popID=self.currentGeneration).crossover.isEmpty()

        for i in xrange(0, size_iterate, 2):
            #Ok, we select 2 parents using the selector (RouletteWheel, etc.)
            genomeMom = self.select(popID=self.currentGeneration)
            genomeDad = self.select(popID=self.currentGeneration)

            if not crossover_empty and self.pCrossover >= 1.0:
                #Crossover all of them
                for it in genomeMom.crossover.applyFunctions(mom=genomeMom,
                                                             dad=genomeDad,
                                                             count=2):
                    (sister, brother) = it
            else:
                #Filp a coin each time to determine if you should crossover
                if not crossover_empty and Util.randomFlipCoin(
                        self.pCrossover):
                    for it in genomeMom.crossover.applyFunctions(mom=genomeMom,
                                                                 dad=genomeDad,
                                                                 count=2):
                        (sister, brother) = it
                else:
                    sister = genomeMom.clone()
                    brother = genomeDad.clone()
                    #And "pre" mutate them
                    sister.premutate(pmut=self.pPreMutation, ga_engine=self)
                    brother.premutate(pmut=self.pPreMutation, ga_engine=self)

            #Now each offspring is mutated
            sister.mutate(pmut=self.pMutation, ga_engine=self)
            brother.mutate(pmut=self.pMutation, ga_engine=self)

            newPop.internalPop.append(sister)
            newPop.internalPop.append(brother)

        if len(self.internalPop) % 2 != 0:
            #Odd-numbered population
            genomeMom = self.select(popID=self.currentGeneration)
            genomeDad = self.select(popID=self.currentGeneration)

            if Util.randomFlipCoin(self.pCrossover):
                for it in genomeMom.crossover.applyFunctions(mom=genomeMom,
                                                             dad=genomeDad,
                                                             count=1):
                    (sister, brother) = it
            else:
                sister = random.choice([genomeMom, genomeDad])
                sister = sister.clone()
                #Do the 2 mutations
                sister.premutate(pmut=self.pPreMutation, ga_engine=self)
                sister.mutate(pmut=self.pMutation, ga_engine=self)

            newPop.internalPop.append(sister)

        #---- Evaluate fitness ------
        logging.debug("Evaluating the new created population.")
        newPop.evaluate()

        #Niching methods- Petrowski's clearing
        self.clear()

        if self.elitism:
            #Avoid too much elitism
            if self.nElitismReplacement >= len(self.internalPop):
                self.nElitismReplacement = len(self.internalPop) - 1

            logging.debug("Doing elitism.")
            if self.getMinimax() == Consts.minimaxType["maximize"]:
                #Replace the n-th worst new ones with the nth best old ones
                for i in xrange(self.nElitismReplacement):
                    if self.internalPop.bestRaw(i).score > newPop.bestRaw(
                            i).score:
                        newPop[len(newPop) - 1 -
                               i] = self.internalPop.bestRaw(i)
            elif self.getMinimax() == Consts.minimaxType["minimize"]:
                for i in xrange(self.nElitismReplacement):
                    if self.internalPop.bestRaw(i).score < newPop.bestRaw(
                            i).score:
                        newPop[len(newPop) - 1 -
                               i] = self.internalPop.bestRaw(i)

        self.internalPop = newPop
        self.internalPop.sort()

        logging.debug("The generation %d was finished.",
                      self.currentGeneration)

        self.currentGeneration += 1

        return (self.currentGeneration >= self.nGenerations)

    def printStats(self):
        """ Print generation statistics

      :rtype: the printed statistics as string

      .. versionchanged:: 0.6
         The return of *printStats* method.
      """
        percent = self.currentGeneration * 100 / float(self.nGenerations)
        message = "Gen. %d (%.2f%%):" % (self.currentGeneration, percent)
        logging.info(message)
        print message,
        sys_stdout.flush()
        self.internalPop.statistics()
        stat_ret = self.internalPop.printStats()
        return message + stat_ret

    def printTimeElapsed(self):
        """ Shows the time elapsed since the begin of evolution """
        total_time = time() - self.time_init
        print "Total time elapsed: %.3f seconds." % total_time
        return total_time

    def dumpStatsDB(self):
        """ Dumps the current statistics to database adapter """
        logging.debug("Dumping stats to the DB Adapter")
        self.internalPop.statistics()
        self.dbAdapter.insert(self)

    #----------------------------------------------------------
    def evolve(self, freq_stats=0, skip_initialize=False):
        """ Do all the generations until the termination criteria, accepts
      the freq_stats (default is 0) to dump statistics at n-generation

      Example:
         >>> ga_engine.evolve(freq_stats=10)
         (...)

      :param freq_stats: if greater than 0, the statistics will be
                         printed every freq_stats generation.
      :rtype: returns a tuple:
        best: the best individual of the evolution
        stopFlagCallback: stopped by the step_callback
        stopFlagTerminationCriteria: stopped by reching the criterion


      .. versionadded:: 0.6
         the return of the best individual

      """

        stopFlagCallback = False
        stopFlagTerminationCriteria = False

        self.time_init = time()

        logging.debug(
            "Starting the DB Adapter and the Migration Adapter if any")
        if self.dbAdapter: self.dbAdapter.open(self)
        if self.migrationAdapter: self.migrationAdapter.start()

        if self.getGPMode():
            gp_function_prefix = self.getParam("gp_function_prefix")
            if gp_function_prefix is not None:
                self.__gp_catch_functions(gp_function_prefix)

        if not skip_initialize:
            #Create the population
            self.initialize()

        #Initial fitness evaluation
        self.internalPop.evaluate()
        self.internalPop.sort()
        logging.debug("Starting loop over evolutionary algorithm.")

        try:
            while True:
                if self.migrationAdapter:
                    logging.debug("Migration adapter: exchange")
                    self.migrationAdapter.exchange()
                    self.internalPop.clearFlags()
                    self.internalPop.sort()

                #The step callback is called before each step
                if not self.stepCallback.isEmpty():
                    for it in self.stepCallback.applyFunctions(self):
                        stopFlagCallback = it

                if not self.terminationCriteria.isEmpty():
                    for it in self.terminationCriteria.applyFunctions(self):
                        stopFlagTerminationCriteria = it

                if freq_stats:
                    if (self.currentGeneration % freq_stats
                            == 0) or (self.getCurrentGeneration() == 0):
                        self.printStats()

                if self.dbAdapter:
                    if self.currentGeneration % self.dbAdapter.getStatsGenFreq(
                    ) == 0:
                        self.dumpStatsDB()

                if stopFlagTerminationCriteria:
                    logging.debug(
                        "Evolution stopped by the Termination Criteria !")
                    if freq_stats:
                        print "\n\tEvolution stopped by Termination Criteria function !\n"
                    break

                if stopFlagCallback:
                    logging.debug(
                        "Evolution stopped by Step Callback function !")
                    if freq_stats:
                        print "\n\tEvolution stopped by Step Callback function !\n"
                    break

                #(Here was interactive mode code, removed)

                if self.step():
                    break  #exit if the number of generations is equal to the max. number of gens.

        #(end While True)

        except KeyboardInterrupt:
            logging.debug("CTRL-C detected, finishing evolution.")
            if freq_stats:
                print "\n\tA break was detected, you have interrupted the evolution !\n"

        #Finished. Clean up the multiprocessing pool
        self.getPopulation().cleanupMultiProcessing()

        if freq_stats != 0:
            self.printStats()
            self.printTimeElapsed()

        if self.dbAdapter:
            logging.debug("Closing the DB Adapter")
            if not (self.currentGeneration % self.dbAdapter.getStatsGenFreq()
                    == 0):
                self.dumpStatsDB()
            self.dbAdapter.commitAndClose()

        if self.migrationAdapter:
            logging.debug("Closing the Migration Adapter")
            if freq_stats: print "Stopping the migration adapter... ",
            self.migrationAdapter.stop()
            if freq_stats: print "done !"

        return (self.bestIndividual(), stopFlagCallback,
                stopFlagTerminationCriteria)

    def select(self, **args):
        """ Select one individual from population

      :param args: this parameters will be sent to the selector

      """
        for it in self.selector.applyFunctions(self.internalPop, **args):
            return it
예제 #19
0
class GPopulation:
   """ GPopulation Class - The container for the population

   **Examples**
      Get the population from the :class:`GSimpleGA.GSimpleGA` (GA Engine) instance
         >>> pop = ga_engine.getPopulation()

      Get the best fitness individual
         >>> bestIndividual = pop.bestFitness()

      Get the best raw individual
         >>> bestIndividual = pop.bestRaw()

      Get the statistics from the :class:`Statistics.Statistics` instance
         >>> stats = pop.getStatistics()
         >>> print stats["rawMax"]
         10.4

      Iterate, get/set individuals
         >>> for ind in pop:
         >>>   print ind
         (...)
         
         >>> for i in xrange(len(pop)):
         >>>    print pop[i]
         (...)

         >>> pop[10] = newGenome
         >>> pop[10].fitness
         12.5

   :param genome: the :term:`Sample genome`, or a GPopulation object, when cloning.

   """

   def __init__(self, genome):
      """ The GPopulation Class creator """

      if isinstance(genome, GPopulation):
          #Cloning a population?
         self.oneSelfGenome  = genome.oneSelfGenome
         self.internalPop    = []
         self.internalPopRaw = []
         self.popSize       = genome.popSize
         self.sortType      = genome.sortType
         self.sorted        = False
         self.minimax       = genome.minimax
         self.scaleMethod   = genome.scaleMethod
         self.allSlots      = [self.scaleMethod]

         self.internalParams = genome.internalParams
         self.multiProcessing = genome.multiProcessing

         self.statted = False
         self.stats   = Statistics()
         self.proc_pool = genome.proc_pool
         return

      logging.debug("New population instance, %s class genomes.", genome.__class__.__name__)
      self.oneSelfGenome  = genome
      self.internalPop    = []
      self.internalPopRaw = []
      self.popSize       = 0
      self.proc_pool = None
      self.sortType      = Consts.CDefPopSortType
      self.sorted        = False
      self.minimax       = Consts.CDefPopMinimax
      self.scaleMethod   = FunctionSlot("Scale Method")
      self.scaleMethod.set(Consts.CDefPopScale)
      self.allSlots      = [self.scaleMethod]

      self.internalParams = {}
      self.multiProcessing = (False, False)

      # Statistics
      self.statted = False
      self.stats   = Statistics()

   #---------------------------------------------------------------------------------
   def setMultiProcessing(self, flag=True, full_copy=False, number_of_processes=None):
        """ Sets the flag to enable/disable the use of python multiprocessing module.
        Use this option when you have more than one core on your CPU and when your
        evaluation function is very slow.
        The parameter "full_copy" defines where the individual data should be copied back
        after the evaluation or not. This parameter is useful when you change the
        individual in the evaluation function.

        :param flag: True (default) or False
        :param full_copy: True or False (default)
        :param number_of_processes: None = use the default, or specify the number

        .. warning:: Use this option only when your evaluation function is slow, se you
                   will get a good tradeoff between the process communication speed and the
                   parallel evaluation.
        """
        #Save the parameters
        old_settings = self.multiProcessing
        self.multiProcessing = (flag, full_copy, number_of_processes)
        #Re-initialize if anything changed.
        if (old_settings != self.multiProcessing):
            self.initializeMultiProcessing()

   #---------------------------------------------------------------------------------
   def initializeMultiProcessing(self):
        """Initialize the multiprocessing interface. Create the process pool."""
        #Close the pool if it exists (we'll be creating a new one)
        self.cleanupMultiProcessing()

        if self.multiProcessing[0]:
            t1 = time.time()
            #Create the process pool with the # of processes
            num_proc = self.multiProcessing[2]
            if num_proc is None:
                self.proc_pool = Pool()
            elif num_proc > 0:
                self.proc_pool = Pool(processes=num_proc)
            else:
                self.proc_pool = Pool()
            print "Multiprocessing initialized in %03.3f sec; will use %d processors." % ( (time.time()-t1), num_proc )

   #---------------------------------------------------------------------------------
   def cleanupMultiProcessing(self):
       """Clean up process pools."""
       if not self.proc_pool is None:
            self.proc_pool.close()

   def setMinimax(self, minimax):
      """ Sets the population minimax

      Example:
         >>> pop.setMinimax(Consts.minimaxType["maximize"])
   
      :param minimax: the minimax type

      """
      self.minimax = minimax

   def __repr__(self):
      """ Returns the string representation of the population """
      ret =  "- GPopulation\n"
      ret += "\tPopulation Size:\t %d\n" % (self.popSize,)
      ret += "\tSort Type:\t\t %s\n" % (Consts.sortType.keys()[Consts.sortType.values().index(self.sortType)].capitalize(),)
      ret += "\tMinimax Type:\t\t %s\n" % (Consts.minimaxType.keys()[Consts.minimaxType.values().index(self.minimax)].capitalize(),)
      for slot in self.allSlots:
         ret+= "\t" + slot.__repr__()
      ret+="\n"
      ret+= self.stats.__repr__()
      return ret

   def __len__(self):
      """ Return the length of population """
      return len(self.internalPop)
      
   def __getitem__(self, key):
      """ Returns the specified individual from population """
      return self.internalPop[key]

   def __iter__(self):
      """ Returns the iterator of the population """
      return iter(self.internalPop)

   def __setitem__(self, key, value):
      """ Set an individual of population """
      self.internalPop[key] = value
      self.clearFlags()

   def clearFlags(self):
      """ Clear the sorted and statted internal flags """
      self.sorted = False
      self.statted = False

   def getStatistics(self):
      """ Return a Statistics class for statistics

      :rtype: the :class:`Statistics.Statistics` instance

      """
      self.statistics()
      return self.stats      

   def statistics(self):
      """ Do statistical analysis of population and set 'statted' to True """
      if self.statted: return
      logging.debug("Running statistical calculations")
      raw_sum = 0
      fit_sum = 0

      len_pop = len(self)
      for ind in xrange(len_pop):
         raw_sum += self[ind].score
         #fit_sum += self[ind].fitness

      self.stats["rawMax"] = max(self, key=key_raw_score).score
      self.stats["rawMin"] = min(self, key=key_raw_score).score
      self.stats["rawAve"] = raw_sum / float(len_pop)
      #self.stats["rawTot"] = raw_sum
      #self.stats["fitTot"] = fit_sum
      
      tmpvar = 0.0
      for ind in xrange(len_pop):
         s = self[ind].score - self.stats["rawAve"]
         s*= s
         tmpvar += s

      tmpvar/= float((len(self) - 1))
      try:
         self.stats["rawDev"] = math_sqrt(tmpvar)
      except:
         self.stats["rawDev"] = 0.0

      self.stats["rawVar"] = tmpvar

      self.statted = True

   def bestFitness(self, index=0):
      """ Return the best scaled fitness individual of population

      :param index: the *index* best individual
      :rtype: the individual

      """
      self.sort()
      return self.internalPop[index]

   def bestRaw(self, index=0):
      """ Return the best raw score individual of population

      :param index: the *index* best raw individual
      :rtype: the individual

      .. versionadded:: 0.6
         The parameter `index`.
      
      """
      if self.sortType == Consts.sortType["raw"]:
         return self.internalPop[index]
      else:
         self.sort()
         return self.internalPopRaw[index]

   def sort(self):
      """ Sort the population """
      if self.sorted: return
      rev = (self.minimax == Consts.minimaxType["maximize"])

      if self.sortType == Consts.sortType["raw"]:
         self.internalPop.sort(cmp=Util.cmp_individual_raw, reverse=rev)
      else:
         self.scale()
         self.internalPop.sort(cmp=Util.cmp_individual_scaled, reverse=rev)
         self.internalPopRaw = self.internalPop[:]
         self.internalPopRaw.sort(cmp=Util.cmp_individual_raw, reverse=rev)

      self.sorted = True

   def setPopulationSize(self, size):
      """ Set the population size

      :param size: the population size

      """
      self.popSize = size

   def setSortType(self, sort_type):
      """ Sets the sort type

      Example:
         >>> pop.setSortType(Consts.sortType["scaled"])

      :param sort_type: the Sort Type

      """
      self.sortType = sort_type

   def create(self, **args):
      """ Clone the example genome to fill the population """
      self.minimax = args["minimax"]
      self.internalPop = [self.oneSelfGenome.clone() for i in xrange(self.popSize)]
      self.clearFlags()

   def __findIndividual(self, individual, end):
      for i in xrange(end):
         if individual.compare(self.internalPop[i]) == 0:
            return True

   def initialize(self, **args):
      """ Initialize all individuals of population,
      this calls the initialize() of individuals """
      logging.debug("Initializing the population")
   
      if self.oneSelfGenome.getParam("full_diversity", True) and hasattr(self.oneSelfGenome, "compare"):
         for i in xrange(len(self.internalPop)):
            curr = self.internalPop[i]
            curr.initialize(**args)
            while self.__findIndividual(curr, i):
               curr.initialize(**args)
      else:
         for gen in self.internalPop:
            gen.initialize(**args)
      self.clearFlags()

   def evaluate(self, **args):
      """ Evaluate all individuals in population, calls the evaluate() method of individuals
   
      :param args: this params are passed to the evaluation function

      """
      # We have multiprocessing
      if self.multiProcessing[0] and MULTI_PROCESSING:
         logging.debug("Evaluating the population using the multiprocessing method")

         #Make sure we have a process pool.
         if self.proc_pool is None:
             self.initializeMultiProcessing()

         # Multiprocessing full_copy parameter
         if self.multiProcessing[1]:
            results = self.proc_pool.map(multiprocessing_eval_full, self.internalPop)
            for i in xrange(len(self.internalPop)):
               self.internalPop[i] = results[i]
         else:
            results = self.proc_pool.map(multiprocessing_eval, self.internalPop)
            for individual, score in zip(self.internalPop, results):
               individual.score = score
      else:
         #Direct evaluation (no multiprocessing)
         for ind in self.internalPop:
            ind.evaluate(**args)

      self.clearFlags()

   def scale(self, **args):
      """ Scale the population using the scaling method

      :param args: this parameter is passed to the scale method

      """
      for it in self.scaleMethod.applyFunctions(self, **args):
         pass

      fit_sum = 0
      for ind in xrange(len(self)):
         fit_sum += self[ind].fitness

      self.stats["fitMax"] = max(self, key=key_fitness_score).fitness
      self.stats["fitMin"] = min(self, key=key_fitness_score).fitness
      self.stats["fitAve"] = fit_sum / float(len(self))

      self.sorted = False

   def printStats(self):
      """ Print statistics of the current population """
      message = ""
      if self.sortType == Consts.sortType["scaled"]:
         message = "Max/Min/Avg Fitness(Raw) [%(fitMax).2f(%(rawMax).2f)/%(fitMin).2f(%(rawMin).2f)/%(fitAve).2f(%(rawAve).2f)]" % self.stats
      else:
         message = "Max/Min/Avg Raw [%(rawMax).2f/%(rawMin).2f/%(rawAve).2f]" % self.stats
      logging.info(message)
      print message
      return message

   def copy(self, pop):
      """ Copy current population to 'pop'

      :param pop: the destination population

      .. warning:: this method do not copy the individuals, only the population logic

      """
      pop.popSize = self.popSize
      pop.sortType = self.sortType
      pop.minimax = self.minimax
      pop.scaleMethod = self.scaleMethod
      #pop.internalParams = self.internalParams.copy()
      pop.internalParams = self.internalParams
      pop.multiProcessing = self.multiProcessing
   
   def getParam(self, key, nvl=None):
      """ Gets an internal parameter

      Example:
         >>> population.getParam("tournamentPool")
         5

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      """
      return self.internalParams.get(key, nvl)


   def setParams(self, **args):
      """ Gets an internal parameter

      Example:
         >>> population.setParams(tournamentPool=5)

      :param args: parameters to set

      .. versionadded:: 0.6
         The `setParams` method.
      """
      self.internalParams.update(args)

   def clear(self):
      """ Remove all individuals from population """
      del self.internalPop[:]
      del self.internalPopRaw[:]
      self.clearFlags()
      
   def clone(self):
      """ Return a brand-new cloned population """
      newpop = GPopulation(self.oneSelfGenome)
      self.copy(newpop)
      return newpop
예제 #20
0
class MigrationScheme(object):
    """ This is the base class for all migration schemes """

    selector = None
    """ This is the function slot for the selection method
   if you want to change the default selector, you must do this: ::

      migration_scheme.selector.set(Selectors.GRouletteWheel) """
    def __init__(self):
        self.selector = FunctionSlot("Selector")
        self.GAEngine = None
        self.nMigrationRate = Consts.CDefGenMigrationRate
        self.nIndividuals = Consts.CDefMigrationNIndividuals
        self.nReplacement = Consts.CDefGenMigrationReplacement
        self.networkCompression = 9

    def isReady(self):
        """ Returns true if is time to migrate """
        return True if self.GAEngine.getCurrentGeneration(
        ) % self.nMigrationRate == 0 else False

    def getCompressionLevel(self):
        """ Get the zlib compression level of network data
      
      The values are in the interval described on the :func:`Network.pickleAndCompress`
      """
        return self.networkCompression

    def setCompressionLevel(self, level):
        """ Set the zlib compression level of network data

      The values are in the interval described on the :func:`Network.pickleAndCompress`
      
      :param level: the zlib compression level
      """
        self.networkCompression = level

    def getNumReplacement(self):
        """ Return the number of individuals that will be
      replaced in the migration process """
        return self.nReplacement

    def setNumReplacement(self, num_individuals):
        """ Return the number of individuals that will be
      replaced in the migration process
      
      :param num_individuals: the number of individuals to be replaced
      """
        self.nReplacement = num_individuals

    def getNumIndividuals(self):
        """ Return the number of individuals that will migrate

      :rtype: the number of individuals to be replaced
      """
        return self.nIndividuals

    def setNumIndividuals(self, num_individuals):
        """ Set the number of individuals that will migrate
      
      :param num_individuals: the number of individuals
      """
        self.nIndividuals = num_individuals

    def setMigrationRate(self, generations):
        """ Sets the generation frequency supposed to migrate
      and receive individuals.

      :param generations: the number of generations      
      """
        self.nMigrationRate = generations

    def getMigrationRate(self):
        """ Return the the generation frequency supposed to migrate
      and receive individuals
      
      :rtype: the number of generations
      """
        return self.nMigrationRate

    def setGAEngine(self, ga_engine):
        """ Sets the GA Engine handler """
        self.GAEngine = ga_engine

    def start(self):
        """ Initializes the migration scheme """
        pass

    def stop(self):
        """ Stops the migration engine """
        pass

    def select(self):
        """ Pickes an individual from population using specific selection method
      
      :rtype: an individual object
      """
        if self.selector.isEmpty():
            return self.GAEngine.select(popID=self.GAEngine.currentGeneration)
        else:
            for it in self.selector.applyFunctions(
                    self.GAEngine.internalPop,
                    popID=self.GAEngine.currentGeneration):
                return it

    def selectPool(self, num_individuals):
        """ Select num_individuals number of individuals and return a pool
      
      :param num_individuals: the number of individuals to select
      :rtype: list with individuals
      """
        pool = [self.select() for i in xrange(num_individuals)]
        return pool

    def exchange(self):
        """ Exchange individuals """
        pass
예제 #21
0
class GenomeBase(object):
   """ GenomeBase Class - The base of all chromosome representation """
   __slots__ = ["evaluator", "initializator", "mutator", "crossover", "internalParams", "score", "fitness"]

   def __init__(self):
      """Genome Constructor"""
      self.evaluator = FunctionSlot("Evaluator")
      self.initializator = FunctionSlot("Initializator")
      self.mutator = FunctionSlot("Mutator")
      self.crossover = FunctionSlot("Crossover")

      self.internalParams = {}
      self.score = 0.0
      self.fitness = 0.0

   def getRawScore(self):
      """ Get the Raw Score of the genome

      :rtype: genome raw score

      """
      return self.score

   def getFitnessScore(self):
      """ Get the Fitness Score of the genome

      :rtype: genome fitness score

      """
      return self.fitness

   def __repr__(self):
      """String representation of Genome"""
      allSlots = [self.evaluator, self.initializator, self.mutator,
                  self.crossover]

      ret = "- GenomeBase\n"
      ret += "\tScore:\t\t\t %.6f\n" % (self.score,)
      ret += "\tFitness:\t\t %.6f\n\n" % (self.fitness,)
      ret += "\tParams:\t\t %s\n\n" % (self.internalParams,)

      for slot in allSlots:
         ret += "\t" + slot.__repr__()
      ret += "\n"

      return ret

   def setParams(self, **args):
      """ Set the internal params

      Example:
         >>> genome.setParams(rangemin=0, rangemax=100, gauss_mu=0, gauss_sigma=1)

      .. note:: All the individuals of the population shares this parameters and uses
                the same instance of this dict.

      :param args: this params will saved in every chromosome for genetic op. use

      """
      self.internalParams.update(args)

   def getParam(self, key, nvl=None):
      """ Gets an internal parameter

      Example:
         >>> genome.getParam("rangemax")
         100

      .. note:: All the individuals of the population shares this parameters and uses
                the same instance of this dict.

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      """
      return self.internalParams.get(key, nvl)

   def resetStats(self):
      """ Clear score and fitness of genome """
      self.score = 0.0
      self.fitness = 0.0

   def evaluate(self, **args):
      """ Called to evaluate genome

      :param args: this parameters will be passes to the evaluator

      """
      self.resetStats()
      for it in self.evaluator.applyFunctions(self, **args):
         self.score += it

   def initialize(self, **args):
      """ Called to initialize genome

      :param args: this parameters will be passed to the initializator

      """
      for it in self.initializator.applyFunctions(self, **args):
         pass

   def mutate(self, **args):
      """ Called to mutate the genome

      :param args: this parameters will be passed to the mutator
      :rtype: the number of mutations returned by mutation operator

      """
      nmuts = 0
      for it in self.mutator.applyFunctions(self, **args):
         nmuts += it
      return nmuts

   def copy(self, g):
      """ Copy the current GenomeBase to 'g'

      :param g: the destination genome

      .. note:: If you are planning to create a new chromosome representation, you
                **must** implement this method on your class.

      """
      g.score = self.score
      g.fitness = self.fitness
      g.evaluator = self.evaluator
      g.initializator = self.initializator
      g.mutator = self.mutator
      g.crossover = self.crossover
      g.internalParams = self.internalParams

   def clone(self):
      """ Clone this GenomeBase

      :rtype: the clone genome

      .. note:: If you are planning to create a new chromosome representation, you
                **must** implement this method on your class.
      """
      newcopy = GenomeBase()
      self.copy(newcopy)
      return newcopy
예제 #22
0
class GSimpleGA:
   """ GA Engine Class - The Genetic Algorithm Core

   Example:
      >>> ga = GSimpleGA.GSimpleGA(genome)
      >>> ga.selector.set(Selectors.GRouletteWheel)
      >>> ga.setGenerations(120)
      >>> ga.terminationCriteria.set(GSimpleGA.ConvergenceCriteria)

   :param genome: the :term:`Sample Genome`
   :param interactiveMode: this flag enables the Interactive Mode, the default is True
   :param seed: the random seed value

   .. note:: if you use the same random seed, all the runs of algorithm will be the same

   """

   selector = None
   """ This is the function slot for the selection method
   if you want to change the default selector, you must do this: ::

      ga_engine.selector.set(Selectors.GRouletteWheel) """

   stepCallback = None
   """ This is the :term:`step callback function` slot,
   if you want to set the function, you must do this: ::
      
      def your_func(ga_engine):
         # Here you have access to the GA Engine
         return False

      ga_engine.stepCallback.set(your_func)
      
   now *"your_func"* will be called every generation.
   When this function returns True, the GA Engine will stop the evolution and show
   a warning, if is False, the evolution continues.
   """

   terminationCriteria = None
   """ This is the termination criteria slot, if you want to set one
   termination criteria, you must do this: ::

      ga_engine.terminationCriteria.set(GSimpleGA.ConvergenceCriteria)
      
   Now, when you run your GA, it will stop when the population converges.

   There are those termination criteria functions: :func:`GSimpleGA.RawScoreCriteria`, :func:`GSimpleGA.ConvergenceCriteria`, :func:`GSimpleGA.RawStatsCriteria`, :func:`GSimpleGA.FitnessStatsCriteria`

   But you can create your own termination function, this function receives
   one parameter which is the GA Engine, follows an example: ::

      def ConvergenceCriteria(ga_engine):
         pop = ga_engine.getPopulation()
         return pop[0] == pop[len(pop)-1]

   When this function returns True, the GA Engine will stop the evolution and show
   a warning, if is False, the evolution continues, this function is called every
   generation.
   """

   def __init__(self, genome, seed=None, interactiveMode=True):
      """ Initializator of GSimpleGA """
      if seed: random.seed(seed)

      if type(interactiveMode) != BooleanType:
         gp.Util.raiseException("Interactive Mode option must be True or False", TypeError)
      
      if not isinstance(genome, GenomeBase):
         gp.Util.raiseException("The genome must be a GenomeBase subclass", TypeError)

      self.internalPop  = GPopulation(genome)
      self.nGenerations = Consts.CDefGAGenerations
      self.pMutation    = Consts.CDefGAMutationRate
      self.pCrossover   = Consts.CDefGACrossoverRate
      self.nElitismReplacement = Consts.CDefGAElitismReplacement
      self.setPopulationSize(Consts.CDefGAPopulationSize)
      self.minimax      = Consts.minimaxType["maximize"]
      self.elitism      = True

      # Adapters
      self.dbAdapter        = None
      self.migrationAdapter = None
      
      self.time_init       = None
      self.interactiveMode = interactiveMode
      self.interactiveGen  = -1
      self.GPMode = False

      self.selector            = FunctionSlot("Selector")
      self.stepCallback        = FunctionSlot("Generation Step Callback")
      self.terminationCriteria = FunctionSlot("Termination Criteria")
      self.selector.set(Consts.CDefGASelector)
      self.allSlots            = [ self.selector, self.stepCallback, self.terminationCriteria ]

      self.internalParams = {}

      self.currentGeneration = 0

      # gp Testing
      CDefGPGenomes = [GTreeGP]
      for classes in CDefGPGenomes:
         if  isinstance(self.internalPop.oneSelfGenome, classes):
            self.setGPMode(True)
            break
      
      logging.debug("A GA Engine was created, nGenerations=%d", self.nGenerations)

   def setGPMode(self, bool_value):
      """ Sets the Genetic Programming mode of the GA Engine
      
      :param bool_value: True or False
      """
      self.GPMode = bool_value

   def getGPMode(self):
      """ Get the Genetic Programming mode of the GA Engine
      
      :rtype: True or False
      """
      return self.GPMode

   def __call__(self, *args, **kwargs):
      """ A method to implement a callable object

      Example:
         >>> ga_engine(freq_stats=10)
         
      .. versionadded:: 0.6
         The callable method.
      """
      if kwargs.get("freq_stats", None):
         return self.evolve(kwargs.get("freq_stats"))
      else:
         return self.evolve()

   def setParams(self, **args):
      """ Set the internal params

      Example:
         >>> ga.setParams(gp_terminals=['x', 'y'])


      :param args: params to save

      ..versionaddd:: 0.6
         Added the *setParams* method.
      """
      self.internalParams.update(args)
   
   def getParam(self, key, nvl=None):
      """ Gets an internal parameter

      Example:
         >>> ga.getParam("gp_terminals")
         ['x', 'y']

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      ..versionaddd:: 0.6
         Added the *getParam* method.
      """
      return self.internalParams.get(key, nvl)

   def setInteractiveGeneration(self, generation):
      """ Sets the generation in which the GA must enter in the
      Interactive Mode
      
      :param generation: the generation number, use "-1" to disable

      .. versionadded::0.6
         The *setInteractiveGeneration* method.
      """
      if generation < -1:
         Util.raiseException("Generation must be >= -1", ValueError)
      self.interactiveGen = generation

   def getInteractiveGeneration(self):
      """ returns the generation in which the GA must enter in the
      Interactive Mode
      
      :rtype: the generation number or -1 if not set

      .. versionadded::0.6
         The *getInteractiveGeneration* method.
      """
      return self.interactiveGen

   def setElitismReplacement(self, numreplace):
      """ Set the number of best individuals to copy to the next generation on the elitism

      :param numreplace: the number of individuals
      
      .. versionadded:: 0.6
         The *setElitismReplacement* method.

      """
      if numreplace < 1:
         Util.raiseException("Replacement number must be >= 1", ValueError)
      self.nElitismReplacement = numreplace


   def setInteractiveMode(self, flag=True):
      """ Enable/disable the interactive mode
      
      :param flag: True or False

      .. versionadded: 0.6
         The *setInteractiveMode* method.
      
      """
      if type(flag) != BooleanType:
         Util.raiseException("Interactive Mode option must be True or False", TypeError)
      self.interactiveMode = flag


   def __repr__(self):
      """ The string representation of the GA Engine """
      ret =  "- GSimpleGA\n"
      ret += "\tgp Mode:\t\t %s\n" % self.getGPMode()
      ret += "\tPopulation Size:\t %d\n" % (self.internalPop.popSize,)
      ret += "\tGenerations:\t\t %d\n" % (self.nGenerations,)
      ret += "\tCurrent Generation:\t %d\n" % (self.currentGeneration,)
      ret += "\tMutation Rate:\t\t %.2f\n" % (self.pMutation,)
      ret += "\tCrossover Rate:\t\t %.2f\n" % (self.pCrossover,)
      ret += "\tMinimax Type:\t\t %s\n" % (
      Consts.minimaxType.keys()[Consts.minimaxType.values().index(self.minimax)].capitalize(),)
      ret += "\tElitism:\t\t %s\n" % (self.elitism,)
      ret += "\tElitism Replacement:\t %d\n" % (self.nElitismReplacement,)
      ret += "\tDB Adapter:\t\t %s\n" % (self.dbAdapter,)
      for slot in self.allSlots:
         ret+= "\t" + slot.__repr__()
      ret+="\n"
      return ret

   def setPopulationSize(self, size):
      """ Sets the population size, calls setPopulationSize() of GPopulation

      :param size: the population size

      .. note:: the population size must be >= 2

      """
      if size < 2:
         Util.raiseException("population size must be >= 2", ValueError)
      self.internalPop.setPopulationSize(size)

   def setSortType(self, sort_type):
      """ Sets the sort type, Consts.sortType["raw"]/Consts.sortType["scaled"]

      Example:
         >>> ga_engine.setSortType(Consts.sortType["scaled"])

      :param sort_type: the Sort Type

      """
      if sort_type not in Consts.sortType.values():
         Util.raiseException("sort type must be a Consts.sortType type", TypeError)
      self.internalPop.sortType = sort_type

   def setMutationRate(self, rate):
      """ Sets the mutation rate, between 0.0 and 1.0

      :param rate: the rate, between 0.0 and 1.0

      """
      if (rate>1.0) or (rate<0.0):
         Util.raiseException("Mutation rate must be >= 0.0 and <= 1.0", ValueError)
      self.pMutation = rate

   def setCrossoverRate(self, rate):
      """ Sets the crossover rate, between 0.0 and 1.0

      :param rate: the rate, between 0.0 and 1.0

      """
      if (rate>1.0) or (rate<0.0):
         Util.raiseException("Crossover rate must be >= 0.0 and <= 1.0", ValueError)
      self.pCrossover = rate

   def setGenerations(self, num_gens):
      """ Sets the number of generations to evolve

      :param num_gens: the number of generations

      """
      if num_gens < 1:
         Util.raiseException("Number of generations must be >= 1", ValueError)
      self.nGenerations = num_gens

   def getGenerations(self):
      """ Return the number of generations to evolve

      :rtype: the number of generations

      .. versionadded:: 0.6
         Added the *getGenerations* method
      """
      return self.nGenerations

   def getMinimax(self):
      """ Gets the minimize/maximize mode

      :rtype: the Consts.minimaxType type

      """
      return self.minimax

   def setMinimax(self, mtype):
      """ Sets the minimize/maximize mode, use Consts.minimaxType

      :param mtype: the minimax mode, from Consts.minimaxType

      """
      if mtype not in Consts.minimaxType.values():
         Util.raiseException("Minimax must be maximize or minimize", TypeError)
      self.minimax = mtype

   def getCurrentGeneration(self):
      """ Gets the current generation

      :rtype: the current generation

      """
      return self.currentGeneration

   def setElitism(self, flag):
      """ Sets the elitism option, True or False

      :param flag: True or False

      """
      if type(flag) != BooleanType:
         Util.raiseException("Elitism option must be True or False", TypeError)
      self.elitism = flag

   def bestIndividual(self):
      """ Returns the population best individual

      :rtype: the best individual

      """
      return self.internalPop.bestRaw()

   def __gp_catch_functions(self, prefix):
      """ Internally used to catch functions with some specific prefix
      as non-terminals of the gp core """
      import __main__ as mod_main

      function_set = {}

      main_dict = mod_main.__dict__
      for obj, addr in main_dict.items():
         if obj[0:len(prefix)] == prefix:
            try:
               op_len = addr.func_code.co_argcount
            except:
               continue
            function_set[obj] = op_len

      if len(function_set) <= 0:
         Util.raiseException("No function set found using function prefix '%s' !" % prefix, ValueError)

      self.setParams(gp_function_set=function_set)

   def initialize(self):
      """ Initializes the GA Engine. Create and initialize population """
      self.internalPop.create(minimax=self.minimax)
      self.internalPop.initialize(ga_engine=self)
      logging.debug("The GA Engine was initialized !")      

   def getPopulation(self):
      """ Return the internal population of GA Engine

      :rtype: the population (:class:`GPopulation.GPopulation`)

      """
      return self.internalPop
   
   def getStatistics(self):
      """ Gets the Statistics class instance of current generation

      :rtype: the statistics instance (:class:`Statistics.Statistics`)

      """
      return self.internalPop.getStatistics()

   
   def clear(self):
      """ Petrowski's Clearing Method """
      

   def step(self):
      """ Just do one step in evolution, one generation """
      genomeMom = None
      genomeDad = None

      newPop = GPopulation(self.internalPop)
      logging.debug("Population was cloned.")

      size_iterate = len(self.internalPop)

      # Odd population size
      if size_iterate % 2 != 0: size_iterate -= 1

      crossover_empty = self.select(popID=self.currentGeneration).crossover.isEmpty()

      from utils import delog
      #############
      delog.decache("mutate and check...")
      #################
      for i in xrange(0, size_iterate, 2):
         genomeMom = self.select(popID=self.currentGeneration)
         genomeDad = self.select(popID=self.currentGeneration)

         if not crossover_empty and self.pCrossover >= 1.0:
            for it in genomeMom.crossover.applyFunctions(mom=genomeMom, dad=genomeDad, count=2):
               (sister, brother) = it
         else:
            if not crossover_empty and Util.randomFlipCoin(self.pCrossover):
               for it in genomeMom.crossover.applyFunctions(mom=genomeMom, dad=genomeDad, count=2):
                  (sister, brother) = it
            else:
               sister = genomeMom.clone()
               brother = genomeDad.clone()
         sister.mutate(pmut=self.pMutation, ga_engine=self)
         brother.mutate(pmut=self.pMutation, ga_engine=self)

         newPop.internalPop.append(sister)
         newPop.internalPop.append(brother)
      delog.deprint_string("over.")

      if len(self.internalPop) % 2 != 0:
         genomeMom = self.select(popID=self.currentGeneration)
         genomeDad = self.select(popID=self.currentGeneration)

         if Util.randomFlipCoin(self.pCrossover):
            for it in genomeMom.crossover.applyFunctions(mom=genomeMom, dad=genomeDad, count=1):
               (sister, brother) = it
         else:
            sister = random.choice([genomeMom, genomeDad])
            sister = sister.clone()
         sister.mutate(pmut=self.pMutation, ga_engine=self)
         newPop.internalPop.append(sister)

      logging.debug("Evaluating the new created population.")
      newPop.evaluate()
      newPop.sort()

      #Niching methods- Petrowski's clearing
      self.clear()

      if self.elitism:
         logging.debug("Doing elitism.")
         if self.getMinimax() == Consts.minimaxType["maximize"]:
            # in ecoc, max value is expected.
            for i in xrange(self.nElitismReplacement):
               if self.internalPop.bestRaw(i).score > newPop.bestRaw(i).score:
                  # check duplicate to avoid repeat indivadual
                  duplicate = False
                  for j in xrange(len(newPop)-self.nElitismReplacement, len(newPop)):
                     if self.internalPop.bestRaw(i).score == newPop.bestRaw(j).score:
                        duplicate = True
                        break
                  if duplicate: continue
                  newPop[len(newPop)-1-i] = newPop[i]
                  newPop[i] = self.internalPop.bestRaw(i)
         elif self.getMinimax() == Consts.minimaxType["minimize"]:
            for i in xrange(self.nElitismReplacement):
               if self.internalPop.bestRaw(i).score < newPop.bestRaw(i).score:
                  newPop[len(newPop)-1-i] = self.internalPop.bestRaw(i)

      self.internalPop = newPop
      self.internalPop.sort()

      logging.debug("The generation %d was finished.", self.currentGeneration)

      self.currentGeneration += 1

      return (self.currentGeneration == self.nGenerations)
   
   def printStats(self):
      """ Print generation statistics

      :rtype: the printed statistics as string

      .. versionchanged:: 0.6
         The return of *printStats* method.
      """
      percent = self.currentGeneration * 100 / float(self.nGenerations)
      #message = "Gen. %d (%.2f%%):" % (self.currentGeneration, percent)
      message = "Gen. %3d:     " % (self.currentGeneration)
      logging.info(message)
      print message,
      sys_stdout.flush()
      self.internalPop.statistics()
      stat_ret = self.internalPop.printStats()
      return message + stat_ret

   def printTimeElapsed(self):
      """ Shows the time elapsed since the begin of evolution """
      total_time = time()-self.time_init
      print "Total time elapsed: %.3f seconds." % total_time
      return total_time

   def evolve(self, freq_stats=0):
      """ Do all the generations until the termination criteria, accepts
      the freq_stats (default is 0) to dump statistics at n-generation

      Example:
         >>> ga_engine.evolve(freq_stats=10)
         (...)

      :param freq_stats: if greater than 0, the statistics will be
                         printed every freq_stats generation.
      :rtype: returns the best individual of the evolution

      .. versionadded:: 0.6
         the return of the best individual

      """
      stopFlagCallback = False
      stopFlagTerminationCriteria = False

      self.time_init = time()

      if self.getGPMode():
         gp_function_prefix = self.getParam("gp_function_prefix")
         if gp_function_prefix is not None:
            self.__gp_catch_functions(gp_function_prefix)

      self.initialize()
      
      logging.debug("Starting loop over evolutionary algorithm.")

      try:      
         while True:

            if not self.stepCallback.isEmpty():
               for it in self.stepCallback.applyFunctions(self):
                  stopFlagCallback = it

            if not self.terminationCriteria.isEmpty():
               for it in self.terminationCriteria.applyFunctions(self):
                  stopFlagTerminationCriteria = it

            if freq_stats:
               if (self.currentGeneration % freq_stats == 0) or (self.getCurrentGeneration() == 0):
                  self.printStats()

            if stopFlagTerminationCriteria:
               logging.debug("Evolution stopped by the Termination Criteria !")
               if freq_stats:
                  print "\n\tEvolution stopped by Termination Criteria function !\n"
               break

            if stopFlagCallback:
               logging.debug("Evolution stopped by Step Callback function !")
               if freq_stats:
                  print "\n\tEvolution stopped by Step Callback function !\n"
               break

            if self.interactiveMode:
               if sys_platform[:3] == "win":
                  if msvcrt.kbhit():
                     if ord(msvcrt.getch()) == Consts.CDefESCKey:
                        print "Loading modules for Interactive Mode...",
                        logging.debug("Windows Interactive Mode key detected ! generation=%d", self.getCurrentGeneration())
                        from gp import Interaction
                        print " done !"
                        interact_banner = "## Pyevolve v.%s - Interactive Mode ##\nPress CTRL-Z to quit interactive mode." % (__version__,)
                        session_locals = { "ga_engine"  : self,
                                           "population" : self.getPopulation(),
                                           "pyevolve"   : pyevolve,
                                           "it"         : Interaction}
                        print
                        code.interact(interact_banner, local=session_locals)

               if (self.getInteractiveGeneration() >= 0) and (self.getInteractiveGeneration() == self.getCurrentGeneration()):
                        print "Loading modules for Interactive Mode...",
                        logging.debug("Manual Interactive Mode key detected ! generation=%d", self.getCurrentGeneration())
                        from gp import Interaction
                        print " done !"
                        interact_banner = "## Pyevolve v.%s - Interactive Mode ##" % (__version__,)
                        session_locals = { "ga_engine"  : self,
                                           "population" : self.getPopulation(),
                                           "pyevolve"   : pyevolve,
                                           "it"         : Interaction}
                        print
                        code.interact(interact_banner, local=session_locals)

            if self.step(): break #exit if the number of generations is equal to the max. number of gens.



      except KeyboardInterrupt:
         logging.debug("CTRL-C detected, finishing evolution.")
         if freq_stats: print "\n\tA break was detected, you have interrupted the evolution !\n"

      if freq_stats != 0:
         self.printStats()
         self.printTimeElapsed()

      return self.bestIndividual()

   def select(self, **args):
      """ Select one individual from population

      :param args: this parameters will be sent to the selector

      """
      for it in self.selector.applyFunctions(self.internalPop, **args):
         return it
예제 #23
0
class ParticleBase(object):
	"""ParticleBase Class - the base of all particle representation """
	
	evaluator = None
	""" This is the :term 'evaluator function' slot, you can add a function with
	the *set* method: ::
	
		particle.evaluator.set(eval_func)
	"""
	
	position_initializator = None
	""" This is the position initialization function of the particle, you can change
	the default initializator using the function slot: :: 
		
		particle.position_initializator.set(Initializator.G1DListInitializatorDimmension)
	
	In this example, the initializator: func:`Initializators.G1DListInitializatorDimmension``
	will be used to create the initial position of the particle..
		
	  """

	velocity_initializator = None
	""" This is the velocity initialization function of the particle, you can change
	the default initializator using the function slot: :: 
		
		particle.velocity_initializator.set(Initializator.G1DListInitializatorDimmension)
	
	In this example, the initializator: func:`Initializators.G1DListInitializatorDimmension``
	will be used to create the initial velocity of the particle..
		
	 """
	
	position_communicator = None
	""" This is the position communication function slot, you can change the default
	communicator using the slot *set* function: ::

	particle.position_communicator.set(Communicators.P1DGlobalPosCommunicator)

	"""
	information_communicator = None
	""" This is the information communication function slot, you can change the default
	communicator usingt the slot *set* function: ::

	particle.information_communicator.set(Communicators.P1DGlobalInfoCommunicator)
	"""
	
	

	def __init__(self):
		""" Particle Constructor """
		self.evaluator = FunctionSlot("Evaluator")
		self.position_initializator = FunctionSlot("Position Initializator")
		self.velocity_initializator = FunctionSlot(" Velocity Initializator")
		self.position_communicator = FunctionSlot("Position Communicator")
		self.information_communicator = FunctionSlot("Information Communicator")
		
		self.allSlots = [self.evaluator, self.position_initializator,
					self.velocity_initializator, self.position_communicator, self.information_communicator]
		
		self.internalParams = {}
		self.fitness = 0.0
		self.ownBestFitness = 0.0
		
	def getFitness(self):
		""" Get the Fitness Score of the particle"
		
		:rtype particle fitness score
		
		"""
		return self.fitness
	
	def getOwnBestFitness(self):
		"""Get the best Fitness score of the particle
		
		:rtype particle best fitness score
		"""
		
		return self.ownBestFitness
	
	def __repr__(self):
		""" String representation of the Particle"""
		ret = "- ParticleBase\n"
		ret += "\tFitness:\t\t\t %.6f\n" %(self.fitness,)
		ret += "\tOwnBestFitness:\t\t\t %.6f\n" %(self.ownBestFitness,)
		ret += "\tInit Params:\t\t %s\n\n" %(self.internalParams,)
		for slot in self.allSlots:
			ret += "\t"+ slot.__repr__()
		ret += "\n"
		
		return ret
	
	def setOwnBestFitness(self,fitness):
		""" Set the best fitness of the particle 
			
			:param fitness: the best fitness of the particle
		"""
		
		self.ownBestFitness = fitness
	
	
	def setParams(self, **args):
		"""Set the initializator params"
		
		Example:
			>>> particle.setParams(rangemin=0, rangeMax=100,dimmensions=4)
		
		:param args: this params will saved in every particle for swarm op. use
		
		"""
		self.internalParams.update(args)
	
	def getParam(self,key,nvl=None):
		""" Gets an initialization parameter
		
		Example:
			>>> particle.getParam("rangemax")
			100
		
		:param key: the key of parma
		:param nvl: if the key doesn't exist, the nvl will be returned
		
		"""
		return self.internalParams.get(key,nvl)	


	def resetStats(self):
		"""Clear fitness of the particle """
		self.fitness = 0.0
	
	def evaluate(self, **args):
		""" Called to evaluate the particle
		
		:param args: these parameters will be passed to the evaluator
		"""
		self.resetStats()
		for it in self.evaluator.applyFunctions(self, **args):
			self.fitness += it
		
	def initializePosition(self, **args):
		"""Called to initialize the particle position
		
		:param args: these parameters will be passed to the initializator
		
		"""
		for it in self.position_initializator.applyFunctions(self, **args):
			pass
	
	
	def initializeVelocity(self, **args):
		"""Called to initialize the particle velocity
		
		:param args: these parameters will be passed to the initializator
		
		"""
		for it in self.velocity_initializator.applyFunctions(self,**args):
			pass

	def copy(self, other):
		""" Copy the current GenomeBase to 'g'
		
		:param other: the destination particle      

		"""
		other.fitness = self.fitness
		other.ownBestFitness = self.ownBestFitness
		other.evaluator = self.evaluator
		other.position_initializator = self.position_initializator
		other.velocity_initializator = self.velocity_initializator
		other.position_communicator = self.position_communicator
		other.information_communicator = self.information_communicator
		other.allSlots = self.allSlots[:]
		other.internalParams = self.internalParams.copy()
		

	def clone(self):
		""" Clone this ParticleBase
		
		:rtype: the clone particle
		
		"""
		
		newcopy = ParticleBase()
		self.copy(newcopy)
		return newcopy
		
		
예제 #24
0
class GPopulation:
    """ GPopulation Class - The container for the population

   **Examples**
      Get the population from the :class:`GSimpleGA.GSimpleGA` (GA Engine) instance
         >>> pop = ga_engine.getPopulation()

      Get the best fitness individual
         >>> bestIndividual = pop.bestFitness()

      Get the best raw individual
         >>> bestIndividual = pop.bestRaw()

      Get the statistics from the :class:`Statistics.Statistics` instance
         >>> stats = pop.getStatistics()
         >>> print stats["rawMax"]
         10.4

      Iterate, get/set individuals
         >>> for ind in pop:
         >>>   print ind
         (...)
         
         >>> for i in xrange(len(pop)):
         >>>    print pop[i]
         (...)

         >>> pop[10] = newGenome
         >>> pop[10].fitness
         12.5

   :param genome: the :term:`Sample genome`, or a GPopulation object, when cloning.

   """
    def __init__(self, genome):
        """ The GPopulation Class creator """

        if isinstance(genome, GPopulation):
            self.oneSelfGenome = genome.oneSelfGenome
            self.internalPop = []
            self.internalPopRaw = []
            self.popSize = genome.popSize
            self.sortType = genome.sortType
            self.sorted = False
            self.minimax = genome.minimax
            self.scaleMethod = genome.scaleMethod
            self.allSlots = [self.scaleMethod]

            self.internalParams = genome.internalParams

            self.statted = False
            self.stats = Statistics()
            return

        logging.debug("New population instance, %s class genomes.",
                      genome.__class__.__name__)
        self.oneSelfGenome = genome
        self.internalPop = []
        self.internalPopRaw = []
        self.popSize = 0
        self.sortType = Consts.CDefPopSortType
        self.sorted = False
        self.minimax = Consts.CDefPopMinimax
        self.scaleMethod = FunctionSlot("Scale Method")
        self.scaleMethod.set(Consts.CDefPopScale)
        self.allSlots = [self.scaleMethod]

        self.internalParams = {}

        # Statistics
        self.statted = False
        self.stats = Statistics()

    def setMinimax(self, minimax):
        """ Sets the population minimax

      Example:
         >>> pop.setMinimax(Consts.minimaxType["maximize"])
   
      :param minimax: the minimax type

      """
        self.minimax = minimax

    def __repr__(self):
        """ Returns the string representation of the population """
        ret = "- GPopulation\n"
        ret += "\tPopulation Size:\t %d\n" % (self.popSize, )
        ret += "\tSort Type:\t\t %s\n" % (Consts.sortType.keys()[
            Consts.sortType.values().index(self.sortType)].capitalize(), )
        ret += "\tMinimax Type:\t\t %s\n" % (Consts.minimaxType.keys()[
            Consts.minimaxType.values().index(self.minimax)].capitalize(), )
        for slot in self.allSlots:
            ret += "\t" + slot.__repr__()
        ret += "\n"
        ret += self.stats.__repr__()
        return ret

    def __len__(self):
        """ Return the length of population """
        return len(self.internalPop)

    def __getitem__(self, key):
        """ Returns the specified individual from population """
        return self.internalPop[key]

    def __iter__(self):
        """ Returns the iterator of the population """
        return iter(self.internalPop)

    def __setitem__(self, key, value):
        """ Set an individual of population """
        self.internalPop[key] = value
        self.clearFlags()

    def clearFlags(self):
        """ Clear the sorted and statted internal flags """
        self.sorted = False
        self.statted = False

    def getStatistics(self):
        """ Return a Statistics class for statistics

      :rtype: the :class:`Statistics.Statistics` instance

      """
        self.statistics()
        return self.stats

    def statistics(self):
        """ Do statistical analysis of population and set 'statted' to True """
        if self.statted: return
        logging.debug("Running statistical calculations")
        raw_sum = 0
        fit_sum = 0

        len_pop = len(self)
        for ind in xrange(len_pop):
            raw_sum += self[ind].score
            #fit_sum += self[ind].fitness

        self.stats["rawMax"] = max(self, key=key_raw_score).score
        self.stats["rawMin"] = min(self, key=key_raw_score).score
        self.stats["rawAve"] = raw_sum / float(len_pop)
        self.stats["Best-Fscore"] = max(self, key=key_raw_score).fscore
        self.stats["Best-Hamdist"] = max(self, key=key_raw_score).hamdist
        self.stats["Best-Accuracy"] = max(self, key=key_raw_score).accuracy
        #self.stats["rawTot"] = raw_sum
        #self.stats["fitTot"] = fit_sum

        tmpvar = 0.0
        for ind in xrange(len_pop):
            s = self[ind].score - self.stats["rawAve"]
            s *= s
            tmpvar += s

        tmpvar /= float((len(self) - 1))
        try:
            self.stats["rawDev"] = math_sqrt(tmpvar)
        except:
            self.stats["rawDev"] = 0.0

        self.stats["rawVar"] = tmpvar

        self.statted = True

    def bestFitness(self, index=0):
        """ Return the best scaled fitness individual of population

      :param index: the *index* best individual
      :rtype: the individual

      """
        self.sort()
        return self.internalPop[index]

    def bestRaw(self, index=0):
        """ Return the best raw score individual of population

      :param index: the *index* best raw individual
      :rtype: the individual

      .. versionadded:: 0.6
         The parameter `index`.
      
      """
        if self.sortType == Consts.sortType["raw"]:
            return self.internalPop[index]
        else:
            self.sort()
            return self.internalPopRaw[index]

    def sort(self):
        """ Sort the population """

        if self.sorted: return
        rev = (self.minimax == Consts.minimaxType["maximize"])

        if self.sortType == Consts.sortType["raw"]:
            self.internalPop.sort(cmp=Util.cmp_individual_raw, reverse=rev)
        else:
            self.scale()
            self.internalPop.sort(cmp=Util.cmp_individual_scaled, reverse=rev)
            self.internalPopRaw = self.internalPop[:]
            self.internalPopRaw.sort(cmp=Util.cmp_individual_raw, reverse=rev)

        self.sorted = True

    def setPopulationSize(self, size):
        """ Set the population size

      :param size: the population size

      """
        self.popSize = size

    def setSortType(self, sort_type):
        """ Sets the sort type

      Example:
         >>> pop.setSortType(Consts.sortType["scaled"])

      :param sort_type: the Sort Type

      """
        self.sortType = sort_type

    def create(self, **args):
        """ Clone the example genome to fill the population """
        self.minimax = args["minimax"]
        self.internalPop = [
            self.oneSelfGenome.clone() for i in xrange(self.popSize)
        ]
        self.clearFlags()

    def __findIndividual(self, individual, end):
        for i in xrange(end):
            if individual.compare(self.internalPop[i]) == 0:
                return True

    def initialize(self, **args):
        """ Initialize all individuals of population,
      this calls the initialize() of individuals """
        logging.debug("Initializing the population")
        if self.oneSelfGenome.getParam("full_diversity", True) and hasattr(
                self.oneSelfGenome, "compare"):
            for i in xrange(len(self.internalPop)):
                curr = self.internalPop[i]
                curr.initialize(**args)
                while self.__findIndividual(curr, i):
                    curr.initialize(**args)
        else:
            for gen in self.internalPop:
                gen.initialize(**args)
        self.clearFlags()

    def evaluate(self, **args):
        """ Evaluate all individuals in population, calls the evaluate() method of individuals
   
      :param args: this params are passed to the evaluation function

      """
        delog.decache("evaluate...")
        for ind in self.internalPop:
            ind.evaluate(**args)
        self.clearFlags()

        delog.deprint_string("over.")

    def scale(self, **args):
        """ Scale the population using the scaling method

      :param args: this parameter is passed to the scale method

      """
        for it in self.scaleMethod.applyFunctions(self, **args):
            pass

        fit_sum = 0
        for ind in xrange(len(self)):
            fit_sum += self[ind].fitness

        self.stats["fitMax"] = max(self, key=key_fitness_score).fitness
        self.stats["fitMin"] = min(self, key=key_fitness_score).fitness
        self.stats["fitAve"] = fit_sum / float(len(self))

        self.sorted = False

    def printStats(self):
        """ Print statistics of the current population """
        message = ""
        if self.sortType == Consts.sortType["scaled"]:
            '''
         format_str = '%%-8s  %%-8s  %%-8%s %%-10%s   %%-10%s'
         message = (format_str % ('s', 's', 's')) % ('Max', 'Min', 'Avg', 'Best-Fscore', 'Best-Hamdist')
         
         message = "Max/Min/Avg Fitness(Raw) [%(fitMax).2f(%(rawMax).2f)/%(fitMin).2f(%(rawMin).2f)/%(fitAve).2f(%(rawAve).2f)]" % self.stats
         '''

            format_str = '%(rawMax).2f      %(rawMin).2f      %(rawAve).2f     %(Best-Fscore).2f          %(Best-Hamdist).2f          %(Best-Accuracy).2f'
            message = format_str % self.stats

        else:
            format_str = '%(rawMax).2f      %(rawMin).2f      %(rawAve).2f     %(Best-Fscore).2f          %(Best-Hamdist).2f          %(Best-Accuracy).2f'
            message = format_str % self.stats
            # message = "Max/Min/Avg Raw [%(rawMax).2f/%(rawMin).2f/%(rawAve).2f]" % self.stats
        logging.info(message)
        print message + "\n"
        return message

    def copy(self, pop):
        """ Copy current population to 'pop'

      :param pop: the destination population

      .. warning:: this method do not copy the individuals, only the population logic

      """
        pop.popSize = self.popSize
        pop.sortType = self.sortType
        pop.minimax = self.minimax
        pop.scaleMethod = self.scaleMethod
        #pop.internalParams = self.internalParams.copy()
        pop.internalParams = self.internalParams
        pop.multiProcessing = self.multiProcessing

    def getParam(self, key, nvl=None):
        """ Gets an internal parameter

      Example:
         >>> population.getParam("tournamentPool")
         5

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      """
        return self.internalParams.get(key, nvl)

    def setParams(self, **args):
        """ Gets an internal parameter

      Example:
         >>> population.setParams(tournamentPool=5)

      :param args: parameters to set

      .. versionadded:: 0.6
         The `setParams` method.
      """
        self.internalParams.update(args)

    def clear(self):
        """ Remove all individuals from population """
        del self.internalPop[:]
        del self.internalPopRaw[:]
        self.clearFlags()

    def clone(self):
        """ Return a brand-new cloned population """
        newpop = GPopulation(self.oneSelfGenome)
        self.copy(newpop)
        return newpop
예제 #25
0
파일: Pso.py 프로젝트: DiNAi/pypso
class SimplePSO(object):
	""" SimplePSO Engine Class - The PSO Algorithm Core
	
	Example:
		>>> topology = Topology.GlobalTopology(particle_rep)
		>>> pso = PSO.SimplePSO(topology)
		>>> pso.setSteps(120)
	
	:param topology: the :term:`Sample Topology``
	:param  interactiveMode: this flag enables the Interactive Mode
	:param seed: the random seed value
	
	.. note:: if you see the same random seed, all the runs of the algorithm will be the same.
	
	"""
	
	stepCallBack = None
	""" This is the the :term: `step callback function` slot,
	if you want to set the function, you must do this: ::
		
		def your_func(pso_engine):
			#Here you have access to the PSO Engine
			return False
		
		pso_engine.stepCallback.set(your_func)
	
	now *"your_func"* will be called every step.
	When this function returns True, the  PSO Engine will stop the evolution and show
	a warning, if is False, the evolution continues.
	"""
	
	terminationCriteria  = None
	""" This is the termination criteria slot, if you want to set one 
	termination criteria, you mus do this: ::
		
		pso_engine.terminationCriteria.set(your_func)
		
	Now, when you run your PSO, it will stop when terminationCriteria be satisfied.
	
	To create your own termination function, you must put at least one parameter
	which is the PSO Engine, follows an example: ::
		
		def ConvergenceCriteria(pso_engine):
			swarm = pso_engine.getSwarm()
			return swarm[0] == swarm[len(swarm)-1]
		
	When this function returns True, the Pso Engine will stop the evolution and show
	a warning. If is False, the evolution  continues, this function is called every
	step.
	
	"""
	
	def __init__(self,topology,seed=None,interactiveMode=True):
		""" Initializator of PSO """
		#random seed
		random.seed(seed)
		#Pso type used by the particle
		self.psoType = Consts.CDefPsoType
		#Topology used
		self.topology = topology
		#Set the population size
		self.setSwarmSize(Consts.CDefSwarmSize)
		#Cognitive and Social Coefficients
		self.C1,self.C2 = Consts.CDefCoefficients
        #Time steps
		self.timeSteps = Consts.CDefSteps
		#Interactive Mode (True or False)
		self.interactiveMode = interactiveMode
		#Current step
		self.currentStep = 0
		#Inertia Factor Minus
		self.inertiaFactorMinus = None
		#Inertia coefficient
		self.inertiaFactor = None
		#Time initial
		self.time_init = None
	    #Optimization type
		self.minimax = Consts.minimaxType["minimize"]
		#Report file adapter 
		self.reportAdapter = None
		#Step Callback
		self.stepCallback = FunctionSlot("Step Callback")
		#Termination Criteria
		self.terminationCriteria = FunctionSlot("Termination Criteria")
		#All slots
		self.allSlots = [self.stepCallback, self.terminationCriteria]
		
		print "A PSO Engine was created, timeSteps=% d" % ( self.timeSteps, )


	def __repr__(self):
		""" The String representation of the PSO Engine """
		ret =   "- PSO-%s-%s Execution\n" % (self.getTopologyType(),self.getPsoType())
		ret +=  "\tSwarm Size:\t %d\n" % (self.topology.swarmSize,)
		ret +=  "\tTime Steps:\t %d\n" % (self.timeSteps,)      
		ret +=  "\tCurrent Step:\t %d\n" % (self.currentStep,)
		ret +=  "\tMinimax Type:\t %s\n" % (Consts.minimaxType.keys()[Consts.minimaxType.values().index(self.minimax)].capitalize(),)
		ret +=  "\tReport Adapter:\t %s\n" % (self.reportAdapter,)
		for slot in self.allSlots:
			ret += "\t" + slot.__repr__()
		ret +="\n"
		return ret

	def setReportAdapter(self,repadapter):
		""" Sets the Report Adapter of the PSO Engine
		
		:param repadapter: one of the :mod:`ReportAdapters` classes instance
		
		.. warning: the use of a Report Adapter can reduce the speed performance of the PSO.
		
		"""
		self.reportAdapter = repadapter
		
	
	def setSwarmSize(self, size):
		""" Sets the swarm size, calls setSwarmSize()  of Topology
		
		:param size: the swarm size
		
		.. note:: the swarm size must be >= 2
		
		"""
		if size < 2:
			Util.raiseException("swarm size must be >= 2", ValueError)
		self.topology.setSwarmSize(size)
	


	def setPsoType(self,psoType):
		""" Sets the psoType, use Consts.psoType(Basic,Constricted,Inertia)
		
		Example:
			>>> pso_engine.setSortType(Consts.psoType["CONSTRICTED"])
      
        :param psoType: The PSO type, from Consts.psoType
        
		"""
		if psoType not in Consts.psoType.values():
			Util.raiseException("PsoType must be implemented !",TypeError)
		self.psoType = psoType

	def getPsoType(self):
		""" Return the Pso Type
		
		:rtype key: pso Type
		"""	
		for key,value in Consts.psoType.items():
			if value == self.psoType:
				return key
		return ""

	def setTimeSteps(self,num_steps):
		""" Sets the number of steps to converge
		
		:param num_steps: the number of steps
		
		"""
		if num_steps < 1:
			Util.raiseException("Number of steps must be >=1", ValueError)
		self.timeSteps = num_steps


	def getMinimax(self):
		""" Gets the minimize/maximize mode
		
		:rtype: The Consts.minimaxType type
		
		"""
		for key,value in Consts.minimaxType.items():
			if value == self.minimax:
				return key
			return ""
			

	
	def getTopologyType(self):
		""" Returns the name of the topology
		
		:rtype name: the name of the topology
		 """
		return self.topology.__class__.__name__

	def setMinimax(self,minimax):
		"""Sets the minimize/maximize mode, use Consts.minimaxType
		
		:param minimax: the minimax mode, from Consts.minimaxType
		
		"""
		if minimax not in Consts.minimaxType.values():
			Util.raiseException("Optimization type must be Maximize or Minimize !", TypeError)
		
		self.minimax = minimax	

	def getCurrentStep(self):
		""" Gets the current step
		
		:rtype: the current step
		"""
		return self.currentStep

	def getReportAdapter(self):
		""" Gets the Report Adapter of the PSO Engine
		
		:rtype: a instance from one of the :mod:`ReportAdapters` classes
		
		"""
		return self.reportAdapter
	
	
	def bestParticle(self):
		""" Returns the swarm best Particle
		
		:rtype: the best particle
		
		"""
		return self.topology.getBestParticle()
		
	def getTopology(self):
		"""Return the internal topology of Pso Engine
		
		:rtype: the topology (:class: 'Topology.Topology')'
	
		"""
		return self.topology
		

	def getStatistics(self):
		""" Gets the Statistics class instance of the current step
		
		:rtype: the statistics instance (:class: `Statistics.Statistics`)`
		
		"""
		return self.topology.getStatistics()

	def dumpStatsReport(self):
		""" Dumps the current statistics to the  report adapter """
		self.topology.statistics()
		self.reportAdapter.insert(self.getStatistics(),self.topology,self.currentStep)
		
		
	def printStats(self):
		""" Print swarm statistics"""
		percent = self.currentStep * 100 / float(self.timeSteps)
		message = "Step: %d (%.2f%%):" % (self.currentStep, percent)
		print message
		self.topology.statistics()
		self.topology.printStats()

	
	def printTimeElapsed(self):
		""" Shows the time elapsed since the beginning of the solution construction """
		print "Total time elapsed: %.3f seconds." % (time()-self.time_init)
    	
	
	def initialize(self):
		""" Initializes the PSO Engine. Create and initialize the swarm """
		self.topology.create(minimax=self.minimax)
		self.topology.initialize()
		print "The PSO Engine was initialized !"
	
	
	def constructSolution(self):
		""" Just do one step in execution, one step."""
		for it in self.topology.position_updater.applyFunctions(self):
			pass

		for it in self.topology.information_updater.applyFunctions(self):
			pass
		
		if self.psoType == Consts.psoType["INERTIA"]:
			self.updateInertiaFactor()
		
		self.currentStep += 1
		
		return (self.currentStep == self.timeSteps)


	
	def execute(self, freq_stats=0):
		""" Do all the steps until the termination criteria or time Steps achieved,
		accepts the freq_stats (default is 0) to dump statistics at n-step
		
		Example:
			>>> pso_engine.evolve(freq_stats=10)
			(...)
		
		:param freq_stats: if greater than 0, the statistics will be 
							printed every freq_stats step.

		"""
		#Start time
		self.time_init = time()
		
		#Creates a new report if reportAdapter is not None.
		if  self.reportAdapter: self.reportAdapter.open()
		
		#Initialize the PSO Engine
		self.initialize()  #Already evaluates all particles


		print "Starting loop over evolutionary algorithm."
		
		try:
			while not self.constructSolution():
				stopFlagCallback = False
				stopFlagTerminationCriteria = False
				
				if not self.stepCallback.isEmpty():
					for it in self.stepCallback.applyFunctions(self):
						stopFlagCallback = it
					
				if not self.terminationCriteria.isEmpty():
					for it in self.terminationCriteria.applyFunctions(self):
						stopFlagTerminationCriteria = it
				
				if freq_stats != 0:
					if (self.currentStep % freq_stats == 0) or (self.currentStep == 1):
						self.printStats()
					
				if self.reportAdapter:
					if self.currentStep % self.reportAdapter.statsGenFreq == 0:
						self.dumpStatsReport()
				
				if stopFlagTerminationCriteria:
					print '\n\tExecution stopped by Termination Criteria function !\n'
					break
				
				if stopFlagCallback:
					print '\n\tExecution stopped by Step Callback function!\n'
					break

			  
				if self.interactiveMode:
					if sys_platform[:3] == "win":
						if msvcrt.kbhit():
							if ord(msvcrt.getch()) == Consts.CDefESCKey:
								print "Loading modules for Interactive mode...",
								import pypso.Interaction
								print "done!\n"
								interact_banner = "## PyPSO v.%s - Interactive Mode ##\nPress CTRL-Z to quit interactive mode." % (pypso.__version__,)
								session_locals = {  "pso_engine"  : self,
													"topology" : self.getTopology(),
													"swarm_statistics": self.getTopology().swarmStats,
													"topology_statistics": self.getTopology().topologyStats,
													"pypso"   : pypso ,
													"it"         : pypso.Interaction}
								print
								code.interact(interact_banner, local=session_locals)
					elif sys_platform[:5] == "linux":
						if Util.kbhit():
							if ord(Util.getch()) == Consts.CDefESCKey:
								print "Loading modules for Interactive mode...",
								import pypso.Interaction
								print "done!\n"
								interact_banner = "## PyPSO v.%s - Interactive Mode ##\nPress CTRL-D to quit interactive mode." % (pypso.__version__,)
								session_locals = {  "pso_engine"  : self,
													"topology" : self.getTopology(),
													"swarm_statistics": self.getTopology().swarmStats,
													"topology_statistics": self.getTopology().topologyStats,
													"pypso"   : pypso ,
													"it"         : pypso.Interaction}
								print
								code.interact(interact_banner, local=session_locals)
                                    
		except KeyboardInterrupt:
			print "\n\tA break was detected, you have interrupted the evolution !\n"

		if freq_stats != 0:
			self.printStats()
			self.printTimeElapsed()
    
		if self.reportAdapter:
			if (self.currentStep % self.reportAdapter.statsGenFreq == 0):
				self.dumpStatsReport()
			self.reportAdapter.saveAndClose()	
예제 #26
0
class GSimpleGA:
    """ GA Engine Class - The Genetic Algorithm Core

   Example:
      >>> ga = GSimpleGA.GSimpleGA(genome)
      >>> ga.selector.set(Selectors.GRouletteWheel)
      >>> ga.setGenerations(120)
      >>> ga.terminationCriteria.set(GSimpleGA.ConvergenceCriteria)

   :param genome: the :term:`Sample Genome`
   :param interactiveMode: this flag enables the Interactive Mode, the default is True
   :param seed: the random seed value

   .. note:: if you use the same random seed, all the runs of algorithm will be the same

   """

    selector = None
    """ This is the function slot for the selection method
   if you want to change the default selector, you must do this: ::

      ga_engine.selector.set(Selectors.GRouletteWheel) """

    stepCallback = None
    """ This is the :term:`step callback function` slot,
   if you want to set the function, you must do this: ::

      def your_func(ga_engine):
         # Here you have access to the GA Engine
         return False

      ga_engine.stepCallback.set(your_func)

   now *"your_func"* will be called every generation.
   When this function returns True, the GA Engine will stop the evolution and show
   a warning, if is False, the evolution continues.
   """

    terminationCriteria = None
    """ This is the termination criteria slot, if you want to set one
   termination criteria, you must do this: ::

      ga_engine.terminationCriteria.set(GSimpleGA.ConvergenceCriteria)

   Now, when you run your GA, it will stop when the population converges.

   There are those termination criteria functions: :func:`GSimpleGA.RawScoreCriteria`, :func:`GSimpleGA.ConvergenceCriteria`, :func:`GSimpleGA.RawStatsCriteria`, :func:`GSimpleGA.FitnessStatsCriteria`

   But you can create your own termination function, this function receives
   one parameter which is the GA Engine, follows an example: ::

      def ConvergenceCriteria(ga_engine):
         pop = ga_engine.getPopulation()
         return pop[0] == pop[len(pop)-1]

   When this function returns True, the GA Engine will stop the evolution and show
   a warning, if is False, the evolution continues, this function is called every
   generation.
   """
    def __init__(self, genome, owner, seed=None, interactiveMode=True):
        """ Initializator of GSimpleGA """
        if seed: random.seed(seed)

        if type(interactiveMode) != BooleanType:
            Util.raiseException(
                "Interactive Mode option must be True or False", TypeError)

        if not isinstance(genome, GenomeBase):
            Util.raiseException("The genome must be a GenomeBase subclass",
                                TypeError)

        self.internalPop = GPopulation(genome)
        self.nGenerations = Consts.CDefGAGenerations
        self.pMutation = Consts.CDefGAMutationRate
        self.pCrossover = Consts.CDefGACrossoverRate
        self.nElitismReplacement = Consts.CDefGAElitismReplacement
        self.setPopulationSize(Consts.CDefGAPopulationSize)
        self.minimax = Consts.minimaxType["maximize"]
        self.elitism = True

        self.owner = owner  ## added 12/15 by Peter Graf, so GA can evaluate constraints
        ## and, now (4/16), so we can save let the owner save the state
        # Adapters
        self.dbAdapter = None
        self.migrationAdapter = None

        self.time_init = None
        self.interactiveMode = interactiveMode
        self.interactiveGen = -1
        self.GPMode = False

        self.selector = FunctionSlot("Selector")
        self.stepCallback = FunctionSlot("Generation Step Callback")
        self.terminationCriteria = FunctionSlot("Termination Criteria")
        self.selector.set(Consts.CDefGASelector)
        self.allSlots = [
            self.selector, self.stepCallback, self.terminationCriteria
        ]

        self.internalParams = {}

        self.currentGeneration = 0

        # GP Testing
        for classes in Consts.CDefGPGenomes:
            if isinstance(self.internalPop.oneSelfGenome, classes):
                self.setGPMode(True)
                break

        logging.debug("A GA Engine was created, nGenerations=%d",
                      self.nGenerations)

    def setGPMode(self, bool_value):
        """ Sets the Genetic Programming mode of the GA Engine

      :param bool_value: True or False
      """
        self.GPMode = bool_value

    def getGPMode(self):
        """ Get the Genetic Programming mode of the GA Engine

      :rtype: True or False
      """
        return self.GPMode

    def __call__(self, *args, **kwargs):
        """ A method to implement a callable object

      Example:
         >>> ga_engine(freq_stats=10)

      .. versionadded:: 0.6
         The callable method.
      """
        if kwargs.get("freq_stats", None):
            return self.evolve(kwargs.get("freq_stats"))
        else:
            return self.evolve()

    def setParams(self, **args):
        """ Set the internal params

      Example:
         >>> ga.setParams(gp_terminals=['x', 'y'])


      :param args: params to save

      ..versionaddd:: 0.6
         Added the *setParams* method.
      """
        self.internalParams.update(args)

    def getParam(self, key, nvl=None):
        """ Gets an internal parameter

      Example:
         >>> ga.getParam("gp_terminals")
         ['x', 'y']

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      ..versionaddd:: 0.6
         Added the *getParam* method.
      """
        return self.internalParams.get(key, nvl)

    def setInteractiveGeneration(self, generation):
        """ Sets the generation in which the GA must enter in the
      Interactive Mode

      :param generation: the generation number, use "-1" to disable

      .. versionadded::0.6
         The *setInteractiveGeneration* method.
      """
        if generation < -1:
            Util.raiseException("Generation must be >= -1", ValueError)
        self.interactiveGen = generation

    def getInteractiveGeneration(self):
        """ returns the generation in which the GA must enter in the
      Interactive Mode

      :rtype: the generation number or -1 if not set

      .. versionadded::0.6
         The *getInteractiveGeneration* method.
      """
        return self.interactiveGen

    def setElitismReplacement(self, numreplace):
        """ Set the number of best individuals to copy to the next generation on the elitism

      :param numreplace: the number of individuals

      .. versionadded:: 0.6
         The *setElitismReplacement* method.

      """
        if numreplace < 1:
            Util.raiseException("Replacement number must be >= 1", ValueError)
        self.nElitismReplacement = numreplace

    def setInteractiveMode(self, flag=True):
        """ Enable/disable the interactive mode

      :param flag: True or False

      .. versionadded: 0.6
         The *setInteractiveMode* method.

      """
        if type(flag) != BooleanType:
            Util.raiseException(
                "Interactive Mode option must be True or False", TypeError)
        self.interactiveMode = flag

    def __repr__(self):
        """ The string representation of the GA Engine """
        ret = "- GSimpleGA\n"
        ret += "\tGP Mode:\t\t %s\n" % self.getGPMode()
        ret += "\tPopulation Size:\t %d\n" % (self.internalPop.popSize, )
        ret += "\tGenerations:\t\t %d\n" % (self.nGenerations, )
        ret += "\tCurrent Generation:\t %d\n" % (self.currentGeneration, )
        ret += "\tMutation Rate:\t\t %.2f\n" % (self.pMutation, )
        ret += "\tCrossover Rate:\t\t %.2f\n" % (self.pCrossover, )
        ret += "\tMinimax Type:\t\t %s\n" % (Consts.minimaxType.keys()[
            Consts.minimaxType.values().index(self.minimax)].capitalize(), )
        ret += "\tElitism:\t\t %s\n" % (self.elitism, )
        ret += "\tElitism Replacement:\t %d\n" % (self.nElitismReplacement, )
        ret += "\tDB Adapter:\t\t %s\n" % (self.dbAdapter, )
        for slot in self.allSlots:
            ret += "\t" + slot.__repr__()
        ret += "\n"
        return ret

    def setMultiProcessing(self, flag=True, full_copy=False):
        """ Sets the flag to enable/disable the use of python multiprocessing module.
      Use this option when you have more than one core on your CPU and when your
      evaluation function is very slow.

      Pyevolve will automaticly check if your Python version has **multiprocessing**
      support and if you have more than one single CPU core. If you don't have support
      or have just only one core, Pyevolve will not use the **multiprocessing**
      feature.

      Pyevolve uses the **multiprocessing** to execute the evaluation function over
      the individuals, so the use of this feature will make sense if you have a
      truly slow evaluation function (which is commom in GAs).

      The parameter "full_copy" defines where the individual data should be copied back
      after the evaluation or not. This parameter is useful when you change the
      individual in the evaluation function.

      :param flag: True (default) or False
      :param full_copy: True or False (default)

      .. warning:: Use this option only when your evaluation function is slow, so you'll
                   get a good tradeoff between the process communication speed and the
                   parallel evaluation. The use of the **multiprocessing** doesn't means
                   always a better performance.

      .. note:: To enable the multiprocessing option, you **MUST** add the *__main__* check
                on your application, otherwise, it will result in errors. See more on the
                `Python Docs <http://docs.python.org/library/multiprocessing.html#multiprocessing-programming>`__
                site.

      .. versionadded:: 0.6
         The `setMultiProcessing` method.

      """
        if type(flag) != BooleanType:
            Util.raiseException("Multiprocessing option must be True or False",
                                TypeError)

        if type(full_copy) != BooleanType:
            Util.raiseException(
                "Multiprocessing 'full_copy' option must be True or False",
                TypeError)

        self.internalPop.setMultiProcessing(flag, full_copy)

    def setMigrationAdapter(self, migration_adapter=None):
        """ Sets the Migration Adapter

      .. versionadded:: 0.6
         The `setMigrationAdapter` method.
      """

        self.migrationAdapter = migration_adapter
        if self.migrationAdapter is not None:
            self.migrationAdapter.setGAEngine(self)

    def setDBAdapter(self, dbadapter=None):
        """ Sets the DB Adapter of the GA Engine

      :param dbadapter: one of the :mod:`DBAdapters` classes instance

      .. warning:: the use the of a DB Adapter can reduce the speed performance of the
                   Genetic Algorithm.
      """
        if (dbadapter
                is not None) and (not isinstance(dbadapter, DBBaseAdapter)):
            Util.raiseException(
                "The DB Adapter must be a DBBaseAdapter subclass", TypeError)
        self.dbAdapter = dbadapter

    def setPopulationSize(self, size):
        """ Sets the population size, calls setPopulationSize() of GPopulation

      :param size: the population size

      .. note:: the population size must be >= 2

      """
        if size < 2:
            Util.raiseException("population size must be >= 2", ValueError)
        self.internalPop.setPopulationSize(size)

    def setSortType(self, sort_type):
        """ Sets the sort type, Consts.sortType["raw"]/Consts.sortType["scaled"]

      Example:
         >>> ga_engine.setSortType(Consts.sortType["scaled"])

      :param sort_type: the Sort Type

      """
        if sort_type not in Consts.sortType.values():
            Util.raiseException("sort type must be a Consts.sortType type",
                                TypeError)
        self.internalPop.sortType = sort_type

    def setMutationRate(self, rate):
        """ Sets the mutation rate, between 0.0 and 1.0

      :param rate: the rate, between 0.0 and 1.0

      """
        if (rate > 1.0) or (rate < 0.0):
            Util.raiseException("Mutation rate must be >= 0.0 and <= 1.0",
                                ValueError)
        self.pMutation = rate

    def setCrossoverRate(self, rate):
        """ Sets the crossover rate, between 0.0 and 1.0

      :param rate: the rate, between 0.0 and 1.0

      """
        if (rate > 1.0) or (rate < 0.0):
            Util.raiseException("Crossover rate must be >= 0.0 and <= 1.0",
                                ValueError)
        self.pCrossover = rate

    def setGenerations(self, num_gens):
        """ Sets the number of generations to evolve

      :param num_gens: the number of generations

      """
        if num_gens < 1:
            Util.raiseException("Number of generations must be >= 1",
                                ValueError)
        self.nGenerations = num_gens

    def getGenerations(self):
        """ Return the number of generations to evolve

      :rtype: the number of generations

      .. versionadded:: 0.6
         Added the *getGenerations* method
      """
        return self.nGenerations

    def getMinimax(self):
        """ Gets the minimize/maximize mode

      :rtype: the Consts.minimaxType type

      """
        return self.minimax

    def setMinimax(self, mtype):
        """ Sets the minimize/maximize mode, use Consts.minimaxType

      :param mtype: the minimax mode, from Consts.minimaxType

      """
        if mtype not in Consts.minimaxType.values():
            Util.raiseException("Minimax must be maximize or minimize",
                                TypeError)
        self.minimax = mtype

    def getCurrentGeneration(self):
        """ Gets the current generation

      :rtype: the current generation

      """
        return self.currentGeneration

    def setElitism(self, flag):
        """ Sets the elitism option, True or False

      :param flag: True or False

      """
        if type(flag) != BooleanType:
            Util.raiseException("Elitism option must be True or False",
                                TypeError)
        self.elitism = flag

    def getDBAdapter(self):
        """ Gets the DB Adapter of the GA Engine

      :rtype: a instance from one of the :mod:`DBAdapters` classes

      """
        return self.dbAdapter

    def bestIndividual(self):
        """ Returns the population best individual

      :rtype: the best individual

      """
        return self.internalPop.bestRaw()

    def __gp_catch_functions(self, prefix):
        """ Internally used to catch functions with some specific prefix
      as non-terminals of the GP core """
        import __main__ as mod_main

        function_set = {}

        main_dict = mod_main.__dict__
        for obj, addr in main_dict.items():
            if obj[0:len(prefix)] == prefix:
                try:
                    op_len = addr.func_code.co_argcount
                except:
                    continue
                function_set[obj] = op_len

        if len(function_set) <= 0:
            Util.raiseException(
                "No function set found using function prefix '%s' !" % prefix,
                ValueError)

        self.setParams(gp_function_set=function_set)

    def initialize(self):
        """ Initializes the GA Engine. Create and initialize population """
        self.internalPop.create(minimax=self.minimax)
        self.internalPop.initialize(ga_engine=self)
        logging.debug("The GA Engine was initialized !")

    def getPopulation(self):
        """ Return the internal population of GA Engine

      :rtype: the population (:class:`GPopulation.GPopulation`)

      """
        return self.internalPop

    def getStatistics(self):
        """ Gets the Statistics class instance of current generation

      :rtype: the statistics instance (:class:`Statistics.Statistics`)

      """
        return self.internalPop.getStatistics()

    def step(self):
        """ Just do one step in evolution, one generation """
        genomeMom = None
        genomeDad = None

        newPop = GPopulation(self.internalPop)

        if (MPI.COMM_WORLD.Get_rank() == 0
            ):  ### assumes WE are on top of hierarchy!
            popsize = len(self.internalPop)
            numAdded = 0
            maxTries = 1000
            numTries = 0

            crossover_empty = self.select(
                popID=self.currentGeneration).crossover.isEmpty()

            ###TODO: enforce constraints!###
            while numAdded < popsize:
                genomeMom = self.select(popID=self.currentGeneration)
                genomeDad = self.select(popID=self.currentGeneration)

                if not crossover_empty and self.pCrossover >= 1.0:
                    for it in genomeMom.crossover.applyFunctions(mom=genomeMom,
                                                                 dad=genomeDad,
                                                                 count=2):
                        (sister, brother) = it
                else:
                    if not crossover_empty and Util.randomFlipCoin(
                            self.pCrossover):
                        for it in genomeMom.crossover.applyFunctions(
                                mom=genomeMom, dad=genomeDad, count=2):
                            (sister, brother) = it
                    else:
                        sister = genomeMom.clone()
                        brother = genomeDad.clone()
    #               logging.debug("done cloning")

                sister.mutate(pmut=self.pMutation, ga_engine=self)
                brother.mutate(pmut=self.pMutation, ga_engine=self)

                if (numTries > maxTries
                        or self.owner.eval_constraints(sister)):
                    newPop.internalPop.append(sister)
                    numAdded += 1
                    print "successfully added sister"
                if (numAdded < popsize
                        and (numTries > maxTries
                             or self.owner.eval_constraints(brother))):
                    newPop.internalPop.append(brother)
                    print "successfully added brother"
                    numAdded += 1
                numTries += 1

        #end rank0 onlye

    #      print "rank %d start eval pop" % MPI.COMM_WORLD.Get_rank()

        logging.debug("Evaluating the newly created population.")
        newPop.evaluate()
        #      print "rank %d done eval pop" % MPI.COMM_WORLD.Get_rank()
        #      if (MPI.COMM_WORLD.Get_rank() == 0):
        #         print "after eval, new pop's positions are:"
        #         for p in newPop:
        #            print p.wt_positions

        if (MPI.COMM_WORLD.Get_rank() == 0
            ):  ### assumes WE are on top of hierarchy!
            if self.elitism:
                logging.debug("Doing elitism, %d" % self.nElitismReplacement)
                if self.getMinimax() == Consts.minimaxType["maximize"]:
                    for i in xrange(self.nElitismReplacement):
                        #re-evaluate before being sure this is the best
                        #               self.internalPop.bestRaw(i).evaluate()
                        if self.internalPop.bestRaw(i).score > newPop.bestRaw(
                                i).score:
                            newPop[len(newPop) - 1 -
                                   i] = self.internalPop.bestRaw(i)
                elif self.getMinimax() == Consts.minimaxType["minimize"]:
                    for i in xrange(self.nElitismReplacement):
                        #re-evaluate before being sure this is the best
                        #               self.internalPop.bestRaw(i).evaluate()
                        if self.internalPop.bestRaw(i).score < newPop.bestRaw(
                                i).score:
                            newPop[len(newPop) - 1 -
                                   i] = self.internalPop.bestRaw(i)

        self.internalPop = newPop
        if (MPI.COMM_WORLD.Get_rank() == 0
            ):  ### assumes WE are on top of hierarchy!
            self.internalPop.sort()
#      if (MPI.COMM_WORLD.Get_rank() == 0):
#         print "after sort, internal pop's positions are:"
#         for p in self.internalPop:
#            print p.wt_positions

        logging.debug("The generation %d was finished.",
                      self.currentGeneration)
        self.currentGeneration += 1

        if (MPI.COMM_WORLD.Get_rank() == 0
            ):  ### assumes WE are on top of hierarchy!
            self.saveState()

        return (self.currentGeneration == self.nGenerations)

    def saveState(self):
        self.owner.saveState(self.internalPop.internalPop,
                             self.currentGeneration)

    def restoreState(self, gen):
        self.owner.restoreState(self.internalPop.internalPop, gen)
        self.currentGeneration = gen

    def printStats(self):
        """ Print generation statistics

      :rtype: the printed statistics as string

      .. versionchanged:: 0.6
         The return of *printStats* method.
      """
        if (MPI.COMM_WORLD.Get_rank() == 0
            ):  ### assumes WE are on top of hierarchy!
            percent = self.currentGeneration * 100 / float(self.nGenerations)
            message = "Gen. %d (%.2f%%):" % (self.currentGeneration, percent)
            logging.info(message)
            print message,
            sys_stdout.flush()
            self.internalPop.statistics()
            stat_ret = self.internalPop.printStats()
            return message + stat_ret
        else:
            return ""

    def printTimeElapsed(self):
        """ Shows the time elapsed since the begin of evolution """
        total_time = time() - self.time_init
        print "Total time elapsed: %.3f seconds." % total_time
        return total_time

    def dumpStatsDB(self):
        """ Dumps the current statistics to database adapter """
        logging.debug("Dumping stats to the DB Adapter")
        self.internalPop.statistics()
        self.dbAdapter.insert(self)

    def evolve(self, freq_stats=0, restore=-1):
        """ Do all the generations until the termination criteria, accepts
      the freq_stats (default is 0) to dump statistics at n-generation

      Example:
         >>> ga_engine.evolve(freq_stats=10)
         (...)

      :param freq_stats: if greater than 0, the statistics will be
                         printed every freq_stats generation.
      :rtype: returns the best individual of the evolution

      .. versionadded:: 0.6
         the return of the best individual

      """

        stopFlagCallback = False
        stopFlagTerminationCriteria = False

        self.time_init = time()

        logging.debug(
            "Starting the DB Adapter and the Migration Adapter if any")
        if self.dbAdapter: self.dbAdapter.open(self)
        if self.migrationAdapter: self.migrationAdapter.start()

        if self.getGPMode():
            gp_function_prefix = self.getParam("gp_function_prefix")
            if gp_function_prefix is not None:
                self.__gp_catch_functions(gp_function_prefix)

        if (MPI.COMM_WORLD.Get_rank() == 0
            ):  ### assumes WE are on top of hierarchy!
            self.initialize()
            if (restore >= 0):
                self.restoreState(restore)

        self.internalPop.evaluate()

        if (MPI.COMM_WORLD.Get_rank() == 0
            ):  ### assumes WE are on top of hierarchy!
            self.internalPop.sort()


#      from mpi4py import MPI
#      if (MPI.COMM_WORLD.Get_rank() == 0):
#         print "after eval, new pop's positions are:"
#         for p in self.internalPop:
#            print p.wt_positions
        logging.debug("Starting loop over evolutionary algorithm.")

        while True:
            if (MPI.COMM_WORLD.Get_rank() == 0
                ):  ### assumes WE are on top of hierarchy!
                if self.migrationAdapter:
                    logging.debug("Migration adapter: exchange")
                    self.migrationAdapter.exchange()
                    self.internalPop.clearFlags()
                    self.internalPop.sort()

                if not self.stepCallback.isEmpty():
                    for it in self.stepCallback.applyFunctions(self):
                        stopFlagCallback = it

                if not self.terminationCriteria.isEmpty():
                    for it in self.terminationCriteria.applyFunctions(self):
                        stopFlagTerminationCriteria = it

                if freq_stats:
                    if (self.currentGeneration % freq_stats
                            == 0) or (self.getCurrentGeneration() == 0):
                        self.printStats()

                if self.dbAdapter:
                    if self.currentGeneration % self.dbAdapter.getStatsGenFreq(
                    ) == 0:
                        self.dumpStatsDB()

                if stopFlagTerminationCriteria:
                    logging.debug(
                        "Evolution stopped by the Termination Criteria !")
                    if freq_stats:
                        print "\n\tEvolution stopped by Termination Criteria function !\n"
                    break

                if stopFlagCallback:
                    logging.debug(
                        "Evolution stopped by Step Callback function !")
                    if freq_stats:
                        print "\n\tEvolution stopped by Step Callback function !\n"
                    break

                if self.interactiveMode:
                    if sys_platform[:3] == "win":
                        if msvcrt.kbhit():
                            if ord(msvcrt.getch()) == Consts.CDefESCKey:
                                print "Loading modules for Interactive Mode...",
                                logging.debug(
                                    "Windows Interactive Mode key detected ! generation=%d",
                                    self.getCurrentGeneration())
                                from pyevolve import Interaction
                                print " done !"
                                interact_banner = "## Pyevolve v.%s - Interactive Mode ##\nPress CTRL-Z to quit interactive mode." % (
                                    pyevolve.__version__, )
                                session_locals = {
                                    "ga_engine": self,
                                    "population": self.getPopulation(),
                                    "pyevolve": pyevolve,
                                    "it": Interaction
                                }
                                print
                                code.interact(interact_banner,
                                              local=session_locals)

                    if (self.getInteractiveGeneration() >=
                            0) and (self.getInteractiveGeneration()
                                    == self.getCurrentGeneration()):
                        print "Loading modules for Interactive Mode...",
                        logging.debug(
                            "Manual Interactive Mode key detected ! generation=%d",
                            self.getCurrentGeneration())
                        from pyevolve import Interaction
                        print " done !"
                        interact_banner = "## Pyevolve v.%s - Interactive Mode ##" % (
                            pyevolve.__version__, )
                        session_locals = {
                            "ga_engine": self,
                            "population": self.getPopulation(),
                            "pyevolve": pyevolve,
                            "it": Interaction
                        }
                        print
                        code.interact(interact_banner, local=session_locals)
            ## end, rank0 only
            if self.step(): break

        if (MPI.COMM_WORLD.Get_rank() == 0
            ):  ### assumes WE are on top of hierarchy!
            if freq_stats != 0:
                self.printStats()
                self.printTimeElapsed()

            if self.dbAdapter:
                logging.debug("Closing the DB Adapter")
                if not (self.currentGeneration %
                        self.dbAdapter.getStatsGenFreq() == 0):
                    self.dumpStatsDB()
                self.dbAdapter.commitAndClose()

            if self.migrationAdapter:
                logging.debug("Closing the Migration Adapter")
                if freq_stats: print "Stopping the migration adapter... ",
                self.migrationAdapter.stop()
                if freq_stats: print "done !"

            return self.bestIndividual()
        else:
            return None

    def select(self, **args):
        """ Select one individual from population

      :param args: this parameters will be sent to the selector

      """
        for it in self.selector.applyFunctions(self.internalPop, **args):
            return it
예제 #27
0
class SimplePSO(object):
    """ SimplePSO Engine Class - The PSO Algorithm Core
	
	Example:
		>>> topology = Topology.GlobalTopology(particle_rep)
		>>> pso = PSO.SimplePSO(topology)
		>>> pso.setSteps(120)
	
	:param topology: the :term:`Sample Topology``
	:param  interactiveMode: this flag enables the Interactive Mode
	:param seed: the random seed value
	
	.. note:: if you see the same random seed, all the runs of the algorithm will be the same.
	
	"""

    stepCallBack = None
    """ This is the the :term: `step callback function` slot,
	if you want to set the function, you must do this: ::
		
		def your_func(pso_engine):
			#Here you have access to the PSO Engine
			return False
		
		pso_engine.stepCallback.set(your_func)
	
	now *"your_func"* will be called every step.
	When this function returns True, the  PSO Engine will stop the evolution and show
	a warning, if is False, the evolution continues.
	"""

    terminationCriteria = None
    """ This is the termination criteria slot, if you want to set one 
	termination criteria, you mus do this: ::
		
		pso_engine.terminationCriteria.set(your_func)
		
	Now, when you run your PSO, it will stop when terminationCriteria be satisfied.
	
	To create your own termination function, you must put at least one parameter
	which is the PSO Engine, follows an example: ::
		
		def ConvergenceCriteria(pso_engine):
			swarm = pso_engine.getSwarm()
			return swarm[0] == swarm[len(swarm)-1]
		
	When this function returns True, the Pso Engine will stop the evolution and show
	a warning. If is False, the evolution  continues, this function is called every
	step.
	
	"""
    def __init__(self, topology, seed=None, interactiveMode=True):
        """ Initializator of PSO """
        #random seed
        random.seed(seed)
        #Pso type used by the particle
        self.psoType = Consts.CDefPsoType
        #Topology used
        self.topology = topology
        #Set the population size
        self.setSwarmSize(Consts.CDefSwarmSize)
        #Cognitive and Social Coefficients
        self.C1, self.C2 = Consts.CDefCoefficients
        #Time steps
        self.timeSteps = Consts.CDefSteps
        #Interactive Mode (True or False)
        self.interactiveMode = interactiveMode
        #Current step
        self.currentStep = 0
        #Inertia Factor Minus
        self.inertiaFactorMinus = None
        #Inertia coefficient
        self.inertiaFactor = None
        #Time initial
        self.time_init = None
        #Optimization type
        self.minimax = Consts.minimaxType["minimize"]
        #Report file adapter
        self.reportAdapter = None
        #Step Callback
        self.stepCallback = FunctionSlot("Step Callback")
        #Termination Criteria
        self.terminationCriteria = FunctionSlot("Termination Criteria")
        #All slots
        self.allSlots = [self.stepCallback, self.terminationCriteria]

        print "A PSO Engine was created, timeSteps=% d" % (self.timeSteps, )

    def __repr__(self):
        """ The String representation of the PSO Engine """
        ret = "- PSO-%s-%s Execution\n" % (self.getTopologyType(),
                                           self.getPsoType())
        ret += "\tSwarm Size:\t %d\n" % (self.topology.swarmSize, )
        ret += "\tTime Steps:\t %d\n" % (self.timeSteps, )
        ret += "\tCurrent Step:\t %d\n" % (self.currentStep, )
        ret += "\tMinimax Type:\t %s\n" % (Consts.minimaxType.keys()[
            Consts.minimaxType.values().index(self.minimax)].capitalize(), )
        ret += "\tReport Adapter:\t %s\n" % (self.reportAdapter, )
        for slot in self.allSlots:
            ret += "\t" + slot.__repr__()
        ret += "\n"
        return ret

    def setReportAdapter(self, repadapter):
        """ Sets the Report Adapter of the PSO Engine
		
		:param repadapter: one of the :mod:`ReportAdapters` classes instance
		
		.. warning: the use of a Report Adapter can reduce the speed performance of the PSO.
		
		"""
        self.reportAdapter = repadapter

    def setSwarmSize(self, size):
        """ Sets the swarm size, calls setSwarmSize()  of Topology
		
		:param size: the swarm size
		
		.. note:: the swarm size must be >= 2
		
		"""
        if size < 2:
            Util.raiseException("swarm size must be >= 2", ValueError)
        self.topology.setSwarmSize(size)

    def setPsoType(self, psoType):
        """ Sets the psoType, use Consts.psoType(Basic,Constricted,Inertia)
		
		Example:
			>>> pso_engine.setSortType(Consts.psoType["CONSTRICTED"])
      
        :param psoType: The PSO type, from Consts.psoType
        
		"""
        if psoType not in Consts.psoType.values():
            Util.raiseException("PsoType must be implemented !", TypeError)
        self.psoType = psoType

    def getPsoType(self):
        """ Return the Pso Type
		
		:rtype key: pso Type
		"""
        for key, value in Consts.psoType.items():
            if value == self.psoType:
                return key
        return ""

    def setTimeSteps(self, num_steps):
        """ Sets the number of steps to converge
		
		:param num_steps: the number of steps
		
		"""
        if num_steps < 1:
            Util.raiseException("Number of steps must be >=1", ValueError)
        self.timeSteps = num_steps

    def getMinimax(self):
        """ Gets the minimize/maximize mode
		
		:rtype: The Consts.minimaxType type
		
		"""
        for key, value in Consts.minimaxType.items():
            if value == self.minimax:
                return key
            return ""

    def getTopologyType(self):
        """ Returns the name of the topology
		
		:rtype name: the name of the topology
		 """
        return self.topology.__class__.__name__

    def setMinimax(self, minimax):
        """Sets the minimize/maximize mode, use Consts.minimaxType
		
		:param minimax: the minimax mode, from Consts.minimaxType
		
		"""
        if minimax not in Consts.minimaxType.values():
            Util.raiseException(
                "Optimization type must be Maximize or Minimize !", TypeError)

        self.minimax = minimax

    def getCurrentStep(self):
        """ Gets the current step
		
		:rtype: the current step
		"""
        return self.currentStep

    def getReportAdapter(self):
        """ Gets the Report Adapter of the PSO Engine
		
		:rtype: a instance from one of the :mod:`ReportAdapters` classes
		
		"""
        return self.reportAdapter

    def bestParticle(self):
        """ Returns the swarm best Particle
		
		:rtype: the best particle
		
		"""
        return self.topology.getBestParticle()

    def getTopology(self):
        """Return the internal topology of Pso Engine
		
		:rtype: the topology (:class: 'Topology.Topology')'
	
		"""
        return self.topology

    def getStatistics(self):
        """ Gets the Statistics class instance of the current step
		
		:rtype: the statistics instance (:class: `Statistics.Statistics`)`
		
		"""
        return self.topology.getStatistics()

    def dumpStatsReport(self):
        """ Dumps the current statistics to the  report adapter """
        self.topology.statistics()
        self.reportAdapter.insert(self.getStatistics(), self.topology,
                                  self.currentStep)

    def printStats(self):
        """ Print swarm statistics"""
        percent = self.currentStep * 100 / float(self.timeSteps)
        message = "Step: %d (%.2f%%):" % (self.currentStep, percent)
        print message
        self.topology.statistics()
        self.topology.printStats()

    def printTimeElapsed(self):
        """ Shows the time elapsed since the beginning of the solution construction """
        print "Total time elapsed: %.3f seconds." % (time() - self.time_init)

    def initialize(self):
        """ Initializes the PSO Engine. Create and initialize the swarm """
        self.topology.create(minimax=self.minimax)
        self.topology.initialize()
        print "The PSO Engine was initialized !"

    def constructSolution(self):
        """ Just do one step in execution, one step."""
        for it in self.topology.position_updater.applyFunctions(self):
            pass

        for it in self.topology.information_updater.applyFunctions(self):
            pass

        if self.psoType == Consts.psoType["INERTIA"]:
            self.updateInertiaFactor()

        self.currentStep += 1

        return (self.currentStep == self.timeSteps)

    def execute(self, freq_stats=0):
        """ Do all the steps until the termination criteria or time Steps achieved,
		accepts the freq_stats (default is 0) to dump statistics at n-step
		
		Example:
			>>> pso_engine.evolve(freq_stats=10)
			(...)
		
		:param freq_stats: if greater than 0, the statistics will be 
							printed every freq_stats step.

		"""
        #Start time
        self.time_init = time()

        #Creates a new report if reportAdapter is not None.
        if self.reportAdapter: self.reportAdapter.open()

        #Initialize the PSO Engine
        self.initialize()  #Already evaluates all particles

        print "Starting loop over evolutionary algorithm."

        try:
            while not self.constructSolution():
                stopFlagCallback = False
                stopFlagTerminationCriteria = False

                if not self.stepCallback.isEmpty():
                    for it in self.stepCallback.applyFunctions(self):
                        stopFlagCallback = it

                if not self.terminationCriteria.isEmpty():
                    for it in self.terminationCriteria.applyFunctions(self):
                        stopFlagTerminationCriteria = it

                if freq_stats != 0:
                    if (self.currentStep % freq_stats
                            == 0) or (self.currentStep == 1):
                        self.printStats()

                if self.reportAdapter:
                    if self.currentStep % self.reportAdapter.statsGenFreq == 0:
                        self.dumpStatsReport()

                if stopFlagTerminationCriteria:
                    print '\n\tExecution stopped by Termination Criteria function !\n'
                    break

                if stopFlagCallback:
                    print '\n\tExecution stopped by Step Callback function!\n'
                    break

                if self.interactiveMode:
                    if sys_platform[:3] == "win":
                        if msvcrt.kbhit():
                            if ord(msvcrt.getch()) == Consts.CDefESCKey:
                                print "Loading modules for Interactive mode...",
                                import pypso.Interaction
                                print "done!\n"
                                interact_banner = "## PyPSO v.%s - Interactive Mode ##\nPress CTRL-Z to quit interactive mode." % (
                                    pypso.__version__, )
                                session_locals = {
                                    "pso_engine": self,
                                    "topology": self.getTopology(),
                                    "swarm_statistics":
                                    self.getTopology().swarmStats,
                                    "topology_statistics":
                                    self.getTopology().topologyStats,
                                    "pypso": pypso,
                                    "it": pypso.Interaction
                                }
                                print
                                code.interact(interact_banner,
                                              local=session_locals)
                    elif sys_platform[:5] == "linux":
                        if Util.kbhit():
                            if ord(Util.getch()) == Consts.CDefESCKey:
                                print "Loading modules for Interactive mode...",
                                import pypso.Interaction
                                print "done!\n"
                                interact_banner = "## PyPSO v.%s - Interactive Mode ##\nPress CTRL-D to quit interactive mode." % (
                                    pypso.__version__, )
                                session_locals = {
                                    "pso_engine": self,
                                    "topology": self.getTopology(),
                                    "swarm_statistics":
                                    self.getTopology().swarmStats,
                                    "topology_statistics":
                                    self.getTopology().topologyStats,
                                    "pypso": pypso,
                                    "it": pypso.Interaction
                                }
                                print
                                code.interact(interact_banner,
                                              local=session_locals)

        except KeyboardInterrupt:
            print "\n\tA break was detected, you have interrupted the evolution !\n"

        if freq_stats != 0:
            self.printStats()
            self.printTimeElapsed()

        if self.reportAdapter:
            if (self.currentStep % self.reportAdapter.statsGenFreq == 0):
                self.dumpStatsReport()
            self.reportAdapter.saveAndClose()
예제 #28
0
파일: GenomeBase.py 프로젝트: blep/Pyevolve
class GenomeBase:
    """ GenomeBase Class - The base of all chromosome representation """

    evaluator = None
    """ This is the :term:`evaluation function` slot, you can add
   a function with the *set* method: ::

      genome.evaluator.set(eval_func)
   """

    initializator = None
    """ This is the initialization function of the genome, you
   can change the default initializator using the function slot: ::

      genome.initializator.set(Initializators.G1DListInitializatorAllele)

   In this example, the initializator :func:`Initializators.G1DListInitializatorAllele`
   will be used to create the initial population.
   """

    mutator = None
    """ This is the mutator function slot, you can change the default
   mutator using the slot *set* function: ::

      genome.mutator.set(Mutators.G1DListMutatorSwap)

   """

    crossover = None
    """ This is the reproduction function slot, the crossover. You
   can change the default crossover method using: ::

      genome.crossover.set(Crossovers.G1DListCrossoverUniform)
   """
    def __init__(self):
        """Genome Constructor"""
        self.evaluator = FunctionSlot("Evaluator")
        self.initializator = FunctionSlot("Initializator")
        self.mutator = FunctionSlot("Mutator")
        self.crossover = FunctionSlot("Crossover")

        self.internalParams = {}
        self.score = 0.0
        self.fitness = 0.0

    def getRawScore(self):
        """ Get the Raw Score of the genome

      :rtype: genome raw score

      """
        return self.score

    def getFitnessScore(self):
        """ Get the Fitness Score of the genome

      :rtype: genome fitness score

      """
        return self.fitness

    def __repr__(self):
        """String representation of Genome"""
        allSlots = self.allSlots = [
            self.evaluator, self.initializator, self.mutator, self.crossover
        ]

        ret = "- GenomeBase\n"
        ret += "\tScore:\t\t\t %.6f\n" % (self.score, )
        ret += "\tFitness:\t\t %.6f\n\n" % (self.fitness, )
        ret += "\tParams:\t\t %s\n\n" % (self.internalParams, )

        for slot in allSlots:
            ret += "\t" + slot.__repr__()
        ret += "\n"

        return ret

    def setParams(self, **args):
        """ Set the internal params

      Example:
         >>> genome.setParams(rangemin=0, rangemax=100, gauss_mu=0, gauss_sigma=1)

      .. note:: All the individuals of the population shares this parameters and uses
                the same instance of this dict.

      :param args: this params will saved in every chromosome for genetic op. use

      """
        self.internalParams.update(args)

    def getParam(self, key, nvl=None):
        """ Gets an internal parameter

      Example:
         >>> genome.getParam("rangemax")
         100

      .. note:: All the individuals of the population shares this parameters and uses
                the same instance of this dict.

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      """
        return self.internalParams.get(key, nvl)

    def resetStats(self):
        """ Clear score and fitness of genome """
        self.score = 0.0
        self.fitness = 0.0

    def evaluate(self, **args):
        """ Called to evaluate genome

      :param args: this parameters will be passes to the evaluator

      """
        self.resetStats()
        for it in self.evaluator.applyFunctions(self, **args):
            self.score += it

    def initialize(self, **args):
        """ Called to initialize genome

      :param args: this parameters will be passed to the initializator

      """
        for it in self.initializator.applyFunctions(self, **args):
            pass

    def mutate(self, **args):
        """ Called to mutate the genome

      :param args: this parameters will be passed to the mutator
      :rtype: the number of mutations returned by mutation operator

      """
        nmuts = 0
        for it in self.mutator.applyFunctions(self, **args):
            nmuts += it
        return nmuts

    def copy(self, g):
        """ Copy the current GenomeBase to 'g'

      :param g: the destination genome      

      .. note:: If you are planning to create a new chromosome representation, you
                **must** implement this method on your class.

      """
        g.score = self.score
        g.fitness = self.fitness
        g.evaluator = self.evaluator
        g.initializator = self.initializator
        g.mutator = self.mutator
        g.crossover = self.crossover
        #g.internalParams = self.internalParams.copy()
        g.internalParams = self.internalParams

    def clone(self):
        """ Clone this GenomeBase

      :rtype: the clone genome   

      .. note:: If you are planning to create a new chromosome representation, you
                **must** implement this method on your class.
      """
        newcopy = GenomeBase()
        self.copy(newcopy)
        return newcopy
예제 #29
0
class GPopulation(object):
    """ GPopulation Class - The container for the population

   **Examples**
      Get the population from the :class:`GSimpleGA.GSimpleGA` (GA Engine) instance
         >>> pop = ga_engine.getPopulation()

      Get the best fitness individual
         >>> bestIndividual = pop.bestFitness()

      Get the best raw individual
         >>> bestIndividual = pop.bestRaw()

      Get the statistics from the :class:`Statistics.Statistics` instance
         >>> stats = pop.getStatistics()
         >>> print stats["rawMax"]
         10.4

      Iterate, get/set individuals
         >>> for ind in pop:
         >>>   print ind
         (...)

         >>> for i in xrange(len(pop)):
         >>>    print pop[i]
         (...)

         >>> pop[10] = newGenome
         >>> pop[10].fitness
         12.5

   :param genome: the :term:`Sample genome`, or a GPopulation object, when cloning.

   """
    def __init__(self, genome):
        """ The GPopulation Class creator """

        if isinstance(genome, GPopulation):
            self.oneSelfGenome = genome.oneSelfGenome
            self.internalPop = []
            self.internalPopRaw = []
            self.popSize = genome.popSize
            self.sortType = genome.sortType
            self.sorted = False
            self.minimax = genome.minimax
            self.scaleMethod = genome.scaleMethod
            self.allSlots = [self.scaleMethod]

            self.internalParams = genome.internalParams
            self.multiProcessing = genome.multiProcessing

            self.statted = False
            self.stats = Statistics()
            return

        logging.debug("New population instance, %s class genomes.",
                      genome.__class__.__name__)
        self.oneSelfGenome = genome
        self.internalPop = []
        self.internalPopRaw = []
        self.popSize = 0
        self.sortType = Consts.CDefPopSortType
        self.sorted = False
        self.minimax = Consts.CDefPopMinimax
        self.scaleMethod = FunctionSlot("Scale Method")
        self.scaleMethod.set(Consts.CDefPopScale)
        self.allSlots = [self.scaleMethod]

        self.internalParams = {}
        self.multiProcessing = (False, False, None)

        # Statistics
        self.statted = False
        self.stats = Statistics()

    def setMultiProcessing(self,
                           flag=True,
                           full_copy=False,
                           max_processes=None):
        """ Sets the flag to enable/disable the use of python multiprocessing module.
      Use this option when you have more than one core on your CPU and when your
      evaluation function is very slow.
      The parameter "full_copy" defines where the individual data should be copied back
      after the evaluation or not. This parameter is useful when you change the
      individual in the evaluation function.

      :param flag: True (default) or False
      :param full_copy: True or False (default)
      :param max_processes: None (default) or an integer value

      .. warning:: Use this option only when your evaluation function is slow, se you
                   will get a good tradeoff between the process communication speed and the
                   parallel evaluation.

      .. versionadded:: 0.6
         The `setMultiProcessing` method.

      """
        self.multiProcessing = (flag, full_copy, max_processes)

    def setMinimax(self, minimax):
        """ Sets the population minimax

      Example:
         >>> pop.setMinimax(Consts.minimaxType["maximize"])

      :param minimax: the minimax type

      """
        self.minimax = minimax

    def __repr__(self):
        """ Returns the string representation of the population """
        ret = "- GPopulation\n"
        ret += "\tPopulation Size:\t %d\n" % (self.popSize, )
        ret += "\tSort Type:\t\t %s\n" % (Consts.sortType.keys()[
            Consts.sortType.values().index(self.sortType)].capitalize(), )
        ret += "\tMinimax Type:\t\t %s\n" % (Consts.minimaxType.keys()[
            Consts.minimaxType.values().index(self.minimax)].capitalize(), )
        for slot in self.allSlots:
            ret += "\t" + slot.__repr__()
        ret += "\n"
        ret += self.stats.__repr__()
        return ret

    def __len__(self):
        """ Return the length of population """
        return len(self.internalPop)

    def __getitem__(self, key):
        """ Returns the specified individual from population """
        return self.internalPop[key]

    def __iter__(self):
        """ Returns the iterator of the population """
        return iter(self.internalPop)

    def __setitem__(self, key, value):
        """ Set an individual of population """
        self.internalPop[key] = value
        self.clearFlags()

    def clearFlags(self):
        """ Clear the sorted and statted internal flags """
        self.sorted = False
        self.statted = False

    def getStatistics(self):
        """ Return a Statistics class for statistics

      :rtype: the :class:`Statistics.Statistics` instance

      """
        self.statistics()
        return self.stats

    def statistics(self):
        """ Do statistical analysis of population and set 'statted' to True """
        if self.statted:
            return
        logging.debug("Running statistical calculations")
        raw_sum = 0

        len_pop = len(self)
        for ind in xrange(len_pop):
            raw_sum += self[ind].score

        self.stats["rawMax"] = max(self, key=key_raw_score).score
        self.stats["rawMin"] = min(self, key=key_raw_score).score
        self.stats["rawAve"] = raw_sum / float(len_pop)

        tmpvar = 0.0
        for ind in xrange(len_pop):
            s = self[ind].score - self.stats["rawAve"]
            s *= s
            tmpvar += s

        tmpvar /= float((len(self) - 1))
        try:
            self.stats["rawDev"] = math_sqrt(tmpvar)
        except:
            self.stats["rawDev"] = 0.0

        self.stats["rawVar"] = tmpvar

        self.statted = True

    def bestFitness(self, index=0):
        """ Return the best scaled fitness individual of population

      :param index: the *index* best individual
      :rtype: the individual

      """
        self.sort()
        return self.internalPop[index]

    def worstFitness(self):
        """ Return the worst scaled fitness individual of the population

      :rtype: the individual

      """
        self.sort()
        return self.internalPop[-1]

    def bestRaw(self, index=0):
        """ Return the best raw score individual of population

      :param index: the *index* best raw individual
      :rtype: the individual

      .. versionadded:: 0.6
         The parameter `index`.

      """
        if self.sortType == Consts.sortType["raw"]:
            return self.internalPop[index]
        else:
            self.sort()
            return self.internalPopRaw[index]

    def worstRaw(self):
        """ Return the worst raw score individual of population

      :rtype: the individual

      .. versionadded:: 0.6
         The parameter `index`.

      """
        if self.sortType == Consts.sortType["raw"]:
            return self.internalPop[-1]
        else:
            self.sort()
            return self.internalPopRaw[-1]

    def sort(self):
        """ Sort the population """
        if self.sorted:
            return
        rev = (self.minimax == Consts.minimaxType["maximize"])

        if self.sortType == Consts.sortType["raw"]:
            self.internalPop.sort(cmp=Util.cmp_individual_raw, reverse=rev)
        else:
            self.scale()
            self.internalPop.sort(cmp=Util.cmp_individual_scaled, reverse=rev)
            self.internalPopRaw = self.internalPop[:]
            self.internalPopRaw.sort(cmp=Util.cmp_individual_raw, reverse=rev)

        self.sorted = True

    def setPopulationSize(self, size):
        """ Set the population size

      :param size: the population size

      """
        self.popSize = size

    def setSortType(self, sort_type):
        """ Sets the sort type

      Example:
         >>> pop.setSortType(Consts.sortType["scaled"])

      :param sort_type: the Sort Type

      """
        self.sortType = sort_type

    def create(self, **args):
        """ Clone the example genome to fill the population """
        self.minimax = args["minimax"]
        self.internalPop = [
            self.oneSelfGenome.clone() for i in xrange(self.popSize)
        ]
        self.clearFlags()

    def __findIndividual(self, individual, end):
        for i in xrange(end):
            if individual.compare(self.internalPop[i]) == 0:
                return True

    def initialize(self, **args):
        """ Initialize all individuals of population,
      this calls the initialize() of individuals """
        logging.debug("Initializing the population")

        if self.oneSelfGenome.getParam("full_diversity", True) and hasattr(
                self.oneSelfGenome, "compare"):
            for i in xrange(len(self.internalPop)):
                curr = self.internalPop[i]
                curr.initialize(**args)
                while self.__findIndividual(curr, i):
                    curr.initialize(**args)
        else:
            for gen in self.internalPop:
                gen.initialize(**args)
        self.clearFlags()

    def evaluate(self, **args):
        """ Evaluate all individuals in population, calls the evaluate() method of individuals

      :param args: this params are passed to the evaluation function

      """
        # We have multiprocessing
        if self.multiProcessing[0] and MULTI_PROCESSING:
            logging.debug(
                "Evaluating the population using the multiprocessing method")
            proc_pool = Pool(processes=self.multiProcessing[2])

            # Multiprocessing full_copy parameter
            if self.multiProcessing[1]:
                results = proc_pool.map(multiprocessing_eval_full,
                                        self.internalPop)
                proc_pool.close()
                proc_pool.join()
                for i in xrange(len(self.internalPop)):
                    self.internalPop[i] = results[i]
            else:
                results = proc_pool.map(multiprocessing_eval, self.internalPop)
                proc_pool.close()
                proc_pool.join()
                for individual, score in zip(self.internalPop, results):
                    individual.score = score
        else:
            for ind in self.internalPop:
                ind.evaluate(**args)

        self.clearFlags()

    def scale(self, **args):
        """ Scale the population using the scaling method

      :param args: this parameter is passed to the scale method

      """
        for it in self.scaleMethod.applyFunctions(self, **args):
            pass

        fit_sum = 0
        for ind in xrange(len(self)):
            fit_sum += self[ind].fitness

        self.stats["fitMax"] = max(self, key=key_fitness_score).fitness
        self.stats["fitMin"] = min(self, key=key_fitness_score).fitness
        self.stats["fitAve"] = fit_sum / float(len(self))

        self.sorted = False

    def printStats(self):
        """ Print statistics of the current population """
        message = ""
        if self.sortType == Consts.sortType["scaled"]:
            message = "Max/Min/Avg Fitness(Raw) [%(fitMax).2f(%(rawMax).2f)/%(fitMin).2f(%(rawMin).2f)/%(fitAve).2f(%(rawAve).2f)]" % self.stats
        else:
            message = "Max/Min/Avg Raw [%(rawMax).2f/%(rawMin).2f/%(rawAve).2f]" % self.stats
        logging.info(message)
        print message
        return message

    def copy(self, pop):
        """ Copy current population to 'pop'

      :param pop: the destination population

      .. warning:: this method do not copy the individuals, only the population logic

      """
        pop.popSize = self.popSize
        pop.sortType = self.sortType
        pop.minimax = self.minimax
        pop.scaleMethod = self.scaleMethod
        pop.internalParams = self.internalParams
        pop.multiProcessing = self.multiProcessing

    def getParam(self, key, nvl=None):
        """ Gets an internal parameter

      Example:
         >>> population.getParam("tournamentPool")
         5

      :param key: the key of param
      :param nvl: if the key doesn't exist, the nvl will be returned

      """
        return self.internalParams.get(key, nvl)

    def setParams(self, **args):
        """ Gets an internal parameter

      Example:
         >>> population.setParams(tournamentPool=5)

      :param args: parameters to set

      .. versionadded:: 0.6
         The `setParams` method.
      """
        self.internalParams.update(args)

    def clear(self):
        """ Remove all individuals from population """
        del self.internalPop[:]
        del self.internalPopRaw[:]
        self.clearFlags()

    def clone(self):
        """ Return a brand-new cloned population """
        newpop = GPopulation(self.oneSelfGenome)
        self.copy(newpop)
        return newpop