Beispiel #1
0
   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
Beispiel #2
0
    def __init__(self, genome, seed=None, interactiveMode=True):
        if seed: random.seed(seed)

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

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

        self.internalPop = self.make_population(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)
Beispiel #3
0
    def __init__(self, genome, seed=None, interactiveMode=True):
        """ Initializator of GSimpleGA """
        if seed:
            random.seed(seed)

        if type(interactiveMode) != bool:
            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.max_time = 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)
Beispiel #4
0
    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
Beispiel #5
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.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()
Beispiel #6
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.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()
Beispiel #7
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(key=cmp_to_key(Util.cmp_individual_raw), reverse=rev)
      else:
         self.scale()
         self.internalPop.sort(key=cmp_to_key(Util.cmp_individual_scaled), reverse=rev)
         self.internalPopRaw = self.internalPop[:]
         self.internalPopRaw.sort(key=cmp_to_key(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
Beispiel #8
0
class GSimpleGA(object):
    """ 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 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 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) != bool:
            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.max_time = 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) != bool:
            Util.raiseException("Interactive Mode option must be True or False", TypeError)
        self.interactiveMode = flag

    def __repr__(self):
        """ The string representation of the GA Engine """
        minimax_type = list(Consts.minimaxType.keys())[list(Consts.minimaxType.values()).index(self.minimax)]
        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" % minimax_type.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, 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.

        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 max_processes: None (default) or an integer value

        .. 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) != bool:
            Util.raiseException("Multiprocessing option must be True or False", TypeError)

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

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

    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) != bool:
            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 setMaxTime(self, seconds):
        """ Sets the maximun evolve time of the GA Engine

        :param seconds: maximum time in seconds
        """
        self.max_time = seconds

    def getMaxTime(self):
        """ Get the maximun evolve time of the GA Engine

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

    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 """
        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

        if self.max_time:
           total_time = time() - self.time_init
           if total_time > self.max_time:
              return True
        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.")

        try:
            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()

                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 ##\n" \
                                                  "Press 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)

                    is_interactive_generation = self.getInteractiveGeneration() == self.getCurrentGeneration()
                    if self.getInteractiveGeneration() >= 0 and is_interactive_generation:
                        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

        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()

        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")
            self.migrationAdapter.stop()

        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
Beispiel #9
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
class SimpleGAWithFixedElitism(pyevolve.GSimpleGA.GSimpleGA):
    # "Reimplementation" of GSimpleGA with ability to create different
    # population types (used by SimpleMPIGA), and fixed elitism where elite
    # individuals also have their fitness evaluated at each generation.
    def __init__(self, genome, seed=None, interactiveMode=True):
        if seed: random.seed(seed)

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

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

        self.internalPop  = self.make_population(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 step(self):
        """ Just do one step in evolution, one generation """
        genomeMom = None
        genomeDad = None

        newPop = self.make_population(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 pyevolve.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 pyevolve.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)

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

        if self.elitism:
            logging.debug("Doing elitism.")
            if self.getMinimax() == Consts.minimaxType["maximize"]:
                for i in range(self.nElitismReplacement):
                        newPop[len(newPop)-1-i] = self.internalPop.bestRaw(i)
            elif self.getMinimax() == Consts.minimaxType["minimize"]:
                for i in range(self.nElitismReplacement):
                        newPop[len(newPop)-1-i] = self.internalPop.bestRaw(i)

        # Evalate after elitism, in order to re-evaluate elite individuals on
        # potentially changed environment.
        logging.debug("Evaluating the new created population.")
        newPop.evaluate()

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

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

        self.currentGeneration += 1

        return (self.currentGeneration == self.nGenerations)

    def make_population(self, genome):
        return SpecifiedPopulation(genome)
Beispiel #11
0
class GSimpleGA(object):
    """ 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 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 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) != bool:# 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.max_time = 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) != bool :#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 """
        minimax_type = Consts.minimaxType.keys()[Consts.minimaxType.values().index(self.minimax)]
        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" % minimax_type.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, 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.

        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 max_processes: None (default) or an integer value

        .. 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, max_processes)

    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 setMaxTime(self, seconds):
        """ Sets the maximun evolve time of the GA Engine

        :param seconds: maximum time in seconds
        """
        self.max_time = seconds

    def getMaxTime(self):
        """ Get the maximun evolve time of the GA Engine

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

    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 """
        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 range(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 range(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 range(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

        if self.max_time:
           total_time = time() - self.time_init
           if total_time > self.max_time:
              return True
        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.")

        try:
            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()

                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 ##\n" \
                                                  "Press 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)

                    is_interactive_generation = self.getInteractiveGeneration() == self.getCurrentGeneration()
                    if self.getInteractiveGeneration() >= 0 and is_interactive_generation:
                        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

        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()

        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")
            self.migrationAdapter.stop()

        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
Beispiel #12
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
Beispiel #13
0
class SimpleGAWithFixedElitism(pyevolve.GSimpleGA.GSimpleGA):
    # "Reimplementation" of GSimpleGA with ability to create different
    # population types (used by SimpleMPIGA), and fixed elitism where elite
    # individuals also have their fitness evaluated at each generation.
    def __init__(self, genome, seed=None, interactiveMode=True):
        if seed: random.seed(seed)

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

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

        self.internalPop = self.make_population(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 step(self):
        """ Just do one step in evolution, one generation """
        genomeMom = None
        genomeDad = None

        newPop = self.make_population(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 pyevolve.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 pyevolve.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)

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

        if self.elitism:
            logging.debug("Doing elitism.")
            if self.getMinimax() == Consts.minimaxType["maximize"]:
                for i in range(self.nElitismReplacement):
                    newPop[len(newPop) - 1 - i] = self.internalPop.bestRaw(i)
            elif self.getMinimax() == Consts.minimaxType["minimize"]:
                for i in range(self.nElitismReplacement):
                    newPop[len(newPop) - 1 - i] = self.internalPop.bestRaw(i)

        # Evalate after elitism, in order to re-evaluate elite individuals on
        # potentially changed environment.
        logging.debug("Evaluating the new created population.")
        newPop.evaluate()

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

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

        self.currentGeneration += 1

        return (self.currentGeneration == self.nGenerations)

    def make_population(self, genome):
        return SpecifiedPopulation(genome)
Beispiel #14
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