@reicek/neataptic-ts 0.1.25 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/.github/copilot-instructions.md +11 -0
  2. package/.github/skills/trace-analyzer-extension/SKILL.md +3 -3
  3. package/.github/skills/trace-analyzer-extension/assets/extension-checklist.md +1 -1
  4. package/.github/skills/trace-analyzer-extension/references/analyzer-extension-workflow.md +1 -1
  5. package/.github/skills/trace-audit-reporting/SKILL.md +3 -3
  6. package/.github/skills/trace-audit-reporting/references/trace-analysis-workflow.md +1 -1
  7. package/package.json +19 -13
  8. package/plans/Flappy_Bird_Folder_Documentation_Pass.md +4 -4
  9. package/plans/README.md +24 -0
  10. package/plans/Roadmap.md +62 -40
  11. package/plans/analyze-trace-solid-split.plans.md +66 -0
  12. package/plans/architecture-solid-split.plans.md +9 -15
  13. package/plans/asciiMaze-typescript-repair.plans.md +1 -1
  14. package/plans/generate-docs-solid-split.plans.md +87 -0
  15. package/plans/methods-docs.plans.md +25 -1
  16. package/plans/methods-solid-split.plans.md +14 -14
  17. package/plans/neat-docs.plans.md +9 -1
  18. package/plans/neat-test-surface-repair.plans.md +1 -1
  19. package/plans/render-docs-html-solid-split.plans.md +68 -0
  20. package/plans/src-no-explicit-any-cleanup.plans.md +1 -1
  21. package/plans/utils-docs.plans.md +6 -1
  22. package/scripts/analyze-trace/analyze-trace.analysis.ts +479 -0
  23. package/scripts/analyze-trace/analyze-trace.constants.ts +35 -0
  24. package/scripts/analyze-trace/analyze-trace.io.ts +69 -0
  25. package/scripts/analyze-trace/analyze-trace.report.ts +100 -0
  26. package/scripts/analyze-trace/analyze-trace.shared.ts +116 -0
  27. package/scripts/analyze-trace/analyze-trace.ts +45 -0
  28. package/scripts/analyze-trace/analyze-trace.types.ts +72 -0
  29. package/scripts/assets/theme.css +80 -23
  30. package/scripts/copy-examples.ts +239 -0
  31. package/scripts/export-onnx.ts +223 -0
  32. package/scripts/generate-bench-tables.ts +378 -37
  33. package/scripts/generate-docs/generate-docs.constants.ts +107 -0
  34. package/scripts/generate-docs/generate-docs.order.ts +355 -0
  35. package/scripts/generate-docs/generate-docs.state.ts +31 -0
  36. package/scripts/generate-docs/generate-docs.targets.ts +165 -0
  37. package/scripts/generate-docs/generate-docs.ts +63 -0
  38. package/scripts/generate-docs/generate-docs.types.ts +112 -0
  39. package/scripts/generate-docs/output/generate-docs.output.folder-index.utils.ts +167 -0
  40. package/scripts/generate-docs/output/generate-docs.output.ordering.utils.ts +353 -0
  41. package/scripts/generate-docs/output/generate-docs.output.readme.utils.ts +420 -0
  42. package/scripts/generate-docs/output/generate-docs.output.ts +123 -0
  43. package/scripts/generate-docs/output/generate-docs.output.warnings.utils.ts +219 -0
  44. package/scripts/generate-docs/symbols/generate-docs.symbols.collection.utils.ts +365 -0
  45. package/scripts/generate-docs/symbols/generate-docs.symbols.jsdoc.utils.ts +373 -0
  46. package/scripts/generate-docs/symbols/generate-docs.symbols.normalize.utils.ts +155 -0
  47. package/scripts/generate-docs/symbols/generate-docs.symbols.render.utils.ts +149 -0
  48. package/scripts/generate-docs/symbols/generate-docs.symbols.signature.utils.ts +289 -0
  49. package/scripts/generate-docs/symbols/generate-docs.symbols.ts +11 -0
  50. package/scripts/mermaid-cli.mjs +102 -22
  51. package/scripts/mermaid-cli.ts +736 -0
  52. package/scripts/render-docs-html/render-docs-html.assets.ts +54 -0
  53. package/scripts/render-docs-html/render-docs-html.mermaid.ts +245 -0
  54. package/scripts/{render-docs-html.sidebar.ts → render-docs-html/render-docs-html.navigation.ts} +141 -144
  55. package/scripts/render-docs-html/render-docs-html.pages.ts +333 -0
  56. package/scripts/render-docs-html/render-docs-html.shared.ts +333 -0
  57. package/scripts/render-docs-html/render-docs-html.types.ts +42 -0
  58. package/scripts/render-docs-html.ts +23 -587
  59. package/scripts/run-docs.ts +238 -0
  60. package/scripts/write-dist-docs-pkg.ts +40 -0
  61. package/src/README.md +75 -75
  62. package/src/architecture/connection/README.md +5 -5
  63. package/src/architecture/layer/README.md +508 -508
  64. package/src/architecture/network/README.md +1458 -1458
  65. package/src/architecture/network/activate/README.md +694 -694
  66. package/src/architecture/network/bootstrap/README.md +77 -77
  67. package/src/architecture/network/connect/README.md +74 -74
  68. package/src/architecture/network/deterministic/README.md +135 -135
  69. package/src/architecture/network/evolve/README.md +364 -364
  70. package/src/architecture/network/gating/README.md +130 -130
  71. package/src/architecture/network/genetic/README.md +399 -399
  72. package/src/architecture/network/mutate/README.md +897 -897
  73. package/src/architecture/network/onnx/README.md +720 -720
  74. package/src/architecture/network/onnx/export/README.md +728 -728
  75. package/src/architecture/network/onnx/export/layers/README.md +450 -450
  76. package/src/architecture/network/onnx/import/README.md +618 -618
  77. package/src/architecture/network/onnx/schema/README.md +32 -32
  78. package/src/architecture/network/prune/README.md +245 -245
  79. package/src/architecture/network/remove/README.md +135 -135
  80. package/src/architecture/network/runtime/README.md +106 -106
  81. package/src/architecture/network/serialize/README.md +542 -542
  82. package/src/architecture/network/slab/README.md +608 -608
  83. package/src/architecture/network/standalone/README.md +212 -212
  84. package/src/architecture/network/stats/README.md +84 -84
  85. package/src/architecture/network/topology/README.md +465 -465
  86. package/src/architecture/network/training/README.md +200 -200
  87. package/src/architecture/node/README.md +5 -5
  88. package/src/architecture/nodePool/README.md +14 -14
  89. package/src/methods/README.md +99 -99
  90. package/src/methods/activation/README.md +189 -189
  91. package/src/methods/cost/README.md +131 -131
  92. package/src/methods/rate/README.md +86 -86
  93. package/src/multithreading/README.md +77 -77
  94. package/src/multithreading/workers/browser/README.md +8 -8
  95. package/src/multithreading/workers/node/README.md +8 -8
  96. package/src/neat/README.md +148 -148
  97. package/src/neat/adaptive/README.md +120 -120
  98. package/src/neat/adaptive/acceptance/README.md +40 -40
  99. package/src/neat/adaptive/complexity/README.md +137 -137
  100. package/src/neat/adaptive/core/README.md +197 -197
  101. package/src/neat/adaptive/lineage/README.md +90 -90
  102. package/src/neat/adaptive/mutation/README.md +284 -284
  103. package/src/neat/compat/README.md +43 -43
  104. package/src/neat/compat/core/README.md +90 -90
  105. package/src/neat/diversity/README.md +35 -35
  106. package/src/neat/diversity/core/README.md +88 -88
  107. package/src/neat/evaluate/README.md +85 -85
  108. package/src/neat/evaluate/auto-distance/README.md +75 -75
  109. package/src/neat/evaluate/entropy-compat/README.md +37 -37
  110. package/src/neat/evaluate/entropy-sharing/README.md +43 -43
  111. package/src/neat/evaluate/fitness/README.md +23 -23
  112. package/src/neat/evaluate/novelty/README.md +120 -120
  113. package/src/neat/evaluate/objectives/README.md +17 -17
  114. package/src/neat/evaluate/shared/README.md +94 -94
  115. package/src/neat/evolve/README.md +96 -96
  116. package/src/neat/evolve/adaptive/README.md +60 -60
  117. package/src/neat/evolve/objectives/README.md +63 -63
  118. package/src/neat/evolve/offspring/README.md +56 -56
  119. package/src/neat/evolve/population/README.md +171 -171
  120. package/src/neat/evolve/runtime/README.md +79 -79
  121. package/src/neat/evolve/speciation/README.md +74 -74
  122. package/src/neat/evolve/warnings/README.md +10 -10
  123. package/src/neat/export/README.md +114 -114
  124. package/src/neat/helpers/README.md +50 -50
  125. package/src/neat/init/README.md +9 -9
  126. package/src/neat/lineage/core/README.md +101 -101
  127. package/src/neat/multiobjective/category/README.md +74 -74
  128. package/src/neat/multiobjective/crowding/README.md +272 -272
  129. package/src/neat/multiobjective/dominance/README.md +171 -171
  130. package/src/neat/multiobjective/fronts/README.md +68 -68
  131. package/src/neat/multiobjective/metrics/README.md +43 -43
  132. package/src/neat/multiobjective/objectives/README.md +31 -31
  133. package/src/neat/multiobjective/shared/README.md +27 -27
  134. package/src/neat/mutation/README.md +97 -97
  135. package/src/neat/mutation/add-conn/README.md +115 -115
  136. package/src/neat/mutation/add-node/README.md +126 -126
  137. package/src/neat/mutation/flow/README.md +149 -149
  138. package/src/neat/mutation/repair/README.md +185 -185
  139. package/src/neat/mutation/select/README.md +117 -117
  140. package/src/neat/mutation/shared/README.md +32 -32
  141. package/src/neat/objectives/README.md +25 -25
  142. package/src/neat/objectives/core/README.md +67 -67
  143. package/src/neat/pruning/README.md +40 -40
  144. package/src/neat/pruning/core/README.md +171 -171
  145. package/src/neat/pruning/facade/README.md +32 -32
  146. package/src/neat/rng/README.md +104 -104
  147. package/src/neat/rng/core/README.md +137 -137
  148. package/src/neat/rng/facade/README.md +50 -50
  149. package/src/neat/selection/README.md +111 -111
  150. package/src/neat/selection/core/README.md +227 -227
  151. package/src/neat/selection/facade/README.md +61 -61
  152. package/src/neat/shared/README.md +163 -163
  153. package/src/neat/speciation/README.md +31 -31
  154. package/src/neat/speciation/threshold/README.md +35 -35
  155. package/src/neat/species/README.md +25 -25
  156. package/src/neat/species/core/README.md +20 -20
  157. package/src/neat/species/core/shared/README.md +18 -18
  158. package/src/neat/species/history/context/README.md +22 -22
  159. package/src/neat/telemetry/accessors/README.md +58 -58
  160. package/src/neat/telemetry/exports/README.md +233 -233
  161. package/src/neat/telemetry/facade/README.md +252 -252
  162. package/src/neat/telemetry/facade/archive/README.md +57 -57
  163. package/src/neat/telemetry/facade/buffer/README.md +43 -43
  164. package/src/neat/telemetry/facade/lineage/README.md +12 -12
  165. package/src/neat/telemetry/facade/objectives/README.md +44 -44
  166. package/src/neat/telemetry/facade/runtime/README.md +26 -26
  167. package/src/neat/telemetry/facade/species/README.md +27 -27
  168. package/src/neat/telemetry/metrics/README.md +696 -696
  169. package/src/neat/telemetry/recorder/README.md +57 -57
  170. package/src/neat/telemetry/types/README.md +32 -32
  171. package/src/neat/topology-intent/README.md +75 -75
  172. package/src/utils/README.md +193 -193
  173. package/test/examples/asciiMaze/browser-entry/README.md +92 -92
  174. package/test/examples/asciiMaze/dashboardManager/README.md +109 -109
  175. package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +28 -28
  176. package/test/examples/asciiMaze/evolutionEngine/README.md +1527 -1527
  177. package/test/examples/asciiMaze/mazeMovement/README.md +105 -105
  178. package/test/examples/asciiMaze/mazeMovement/finalization/README.md +16 -16
  179. package/test/examples/asciiMaze/mazeMovement/policy/README.md +57 -57
  180. package/test/examples/asciiMaze/mazeMovement/runtime/README.md +52 -52
  181. package/test/examples/asciiMaze/mazeMovement/shaping/README.md +46 -46
  182. package/test/examples/flappy_bird/browser-entry/README.md +508 -508
  183. package/test/examples/flappy_bird/browser-entry/host/README.md +101 -101
  184. package/test/examples/flappy_bird/browser-entry/host/resize/README.md +144 -144
  185. package/test/examples/flappy_bird/browser-entry/network-view/README.md +194 -194
  186. package/test/examples/flappy_bird/browser-entry/playback/README.md +278 -278
  187. package/test/examples/flappy_bird/browser-entry/playback/background/README.md +129 -129
  188. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +502 -502
  189. package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +139 -139
  190. package/test/examples/flappy_bird/browser-entry/playback/snapshot/README.md +10 -10
  191. package/test/examples/flappy_bird/browser-entry/playback/trail/README.md +43 -43
  192. package/test/examples/flappy_bird/browser-entry/playback/worker-channel/README.md +30 -30
  193. package/test/examples/flappy_bird/browser-entry/runtime/README.md +59 -59
  194. package/test/examples/flappy_bird/browser-entry/visualization/README.md +276 -276
  195. package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +16 -16
  196. package/test/examples/flappy_bird/constants/README.md +1070 -1070
  197. package/test/examples/flappy_bird/environment/README.md +22 -22
  198. package/test/examples/flappy_bird/evaluation/README.md +32 -32
  199. package/test/examples/flappy_bird/evaluation/rollout/README.md +141 -141
  200. package/test/examples/flappy_bird/flappy-evolution-worker/README.md +425 -425
  201. package/test/examples/flappy_bird/simulation-shared/README.md +170 -170
  202. package/test/examples/flappy_bird/simulation-shared/observation/README.md +109 -109
  203. package/test/examples/flappy_bird/trainer/README.md +325 -325
  204. package/test/examples/flappy_bird/trainer/evaluation/README.md +74 -74
  205. package/scripts/analyze-trace.ts +0 -590
  206. package/scripts/copy-examples.mjs +0 -114
  207. package/scripts/export-onnx.mjs +0 -86
  208. package/scripts/generate-bench-tables.mjs +0 -182
  209. package/scripts/generate-docs.ts +0 -2900
  210. package/scripts/write-dist-docs-pkg.mjs +0 -16
