예제 #1
0
   def mate(self, genome1, genome2):
      """
      Mate two genomes based on their fitness.

      Args:
         genome1: the first parent genome
         genome2: the second parent genome

      Returns:
         genome3: the resultant child genome
      """
      # we want genome1 to be the most fit
      if genome2.fitness > genome1.fitness:
         tempGenome = genome1
         genome1 = genome2
         genome2 = tempGenome
      assert genome1.fitness >= genome2.fitness  
      genome3 = Genome(INPUTS, OUTPUTS, self.name, None)
      
      possible_genes = set(genome1.genes) | set(genome2.genes)
      for innov in possible_genes:
         if random.random() < GENE_DOMINANCE:
            if innov in genome1.genes:
               genome3.genes[innov] = genome1.genes[innov]
         else:
            if innov in genome2.genes:
               genome3.genes[innov] = genome2.genes[innov]
      
      # creating the nodes
      genome3.generateNodes()
      genome3.mutate()
      
      return genome3
예제 #2
0
 def make_child(self, mom, dad):
     child = Genome()
     for n in range(child.node_count):
         giver = (mom, dad)[random() > 0.5]
         child.node_net[n] = giver.node_net[n]
     child.mutate()
     return child
예제 #3
0
class Organism(object):
    """ Wrapper class that provides fitness metrics. """
    def __init__(self, genome):
        self.genome = Genome(genome)
        self.policy = Network(self.genome)
        self.evals = list()

    def __cmp__(self, other):
        return cmp(self.fitness, other.fitness)

    def __str__(self):
        return '%.3f' % self.fitness

    def crossover(self, other):
        """ Return a new organism by recombining the parents. """
        return Organism(self.genome.crossover(other.genome))

    def mutate(self, frac=0.1, std=1.0, repl=0.25):
        """ Mutate the organism by mutating its genome. """
        self.genome.mutate(frac, std, repl)
        self.policy = Network(self.genome)

    def copy(self):
        """ Return a deep copy of this organism. """
        org = Organism(self.genome)
        org.evals = list(self.evals)
        return org

    @property
    def fitness(self):
        """ Average return. """
        try:
            return sum(self.evals, 0.) / len(self.evals)
        except ZeroDivisionError:
            return 0.
예제 #4
0
class Organism(object):
  """ Wrapper class that provides fitness metrics. """
  def __init__(self, genome):
    self.genome = Genome(genome)
    self.policy = Network(self.genome)
    self.evals = list()

  def __cmp__(self, other):
    return cmp(self.fitness, other.fitness)

  def __str__(self):
    return '%.3f' % self.fitness

  def crossover(self, other):
    """ Return a new organism by recombining the parents. """
    return Organism(self.genome.crossover(other.genome))

  def mutate(self, frac=0.1, std=1.0, repl=0.25):
    """ Mutate the organism by mutating its genome. """
    self.genome.mutate(frac, std, repl)
    self.policy = Network(self.genome)

  def copy(self):
    """ Return a deep copy of this organism. """
    org = Organism(self.genome)
    org.evals = list(self.evals)
    return org

  @property
  def fitness(self):
    """ Average return. """
    try:
      return sum(self.evals, 0.) / len(self.evals)
    except ZeroDivisionError:
      return 0.
예제 #5
0
 def create_population(self):
     """
     Creates a genome population using self.pop_size
     """
     self.generation = 0
     for genome_num in range(self.pop_size):
         genome = Genome()
         genome.mutate()
         self.genomes.append(genome)
         txt = self.data_loc(self.generation, genome_num)
         savetxt(txt, genome.node_net, fmt="%f")
