Example #1
0
    def genome_distance(self, genome0, genome1):
        '''
		Computes genome distance between two genomes
		'''
        node_distance = 0.0
        # Determine node distance
        if genome0.nodes or genome1.nodes:
            # Number of disjoing nodes between genomes
            disjoint_nodes = 0
            # Count number of disjoint node genes between genomes
            for genome_1_node_key in iterkeys(genome1.nodes):
                if genome_1_node_key not in genome0.nodes:
                    disjoint_nodes += 1
            # Determine genetic distance between  individual node genes
            for genome_0_node_key, genome_0_node in iteritems(genome0.nodes):
                genome_1_node = genome1.nodes.get(genome_0_node_key)
                if genome_1_node is None:
                    disjoint_nodes += 1
                else:
                    # Homologous genes compute their own distance value.
                    node_distance += self.node_gene_distance(
                        genome_0_node, genome_1_node)
            # Find most number of nodes in either genome
            max_nodes = max(len(genome0.nodes), len(genome1.nodes))
            # Determine final node genetic distance
            node_distance = (node_distance +
                             (self.compatibility_disjoint_coefficient *
                              disjoint_nodes)) / max_nodes

        # Determine connection gene distance
        connection_distance = 0.0
        if genome0.connections or genome1.connections:
            disjoint_connections = 0
            for genome_1_conn_key in iterkeys(genome1.connections):
                if genome_1_conn_key not in genome0.connections:
                    disjoint_connections += 1

            for genome_0_conn_key, genome_0_conn in iteritems(
                    genome0.connections):
                genome_1_conn = genome1.connections.get(genome_0_conn_key)
                if genome_1_conn is None:
                    disjoint_connections += 1
                else:
                    # Homologous genes compute their own distance value.
                    connection_distance += self.connection_gene_distance(
                        genome_0_conn, genome_1_conn)

            max_conn = max(len(genome0.connections), len(genome1.connections))
            connection_distance = (connection_distance +
                                   (self.compatibility_disjoint_coefficient *
                                    disjoint_connections)) / max_conn

        distance = node_distance + connection_distance
        return distance
Example #2
0
    def __init__(self, **values):
        self.start_date = values.pop('start_date', None)
        if self.start_date:
            self.start_date = convert_to_datetime(self.start_date)

        # Check field names and yank out all None valued fields
        for key, value in list(iteritems(values)):
            if key not in self.FIELD_NAMES:
                raise TypeError('Invalid field name: %s' % key)
            if value is None:
                del values[key]

        self.fields = []
        assign_defaults = False
        for field_name in self.FIELD_NAMES:
            if field_name in values:
                exprs = values.pop(field_name)
                is_default = False
                assign_defaults = not values
            elif assign_defaults:
                exprs = DEFAULT_VALUES[field_name]
                is_default = True
            else:
                exprs = '*'
                is_default = True

            field_class = self.FIELDS_MAP[field_name]
            field = field_class(field_name, exprs, is_default)
            self.fields.append(field)
Example #3
0
    def update(self, species_set, generation):
        '''
        Updates species fitness history, checks for stagnated species,
        and returns a list with stagnant species to remove.

        species_set -- set containing the species and their ids
        generation  -- the current generation number
        '''
        species_data = []
        for sid, species in iteritems(species_set.species):
            if species.fitness_history:
                prev_fitness = max(species.fitness_history)
            else:
                prev_fitness = -sys.float_info.max
            species.fitness = self.species_fitness_func(
                species.get_fitnesses())
            species.fitness_history.append(species.fitness)
            species.adjusted_fitness = None
            if prev_fitness is None or species.fitness > prev_fitness:
                species.last_improved = generation
            species_data.append((sid, species))
        # Sort in ascending fitness order.
        species_data.sort(key=lambda x: x[1].fitness)
        result = []
        species_fitnesses = []
        num_non_stagnant_species = len(species_data)
        for idx, (sid, species) in enumerate(species_data):
            # Override stagnant state if marking this species as stagnant would
            #   result in the total number of species dropping below the limit
            stagnant_time = generation - species.last_improved
            is_stagnant = False
            if num_non_stagnant_species > self.species_elitism:
                is_stagnant = stagnant_time >= self.max_stagnation
            if (len(species_data) - idx) <= self.species_elitism:
                is_stagnant = False
            if is_stagnant:
                num_non_stagnant_species -= 1
            result.append((sid, species, is_stagnant))
            species_fitnesses.append(species.fitness)
        return result
