Skip to content

Commit a712228

Browse files
Drop Python 3.6 and 3.7 support, made minor internal modernizations.
1 parent 5565f7e commit a712228

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+244
-180
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4141
- Includes test/visualization script for trained controllers
4242
- Full documentation in example README with usage tips
4343

44+
### Changed
45+
- Dropped support for Python 3.6 and 3.7; neat-python now requires Python 3.8 or newer.
46+
- Modernized internal implementation in `neat/` and `examples/` to use Python 3 features
47+
such as f-strings, comprehensions, dataclasses for internal helpers, and type hints,
48+
without changing the public API.
49+
4450
### Fixed
4551
- **Population Size Drift**: Fixed small mismatches between actual population size and configured `pop_size`
4652
- `DefaultReproduction.reproduce()` now strictly enforces `len(population) == config.pop_size` for every non-extinction generation

examples/inverted-double-pendulum/evolve-feedforward.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def run(config_file):
8484
with open('winner-feedforward.pickle', 'wb') as f:
8585
pickle.dump(winner, f)
8686

87-
print('\n\nBest genome:\n{!s}'.format(winner))
87+
print(f'\n\nBest genome:\n{winner!s}')
8888

8989
# Visualize the results
9090
visualize.plot_stats(stats, ylog=False, view=True, filename="feedforward-fitness.svg")

examples/inverted-double-pendulum/visualize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def plot_spikes(spikes, view=False, filename=None, title=None):
5858
if title is None:
5959
plt.title("Izhikevich's spiking neuron model")
6060
else:
61-
plt.title("Izhikevich's spiking neuron model ({0!s})".format(title))
61+
plt.title(f"Izhikevich's spiking neuron model ({title!s})")
6262

6363
plt.subplot(3, 1, 2)
6464
plt.ylabel("Recovery (u)")

examples/memory-fixed/evolve.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,28 +98,28 @@ def run():
9898
stats.save()
9999

100100
# Show output of the most fit genome against a random input.
101-
print('\nBest genome:\n{!s}'.format(winner))
101+
print(f'\nBest genome:\n{winner!s}')
102102
print('\nOutput:')
103103
winner_net = neat.nn.RecurrentNetwork.create(winner, config)
104104
num_correct = 0
105105
for n in range(num_tests):
106-
print('\nRun {0} output:'.format(n))
106+
print(f'\nRun {n} output:')
107107
seq = [random.choice((0.0, 1.0)) for _ in range(N)]
108108
winner_net.reset()
109109
for s in seq:
110110
inputs = [s, 0.0]
111111
winner_net.activate(inputs)
112-
print('\tseq {0}'.format(inputs))
112+
print(f'\tseq {inputs}')
113113

114114
correct = True
115115
for s in seq:
116116
output = winner_net.activate([0, 1])
117-
print("\texpected {0:1.5f} got {1:1.5f}".format(s, output[0]))
117+
print(f"\texpected {s:1.5f} got {output[0]:1.5f}")
118118
correct = correct and round(output[0]) == s
119119
print("OK" if correct else "FAIL")
120120
num_correct += 1 if correct else 0
121121

122-
print("{0} of {1} correct {2:.2f}%".format(num_correct, num_tests, 100.0 * num_correct / num_tests))
122+
print(f"{num_correct} of {num_tests} correct {100.0 * num_correct / num_tests:.2f}%")
123123

124124
node_names = {-1: 'input', -2: 'gate', 0: 'output'}
125125
visualize.draw_net(config, winner, True, node_names=node_names)

examples/memory-fixed/visualize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def plot_spikes(spikes, view=False, filename=None, title=None):
5757
if title is None:
5858
plt.title("Izhikevich's spiking neuron model")
5959
else:
60-
plt.title("Izhikevich's spiking neuron model ({0!s})".format(title))
60+
plt.title(f"Izhikevich's spiking neuron model ({title!s})")
6161

6262
plt.subplot(3, 1, 2)
6363
plt.ylabel("Recovery (u)")

examples/memory-variable/evolve.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ def run():
8585
stats.save()
8686

8787
# Show output of the most fit genome against a random input.
88-
print('\nBest genome:\n{!s}'.format(winner))
88+
print(f'\nBest genome:\n{winner!s}')
8989
print('\nOutput:')
9090
winner_net = neat.nn.RecurrentNetwork.create(winner, config)
9191
num_correct = 0
9292
for n in range(num_tests):
93-
print('\nRun {0} output:'.format(n))
93+
print(f'\nRun {n} output:')
9494

9595
num_inputs = random.randint(1, max_inputs)
9696
num_ignore = random.randint(0, max_ignore)
@@ -101,12 +101,12 @@ def run():
101101

102102
correct = True
103103
for i, o in zip(seq, outputs):
104-
print("\texpected {0:1.5f} got {1:1.5f}".format(i, o[0]))
104+
print(f"\texpected {i:1.5f} got {o[0]:1.5f}")
105105
correct = correct and round(o[0]) == i
106106
print("OK" if correct else "FAIL")
107107
num_correct += 1 if correct else 0
108108

109-
print("{0} of {1} correct {2:.2f}%".format(num_correct, num_tests, 100.0 * num_correct / num_tests))
109+
print(f"{num_correct} of {num_tests} correct {100.0 * num_correct / num_tests:.2f}%")
110110

111111
node_names = {-1: 'input', -2: 'record', -3: 'play', 0: 'output'}
112112
visualize.draw_net(config, winner, True, node_names=node_names)