@@ -38,69 +38,71 @@ flowchart TD
38
38
 
39
39
  ## neat/mutation/add-conn/mutation.add-conn.ts
40
40
 
41
- ### collectCandidatePairsForConn
41
+ ### assignInnovationForConnection
42
42
 
43
43
  ```ts
44
- collectCandidatePairsForConn(
45
- genomeToInspect: GenomeWithMetadata,
46
- ): [NodeWithMetadata, NodeWithMetadata][]
44
+ assignInnovationForConnection(
45
+ connection: ConnectionWithMetadata,
46
+ pairNodes: { symmetricKey: string; legacyForwardKey: string; legacyReverseKey: string; },
47
+ internal: NeatControllerForMutation,
48
+ ): void
47
49
  ```
48
50
 
49
- Collect legal (from,to) node pairs not already connected.
51
+ Assign an innovation id for a new connection, reusing when possible.
50
52
 
51
- This helper defines the search space for connection growth. It respects the
52
- node ordering conventions used by the genome representation so mutation does
53
- not propose obviously invalid source-target directions before later cycle
54
- checks even run.
53
+ Innovation assignment is the historical memory for connection growth. If the
54
+ unordered node pair has been seen before, this helper reuses that innovation
55
+ id. Otherwise it allocates a new global id and stores it under both the
56
+ symmetric key and the legacy directional aliases.
55
57
 
