Skip to content

Commit 7fdb3fd

Browse files
Updated documentation and examples.
Fixed bug in statistics reporter.
1 parent aaa527f commit 7fdb3fd

26 files changed

+536
-245
lines changed

docs/activation.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
.. _activation-functions-label:
12

23
Overview of builtin activation functions
34
========================================
45

5-
The implementation of these functions can be found in the `nn module
6-
<https://github.com/CodeReclaimers/neat-python/blob/master/neat/nn/__init__.py>`_.
6+
The implementation of these functions can be found in the `activations module
7+
<https://github.com/CodeReclaimers/neat-python/blob/master/neat/activations.py>`_.
78

89
abs
910
---

docs/config_file.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ required for your particular implementation.
6464
The probability that mutation will change the node's activation function. Valid values are on [0.0, 1.0].
6565
* *activation_options*
6666
A space-separated list of the activation functions that may be used in constructing networks. The
67-
set of available functions can be found here: .. _`activation`:
67+
set of available functions can be found here: :ref:`activation-functions-label`
6868

6969
* *aggregation_default*
7070
The default aggregation function assigned to new nodes.

examples/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## neat-python examples ##
2+
3+
Note that there is a significant amount of duplication between these scripts, and this is intentional. The goal is to
4+
make it easier to see what the example is doing, without making the user dig through a bunch of code that is not
5+
directly related to the NEAT library usage.
6+
7+
## The examples ##
8+
9+
* `circuits` Uses an external circuit simulator (PySpice) to create electronic circuits that reproduce an arbitrary function of the input voltage.
10+
11+
* `memory-fixed` Reproduce a fixed-length sequence of binary inputs.
12+
13+
* `memory-variable` Reproduce a variable-length sequence of binary inputs.
14+
15+
* `picture2d` Generate 2D color or grayscale images using Compositional Pattern-Producing Networks.
16+
17+
* `single-pole-balancing` Balance a pole on top of a movable cart.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/usr/bin/env bash
2-
rm *.csv
2+
rm *.csv *.svg *.gv

examples/circuits/config

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
[NEAT]
2-
max_fitness_threshold = -0.05
3-
pop_size = 100
2+
max_fitness_threshold = -0.01
3+
pop_size = 350
44
reset_on_extinction = False
55

66
[CircuitGenome]
77
# component type options
88
component_default = resistor
9-
component_mutate_rate = 0.1
9+
component_mutate_rate = 0.2
1010
component_options = resistor diode
1111

1212
# component value options
1313
value_init_mean = 3.0
14-
value_init_stdev = 0.1
15-
value_max_value = 9.0
14+
value_init_stdev = 0.5
15+
value_max_value = 6.0
1616
value_min_value = 0.0
1717
value_mutate_power = 0.5
18-
value_mutate_rate = 0.7
18+
value_mutate_rate = 0.8
1919
value_replace_rate = 0.1
2020

2121
# genome compatibility options
2222
compatibility_disjoint_coefficient = 1.0
23-
compatibility_threshold = 3.0
23+
compatibility_threshold = 2.5
2424
compatibility_weight_coefficient = 1.0
2525

2626
# connection add/remove rates
@@ -29,19 +29,19 @@ conn_delete_prob = 0.5
2929

3030
# connection enable options
3131
enabled_default = True
32-
enabled_mutate_rate = 0.01
32+
enabled_mutate_rate = 0.05
3333

3434
# node add/remove rates
3535
node_add_prob = 0.2
3636
node_delete_prob = 0.2
3737

3838
# network parameters
39-
num_inputs = 1
39+
num_inputs = 3
4040
num_outputs = 1
4141

4242
[DefaultStagnation]
4343
species_fitness_func = max
44-
max_stagnation = 20
44+
max_stagnation = 25
4545

4646
[DefaultReproduction]
4747
elitism = 2

examples/circuits/evolve.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,18 @@ def add_hidden_nodes(self, config):
321321

322322
@classmethod
323323
def create(cls, config, key):
324-
return cls.create_unconnected(config, key)
324+
c = cls(key, config)
325+
326+
# Create node genes for the output pins.
327+
for node_key in config.output_keys:
328+
c.nodes[node_key] = cls.create_node(config, node_key)
329+
330+
for input_id in config.input_keys:
331+
for node_id in iterkeys(c.nodes):
332+
connection = cls.create_connection(config, input_id, node_id)
333+
c.connections[connection.key] = connection
334+
335+
return c
325336

326337
@staticmethod
327338
def create_node(config, node_id):
@@ -335,17 +346,6 @@ def create_connection(config, input_id, output_id):
335346
connection.init_attributes(config)
336347
return connection
337348