Example #4
0
    def mutate_delete_node(self, gen=None):
        '''
		Mutation for deleting a node gene to the genome.

		gen -- optional argument for current generation mutation occurs
		'''
        available_nodes = [
            k for k in iterkeys(self.nodes) if k not in self.output_keys
        ]
        if not available_nodes:
            return
        # Choose random node to delete
        del_key = np.random.choice(available_nodes)
        # Iterate through all connections and find connections to node
        conn_to_delete = set()
        for k, v in iteritems(self.connections):
            if del_key in v.key:
                conn_to_delete.add(v.key)
        for i in conn_to_delete:
            del self.connections[i]
        # Delete node key
        del self.nodes[del_key]
        return del_key
    def run(self, task, goal, generations=None):
        '''
		Run evolution on a given task for a number of generations or until
		a goal is reached.

		task -- the task to be solved
		goal -- the goal to reach for the given task that defines a solution
		generations -- the max number of generations to run evolution for
		'''
        self.current_gen = 0
        reached_goal = False
        # Plot data
        best_fitnesses = []
        max_complexity = []
        min_complexity = []
        avg_complexity = []
        while self.current_gen < generations and not reached_goal:
            # Assess fitness of current population
            task(list(iteritems(self.population)))
            # Find best genome in current generation and update avg fitness
            curr_best = None
            curr_max_complex = None
            curr_min_complex = None
            avg_complexities = 0
            for genome in itervalues(self.population):
                avg_complexities += genome.complexity()
                # Update generation's most fit
                if curr_best is None or genome.fitness > curr_best.fitness:
                    curr_best = genome
                # Update generation's most complex
                if curr_max_complex is None or genome.complexity(
                ) > curr_max_complex.complexity():
                    curr_max_complex = genome
                # Update generation's least complex
                if curr_min_complex is None or genome.complexity(
                ) < curr_min_complex.complexity():
                    curr_min_complex = genome

            # Update global best genome if possible
            if self.best_genome is None or curr_best.fitness > self.best_genome.fitness:
                self.best_genome = curr_best

            # Update global most and least complex genomes
            if self.max_complex_genome is None or curr_max_complex.complexity(
            ) > self.max_complex_genome.complexity():
                self.max_complex_genome = curr_max_complex
            if self.min_complex_genome is None or curr_min_complex.complexity(
            ) < self.min_complex_genome.complexity():
                self.min_complex_genome = curr_min_complex

            self.max_dict[self.current_gen] = self.max_complex_genome

            # Reporters
            report_fitness(self)
            report_species(self.species, self.current_gen)
            report_output(self)
            best_fitnesses.append(self.best_genome.fitness)
            max_complexity.append(self.max_complex_genome.complexity())
            min_complexity.append(self.min_complex_genome.complexity())
            avg_complexity.append(
                (avg_complexities + 0.0) / len(self.population))
            self.avg_complex = (avg_complexities + 0.0) / len(self.population)
            avg_complexities = 0

            # Reached fitness goal, we can stop
            if self.best_genome.fitness >= goal:
                reached_goal = True

            # Create new unspeciated popuation based on current population's fitness
            self.population = self.reproduction.reproduce_with_species(
                self.species, self.size, self.current_gen)
            # Check for species extinction (species did not perform well)
            if not self.species.species:
                print("!!! Species went extinct !!!")
                self.population = self.reproduction.create_new_population(
                    self.size)

            # Speciate new population
            self.species.speciate(self.population, self.current_gen)
            self.current_gen += 1

        generations = range(self.current_gen)
        plot_fitness(generations, best_fitnesses)
        return self.best_genome
	def speciate(self,population,generation):
		'''
		Speciates a population.
		'''
		# Compatibility threshold
		compatibility_threshold = self.threshold
		# Set of unspeciated members of the population
		unspeciated = set(iterkeys(population))
		# Means of determining distances
		distances = GenomeDistanceCache()
		# New representatives and members of species
		new_representatives = {}
		new_members = {}
		# Traverse through set of species from last generation
		for sid, species in iteritems(self.species):
			# Candidates for current species representatives
			candidate_representatives = []
			# Traverese genomes in the unspeciated and check their distance
			# from the current species representative
			for gid in unspeciated:
				genome = population[gid]
				genome_distance = distances(species.representative, genome)
				candidate_representatives.append((genome_distance, genome))
			# The new representative for the current species is the 
			# closest to the current representative
			_, new_rep = min(candidate_representatives, key=lambda x: x[0])
			new_rid = new_rep.key
			new_representatives[sid] = new_rid
			new_members[sid] = [new_rid]
			unspeciated.remove(new_rid)

		# Partition the population in species based on genetic similarity
		while unspeciated:
			gid = unspeciated.pop()
			genome = population[gid]
			# Find the species with the most similar representative to the
			# 	current genome from the unspeciated set
			candidate_species = []
			# Traverse species and their representatives
			for sid, rid in iteritems(new_representatives):
				representative = population[rid]
				# Determine current genome's distance from representative
				genome_distance = distances(representative, genome)
				# If it's below threshold, add it to list for adding to the species
				if genome_distance < compatibility_threshold:
					candidate_species.append((genome_distance, sid))
			# Add current genome to the species its most genetically similar to
			if candidate_species:
				_, sid = min(candidate_species, key=lambda x: x[0])
				new_members[sid].append(gid)
			else:
				# No species is similar enough so we create a mnew species with
				# 	the current genome as its representative
				sid = next(self.species_indexer)
				new_representatives[sid] = gid
				new_members[sid] = [gid]
			# Update species collection based on new speciation
			self.genome_to_species = {}
			for sid, rid in iteritems(new_representatives):
				# Add species if not existing in current species set
				s = self.species.get(sid)
				if s is None:
					s = Species(sid, generation)
					self.species[sid] = s
				# Collect and add members to current species
				members = new_members[sid]
				for gid in members:
					self.genome_to_species[gid] = sid
				# Update current species members and represenative
				member_dict = {gid:population[gid] for gid in members}
				s.update(population[rid], member_dict)
Example #7
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