examples/memory-variable/visualize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def plot_spikes(spikes, view=False, filename=None, title=None):
5858
if title is None:
5959
plt.title("Izhikevich's spiking neuron model")
6060
else:
61-
plt.title("Izhikevich's spiking neuron model ({0!s})".format(title))
61+
plt.title(f"Izhikevich's spiking neuron model ({title!s})")
6262

6363
plt.subplot(3, 1, 2)
6464
plt.ylabel("Recovery (u)")

examples/neuron-demo/demo-ctrnn.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
output = net.advance([], 0.002, 0.002)
2626
times.append(net.time_seconds)
2727
outputs.append(output)
28-
print("{0:.7f} {1:.7f}".format(output[0], output[1]))
28+
print(f"{output[0]:.7f} {output[1]:.7f}")
2929

3030
outputs = np.array(outputs).T
3131

examples/neuron-demo/demo-iznn.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def plot_spikes(spikes, title):
1818
plt.grid()
1919
plt.plot(t_values, v_values, "g-")
2020

21-
plt.title("Izhikevich's spiking neuron model ({0!s})".format(title))
21+
plt.title(f"Izhikevich's spiking neuron model ({title!s})")
2222

2323
plt.subplot(4, 1, 2)
2424
plt.ylabel("Fired")
@@ -39,7 +39,7 @@ def plot_spikes(spikes, title):
3939
plt.plot(t_values, I_values, "r-o")
4040

4141
fig = plt.figure()
42-
plt.title("Izhikevich's spiking neuron model u/v ({0!s})".format(title))
42+
plt.title(f"Izhikevich's spiking neuron model u/v ({title!s})")
4343
plt.xlabel("Recovery (u)")
4444
plt.ylabel("Potential (mv)")
4545
plt.grid()
@@ -55,7 +55,7 @@ def show(title, a, b, c, d):
5555
for i in range(1000):
5656
n.current = 0.0 if i < 100 or i > 800 else 10.0
5757
spike_train.append((1.0 * i, n.current, n.v, n.u, n.fired))
58-
print('{0:d}\t{1:f}\t{2:f}\t{3:f}'.format(i, n.current, n.v, n.u))
58+
print(f'{i:d}\t{n.current:f}\t{n.v:f}\t{n.u:f}')
5959
n.advance(0.25)
6060

6161
plot_spikes(spike_train, title)

examples/openai-lander/evolve.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def compute_fitness(genome, net, episodes, min_reward, max_reward):
7070
return reward_error
7171

7272

73-
class PooledErrorCompute(object):
73+
class PooledErrorCompute:
7474
def __init__(self, num_workers):
7575
self.num_workers = num_workers
7676
self.test_episodes = []
@@ -110,7 +110,7 @@ def simulate(self, nets):
110110

111111
self.test_episodes.append((score, data))
112112

113-
print("Score range [{:.3f}, {:.3f}]".format(min(scores), max(scores)))
113+
print(f"Score range [{min(scores):.3f}, {max(scores):.3f}]")
114114

115115
def evaluate_genomes(self, genomes, config):
116116
self.generation += 1
@@ -120,19 +120,19 @@ def evaluate_genomes(self, genomes, config):
120120
for gid, g in genomes:
121121
nets.append((g, neat.nn.FeedForwardNetwork.create(g, config)))
122122

123-
print("network creation time {0}".format(time.time() - t0))
123+
print(f"network creation time {time.time() - t0}")
124124
t0 = time.time()
125125

126126
# Periodically generate a new set of episodes for comparison.
127127
if 1 == self.generation % 10:
128128
self.test_episodes = self.test_episodes[-300:]
129129
self.simulate(nets)
130-
print("simulation run time {0}".format(time.time() - t0))
130+
print(f"simulation run time {time.time() - t0}")
131131
t0 = time.time()
132132

133133
# Assign a composite fitness to each genome; genomes can make progress either
134134
# by improving their total reward or by making more accurate reward estimates.
135-
print("Evaluating {0} test episodes".format(len(self.test_episodes)))
135+
print(f"Evaluating {len(self.test_episodes)} test episodes")
136136
if self.num_workers < 2:
137137
for genome, net in nets:
138138
reward_error = compute_fitness(genome, net, self.test_episodes, self.min_reward, self.max_reward)
@@ -149,7 +149,7 @@ def evaluate_genomes(self, genomes, config):
149149
reward_error = job.get(timeout=None)
150150
genome.fitness = -np.sum(reward_error) / len(self.test_episodes)
151151

152-
print("final fitness compute time {0}\n".format(time.time() - t0))
152+
print(f"final fitness compute time {time.time() - t0}\n")
153153

154154

155155
def run():
@@ -187,10 +187,10 @@ def run():
187187
plt.close()
188188

189189
mfs = sum(stats.get_fitness_mean()[-5:]) / 5.0
190-
print("Average mean fitness over last 5 generations: {0}".format(mfs))
190+
print(f"Average mean fitness over last 5 generations: {mfs}")
191191

192192
mfs = sum(stats.get_fitness_stat(min)[-5:]) / 5.0
193-
print("Average min fitness over last 5 generations: {0}".format(mfs))
193+
print(f"Average min fitness over last 5 generations: {mfs}")
194194

195195
# Use the best genomes seen so far as an ensemble-ish control system.
196196
best_genomes = stats.best_unique_genomes(3)
@@ -245,7 +245,7 @@ def run():
245245
# Save the winners.
246246
if best_genomes:
247247
for n, g in enumerate(best_genomes):
248-
name = 'winner-{0}'.format(n)
248+
name = f'winner-{n}'
249249
with open(name + '.pickle', 'wb') as f:
250250
pickle.dump(g, f)
251251

0 commit comments

Comments
 (0)