56
58
  Parameters:
57
- - `genomeToInspect` - - genome to scan
59
+ - `connection` - - newly created connection
60
+ - `pairNodes` - - resolved pair metadata
61
+ - `internal` - - neat controller context
58
62
 
59
- Returns: candidate node pairs
63
+ Returns: void
60
64
 
61
- ### filterPairsWithInnovations
65
+ ### buildLegacyKeyForConn
62
66
 
63
67
  ```ts
64
- filterPairsWithInnovations(
65
- pairs: [NodeWithMetadata, NodeWithMetadata][],
66
- internal: NeatControllerForMutation,
67
- ): [NodeWithMetadata, NodeWithMetadata][]
68
+ buildLegacyKeyForConn(
69
+ sourceNode: NodeWithMetadata,
70
+ targetNode: NodeWithMetadata,
71
+ ): string
68
72
  ```
69
73
 
70
- Filter candidate pairs that already have innovation reuse keys.
74
+ Build a legacy directional innovation key.
71
75
 
72
- Reuse candidates are especially valuable because they let independently
73
- discovered structure share the same innovation identity. This helper pulls out
74
- those historically known pairs so the selection path can favor them when such pairs
75
- exist.
76
+ Legacy directional keys are still stored so older code paths or preserved
77
+ historical records can resolve to the same innovation id as the modern
78
+ symmetric key.
76
79
 