338-
@classmethod
339-
def create_unconnected(cls, config, key):
340-
'''Create a genome for a network with no hidden nodes and no connections.'''
341-
c = cls(key, config)
342-
343-
# Create node genes for the output pins.
344-
for node_key in config.output_keys:
345-
c.nodes[node_key] = cls.create_node(config, node_key)
346-
347-
return c
348-
349349

350350
def get_pins(key):
351351
pins = []
@@ -366,8 +366,10 @@ def create_circuit(genome, config):
366366
circuit.include(spice_library['1N4148'])
367367

368368
Vbase = circuit.V('base', 'input1', circuit.gnd, 2)
369-
circuit.R('test1', 'node0', circuit.gnd, 1e6)
370-
circuit.R('test2', 'node0', 'input1', 1e6)
369+
Vcc = circuit.V('cc', 'input2', circuit.gnd, 5)
370+
Vgnd = circuit.V('gnd', 'input3', circuit.gnd, 0)
371+
#circuit.R('test1', 'node0', circuit.gnd, 1e6)
372+
#circuit.R('test2', 'node0', 'input1', 1e6)
371373
ridx = 1
372374
xidx = 1
373375
for key, c in iteritems(genome.connections):
@@ -386,7 +388,8 @@ def create_circuit(genome, config):
386388
V_MAX = 2.0
387389

388390
def get_expected(inputs):
389-
return 0.5 * inputs + 0.15 * inputs ** 2
391+
#return 0.5 * inputs - 0.15 * inputs ** 2
392+
return 2.0 / (1.0 + np.exp(-2.5 * (inputs - 1.0)))
390393

391394

392395
def simulate(genome, config):
@@ -402,6 +405,7 @@ def simulate(genome, config):
402405
expected = get_expected(inputs)
403406

404407
return -np.sum((expected - outputs) ** 2)
408+
#return -np.max(np.abs(expected - outputs))
405409
except Exception as e:
406410
return -1000
407411

@@ -426,7 +430,7 @@ def run(config_file):
426430
p.add_reporter(stats)
427431

428432
# Run for up to 300 generations.
429-
winner = p.run(eval_genomes, 100)
433+
winner = p.run(eval_genomes, 1000)
430434

431435
# Display the winning genome.
432436
print('\nBest genome:\n{!s}'.format(winner))
@@ -448,7 +452,7 @@ def run(config_file):
448452
plt.gca().set_aspect(1)
449453
plt.show()
450454

451-
visualize.plot_stats(stats, ylog=False, view=False)
455+
visualize.plot_stats(stats, ylog=True, view=False)
452456
visualize.plot_species(stats, view=False)
453457

454458

examples/memory-fixed/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Fixed-length Sequence Memory Evolution ##
2+
3+
`evolve.py` Generates networks to reproduce a fixed-length sequence of binary inputs.

examples/memory-fixed/clean.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env bash
2+
rm *.csv *.svg *.gv

examples/memory-fixed/config

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# NEAT configuration for the bit-sequence memory experiment.
2+
3+
# The `NEAT` section specifies parameters particular to the NEAT algorithm
4+
# or the experiment itself. This is the only required section.
5+
[NEAT]
6+
pop_size = 2000
7+
max_fitness_threshold = 0.95
8+
reset_on_extinction = 0
9+
10+
[DefaultGenome]
11+
num_inputs = 2
12+
num_hidden = 1
13+
num_outputs = 1
14+
initial_connection = partial 0.5
15+
feed_forward = 0
16+
compatibility_threshold = 3.0
17+
compatibility_disjoint_coefficient = 1.0
18+
compatibility_weight_coefficient = 0.6
19+
conn_add_prob = 0.1
20+
conn_delete_prob = 0.1
21+
node_add_prob = 0.1
22+
node_delete_prob = 0.1
23+
activation_default = my_sinc_function
24+
activation_options = sigmoid my_sinc_function
25+
activation_mutate_rate = 0.1
26+
aggregation_default = sum
27+
aggregation_options = sum
28+
aggregation_mutate_rate = 0.0
29+
bias_init_mean = 0.0
30+
bias_init_stdev = 1.0
31+
bias_replace_rate = 0.1
32+
bias_mutate_rate = 0.7
33+
bias_mutate_power = 0.5
34+
bias_max_value = 30.0
35+
bias_min_value = -30.0
36+
response_init_mean = 1.0
37+
response_init_stdev = 0.1
38+
response_replace_rate = 0.1
39+
response_mutate_rate = 0.1
40+
response_mutate_power = 0.1
41+
response_max_value = 30.0
42+
response_min_value = -30.0
43+
44+
weight_max_value = 30
45+
weight_min_value = -30
46+
weight_init_mean = 0.0
47+
weight_init_stdev = 1.0
48+
weight_mutate_rate = 0.8
49+
weight_replace_rate = 0.1
50+
weight_mutate_power = 0.5
51+
enabled_default = True
52+
enabled_mutate_rate = 0.01
53+
54+
[DefaultStagnation]
55+
species_fitness_func = max
56+
max_stagnation = 20
57+
58+
[DefaultReproduction]
59+
elitism = 2
60+
survival_threshold = 0.2
61+

