You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add configurable options to match Stanley & Miikkulainen (2002) while
preserving backward compatibility through defaults that match existing
behavior. Changes include:
- Fix 75% disable rule to replace (not layer on) inherited enabled value
- Match connection genes by innovation number in distance function
- Add separate excess coefficient, node gene flag, enable penalty config
- Add canonical fitness sharing, proportional spawn, interspecies crossover
- Add dynamic compatibility threshold adjustment
- Prune dangling nodes after deletion mutations
- Document complete distance formula and all new parameters
All 552 tests pass (28 new). XOR smoke test verified.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+15Lines changed: 15 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,6 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
## [Unreleased]
10
10
11
11
### Added
12
+
-**Canonical fitness sharing option** via `fitness_sharing = canonical` in `[DefaultReproduction]` config section. Default `normalized` preserves existing behavior. (NEAT paper compliance)
13
+
-**Interspecies crossover** via `interspecies_crossover_prob` parameter in `[DefaultReproduction]`. Default 0.0 (disabled). (NEAT paper compliance)
14
+
-**Dynamic compatibility threshold** via `target_num_species` in `[DefaultSpeciesSet]`. Default `none` (static threshold). Related parameters: `threshold_adjust_rate`, `threshold_min`, `threshold_max`. (NEAT paper compliance)
15
+
-**Separate excess gene coefficient** via `compatibility_excess_coefficient` in `[DefaultGenome]`. Defaults to `compatibility_disjoint_coefficient` (set to `auto`). (NEAT paper compliance)
16
+
-**Configurable node gene distance** via `compatibility_include_node_genes` in `[DefaultGenome]`. Default True (current behavior). Set to False for canonical NEAT distance formula. (NEAT paper compliance)
17
+
-**Configurable enabled-state penalty** via `compatibility_enable_penalty` in `[DefaultGenome]`. Default 1.0 (current behavior). Set to 0.0 for canonical NEAT distance formula. (NEAT paper compliance)
18
+
-**Canonical spawn allocation** via `spawn_method = proportional` in `[DefaultReproduction]`. Default `smoothed` (current behavior). (NEAT paper compliance)
19
+
20
+
### Changed
21
+
-**Distance function now matches genes by innovation number**, consistent with crossover behavior. Previously used tuple keys (endpoint pairs). This affects speciation when the same connection endpoints receive different innovation numbers in different generations (uncommon but possible). (NEAT paper compliance)
22
+
-**Dangling nodes are now pruned** after `mutate_delete_node` and `mutate_delete_connection`. Hidden nodes that become disconnected from all outputs are automatically removed along with their connections. This reduces structural bloat in long evolution runs.
23
+
24
+
### Fixed
25
+
-**75% disable rule** now matches the NEAT paper specification (Stanley & Miikkulainen, 2002, p. 111). Previously, the rule was applied after random attribute inheritance, producing an effective ~87.5% disable rate. Now correctly produces 75%. This may affect evolution dynamics in existing configurations.
26
+
12
27
-**GPU-accelerated evaluation** for CTRNN and Izhikevich spiking networks via optional CuPy dependency
13
28
-`GPUCTRNNEvaluator` and `GPUIZNNEvaluator` in `neat.gpu.evaluator`
14
29
- Batch-evaluates entire populations on GPU using padded tensor operations
Copy file name to clipboardExpand all lines: docs/academic_research.rst
+76-56Lines changed: 76 additions & 56 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,7 +31,7 @@ neat-python correctly implements the most complex and critical aspects of the NE
31
31
* **Structural mutations** (add-node and add-connection) following standard NEAT practice
32
32
* **75% disable rule** during crossover (with implementation note below)
33
33
34
-
However, neat-python contains several implementation choices that differ from the original 2002 NEAT paper. These design decisions that create a NEAT variant with different evolutionary dynamics. Researchers must understand these differences when publishing results.
34
+
However, neat-python contains several implementation choices that differ from the original 2002 NEAT paper. Many of these differences are now configurable, allowing researchers to choose between neat-python's default behavior and canonical NEAT behavior.
35
35
36
36
Key Implementation Differences
37
37
-------------------------------
@@ -41,7 +41,7 @@ High-Impact Differences
41
41
42
42
**1. Fitness Sharing Mechanism**
43
43
44
-
The most significant deviation affects selection pressure and speciation dynamics.
44
+
The default behavior uses normalized fitness sharing, which differs from canonical NEAT.
45
45
46
46
*Canonical NEAT:*
47
47
@@ -50,41 +50,60 @@ The most significant deviation affects selection pressure and speciation dynamic
50
50
adjusted_fitness = raw_fitness / species_size
51
51
# offspring_allocation proportional to sum(adjusted_fitness per species)
52
52
53
-
*neat-python implementation:*
53
+
*neat-python default (normalized):*
54
54
55
55
.. code-block:: python
56
56
57
-
# From neat/reproduction.py
58
-
# Species fitness is normalized to [0,1] based on population min/max
59
-
af = (msf - min_fitness) / fitness_range
57
+
af = (msf - min_fitness) / fitness_range # Normalized to [0, 1]
60
58
61
-
**Impact:** This creates rank-like selection pressure that scales with fitness variance. In low-variance populations, near-optimal species can be effectively eliminated. Species with many members are not penalized as in canonical NEAT, fundamentally altering the "protection of innovation" mechanism central to NEAT's design.
59
+
**Impact:** Normalized sharing creates rank-like selection pressure. In low-variance populations, near-optimal species can be effectively eliminated.
62
60
63
-
**Example:** If Species A has fitness 100 and Species B has fitness 101, canonical NEAT allocates offspring nearly equally (~50/50), while neat-python gives Species B 100% of offspring (minus minimums), reducing Species A to minimum size.
61
+
**Configurable:** Set ``fitness_sharing = canonical`` in ``[DefaultReproduction]`` for paper-faithful behavior. Default ``normalized`` preserves existing behavior.
64
62
65
63
**2. Genomic Distance Metric**
66
64
67
-
The distance metric used for speciation differs in three ways from the canonical formula δ = c₁·E/N + c₂·D/N + c₃·W̄:
65
+
The distance metric used for speciation is now configurable to match the canonical formula δ = c₁·E/N + c₂·D/N + c₃·W̄.
68
66
69
-
1. Node genes are included in the distance calculation (normalized by max node count)
70
-
2. Enabled/disabled state mismatch adds a +1 penalty per connection
71
-
3. Per-connection distance combines weight differences with enabled state and disjoint penalties
67
+
Connection genes are matched by **innovation number** (consistent with crossover). The full distance formula is:
**Impact:** Speciation boundaries differ from canonical NEAT, structural mutations may be penalized twice, and cluster dynamics differ even with identical hyperparameters. Direct comparison to published NEAT results becomes problematic.
The 75% disable rule is applied AFTER random attribute inheritance rather than INSTEAD of it, resulting in an effective disable rate of approximately 87.5% when one parent has the gene disabled:
75
+
.. math::
80
76
81
-
.. code-block:: python
77
+
\delta_{\text{connections}} = \frac{c_2\cdot D + c_1\cdot E + \sum_{i \in M} d_i}{N_c}
78
+
79
+
Where:
80
+
81
+
- *D* = number of disjoint connection genes (innovation within the other genome's range)
82
+
- *E* = number of excess connection genes (innovation beyond the other genome's range)
83
+
- *M* = set of homologous (matching) connection gene pairs
84
+
- *d_i* = (|w₁ - w₂| + p · [e₁ ≠ e₂]) · c₃ for each matching pair, where *p* is ``compatibility_enable_penalty`` (default 1.0)
85
+
- *N_c* = max number of connection genes in either genome
86
+
- *c₁* = ``compatibility_excess_coefficient`` (defaults to ``compatibility_disjoint_coefficient`` if set to ``auto``)
87
+
- *c₂* = ``compatibility_disjoint_coefficient``
88
+
- *c₃* = ``compatibility_weight_coefficient``
89
+
90
+
**Node gene distance** (when ``compatibility_include_node_genes`` is True, the default):
# Random inheritance: 50% chance of inheriting disabled state
85
-
# Then 75% chance of disabling: 0.5 + (0.5 × 0.75) = 0.875
96
+
Where node distance *d_i^(n)* includes bias difference, response difference, time_constant difference, and +1 penalties for activation and aggregation function mismatches, all scaled by c₃.
86
97
87
-
**Impact:** This reduces re-enabling of previously disabled connections compared to canonical NEAT and may affect network growth patterns and structural evolution.
98
+
**To approximate canonical NEAT distance:**
99
+
100
+
- Set ``compatibility_include_node_genes = False``
101
+
- Set ``compatibility_enable_penalty = 0.0``
102
+
- Set ``compatibility_excess_coefficient`` explicitly if c₁ ≠ c₂
103
+
104
+
**3. Disable Rule Implementation**
105
+
106
+
The 75% disable rule now correctly matches the paper: when either parent has a gene disabled, the offspring's enabled state is determined by a fresh 75/25 coin flip (75% disabled, 25% enabled), replacing whatever value was randomly inherited.
88
107
89
108
Medium-Impact Differences
90
109
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -104,42 +123,43 @@ The original NEAT paper assumed fixed sigmoid activation and summation. This sig
104
123
105
124
neat-python uses "best-fit" clustering (place genome in species with closest representative) rather than the original "first-fit" approach (place in first compatible species). This is generally considered an improvement for stability and is less order-dependent.
106
125
107
-
**6. Static Compatibility Threshold**
126
+
**6. Dynamic Compatibility Threshold**
108
127
109
-
The compatibility threshold for speciation is fixed rather than dynamically adjusted to maintain target species counts. This can cause species counts to drift over time.
128
+
By default the compatibility threshold for speciation is fixed. Set ``target_num_species`` in ``[DefaultSpeciesSet]`` to enable dynamic adjustment toward a target species count (as described in the paper). Configure ``threshold_adjust_rate``, ``threshold_min``, and ``threshold_max`` to control the adjustment behavior.
110
129
111
130
**7. Initial Connectivity**
112
131
113
132
Multiple initialization topologies are available (``unconnected``, ``fs_neat_nohidden``, ``fs_neat``, ``full``, ``partial``, etc.). Canonical NEAT starts with minimal structure. Initial topology significantly affects convergence and solution quality.
114
133
115
134
**8. Spawn Allocation Smoothing**
116
135
117
-
Offspring counts are smoothed relative to previous species sizes with exact matching routines enforcing per-species minimums. This improves stability in practice but deviates from the paper.
136
+
By default, offspring counts are smoothed relative to previous species sizes. Set ``spawn_method = proportional`` in ``[DefaultReproduction]`` for direct proportional allocation (canonical NEAT). Default ``smoothed`` preserves existing behavior.
118
137
119
138
Recommendations for Academic Use
120
139
---------------------------------
121
140
122
141
For Strict NEAT Replication Studies
123
142
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
124
143
125
-
If your research requires exact canonical NEAT behavior for comparison with other implementations or replication of published results:
144
+
If your research requires exact canonical NEAT behavior for comparison with other implementations or replication of published results, all major differences are now configurable:
126
145
127
-
**1. Modify fitness sharing in reproduction logic**
146
+
**1. Use canonical fitness sharing and spawn allocation**
128
147
129
-
The ``neat/reproduction.py`` module (lines 176-225) needs modification to implement canonical fitness sharing without normalization.
148
+
Set ``fitness_sharing = canonical`` and ``spawn_method = proportional`` in ``[DefaultReproduction]``.
130
149
131
-
**2. Modify the distance metric**
150
+
**2. Configure the distance metric for canonical NEAT**
132
151
133
-
Adjust ``neat/genome.py`` to match the canonical formula:
152
+
Set ``compatibility_include_node_genes = False`` and ``compatibility_enable_penalty = 0.0`` in ``[DefaultGenome]``. Optionally set ``compatibility_excess_coefficient`` if c₁ ≠ c₂.
134
153
135
-
* Remove node gene contributions
136
-
* Remove enabled/disabled penalty
137
-
* Compute W̄ as pure average weight difference on matching connections
138
-
* Expose c₁, c₂, c₃ as separate configuration parameters
154
+
**3. Enable dynamic compatibility threshold**
139
155
140
-
**3. Configure for canonical search space**
156
+
Set ``target_num_species`` in ``[DefaultSpeciesSet]`` to maintain a target species count.
141
157
142
-
Lock configuration to canonical NEAT parameters:
158
+
**4. Enable interspecies crossover**
159
+
160
+
Set ``interspecies_crossover_prob`` to a small value (e.g., 0.001) in ``[DefaultReproduction]``.
161
+
162
+
**5. Lock activation and aggregation to canonical defaults**
143
163
144
164
.. code-block:: ini
145
165
@@ -152,13 +172,7 @@ Lock configuration to canonical NEAT parameters:
For species count stabilization, add threshold adjustment mechanisms.
158
-
159
-
**5. Run sensitivity analysis**
160
-
161
-
Evaluate the impact of the disable rate difference (75% vs 87.5%).
175
+
See the "Configuration for Canonical Behavior" section below for a complete template.
162
176
163
177
For General NEAT-Variant Research
164
178
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -209,7 +223,7 @@ Provide complete, runnable code including the specific neat-python version and a
209
223
Configuration for Canonical Behavior
210
224
-------------------------------------
211
225
212
-
If you need behavior closer to canonical NEAT without code modification, use this configuration template as a starting point:
226
+
neat-python now supports canonical NEAT behavior through configuration alone. Use this template as a starting point:
213
227
214
228
.. code-block:: ini
215
229
@@ -224,21 +238,25 @@ If you need behavior closer to canonical NEAT without code modification, use thi
224
238
activation_default = sigmoid
225
239
activation_mutate_rate = 0.0
226
240
activation_options = sigmoid
227
-
241
+
228
242
aggregation_default = sum
229
243
aggregation_mutate_rate = 0.0
230
244
aggregation_options = sum
231
-
245
+
232
246
# Disable response evolution
233
247
response_mutate_rate = 0.0
234
248
response_replace_rate = 0.0
235
-
249
+
236
250
# Minimal initial connectivity
237
251
initial_connection = unconnected
238
-
252
+
239
253
# Standard NEAT topology
240
254
feed_forward = True
241
-
255
+
256
+
# Canonical distance metric: no node genes, no enable penalty
257
+
compatibility_include_node_genes = False
258
+
compatibility_enable_penalty = 0.0
259
+
242
260
# Configure other mutation rates as needed for your problem
243
261
bias_mutate_rate = 0.7
244
262
bias_replace_rate = 0.1
@@ -251,17 +269,19 @@ If you need behavior closer to canonical NEAT without code modification, use thi
251
269
252
270
[DefaultSpeciesSet]
253
271
compatibility_threshold = 3.0
272
+
target_num_species = 10
254
273
255
274
[DefaultStagnation]
256
275
species_fitness_func = max
257
276
max_stagnation = 20
258
277
species_elitism = 2
259
278
260
279
[DefaultReproduction]
261
-
elitism = 2
262
-
survival_threshold = 0.2
263
-
264
-
Note that even with this configuration, fitness sharing and distance metric differences remain. Complete canonical behavior requires code modification.
280
+
elitism = 2
281
+
survival_threshold = 0.2
282
+
fitness_sharing = canonical
283
+
spawn_method = proportional
284
+
interspecies_crossover_prob = 0.001
265
285
266
286
Working with Deterministic Evolution
267
287
-------------------------------------
@@ -380,13 +400,13 @@ See :doc:`customization` for guidance on extending the library.
380
400
Summary
381
401
-------
382
402
383
-
neat-python is a robust, well-maintained NEAT implementation suitable for serious academic research when used with awareness of its characteristics. The library correctly implements the most critical algorithmic components (innovation tracking, crossover, structural mutations) but differs from canonical NEAT in fitness sharing and distance calculation.
403
+
neat-python is a robust, well-maintained NEAT implementation suitable for serious academic research. The library correctly implements the critical algorithmic components (innovation tracking, crossover, structural mutations) and now offers configuration options for canonical NEAT behavior.
384
404
385
-
**For canonical NEAT replication:** Code modifications and careful configuration are necessary.
405
+
**For canonical NEAT replication:** All major differences from the paper are configurable without code modification. See the configuration template above.
386
406
387
-
**For NEAT-variant research:** The library is excellent as-is, provided implementation choices are documented in publications.
407
+
**For NEAT-variant research:** The library is excellent with default settings, provided implementation choices are documented in publications.
388
408
389
-
**For all research:** Understanding these differences enables informed use and appropriate interpretation of results. The pure Python implementation and comprehensive test suite make verification and modification straightforward—a significant advantage for academic research.
409
+
**For all research:** The pure Python implementation and comprehensive test suite make verification and modification straightforward—a significant advantage for academic research.
0 commit comments