77
80
  Parameters:
78
- - `pairs` - - candidate node pairs
79
- - `internal` - - neat controller context
81
+ - `sourceNode` - - source node
82
+ - `targetNode` - - target node
80
83
 
81
- Returns: reuse candidates
84
+ Returns: directional innovation key
82
85
 
83
- ### selectPairPool
86
+ ### buildSymmetricKeyForConn
84
87
 
85
88
  ```ts
86
- selectPairPool(
87
- allPairs: [NodeWithMetadata, NodeWithMetadata][],
88
- reusePairs: [NodeWithMetadata, NodeWithMetadata][],
89
- ): [NodeWithMetadata, NodeWithMetadata][]
89
+ buildSymmetricKeyForConn(
90
+ sourceNode: NodeWithMetadata,
91
+ targetNode: NodeWithMetadata,
92
+ ): string
90
93
  ```
91
94
 
92
- Build the final selection pool based on reuse and hidden-node preference.
95
+ Build a symmetric innovation key for an unordered node pair.
93
96
 
94
- Pool selection is opinionated but still simple: prefer pairs with known
95
- innovation history, otherwise prefer hidden-to-hidden growth, otherwise fall
96
- back to the full candidate set. That keeps the chapter's structural bias
97
- readable in one place.
97
+ The symmetric key is the preferred reuse identity because connection growth
98
+ is treated as one structural relationship between two genes, not as a
99
+ direction-specific novelty record.
98
100
 
99
101
  Parameters:
100
- - `allPairs` - - all candidate pairs
101
- - `reusePairs` - - pairs with historical innovations
102
+ - `sourceNode` - - source node
103
+ - `targetNode` - - target node
102
104
 
103
- Returns: selection pool
105
+ Returns: symmetric innovation key
104
106
 
105
107
  ### choosePairForConn
106
108
 
@@ -122,45 +124,25 @@ Parameters:
122
124
 
123
125
  Returns: chosen pair or null
124
126
 
125
- ### resolvePairNodes
126
-
127
- ```ts
128
- resolvePairNodes(
129
- chosenPair: [NodeWithMetadata, NodeWithMetadata],
130
- ): { sourceNode: NodeWithMetadata; targetNode: NodeWithMetadata; symmetricKey: string; legacyForwardKey: string; legacyReverseKey: string; }
131
- ```
132
-
133
- Resolve nodes and innovation key details for a chosen pair.
134
-
135
- Once selection has picked a pair, the mutation path needs more than the raw
136
- nodes. It also needs the symmetric key used for modern innovation reuse and
137
- the directional legacy keys kept for backward-compatible lookups.
138
-
139
- Parameters:
140
- - `chosenPair` - - pair to connect
141
-
142
- Returns: resolved pair metadata
143
-
144
- ### shouldAbortForCycle
127
+ ### collectCandidatePairsForConn
145
128
 
146
129
  ```ts
147
- shouldAbortForCycle(
130
+ collectCandidatePairsForConn(
148
131
  genomeToInspect: GenomeWithMetadata,
149
- pairNodes: { sourceNode: NodeWithMetadata; targetNode: NodeWithMetadata; },
150
- ): boolean
132
+ ): [NodeWithMetadata, NodeWithMetadata][]
151
133
  ```
152
134
 
153
- Determine whether adding the connection would create a cycle.
135
+ Collect legal (from,to) node pairs not already connected.
154
136
 
155
- The add-connection path only enforces cycle checks when the genome requests
156
- acyclic topology. That keeps recurrent-capable runs permissive while still
157
- giving feed-forward-style runs one clear abort seam.
137
+ This helper defines the search space for connection growth. It respects the
138
+ node ordering conventions used by the genome representation so mutation does
139
+ not propose obviously invalid source-target directions before later cycle
140
+ checks even run.
158
141
 