examples/memory-fixed/evolve.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""
2+
This example produces networks that can remember a fixed-length sequence of bits. It is
3+
intentionally very (overly?) simplistic just to show the usage of the NEAT library. However,
4+
if you come up with a more interesting or impressive example, please submit a pull request!
5+
6+
This example also demonstrates the use of a custom activation function.
7+
"""
8+
9+
from __future__ import print_function
10+
11+
import math
12+
import os
13+
import random
14+
15+
import neat
16+
import visualize
17+
18+
# N is the length of the test sequence.
19+
N = 3
20+
# num_tests is the number of random examples each network is tested against.
21+
num_tests = 2 ** N
22+
23+
24+
def eval_genomes(genomes, config):
25+
for genome_id, genome in genomes:
26+
net = neat.nn.RecurrentNetwork.create(genome, config)
27+
28+
error = 0.0
29+
for _ in range(num_tests):
30+
# Create a random sequence, and feed it to the network with the
31+
# second input set to zero.
32+
seq = [random.choice((0.0, 1.0)) for _ in range(N)]
33+
net.reset()
34+
for s in seq:
35+
inputs = [s, 0.0]
36+
net.activate(inputs)
37+
38+
# Set the second input to one, and get the network output.
39+
for s in seq:
40+
inputs = [0.0, 1.0]
41+
output = net.activate(inputs)
42+
43+
error += (output[0] - s) ** 2
44+
45+
genome.fitness = 1.0 - 4.0 * (error / (N * num_tests))
46+
47+
48+
def run():
49+
# Determine path to configuration file.
50+
local_dir = os.path.dirname(__file__)
51+
config_path = os.path.join(local_dir, 'config')
52+
config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction, neat.DefaultStagnation, config_path)
53+
54+
# Demonstration of saving a configuration back to a text file.
55+
config.save('test_save_config.txt')
56+
57+
# Demonstration of how to add your own custom activation function.
58+
# This sinc function will be available if my_sinc_function is included in the
59+
# config file activation_options option under the DefaultGenome section.
60+
# Note that sinc is not necessarily useful for this example, it was chosen
61+
# arbitrarily just to demonstrate adding a custom activation function.
62+
def sinc(x):
63+
return 1.0 if x == 0 else math.sin(x) / x
64+
65+
config.genome_config.add_activation('my_sinc_function', sinc)
66+
67+
pop = neat.Population(config)
68+
stats = neat.StatisticsReporter()
69+
pop.add_reporter(stats)
70+
pop.add_reporter(neat.StdOutReporter())
71+
72+
winner = pop.run(eval_genomes, 1000)
73+
74+
# Log statistics.
75+
stats.save()
76+
77+
# Show output of the most fit genome against a random input.
78+
print('\nBest genome:\n{!s}'.format(winner))
79+
print('\nOutput:')
80+
winner_net = neat.nn.RecurrentNetwork.create(winner, config)
81+
for n in range(num_tests):
82+
print('\nRun {0} output:'.format(n))
83+
seq = [random.choice((0.0, 1.0)) for _ in range(N)]
84+
winner_net.reset()
85+
for s in seq:
86+
inputs = [s, 0.0]
87+
winner_net.activate(inputs)
88+
print('\tseq {0}'.format(inputs))
89+
90+
correct = True
91+
for s in seq:
92+
output = winner_net.activate([0, 1])
93+
print("\texpected {0:1.5f} got {1:1.5f}".format(s, output[0]))
94+
correct = correct and round(output[0]) == s
95+
print("OK" if correct else "FAIL")
96+
97+
node_names = {-1: 'I', -2: 'T', 0: 'rI'}
98+
visualize.draw_net(config, winner, True, node_names=node_names)
99+
visualize.plot_stats(stats, ylog=False, view=True)
100+
101+
102+
if __name__ == '__main__':
103+
run()

0 commit comments

Comments
 (0)