forked from EvolutionGym/evogym
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpopulation.py
105 lines (81 loc) · 4.93 KB
/
population.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import neat
import numpy as np
class Population(neat.Population):
def run(self, fitness_function, constraint_function=None, n=None):
"""
Runs NEAT's genetic algorithm for at most n generations. If n
is None, run until solution is found or extinction occurs.
The user-provided fitness_function must take only two arguments:
1. The population as a list of (genome id, genome) tuples.
2. The current configuration object.
The return value of the fitness function is ignored, but it must assign
a Python float to the `fitness` member of each genome.
The fitness function is free to maintain external state, perform
evaluations in parallel, etc.
It is assumed that fitness_function does not modify the list of genomes,
the genomes themselves (apart from updating the fitness member),
or the configuration object.
"""
if self.config.no_fitness_termination and (n is None):
raise RuntimeError("Cannot have no generational limit with no fitness termination")
k = 0
while n is None or k < n:
k += 1
self.reporters.start_generation(self.generation)
# Evaluate all genomes using the user-provided constraint function.
# If some genomes violate the constraint, generate new genomes and replace them, until all genomes satisfy the constraint.
if constraint_function is not None:
genomes = list(self.population.items())
validity = constraint_function(genomes, self.config, self.generation)
valid_idx = np.where(validity)[0]
valid_genomes = np.array(genomes)[valid_idx]
while len(valid_genomes) < self.config.pop_size:
new_population = self.reproduction.create_new(self.config.genome_type,
self.config.genome_config,
self.config.pop_size - len(valid_genomes))
new_genomes = list(new_population.items())
validity = constraint_function(new_genomes, self.config, self.generation)
valid_idx = np.where(validity)[0]
valid_genomes = np.vstack([valid_genomes, np.array(new_genomes)[valid_idx]])
self.population = dict(valid_genomes)
self.species.speciate(self.config, self.population, self.generation)
# Evaluate all genomes using the user-provided function.
fitness_function(list(self.population.items()), self.config, self.generation)
# Gather and report statistics.
best = None
for g in self.population.values():
if g.fitness is None:
raise RuntimeError("Fitness not assigned to genome {}".format(g.key))
if best is None or g.fitness > best.fitness:
best = g
self.reporters.post_evaluate(self.config, self.population, self.species, best)
# Track the best genome ever seen.
if self.best_genome is None or best.fitness > self.best_genome.fitness:
self.best_genome = best
if not self.config.no_fitness_termination:
# End if the fitness threshold is reached.
fv = self.fitness_criterion(g.fitness for g in self.population.values())
if fv >= self.config.fitness_threshold:
self.reporters.found_solution(self.config, self.generation, best)
break
# Create the next generation from the current generation.
self.population = self.reproduction.reproduce(self.config, self.species,
self.config.pop_size, self.generation)
# Check for complete extinction.
if not self.species.species:
self.reporters.complete_extinction()
# If requested by the user, create a completely new population,
# otherwise raise an exception.
if self.config.reset_on_extinction:
self.population = self.reproduction.create_new(self.config.genome_type,
self.config.genome_config,
self.config.pop_size)
else:
raise CompleteExtinctionException()
# Divide the new population into species.
self.species.speciate(self.config, self.population, self.generation)
self.reporters.end_generation(self.config, self.population, self.species)
self.generation += 1
if self.config.no_fitness_termination:
self.reporters.found_solution(self.config, self.generation, self.best_genome)
return self.best_genome