159
142
  Parameters:
160
- - `genomeToInspect` - - genome to inspect
161
- - `pairNodes` - - resolved pair nodes
143
+ - `genomeToInspect` - - genome to scan
162
144
 
163
- Returns: true if the connection should be aborted
145
+ Returns: candidate node pairs
164
146
 
165
147
  ### connectChosenPair
166
148
 
@@ -183,89 +165,107 @@ Parameters:
183
165
 
184
166
  Returns: created connection or undefined
185
167
 
186
- ### assignInnovationForConnection
168
+ ### createsCycle
187
169
 
188
170
  ```ts
189
- assignInnovationForConnection(
190
- connection: ConnectionWithMetadata,
191
- pairNodes: { symmetricKey: string; legacyForwardKey: string; legacyReverseKey: string; },
171
+ createsCycle(
172
+ sourceNode: NodeWithMetadata,
173
+ targetNode: NodeWithMetadata,
174
+ ): boolean
175
+ ```
176
+
177
+ Detect whether adding a connection would create a cycle.
178
+
179
+ The cycle check walks forward from the proposed target node and looks for a
180
+ path back to the proposed source. If one exists, adding the new edge would
181
+ close a loop and the caller can abort the structural edit for acyclic runs.
182
+
183
+ Parameters:
184
+ - `sourceNode` - - source node of the new connection
185
+ - `targetNode` - - target node of the new connection
186
+
187
+ Returns: true when a cycle is detected
188
+
189
+ ### filterPairsWithInnovations
190
+
191
+ ```ts
192
+ filterPairsWithInnovations(
193
+ pairs: [NodeWithMetadata, NodeWithMetadata][],
192
194
  internal: NeatControllerForMutation,
193
- ): void
195
+ ): [NodeWithMetadata, NodeWithMetadata][]
194
196
  ```
195
197
 
196
- Assign an innovation id for a new connection, reusing when possible.
198
+ Filter candidate pairs that already have innovation reuse keys.
197
199
 
198
- Innovation assignment is the historical memory for connection growth. If the
199
- unordered node pair has been seen before, this helper reuses that innovation
200
- id. Otherwise it allocates a new global id and stores it under both the
201
- symmetric key and the legacy directional aliases.
200
+ Reuse candidates are especially valuable because they let independently
201
+ discovered structure share the same innovation identity. This helper pulls out
202
+ those historically known pairs so the selection path can favor them when such pairs
203
+ exist.
202
204
 
203
205
  Parameters:
204
- - `connection` - - newly created connection
205
- - `pairNodes` - - resolved pair metadata
206
+ - `pairs` - - candidate node pairs
206
207
  - `internal` - - neat controller context
207
208
 
208
- Returns: void
209
+ Returns: reuse candidates
209
210
 
210
- ### buildSymmetricKeyForConn
211
+ ### resolvePairNodes
211
212
 
212
213
  ```ts
213
- buildSymmetricKeyForConn(
214
- sourceNode: NodeWithMetadata,
215
- targetNode: NodeWithMetadata,
216
- ): string
214
+ resolvePairNodes(
215
+ chosenPair: [NodeWithMetadata, NodeWithMetadata],
216
+ ): { sourceNode: NodeWithMetadata; targetNode: NodeWithMetadata; symmetricKey: string; legacyForwardKey: string; legacyReverseKey: string; }
217
217
  ```
218
218
 
219
- Build a symmetric innovation key for an unordered node pair.
219
+ Resolve nodes and innovation key details for a chosen pair.
220
220
 
221
- The symmetric key is the preferred reuse identity because connection growth
222
- is treated as one structural relationship between two genes, not as a
223
- direction-specific novelty record.
221
+ Once selection has picked a pair, the mutation path needs more than the raw
222
+ nodes. It also needs the symmetric key used for modern innovation reuse and
223
+ the directional legacy keys kept for backward-compatible lookups.
224
224
 
225
225
  Parameters:
226
- - `sourceNode` - - source node
227
- - `targetNode` - - target node
226
+ - `chosenPair` - - pair to connect
228
227
 
229
- Returns: symmetric innovation key
228
+ Returns: resolved pair metadata
230
229
 
231
- ### buildLegacyKeyForConn
230
+ ### selectPairPool
232
231
 
233
232
  ```ts
234
- buildLegacyKeyForConn(
235
- sourceNode: NodeWithMetadata,
236
- targetNode: NodeWithMetadata,
237
- ): string
233
+ selectPairPool(
234
+ allPairs: [NodeWithMetadata, NodeWithMetadata][],
235
+ reusePairs: [NodeWithMetadata, NodeWithMetadata][],
236
+ ): [NodeWithMetadata, NodeWithMetadata][]
238
237
  ```
239
238
 
240
- Build a legacy directional innovation key.
239
+ Build the final selection pool based on reuse and hidden-node preference.
241
240
 
242
- Legacy directional keys are still stored so older code paths or preserved
243
- historical records can resolve to the same innovation id as the modern
244
- symmetric key.
241
+ Pool selection is opinionated but still simple: prefer pairs with known
242
+ innovation history, otherwise prefer hidden-to-hidden growth, otherwise fall
243
+ back to the full candidate set. That keeps the chapter's structural bias
244
+ readable in one place.
245
245
 
246
246
  Parameters:
