6
6
import time
7
7
8
8
from neat .config import Config
9
- from neat .indexer import Indexer
10
9
from neat .reporting import ReporterSet , StatisticsReporter , StdOutReporter
11
- from neat .species import Species
10
+ from neat .species import SpeciesSet
12
11
13
12
14
13
class CompleteExtinctionException (Exception ):
@@ -45,17 +44,16 @@ def __init__(self, config, initial_population=None):
45
44
self .add_reporter (StdOutReporter ())
46
45
47
46
self .config = config
48
- self .species_indexer = Indexer (1 )
49
- self .reproduction = config .reproduction_type (self .config , self .reporters )
47
+ self .reproduction = config .reproduction_type (config , self .reporters )
50
48
51
- self .species = []
49
+ self .species = SpeciesSet ( config )
52
50
self .generation = - 1
53
51
self .total_evaluations = 0
54
52
55
53
# Create a population if one is not given, then partition into species.
56
54
if initial_population is None :
57
55
initial_population = self .reproduction .create_new (config .pop_size )
58
- self ._speciate (initial_population )
56
+ self .species . speciate (initial_population )
59
57
60
58
def add_reporter (self , reporter ):
61
59
self .reporters .add (reporter )
@@ -86,39 +84,6 @@ def save_checkpoint(self, filename=None, checkpoint_type="user"):
86
84
random .getstate ())
87
85
pickle .dump (data , f , protocol = pickle .HIGHEST_PROTOCOL )
88
86
89
- def _speciate (self , population ):
90
- """
91
- Place genomes into species by genetic similarity.
92
-
93
- Note that this method assumes the current representatives of the species are from the old
94
- generation, and that after speciation has been performed, the old representatives should be
95
- dropped and replaced with representatives from the new generation. If you violate this
96
- assumption, you should make sure other necessary parts of the code are updated to reflect
97
- the new behavior.
98
- """
99
- for individual in population :
100
- # Find the species with the most similar representative.
101
- min_distance = None
102
- closest_species = None
103
- for s in self .species :
104
- distance = individual .distance (s .representative )
105
- if distance < self .config .compatibility_threshold \
106
- and (min_distance is None or distance < min_distance ):
107
- closest_species = s
108
- min_distance = distance
109
-
110
- if closest_species :
111
- closest_species .add (individual )
112
- else :
113
- # No species is similar enough, create a new species for this individual.
114
- self .species .append (Species (individual , self .species_indexer .get_next ()))
115
-
116
- # Only keep non-empty species.
117
- self .species = [s for s in self .species if s .members ]
118
-
119
- # Select a random current member as the new representative.
120
- for s in self .species :
121
- s .representative = random .choice (s .members )
122
87
123
88
def run (self , fitness_function , n ):
124
89
"""
@@ -143,21 +108,21 @@ def run(self, fitness_function, n):
143
108
144
109
# Collect a list of all members from all species.
145
110
population = []
146
- for s in self .species :
111
+ for s in self .species . species :
147
112
population .extend (s .members )
148
113
149
114
# Evaluate all individuals in the population using the user-provided function.
150
115
# TODO: Add an option to only evaluate each genome once, to reduce number of
151
116
# fitness evaluations in cases where the fitness is known to be the same if the
152
117
# genome doesn't change--in these cases, evaluating unmodified elites in each
153
118
# generation is a waste of time. The user can always take care of this in their
154
- # fitness function in the for now if they wish.
119
+ # fitness function in the time being if they wish.
155
120
fitness_function (population )
156
121
self .total_evaluations += len (population )
157
122
158
123
# Gather and report statistics.
159
124
best = max (population )
160
- self .reporters .post_evaluate (population , self .species , best )
125
+ self .reporters .post_evaluate (population , self .species . species , best )
161
126
162
127
# Save the best genome from the current generation if requested.
163
128
if self .config .save_best :
@@ -170,25 +135,25 @@ def run(self, fitness_function, n):
170
135
break
171
136
172
137
# Create the next generation from the current generation.
173
- self . species , new_population = self .reproduction .reproduce (self .species , self .config .pop_size )
138
+ new_population = self .reproduction .reproduce (self .species , self .config .pop_size )
174
139
175
140
# Check for complete extinction
176
- if not self .species :
141
+ if not self .species . species :
177
142
self .reporters .complete_extinction ()
178
143
179
144
# If requested by the user, create a completely new population,
180
145
# otherwise raise an exception.
181
146
if self .config .reset_on_extinction :
182
- new_population = self ._create_population ( )
147
+ new_population = self .reproduction . create_new ( self . config . pop_size )
183
148
else :
184
149
raise CompleteExtinctionException ()
185
150
186
151
# Update species age.
187
- for s in self .species :
152
+ for s in self .species . species :
188
153
s .age += 1
189
154
190
155
# Divide the new population into species.
191
- self ._speciate (new_population )
156
+ self .species . speciate (new_population )
192
157
193
158
# Save checkpoints if necessary.
194
159
if self .config .checkpoint_time_interval is not None :
0 commit comments