예제 #6
0
    def get_new_population(self, adjusted_species_sizes, remaining_species,
                           species_set, generation_tracker, backprop_mutation):
        """
        Creates the dictionary of the new genomes for the next generation population
        :param: genetation_tracker:
        :param adjusted_species_sizes:
        :param remaining_species:
        :param species_set:
        :param new_population:
        :return:
        """
        new_population = {}

        for species_size, species in zip(adjusted_species_sizes,
                                         remaining_species):

            assert (species_size > 0)

            # List of old species members
            old_species_members = list(species.members.values())
            # Reset the members for the current species
            species.members = {}
            # Save the species in the species set object
            species_set.species[species.key] = species

            # Sort the members into the descending fitness
            old_species_members.sort(reverse=True, key=lambda x: x.fitness)

            # Double check that it is descending
            if len(old_species_members) > 1:
                assert (old_species_members[0].fitness >=
                        old_species_members[1].fitness)

            # If we have specified a number of genomes to carry over, carry them over to the new population
            num_genomes_without_crossover = int(
                round(species_size *
                      self.config.chance_for_mutation_without_crossover))
            if num_genomes_without_crossover > 0:

                for member in old_species_members[:
                                                  num_genomes_without_crossover]:

                    # Check if we should carry over a member un-mutated or not
                    if not self.config.keep_unmutated_top_percentage:
                        child = copy.deepcopy(member)

                        child.mutate(
                            reproduction_instance=self,
                            innovation_tracker=self.innovation_tracker,
                            config=self.config,
                            backprop_mutation=backprop_mutation)

                        if not child.check_connection_enabled_amount(
                        ) and not child.check_num_paths(
                                only_add_enabled_connections=True):
                            raise Exception(
                                'This child has no enabled connections')

                        new_population[child.key] = child
                        self.ancestors[child.key] = ()
                        # new_population[member.key] = member
                        species_size -= 1
                        assert (species_size >= 0)
                    else:
                        # Else we just add the current member to the new population
                        new_population[member.key] = member
                        species_size -= 1
                        assert (species_size >= 0)

            # If there are no more genomes for the current species, then restart the loop for the next species
            if species_size <= 0:
                continue

            # Only use the survival threshold fraction to use as parents for the next generation.
            reproduction_cutoff = int(
                math.ceil(
                    (1 - self.config.chance_for_mutation_without_crossover) *
                    len(old_species_members)))

            # Need at least two parents no matter what the previous result
            reproduction_cutoff = max(reproduction_cutoff, 2)
            old_species_members = old_species_members[:reproduction_cutoff]

            # Randomly choose parents and choose whilst there can still be additional genomes for the given species
            while species_size > 0:
                species_size -= 1

                # TODO: If you don't allow them to mate with themselves then it's a problem because if the species previous
                # TODO: size is 1, then how can you do with or without crossover?
                parent_1 = copy.deepcopy(random.choice(old_species_members))
                parent_2 = copy.deepcopy(random.choice(old_species_members))

                # Has to be a deep copy otherwise the connections which are crossed over are also modified if mutation
                # occurs on the child.
                parent_1 = copy.deepcopy(parent_1)
                parent_2 = copy.deepcopy(parent_2)

                self.genome_indexer += 1
                genome_id = self.genome_indexer

                child = Genome(key=genome_id)
                # TODO: Save the parent_1 and parent_2 mutation history as well as what connections they had
                # Create the genome from the parents
                num_connections_enabled = child.crossover(genome_1=parent_1,
                                                          genome_2=parent_2,
                                                          config=self.config)

                # If there are no connections enabled we forget about this child and don't add it to the existing
                # population
                if num_connections_enabled:
                    child.mutate(reproduction_instance=self,
                                 innovation_tracker=self.innovation_tracker,
                                 config=self.config,
                                 generation_tracker=generation_tracker,
                                 backprop_mutation=backprop_mutation)

                    if not child.check_connection_enabled_amount(
                    ) and not child.check_num_paths(
                            only_add_enabled_connections=True):
                        raise Exception(
                            'This child has no enabled connections')

                    new_population[child.key] = child
                    self.ancestors[child.key] = (parent_1.key, parent_2.key)
                else:
                    # Else if the crossover resulted in an invalid genome.
                    assert num_connections_enabled == 0
                    species_size += 1
                    self.genome_indexer -= 1

        return new_population