247
- - `sourceNode` - - source node
248
- - `targetNode` - - target node
247
+ - `allPairs` - - all candidate pairs
248
+ - `reusePairs` - - pairs with historical innovations
249
249
 
250
- Returns: directional innovation key
250
+ Returns: selection pool
251
251
 
252
- ### createsCycle
252
+ ### shouldAbortForCycle
253
253
 
254
254
  ```ts
255
- createsCycle(
256
- sourceNode: NodeWithMetadata,
257
- targetNode: NodeWithMetadata,
255
+ shouldAbortForCycle(
256
+ genomeToInspect: GenomeWithMetadata,
257
+ pairNodes: { sourceNode: NodeWithMetadata; targetNode: NodeWithMetadata; },
258
258
  ): boolean
259
259
  ```
260
260
 
261
- Detect whether adding a connection would create a cycle.
261
+ Determine whether adding the connection would create a cycle.
262
262
 
263
- The cycle check walks forward from the proposed target node and looks for a
264
- path back to the proposed source. If one exists, adding the new edge would
265
- close a loop and the caller can abort the structural edit for acyclic runs.
263
+ The add-connection path only enforces cycle checks when the genome requests
264
+ acyclic topology. That keeps recurrent-capable runs permissive while still
265
+ giving feed-forward-style runs one clear abort seam.
266
266
 
267
267
  Parameters:
268
- - `sourceNode` - - source node of the new connection
269
- - `targetNode` - - target node of the new connection
268
+ - `genomeToInspect` - - genome to inspect
269
+ - `pairNodes` - - resolved pair nodes
270
270
 
271
- Returns: true when a cycle is detected
271
+ Returns: true if the connection should be aborted
@@ -42,68 +42,103 @@ flowchart TD
42
42
 
43
43
  ## neat/mutation/add-node/mutation.add-node.ts
44
44
 
45
- ### ensureBootstrapConnection
45
+ ### applySplitWithExistingRecord
46
46
 
47
47
  ```ts
48
- ensureBootstrapConnection(
49
- genomeToSeed: GenomeWithMetadata,
48
+ applySplitWithExistingRecord(
49
+ genomeToEdit: GenomeWithMetadata,
50
+ connectionToSplit: ConnectionWithMetadata,
51
+ splitDescriptor: { splitKey: string; originalWeight: number; },
52
+ splitRecord: { newNodeGeneId: number; inInnov: number; outInnov: number; },
53
+ NodeClass: new (type: "input" | "output" | "hidden") => unknown,
54
+ ): void
55
+ ```
56
+
57
+ Apply a split using an existing innovation record.
58
+
59
+ This is the preferred path when the same structural split has already been
60
+ observed elsewhere in the population history. Reusing the stored node gene id
61
+ and edge innovation ids preserves historical identity, which makes later
62
+ alignment-based operations treat equivalent splits as equivalent structure.
63
+
64
+ Parameters:
65
+ - `genomeToEdit` - - genome being modified
66
+ - `connectionToSplit` - - connection being split
67
+ - `splitDescriptor` - - metadata for the split
68
+ - `splitRecord` - - existing innovation record
69
+ - `NodeClass` - - node constructor
70
+
71
+ Returns: void
72
+
73
+ ### applySplitWithNewRecord
74
+
75
+ ```ts
76
+ applySplitWithNewRecord(
77
+ genomeToEdit: GenomeWithMetadata,
78
+ connectionToSplit: ConnectionWithMetadata,
79
+ splitDescriptor: { splitKey: string; originalWeight: number; },
80
+ NodeClass: new (type: "input" | "output" | "hidden") => unknown,
50
81
  internal: NeatControllerForMutation,
51
82
  ): void
52
83
  ```
53
84
 
54
- Ensure the genome has at least one connection by linking input to output.
85
+ Apply a split and create a new innovation record.
55
86
 
56
- A connection split only makes sense when a genome already has an edge to cut.
57
- This helper is the bootstrap escape hatch for extremely sparse genomes. It
58
- seeds the smallest possible forward connection so the add-node path can keep
59
- behaving like a split-based structural mutation instead of bailing out
60
- immediately.
87
+ This path handles genuinely novel structural growth. It inserts a fresh
88
+ hidden node, assigns new innovations to the replacement edges, and records
89
+ the resulting identity under the split key so future genomes can reuse it.
61
90
 
62
91
  Parameters:
63
- - `genomeToSeed` - - genome that may need a bootstrap connection
64
- - `internal` - - neat controller context retained for compatibility with existing callers
92
+ - `genomeToEdit` - - genome being modified
93
+ - `connectionToSplit` - - connection being split
94
+ - `splitDescriptor` - - metadata for the split
95
+ - `NodeClass` - - node constructor
96
+ - `internal` - - neat controller context
65
97
 
66
98
  Returns: void
67
99
 
68
- ### findFirstNodeByType
100
+ ### assignInnovationsForNewSplit
69
101
 
70
102
  ```ts
71
- findFirstNodeByType(
72
- genomeToSearch: GenomeWithMetadata,
73
- nodeType: "input" | "output" | "hidden",
74
- ): NodeWithMetadata | undefined
103
+ assignInnovationsForNewSplit(
104
+ newNode: NodeWithMetadata,
105
+ splitConnections: { incomingConnection?: ConnectionWithMetadata | undefined; outgoingConnection?: ConnectionWithMetadata | undefined; },
106
+ internal: NeatControllerForMutation,
107
+ ): { newNodeGeneId: number; inInnov: number; outInnov: number; }
75
108
  ```
76
109
 
77
- Find the first node of a given type.
110
+ Assign new innovations for a split and build the innovation record.
78
111
 
79
- The add-node bootstrap path only needs a minimal node lookup strategy, so
80
- this helper stays intentionally simple and deterministic.
112
+ New split records are the durable memory that turns a one-off structural edit
113
+ into reusable innovation history. This helper assigns the next global
114
+ innovation ids to the replacement edges and packages those ids together with
115
+ the new node gene id so later equivalent splits can be recognized quickly.
81
116
 
82
117
  Parameters:
83
- - `genomeToSearch` - - genome whose nodes are searched
84
- - `nodeType` - - node type to match
118
+ - `newNode` - - newly created hidden node
119
+ - `splitConnections` - - incoming/outgoing connections
120
+ - `internal` - - neat controller context
85
121
 
86
- Returns: the first matching node or undefined
122
+ Returns: innovation record for the split
87
123
 
88
- ### collectEnabledConnections
124
+ ### buildSplitDescriptor
89
125
 
90
126
  ```ts
91
- collectEnabledConnections(
92
- genomeToInspect: GenomeWithMetadata,
93
- ): ConnectionWithMetadata[]
127
+ buildSplitDescriptor(
128
+ connectionToSplit: ConnectionWithMetadata,
129
+ ): { splitKey: string; originalWeight: number; }
94
130
  ```
95
131
 
96
- Collect all enabled connections from a genome.
132
+ Build the split descriptor used for innovation lookup and connection creation.
97
133
 
98
- Split mutations only operate on live structural edges. Disabled connections
99
- remain historical artifacts and should not become split candidates because
100
- doing so would grow new structure from topology the runtime is not currently
101
- using.
134
+ The descriptor is the compact identity packet for a split. Its key captures
135
+ which source and target genes were separated, while its preserved weight lets
136
+ the outgoing replacement edge inherit the old signal strength.
102
137
 
103
138
  Parameters:
104
- - `genomeToInspect` - - genome to inspect
139
+ - `connectionToSplit` - - connection being split
105
140
 
106
- Returns: enabled connections list
141
+ Returns: split descriptor
107
142
 
108
143
  ### chooseConnectionForSplit
109
144
 
@@ -126,24 +161,51 @@ Parameters:
126
161
 
127
162
  Returns: selected connection or null
128
163
 
129
- ### buildSplitDescriptor
164
+ ### collectEnabledConnections
130
165
 
131
166
  ```ts
132
- buildSplitDescriptor(
167
+ collectEnabledConnections(
168
+ genomeToInspect: GenomeWithMetadata,
169
+ ): ConnectionWithMetadata[]
170
+ ```
171
+
172
+ Collect all enabled connections from a genome.
173
+
174
+ Split mutations only operate on live structural edges. Disabled connections
175
+ remain historical artifacts and should not become split candidates because
176
+ doing so would grow new structure from topology the runtime is not currently
177
+ using.
178
+
179
+ Parameters:
180
+ - `genomeToInspect` - - genome to inspect
181
+
182
+ Returns: enabled connections list
183
+
184
+ ### connectSplitEdges
185
+
186
+ ```ts
187
+ connectSplitEdges(
188
+ genomeToEdit: GenomeWithMetadata,
133
189
  connectionToSplit: ConnectionWithMetadata,
134
- ): { splitKey: string; originalWeight: number; }
190
+ newNode: NodeWithMetadata,
191
+ originalWeight: number,
192
+ ): { incomingConnection?: ConnectionWithMetadata | undefined; outgoingConnection?: ConnectionWithMetadata | undefined; }
135
193
  ```
136
194
 
137
- Build the split descriptor used for innovation lookup and connection creation.
195
+ Create the incoming and outgoing split connections.
138
196
 
139
- The descriptor is the compact identity packet for a split. Its key captures
140
- which source and target genes were separated, while its preserved weight lets
141
- the outgoing replacement edge inherit the old signal strength.
197
+ A split replaces one edge with two edges. The incoming edge starts with the
198
+ chapter's default bootstrap weight, while the outgoing edge preserves the
199
+ original connection weight so the pre-split signal can still pass forward in
200
+ a comparable way.
142
201
 
143
202
  Parameters:
203
+ - `genomeToEdit` - - genome being modified
144
204
  - `connectionToSplit` - - connection being split
205
+ - `newNode` - - newly created hidden node
206
+ - `originalWeight` - - weight to preserve on the outgoing connection
145
207
 
146
- Returns: split descriptor
208
+ Returns: incoming/outgoing connection handles
147
209
 
148
210
  ### disconnectOriginalConnection
149
211
 
@@ -166,60 +228,48 @@ Parameters:
166
228
 
167
229
  Returns: void
168
230
 
169
- ### applySplitWithExistingRecord
231
+ ### ensureBootstrapConnection
170
232
 
171
233
  ```ts
172
- applySplitWithExistingRecord(
173
- genomeToEdit: GenomeWithMetadata,
174
- connectionToSplit: ConnectionWithMetadata,
175
- splitDescriptor: { splitKey: string; originalWeight: number; },
176
- splitRecord: { newNodeGeneId: number; inInnov: number; outInnov: number; },
177
- NodeClass: new (type: "input" | "output" | "hidden") => unknown,
234
+ ensureBootstrapConnection(
235
+ genomeToSeed: GenomeWithMetadata,
236
+ internal: NeatControllerForMutation,
178
237
  ): void
179
238
  ```