예제 #7
0
    indiv.run()

population.sort(key=lambda x: x.score, reverse=True)

generation = 1
print("TOTAL GENERATIONS: ", cfg.NUM_GENERATIONS)

while (generation <= cfg.NUM_GENERATIONS):
    print("CURRENT GENERATION: ", generation)
    offspring = []
    parent_pool = population[:cfg.POPULATION_SIZE // 2]
    while (len(offspring) < cfg.POPULATION_SIZE * 9 // 10):
        parent1, parent2 = random.sample(parent_pool, 2)
        child1 = Genome()
        child1.uniform_crossover(parent1, parent2)
        child1.mutate()
        child1.run()
        child2 = Genome()
        child2.scaled_crossover(parent1, parent2)
        child2.mutate()
        child2.run()
        offspring.append(child1)
        offspring.append(child2)

    population = population[:cfg.POPULATION_SIZE // 10] + offspring
    population.sort(key=lambda x: x.score, reverse=True)
    print("POPULATION_SIZE: ", len(population))
    print("BEST GENOME FOR THIS GEN ")
    population[0].print_self()
    generation += 1
예제 #8
0
    def reproduce_with_species(self, species_set, pop_size, generation):
        '''
		Creates and speciates genomes.

		species_set -- set of current species
		pop_size    -- population size
		generation  -- current generation
		'''
        all_fitnesses = []
        remaining_species = []
        # Traverse species and grab fitnesses from non-stagnated species
        for sid, species, species_is_stagnant in self.stagnation.update(
                species_set, generation):
            if species_is_stagnant:
                print("!!! Species {} Stagnated !!!".format(sid))
                # self.reporters.species_stagnant(sid, species)
                pass
            else:
                # Add fitnesses of members of current species
                all_fitnesses.extend(member.fitness
                                     for member in itervalues(species.members))
                remaining_species.append(species)
        # No species
        if not remaining_species:
            species_set.species = {}
            return {}
        # Find min/max fitness across entire population
        min_population_fitness = min(all_fitnesses)
        max_population_fitness = max(all_fitnesses)
        # Do not allow the fitness range to be zero, as we divide by it below.
        population_fitness_range = max(
            1.0, max_population_fitness - min_population_fitness)
        # Compute adjusted fitness and record minimum species size
        for species in remaining_species:
            # Determine current species average fitness
            mean_species_fitness = mean(
                [member.fitness for member in itervalues(species.members)])
            max_species_fitness = max(
                [member.fitness for member in itervalues(species.members)])
            # Determine current species adjusted fitness and update it
            species_adjusted_fitness = (
                mean_species_fitness -
                min_population_fitness) / population_fitness_range
            species.adjusted_fitness = species_adjusted_fitness
            species.max_fitness = max_species_fitness
        adjusted_fitnesses = [
            species.adjusted_fitness for species in remaining_species
        ]
        avg_adjusted_fitness = mean(adjusted_fitnesses)
        # Compute the number of new members for each species in the new generation.
        previous_sizes = [
            len(species.members) for species in remaining_species
        ]
        min_species_size = max(2, self.species_elitism)
        spawn_amounts = self.compute_species_sizes(adjusted_fitnesses,
                                                   previous_sizes, pop_size,
                                                   min_species_size)
        new_population = {}
        species_set.species = {}
        for spawn, species in zip(spawn_amounts, remaining_species):
            # If elitism is enabled, each species always at least gets to retain its elites.
            spawn = max(spawn, self.species_elitism)
            assert spawn > 0
            # The species has at least one member for the next generation, so retain it.
            old_species_members = list(iteritems(species.members))
            # Update species with blank slate
            species.members = {}
            # Update species in species set accordingly
            species_set.species[species.key] = species
            # Sort old species members in order of descending fitness.
            old_species_members.sort(reverse=True, key=lambda x: x[1].fitness)
            # Clone elites to new generation.
            if self.species_elitism > 0:
                for member_key, member in old_species_members[:self.
                                                              species_elitism]:
                    new_population[member_key] = member
                    spawn -= 1
            # If the species only has room for the elites, move onto next species
            if spawn <= 0: continue
            # Only allow fraction of species members to reproduce
            reproduction_cutoff = int(
                ceil(self.species_reproduction_threshold *
                     len(old_species_members)))
            # Use at least two parents no matter what the threshold fraction result is.
            reproduction_cutoff = max(reproduction_cutoff, 2)
            old_species_members = old_species_members[:reproduction_cutoff]

            # Randomly choose parents and produce the number of offspring allotted to the species.
            # NOTE: Asexual reproduction for now
            while spawn > 0:
                spawn -= 1
                parent1_key, parent1 = random.choice(old_species_members)
                # parent2_key, parent2 = random.choice(old_species_members)
                child_key = next(self.genome_indexer)
                child = Genome(child_key)
                # child.crossover(parent1, parent2)
                child.copy(parent1, generation)
                child.mutate(generation)
                new_population[child_key] = child
        return new_population
예제 #9
0
class Agent():
    def __init__(self, tile = None, genome = None, trust_parameter = None, performAction = None):
        self.performAction = performAction
        if performAction == None:
            self.performAction = defaultPerformAction
        if trust_parameter == None:
            trust_parameter = TRUST_PARAMETER
        self.trust_parameter = trust_parameter
        if LEARN_TRUST:
            self.trust = Trust()
        self.genome = genome
        self.tile = None
        if genome == None:
            # then generate your own genome
            self.genome = Genome()
        # self.statistics = self.genome.statistics
        self.name = self.genome.name
        self.available_moves = GAMES_PER_ITER
        self.fitness = 0
        self.ID = returnRandomID()
        self.parents = []
        self.games_played = 0
        if tile != None:
            tile.acceptAgent(self)
        self.initializeStatistics()
    def compute_optimality(self):
        # compares a gene map g to the optimal gene map
        g = self.genome.gene_map
        cor = 0
        for k in g.keys():
            if not len(k):
                cor += g[k] == COOP_SIGNAL
            else:
                cor += int(k[-1]) == g[k]
        return cor * 1./len(g)
    def initializeStatistics(self):
        # instantiates the data structure to hold the statistics
        # in lieu of the old (gene-centric) way of computing statistics,
        # now they are updated by gameMaster based on actions that were
        # actually executed, as well as by the decideAction() function.
        # Each statistic is intended to be displayed as a ratio, where
        # the first value is the number of times that situation happend
        # versus the number of times that situation COULD have happened
        stats = dict()
        stats['popular'] = [0, 0] # the frequency with which other agents agree to play with this agent
        stats['selective'] = [0, 0] # the frequency with which this agent agrees to play with other agents
        stats['cooperator'] = [0, 0] # the fraction of the time the agent cooperates.
        stats['defector'] = [0, 0] # the fraction of the time the agent defects
        stats['quitter'] = [0, 0] # the fraction of the time the agent quits
        stats['collaborator'] = [0, 0] # cooperated with a cooperator
        stats['sucker'] = [0, 0] # cooperated against a defector
        stats['traitor'] = [0, 0] # defected against a cooperator
        stats['prisoner'] = [0, 0] # defected against a defector
        stats['cruel'] = [0, 0] # defected following a cooperation
        stats['nice'] = [0, 0] # cooperated following a cooperation
        stats['forgiving'] = [0, 0] # cooperated following a defection
        stats['vengeful'] = [0, 0] # defected following a defection
        stats['timid'] = [0, 0] # quit following a cooperation
        stats['retreating'] = [0, 0] # quit following a defection
        stats['optimality'] = [self.compute_optimality(), 1] # the closeness of the agent's genome to an optimal (tit-for-tat) genome.
        self.stats = stats
        # stats_to_increment is a map of stats whose total must be
        # incremented given the opponent's previous action
        self.stats_to_increment = {COOP_SIGNAL:['cruel','nice','timid'], DEFECT_SIGNAL:['forgiving','vengeful','retreating']}
        # stat_update_map is a dict() where
        # stat_update_map[preceeding_opponent_move][your_signal] = the
        # stat to increment.
        stat_update_map = dict()
        coop_d = {COOP_SIGNAL:'nice',DEFECT_SIGNAL:'cruel',QUIT_SIGNAL:'timid'}
        defe_d = {COOP_SIGNAL:'forgiving',DEFECT_SIGNAL:'vengeful',QUIT_SIGNAL:'retreating'}
        stat_update_map[COOP_SIGNAL] = coop_d
        stat_update_map[DEFECT_SIGNAL] = defe_d
        self.stat_update_map = stat_update_map
    def decideAction(self, history):
        # decides on what action to take given the game's current history
        action_code = self.genome.getAction(history)
        signal = self.performAction(self, action_code)
        self.stats['cooperator'][1] += 1
        self.stats['defector'][1] += 1
        self.stats['quitter'][1] += 1
        if signal == COOP_SIGNAL:
            self.stats['cooperator'][0] += 1
        if signal == DEFECT_SIGNAL:
            self.stats['defector'][0] += 1
        if signal == QUIT_SIGNAL:
            self.stats['quitter'][0] += 1
        if len(history):
            opp_move = int(history[-1])
            for cur_stat in self.stats_to_increment[opp_move]:
                self.stats[cur_stat][1] += 1
            self.stats[self.stat_update_map[opp_move][signal]][0]+=1
        return signal
    def reproduce(self):
        # returns a child of this agent.
        childGenome = self.genome.mutate()
        child = Agent(self.tile, childGenome, self.trust_parameter, self.performAction)
        #child.name = child.genome.name
        child.parents = [x for x in self.parents]
        child.parents.append((self.ID, self.fitness))
        if LEARN_TRUST:
            child.trust = self.trust.reproduce() # no more telepathy!
        return child
    def cooperate(self):
        # returns the cooperate signal
        return COOP_SIGNAL
    def defect(self):
        # returns the defect signal
        return DEFECT_SIGNAL
    def quit(self):
        # returns the quit signal
        return QUIT_SIGNAL
    def move(self):
        # moves the agent. The agent randomly selects a tile from the
        # set of neighboring tiles (including the current tile) and then
        # asks the tile if it can move there. if the tile accepts, the
        # tile-side funciton ('acceptAgent') handles all the
        # modifications to the source tile, the target tile, and the
        # agent itself.
        if self.tile == None:
            return
        target = choice([self.tile] + self.tile.neighbors)
        target.acceptAgent(self)
    def trustFunction(self, agent):
        # this returns the probability of playing with the agent
        # specified by 'agent'. It is given by a modified logistic
        # function, where the agent has control over point where this
        # probability crosses 0.5. The scale parameter, however,
        # is hard-coded
        #
        # first, compute the number of different characters between
        # names.
        name_diffs = sum([x[0]!=x[1] for x in zip(self.name, agent.name)])
        return 1./(1+exp((name_diffs - self.trust_parameter)*1./TRUST_SCALE_FACTOR))
    def updateTrust(self, agent, delta_fitness):
        # updates the agent's trust in accordance with the trust object.
        self.trust.updateWeights(agent.name, delta_fitness)
    def decideToPlay(self, agent):
        # accepts an agent as input and decides whether or not to play
        # with them
        if ALWAYS_PLAY:
            return True
        if LEARN_TRUST:
            return self.trust.decideToPlay(agent.name)
        else:
            p = self.trustFunction(agent)
            return random() <= p
    def incTrustParameter(self):
        # increments the trust parameter
        self.trust_parameter += TRUST_INCREMENT_PARAMETER
    def decTrustParameter(self):
        # decrements the trust parameter
        self.trust_parameter -= TRUST_INCREMENT_PARAMETER
    def die(self):
        # 'kills' the agent
        self.tile.removeAgent(self)
예제 #10
0
 def test_genome(self):
     genome = Genome()
     genome.mutate()