180
239
 
181
- Apply a split using an existing innovation record.
240
+ Ensure the genome has at least one connection by linking input to output.
182
241
 
183
- This is the preferred path when the same structural split has already been
184
- observed elsewhere in the population history. Reusing the stored node gene id
185
- and edge innovation ids preserves historical identity, which makes later
186
- alignment-based operations treat equivalent splits as equivalent structure.
242
+ A connection split only makes sense when a genome already has an edge to cut.
243
+ This helper is the bootstrap escape hatch for extremely sparse genomes. It
244
+ seeds the smallest possible forward connection so the add-node path can keep
245
+ behaving like a split-based structural mutation instead of bailing out
246
+ immediately.
187
247
 
188
248
  Parameters:
189
- - `genomeToEdit` - - genome being modified
190
- - `connectionToSplit` - - connection being split
191
- - `splitDescriptor` - - metadata for the split
192
- - `splitRecord` - - existing innovation record
193
- - `NodeClass` - - node constructor
249
+ - `genomeToSeed` - - genome that may need a bootstrap connection
250
+ - `internal` - - neat controller context retained for compatibility with existing callers
194
251
 
195
252
  Returns: void
196
253
 
197
- ### applySplitWithNewRecord
254
+ ### findFirstNodeByType
198
255
 
199
256
  ```ts
200
- applySplitWithNewRecord(
201
- genomeToEdit: GenomeWithMetadata,
202
- connectionToSplit: ConnectionWithMetadata,
203
- splitDescriptor: { splitKey: string; originalWeight: number; },
204
- NodeClass: new (type: "input" | "output" | "hidden") => unknown,
205
- internal: NeatControllerForMutation,
206
- ): void
257
+ findFirstNodeByType(
258
+ genomeToSearch: GenomeWithMetadata,
259
+ nodeType: "input" | "output" | "hidden",
260
+ ): NodeWithMetadata | undefined
207
261
  ```
208
262
 
209
- Apply a split and create a new innovation record.
263
+ Find the first node of a given type.
210
264
 
211
- This path handles genuinely novel structural growth. It inserts a fresh
212
- hidden node, assigns new innovations to the replacement edges, and records
213
- the resulting identity under the split key so future genomes can reuse it.
265
+ The add-node bootstrap path only needs a minimal node lookup strategy, so
266
+ this helper stays intentionally simple and deterministic.
214
267
 
215
268
  Parameters:
216
- - `genomeToEdit` - - genome being modified
217
- - `connectionToSplit` - - connection being split
218
- - `splitDescriptor` - - metadata for the split
219
- - `NodeClass` - - node constructor
220
- - `internal` - - neat controller context
269
+ - `genomeToSearch` - - genome whose nodes are searched
270
+ - `nodeType` - - node type to match
221
271
 
222
- Returns: void
272
+ Returns: the first matching node or undefined
223
273
 
224
274
  ### resolveInsertIndex
225
275
 
@@ -241,53 +291,3 @@ Parameters:
241
291
  - `targetNode` - - original target node of the split connection
242
292
 
243
293
  Returns: insertion index
244
-
245
- ### connectSplitEdges
246
-
247
- ```ts
248
- connectSplitEdges(
249
- genomeToEdit: GenomeWithMetadata,
250
- connectionToSplit: ConnectionWithMetadata,
251
- newNode: NodeWithMetadata,
252
- originalWeight: number,
253
- ): { incomingConnection?: ConnectionWithMetadata | undefined; outgoingConnection?: ConnectionWithMetadata | undefined; }
254
- ```
255
-
256
- Create the incoming and outgoing split connections.
257
-
258
- A split replaces one edge with two edges. The incoming edge starts with the
259
- chapter's default bootstrap weight, while the outgoing edge preserves the
260
- original connection weight so the pre-split signal can still pass forward in
261
- a comparable way.
262
-
263
- Parameters:
264
- - `genomeToEdit` - - genome being modified
265
- - `connectionToSplit` - - connection being split
266
- - `newNode` - - newly created hidden node
267
- - `originalWeight` - - weight to preserve on the outgoing connection
268
-
269
- Returns: incoming/outgoing connection handles
270
-
271
- ### assignInnovationsForNewSplit
272
-
273
- ```ts
274
- assignInnovationsForNewSplit(
275
- newNode: NodeWithMetadata,
276
- splitConnections: { incomingConnection?: ConnectionWithMetadata | undefined; outgoingConnection?: ConnectionWithMetadata | undefined; },
277
- internal: NeatControllerForMutation,
278
- ): { newNodeGeneId: number; inInnov: number; outInnov: number; }
279
- ```
280
-
281
- Assign new innovations for a split and build the innovation record.
282
-
283
- New split records are the durable memory that turns a one-off structural edit
284
- into reusable innovation history. This helper assigns the next global
285
- innovation ids to the replacement edges and packages those ids together with
286
- the new node gene id so later equivalent splits can be recognized quickly.
287
-
288
- Parameters:
289
- - `newNode` - - newly created hidden node
290
- - `splitConnections` - - incoming/outgoing connections
291
- - `internal` - - neat controller context
292
-
293
- Returns: innovation record for the split