@reicek/neataptic-ts 0.1.0

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 (272) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
  4. package/.github/workflows/ci.yml +41 -0
  5. package/.github/workflows/deploy-pages.yml +29 -0
  6. package/.github/workflows/manual_release_pipeline.yml +62 -0
  7. package/.github/workflows/publish.yml +85 -0
  8. package/.github/workflows/release_dispatch.yml +38 -0
  9. package/.travis.yml +5 -0
  10. package/CONTRIBUTING.md +92 -0
  11. package/LICENSE +24 -0
  12. package/ONNX_EXPORT.md +87 -0
  13. package/README.md +1173 -0
  14. package/RELEASE.md +54 -0
  15. package/dist-docs/package.json +1 -0
  16. package/dist-docs/scripts/generate-docs.d.ts +2 -0
  17. package/dist-docs/scripts/generate-docs.d.ts.map +1 -0
  18. package/dist-docs/scripts/generate-docs.js +536 -0
  19. package/dist-docs/scripts/generate-docs.js.map +1 -0
  20. package/dist-docs/scripts/render-docs-html.d.ts +2 -0
  21. package/dist-docs/scripts/render-docs-html.d.ts.map +1 -0
  22. package/dist-docs/scripts/render-docs-html.js +148 -0
  23. package/dist-docs/scripts/render-docs-html.js.map +1 -0
  24. package/docs/FOLDERS.md +14 -0
  25. package/docs/README.md +1173 -0
  26. package/docs/architecture/README.md +1391 -0
  27. package/docs/architecture/index.html +938 -0
  28. package/docs/architecture/network/README.md +1210 -0
  29. package/docs/architecture/network/index.html +908 -0
  30. package/docs/assets/ascii-maze.bundle.js +16542 -0
  31. package/docs/assets/ascii-maze.bundle.js.map +7 -0
  32. package/docs/index.html +1419 -0
  33. package/docs/methods/README.md +670 -0
  34. package/docs/methods/index.html +477 -0
  35. package/docs/multithreading/README.md +274 -0
  36. package/docs/multithreading/index.html +215 -0
  37. package/docs/multithreading/workers/README.md +23 -0
  38. package/docs/multithreading/workers/browser/README.md +39 -0
  39. package/docs/multithreading/workers/browser/index.html +70 -0
  40. package/docs/multithreading/workers/index.html +57 -0
  41. package/docs/multithreading/workers/node/README.md +33 -0
  42. package/docs/multithreading/workers/node/index.html +66 -0
  43. package/docs/neat/README.md +1284 -0
  44. package/docs/neat/index.html +906 -0
  45. package/docs/src/README.md +2659 -0
  46. package/docs/src/index.html +1579 -0
  47. package/jest.config.ts +32 -0
  48. package/package.json +99 -0
  49. package/plans/HyperMorphoNEAT.md +293 -0
  50. package/plans/ONNX_EXPORT_PLAN.md +46 -0
  51. package/scripts/generate-docs.ts +486 -0
  52. package/scripts/render-docs-html.ts +138 -0
  53. package/scripts/types.d.ts +2 -0
  54. package/src/README.md +2659 -0
  55. package/src/architecture/README.md +1391 -0
  56. package/src/architecture/activationArrayPool.ts +135 -0
  57. package/src/architecture/architect.ts +635 -0
  58. package/src/architecture/connection.ts +148 -0
  59. package/src/architecture/group.ts +406 -0
  60. package/src/architecture/layer.ts +804 -0
  61. package/src/architecture/network/README.md +1210 -0
  62. package/src/architecture/network/network.activate.ts +223 -0
  63. package/src/architecture/network/network.connect.ts +157 -0
  64. package/src/architecture/network/network.deterministic.ts +167 -0
  65. package/src/architecture/network/network.evolve.ts +426 -0
  66. package/src/architecture/network/network.gating.ts +186 -0
  67. package/src/architecture/network/network.genetic.ts +247 -0
  68. package/src/architecture/network/network.mutate.ts +624 -0
  69. package/src/architecture/network/network.onnx.ts +463 -0
  70. package/src/architecture/network/network.prune.ts +216 -0
  71. package/src/architecture/network/network.remove.ts +96 -0
  72. package/src/architecture/network/network.serialize.ts +309 -0
  73. package/src/architecture/network/network.slab.ts +262 -0
  74. package/src/architecture/network/network.standalone.ts +246 -0
  75. package/src/architecture/network/network.stats.ts +59 -0
  76. package/src/architecture/network/network.topology.ts +86 -0
  77. package/src/architecture/network/network.training.ts +1278 -0
  78. package/src/architecture/network.ts +1302 -0
  79. package/src/architecture/node.ts +1288 -0
  80. package/src/architecture/onnx.ts +3 -0
  81. package/src/config.ts +83 -0
  82. package/src/methods/README.md +670 -0
  83. package/src/methods/activation.ts +372 -0
  84. package/src/methods/connection.ts +31 -0
  85. package/src/methods/cost.ts +347 -0
  86. package/src/methods/crossover.ts +63 -0
  87. package/src/methods/gating.ts +43 -0
  88. package/src/methods/methods.ts +8 -0
  89. package/src/methods/mutation.ts +300 -0
  90. package/src/methods/rate.ts +257 -0
  91. package/src/methods/selection.ts +65 -0
  92. package/src/multithreading/README.md +274 -0
  93. package/src/multithreading/multi.ts +339 -0
  94. package/src/multithreading/workers/README.md +23 -0
  95. package/src/multithreading/workers/browser/README.md +39 -0
  96. package/src/multithreading/workers/browser/testworker.ts +99 -0
  97. package/src/multithreading/workers/node/README.md +33 -0
  98. package/src/multithreading/workers/node/testworker.ts +72 -0
  99. package/src/multithreading/workers/node/worker.ts +70 -0
  100. package/src/multithreading/workers/workers.ts +22 -0
  101. package/src/neat/README.md +1284 -0
  102. package/src/neat/neat.adaptive.ts +544 -0
  103. package/src/neat/neat.compat.ts +164 -0
  104. package/src/neat/neat.constants.ts +20 -0
  105. package/src/neat/neat.diversity.ts +217 -0
  106. package/src/neat/neat.evaluate.ts +328 -0
  107. package/src/neat/neat.evolve.ts +1026 -0
  108. package/src/neat/neat.export.ts +249 -0
  109. package/src/neat/neat.helpers.ts +235 -0
  110. package/src/neat/neat.lineage.ts +220 -0
  111. package/src/neat/neat.multiobjective.ts +260 -0
  112. package/src/neat/neat.mutation.ts +718 -0
  113. package/src/neat/neat.objectives.ts +157 -0
  114. package/src/neat/neat.pruning.ts +190 -0
  115. package/src/neat/neat.selection.ts +269 -0
  116. package/src/neat/neat.speciation.ts +460 -0
  117. package/src/neat/neat.species.ts +151 -0
  118. package/src/neat/neat.telemetry.exports.ts +469 -0
  119. package/src/neat/neat.telemetry.ts +933 -0
  120. package/src/neat/neat.types.ts +275 -0
  121. package/src/neat.ts +1042 -0
  122. package/src/neataptic.ts +10 -0
  123. package/test/architecture/activationArrayPool.capacity.test.ts +19 -0
  124. package/test/architecture/activationArrayPool.test.ts +46 -0
  125. package/test/architecture/connection.test.ts +290 -0
  126. package/test/architecture/group.test.ts +950 -0
  127. package/test/architecture/layer.test.ts +1535 -0
  128. package/test/architecture/network.pruning.test.ts +65 -0
  129. package/test/architecture/node.test.ts +1602 -0
  130. package/test/examples/asciiMaze/asciiMaze.e2e.test.ts +499 -0
  131. package/test/examples/asciiMaze/asciiMaze.ts +41 -0
  132. package/test/examples/asciiMaze/browser-entry.ts +164 -0
  133. package/test/examples/asciiMaze/browserLogger.ts +221 -0
  134. package/test/examples/asciiMaze/browserTerminalUtility.ts +48 -0
  135. package/test/examples/asciiMaze/colors.ts +119 -0
  136. package/test/examples/asciiMaze/dashboardManager.ts +968 -0
  137. package/test/examples/asciiMaze/evolutionEngine.ts +1248 -0
  138. package/test/examples/asciiMaze/fitness.ts +136 -0
  139. package/test/examples/asciiMaze/index.html +128 -0
  140. package/test/examples/asciiMaze/index.ts +26 -0
  141. package/test/examples/asciiMaze/interfaces.ts +235 -0
  142. package/test/examples/asciiMaze/mazeMovement.ts +996 -0
  143. package/test/examples/asciiMaze/mazeUtils.ts +278 -0
  144. package/test/examples/asciiMaze/mazeVision.ts +402 -0
  145. package/test/examples/asciiMaze/mazeVisualization.ts +585 -0
  146. package/test/examples/asciiMaze/mazes.ts +245 -0
  147. package/test/examples/asciiMaze/networkRefinement.ts +76 -0
  148. package/test/examples/asciiMaze/networkVisualization.ts +901 -0
  149. package/test/examples/asciiMaze/terminalUtility.ts +73 -0
  150. package/test/methods/activation.test.ts +1142 -0
  151. package/test/methods/connection.test.ts +146 -0
  152. package/test/methods/cost.test.ts +1123 -0
  153. package/test/methods/crossover.test.ts +202 -0
  154. package/test/methods/gating.test.ts +144 -0
  155. package/test/methods/mutation.test.ts +451 -0
  156. package/test/methods/optimizers.advanced.test.ts +80 -0
  157. package/test/methods/optimizers.behavior.test.ts +105 -0
  158. package/test/methods/optimizers.formula.test.ts +89 -0
  159. package/test/methods/rate.cosineWarmRestarts.test.ts +44 -0
  160. package/test/methods/rate.linearWarmupDecay.test.ts +41 -0
  161. package/test/methods/rate.reduceOnPlateau.test.ts +45 -0
  162. package/test/methods/rate.test.ts +684 -0
  163. package/test/methods/selection.test.ts +245 -0
  164. package/test/multithreading/activations.functions.test.ts +54 -0
  165. package/test/multithreading/multi.test.ts +290 -0
  166. package/test/multithreading/worker.node.process.test.ts +39 -0
  167. package/test/multithreading/workers.coverage.test.ts +36 -0
  168. package/test/multithreading/workers.dynamic.import.test.ts +8 -0
  169. package/test/neat/neat.adaptive.complexityBudget.test.ts +34 -0
  170. package/test/neat/neat.adaptive.criterion.complexity.test.ts +50 -0
  171. package/test/neat/neat.adaptive.mutation.strategy.test.ts +37 -0
  172. package/test/neat/neat.adaptive.operator.decay.test.ts +31 -0
  173. package/test/neat/neat.adaptive.phasedComplexity.test.ts +25 -0
  174. package/test/neat/neat.adaptive.pruning.test.ts +25 -0
  175. package/test/neat/neat.adaptive.targetSpecies.test.ts +43 -0
  176. package/test/neat/neat.additional.coverage.test.ts +126 -0
  177. package/test/neat/neat.advanced.enhancements.test.ts +85 -0
  178. package/test/neat/neat.advanced.test.ts +589 -0
  179. package/test/neat/neat.diversity.autocompat.test.ts +47 -0
  180. package/test/neat/neat.diversity.metrics.test.ts +21 -0
  181. package/test/neat/neat.diversity.stats.test.ts +44 -0
  182. package/test/neat/neat.enhancements.test.ts +79 -0
  183. package/test/neat/neat.entropy.ancestorAdaptive.test.ts +133 -0
  184. package/test/neat/neat.entropy.compat.csv.test.ts +108 -0
  185. package/test/neat/neat.evolution.pruning.test.ts +39 -0
  186. package/test/neat/neat.fastmode.autotune.test.ts +42 -0
  187. package/test/neat/neat.innovation.test.ts +134 -0
  188. package/test/neat/neat.lineage.antibreeding.test.ts +35 -0
  189. package/test/neat/neat.lineage.entropy.test.ts +56 -0
  190. package/test/neat/neat.lineage.inbreeding.test.ts +49 -0
  191. package/test/neat/neat.lineage.pressure.test.ts +29 -0
  192. package/test/neat/neat.multiobjective.adaptive.test.ts +57 -0
  193. package/test/neat/neat.multiobjective.dynamic.schedule.test.ts +46 -0
  194. package/test/neat/neat.multiobjective.dynamic.test.ts +31 -0
  195. package/test/neat/neat.multiobjective.fastsort.delegation.test.ts +51 -0
  196. package/test/neat/neat.multiobjective.prune.test.ts +39 -0
  197. package/test/neat/neat.multiobjective.test.ts +21 -0
  198. package/test/neat/neat.mutation.undefined.pool.test.ts +24 -0
  199. package/test/neat/neat.objective.events.test.ts +26 -0
  200. package/test/neat/neat.objective.importance.test.ts +21 -0
  201. package/test/neat/neat.objective.lifetimes.test.ts +33 -0
  202. package/test/neat/neat.offspring.allocation.test.ts +22 -0
  203. package/test/neat/neat.operator.bandit.test.ts +17 -0
  204. package/test/neat/neat.operator.phases.test.ts +38 -0
  205. package/test/neat/neat.pruneInactive.behavior.test.ts +54 -0
  206. package/test/neat/neat.reenable.adaptation.test.ts +18 -0
  207. package/test/neat/neat.rng.state.test.ts +22 -0
  208. package/test/neat/neat.spawn.add.test.ts +123 -0
  209. package/test/neat/neat.speciation.test.ts +96 -0
  210. package/test/neat/neat.species.allocation.telemetry.test.ts +26 -0
  211. package/test/neat/neat.species.history.csv.test.ts +24 -0
  212. package/test/neat/neat.telemetry.advanced.test.ts +226 -0
  213. package/test/neat/neat.telemetry.csv.lineage.test.ts +19 -0
  214. package/test/neat/neat.telemetry.parity.test.ts +42 -0
  215. package/test/neat/neat.telemetry.stream.test.ts +19 -0
  216. package/test/neat/neat.telemetry.test.ts +16 -0
  217. package/test/neat/neat.test.ts +422 -0
  218. package/test/neat/neat.utilities.test.ts +44 -0
  219. package/test/network/__suppress_console.ts +9 -0
  220. package/test/network/acyclic.topoorder.test.ts +17 -0
  221. package/test/network/checkpoint.metricshook.test.ts +36 -0
  222. package/test/network/error.handling.test.ts +581 -0
  223. package/test/network/evolution.test.ts +285 -0
  224. package/test/network/genetic.test.ts +208 -0
  225. package/test/network/learning.capability.test.ts +244 -0
  226. package/test/network/mutation.effects.test.ts +492 -0
  227. package/test/network/network.activate.test.ts +115 -0
  228. package/test/network/network.activateBatch.test.ts +30 -0
  229. package/test/network/network.deterministic.test.ts +64 -0
  230. package/test/network/network.evolve.branches.test.ts +75 -0
  231. package/test/network/network.evolve.multithread.branches.test.ts +83 -0
  232. package/test/network/network.evolve.test.ts +100 -0
  233. package/test/network/network.gating.removal.test.ts +93 -0
  234. package/test/network/network.mutate.additional.test.ts +145 -0
  235. package/test/network/network.mutate.edgecases.test.ts +101 -0
  236. package/test/network/network.mutate.test.ts +101 -0
  237. package/test/network/network.prune.earlyexit.test.ts +38 -0
  238. package/test/network/network.remove.errors.test.ts +45 -0
  239. package/test/network/network.slab.fallbacks.test.ts +22 -0
  240. package/test/network/network.stats.test.ts +45 -0
  241. package/test/network/network.training.advanced.test.ts +149 -0
  242. package/test/network/network.training.basic.test.ts +228 -0
  243. package/test/network/network.training.helpers.test.ts +183 -0
  244. package/test/network/onnx.export.test.ts +310 -0
  245. package/test/network/onnx.import.test.ts +129 -0
  246. package/test/network/pruning.topology.test.ts +282 -0
  247. package/test/network/regularization.determinism.test.ts +83 -0
  248. package/test/network/regularization.dropconnect.test.ts +17 -0
  249. package/test/network/regularization.dropconnect.validation.test.ts +18 -0
  250. package/test/network/regularization.stochasticdepth.test.ts +27 -0
  251. package/test/network/regularization.test.ts +843 -0
  252. package/test/network/regularization.weightnoise.test.ts +30 -0
  253. package/test/network/setupTests.ts +2 -0
  254. package/test/network/standalone.test.ts +332 -0
  255. package/test/network/structure.serialization.test.ts +660 -0
  256. package/test/training/training.determinism.mixed-precision.test.ts +134 -0
  257. package/test/training/training.earlystopping.test.ts +91 -0
  258. package/test/training/training.edge-cases.test.ts +91 -0
  259. package/test/training/training.extensions.test.ts +47 -0
  260. package/test/training/training.gradient.features.test.ts +110 -0
  261. package/test/training/training.gradient.refinements.test.ts +170 -0
  262. package/test/training/training.gradient.separate-bias.test.ts +41 -0
  263. package/test/training/training.optimizer.test.ts +48 -0
  264. package/test/training/training.plateau.smoothing.test.ts +58 -0
  265. package/test/training/training.smoothing.types.test.ts +174 -0
  266. package/test/training/training.train.options.coverage.test.ts +52 -0
  267. package/test/utils/console-helper.ts +76 -0
  268. package/test/utils/jest-setup.ts +60 -0
  269. package/test/utils/test-helpers.ts +175 -0
  270. package/tsconfig.docs.json +12 -0
  271. package/tsconfig.json +21 -0
  272. package/webpack.config.js +49 -0
@@ -0,0 +1,249 @@
1
+ import { NeatLike } from './neat.types';
2
+
3
+ // ----------------------------------------------------------------------------------
4
+ // Export / Import helpers for NEAT evolutionary state.
5
+ // These utilities deliberately avoid importing the concrete Neat class directly so
6
+ // they can be mixed into lighter-weight facades or used in static contexts.
7
+ // ----------------------------------------------------------------------------------
8
+
9
+ /**
10
+ * JSON representation of an individual genome (network). The concrete shape is
11
+ * produced by `Network#toJSON()` and re‑hydrated via `Network.fromJSON()`. We use
12
+ * an open record signature here because the network architecture may evolve with
13
+ * plugins / future features (e.g. CPPNs, substrate metadata, ONNX export tags).
14
+ */
15
+ export interface GenomeJSON {
16
+ [key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
17
+ }
18
+
19
+ /**
20
+ * Serialized meta information describing a NEAT run, excluding the concrete
21
+ * population genomes. This allows you to persist & resume experiment context
22
+ * (innovation history, current generation, IO sizes, hyper‑parameters) without
23
+ * committing to a particular population snapshot.
24
+ */
25
+ export interface NeatMetaJSON {
26
+ /** Number of input nodes expected by evolved networks. */
27
+ input: number;
28
+ /** Number of output nodes produced by evolved networks. */
29
+ output: number;
30
+ /** Current evolutionary generation index (0-based). */
31
+ generation: number;
32
+ /** Full options object (hyper‑parameters) used to configure NEAT. */
33
+ options: any; // retained as any until options interface is extracted
34
+ /** Innovation records for node split mutations: [compositeKey, innovationId]. */
35
+ nodeSplitInnovations: [any, any][]; // key/value pairs serialised from Map
36
+ /** Innovation records for connection mutations: [compositeKey, innovationId]. */
37
+ connInnovations: [any, any][];
38
+ /** Next global innovation number that will be assigned. */
39
+ nextGlobalInnovation: number;
40
+ }
41
+
42
+ /**
43
+ * Top‑level bundle containing both NEAT meta information and the full array of
44
+ * serialized genomes (population). This is what you get from `exportState()` and
45
+ * feed into `importStateImpl()` to resume exactly where you left off.
46
+ */
47
+ export interface NeatStateJSON {
48
+ /** Serialized NEAT meta (innovation history, generation, options, etc.). */
49
+ neat: NeatMetaJSON;
50
+ /** Array of serialized genomes representing the current population. */
51
+ population: GenomeJSON[];
52
+ }
53
+
54
+ /**
55
+ * Export the current population (array of genomes) into plain JSON objects.
56
+ * Each genome is converted via its `toJSON()` method. You can persist this
57
+ * result (e.g. to disk, a database, or localStorage) and later rehydrate it
58
+ * with {@link importPopulation}.
59
+ *
60
+ * Why export population only? Sometimes you want to snapshot *just* the set of
61
+ * candidate solutions (e.g. for ensemble evaluation) without freezing the
62
+ * innovation counters or hyper‑parameters.
63
+ *
64
+ * Example:
65
+ * ```ts
66
+ * // Assuming `neat` is an instance exposing this helper
67
+ * const popSnapshot = neat.exportPopulation();
68
+ * fs.writeFileSync('population.json', JSON.stringify(popSnapshot, null, 2));
69
+ * ```
70
+ * @category Serialization
71
+ * @returns Array of genome JSON objects.
72
+ */
73
+ export function exportPopulation(this: NeatLike): GenomeJSON[] {
74
+ // 1. Map each genome in the current population to its serializable form
75
+ return (this as any).population.map((genome: any) => genome.toJSON());
76
+ }
77
+
78
+ /**
79
+ * Import (replace) the current population from an array of serialized genomes.
80
+ * This does not touch NEAT meta state (generation, innovations, etc.)—only the
81
+ * population array and implied `popsize` are updated.
82
+ *
83
+ * Example:
84
+ * ```ts
85
+ * const populationData: GenomeJSON[] = JSON.parse(fs.readFileSync('population.json','utf8'));
86
+ * neat.importPopulation(populationData); // population replaced
87
+ * neat.evolve(); // continue evolving with new starting genomes
88
+ * ```
89
+ *
90
+ * Edge cases handled:
91
+ * - Empty array => becomes an empty population (popsize=0).
92
+ * - Malformed entries will throw if `Network.fromJSON` rejects them.
93
+ *
94
+ * @param populationJSON Array of serialized genome objects.
95
+ */
96
+ export function importPopulation(
97
+ this: NeatLike,
98
+ populationJSON: GenomeJSON[]
99
+ ): void {
100
+ /** const Network class used for genome (network) rehydration */
101
+ const Network = require('../architecture/network').default;
102
+ // 1. Recreate each genome via Network.fromJSON
103
+ (this as any).population = populationJSON.map((serializedGenome: any) =>
104
+ Network.fromJSON(serializedGenome)
105
+ );
106
+ // 2. Keep popsize option in sync with actual population length
107
+ (this as any).options.popsize = (this as any).population.length;
108
+ }
109
+
110
+ /**
111
+ * Convenience helper that returns a full evolutionary snapshot: both NEAT meta
112
+ * information and the serialized population array. Use this when you want a
113
+ * truly *pause‑and‑resume* capability including innovation bookkeeping.
114
+ *
115
+ * Example:
116
+ * ```ts
117
+ * const state = neat.exportState();
118
+ * fs.writeFileSync('state.json', JSON.stringify(state));
119
+ * // ...later / elsewhere...
120
+ * const raw = JSON.parse(fs.readFileSync('state.json','utf8')) as NeatStateJSON;
121
+ * const neat2 = Neat.importState(raw, fitnessFn); // identical evolutionary context
122
+ * ```
123
+ * @returns A {@link NeatStateJSON} bundle containing meta + population.
124
+ */
125
+ export function exportState(this: NeatLike): NeatStateJSON {
126
+ /** const lazily loaded export helpers (avoids circular deps) */
127
+ const { toJSONImpl, exportPopulation } = require('./neat.export');
128
+ // 1. Serialize meta
129
+ // 2. Serialize population
130
+ // 3. Package into a bundle for persistence
131
+ return {
132
+ neat: toJSONImpl.call(this as any),
133
+ population: exportPopulation.call(this as any),
134
+ };
135
+ }
136
+
137
+ /**
138
+ * Static-style helper that rehydrates a full evolutionary state previously
139
+ * produced by {@link exportState}. Invoke this with the NEAT *class* (not an
140
+ * instance) bound as `this`, e.g. `Neat.importStateImpl(bundle, fitnessFn)`.
141
+ * It constructs a new NEAT instance using the meta data, then imports the
142
+ * population (if present).
143
+ *
144
+ * Safety & validation:
145
+ * - Throws if the bundle is not an object.
146
+ * - Silently skips population import if `population` is missing or not an array.
147
+ *
148
+ * Example:
149
+ * ```ts
150
+ * const bundle: NeatStateJSON = JSON.parse(fs.readFileSync('state.json','utf8'));
151
+ * const neat = Neat.importStateImpl(bundle, fitnessFn);
152
+ * neat.evolve();
153
+ * ```
154
+ * @param stateBundle Full state bundle from {@link exportState}.
155
+ * @param fitnessFunction Fitness evaluation callback used for new instance.
156
+ * @returns Rehydrated NEAT instance ready to continue evolving.
157
+ */
158
+ export function importStateImpl(
159
+ this: any,
160
+ stateBundle: NeatStateJSON,
161
+ fitnessFunction: (network: any) => number
162
+ ): any {
163
+ // 1. Basic validation of bundle shape
164
+ if (!stateBundle || typeof stateBundle !== 'object')
165
+ throw new Error('Invalid state bundle');
166
+ // 2. Reconstruct Neat meta & instance
167
+ const neatInstance = this.fromJSON(stateBundle.neat, fitnessFunction);
168
+ // 3. Import population if provided
169
+ if (Array.isArray(stateBundle.population))
170
+ neatInstance.import(stateBundle.population);
171
+ // 4. Return fully restored instance
172
+ return neatInstance;
173
+ }
174
+
175
+ /**
176
+ * Serialize NEAT meta (excluding the mutable population) for persistence of
177
+ * innovation history and experiment configuration. This is sufficient to
178
+ * recreate a *blank* NEAT run at the same evolutionary generation with the
179
+ * same innovation counters, enabling deterministic continuation when combined
180
+ * later with a saved population.
181
+ *
182
+ * Example:
183
+ * ```ts
184
+ * const meta = neat.toJSONImpl();
185
+ * fs.writeFileSync('neat-meta.json', JSON.stringify(meta));
186
+ * // ... later ...
187
+ * const metaLoaded = JSON.parse(fs.readFileSync('neat-meta.json','utf8')) as NeatMetaJSON;
188
+ * const neat2 = Neat.fromJSONImpl(metaLoaded, fitnessFn); // empty population
189
+ * ```
190
+ * @returns {@link NeatMetaJSON} object describing current NEAT meta state.
191
+ */
192
+ export function toJSONImpl(this: NeatLike): NeatMetaJSON {
193
+ // 1. Return a plain object with primitive / array serializable fields only
194
+ return {
195
+ input: (this as any).input,
196
+ output: (this as any).output,
197
+ generation: (this as any).generation,
198
+ options: (this as any).options,
199
+ nodeSplitInnovations: Array.from(
200
+ (this as any)._nodeSplitInnovations.entries()
201
+ ),
202
+ connInnovations: Array.from((this as any)._connInnovations.entries()),
203
+ nextGlobalInnovation: (this as any)._nextGlobalInnovation,
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Static-style implementation that rehydrates a NEAT instance from previously
209
+ * exported meta JSON produced by {@link toJSONImpl}. This does *not* restore a
210
+ * population; callers typically follow up with `importPopulation` or use
211
+ * {@link importStateImpl} for a complete restore.
212
+ *
213
+ * Example:
214
+ * ```ts
215
+ * const meta: NeatMetaJSON = JSON.parse(fs.readFileSync('neat-meta.json','utf8'));
216
+ * const neat = Neat.fromJSONImpl(meta, fitnessFn); // empty population, same innovations
217
+ * neat.importPopulation(popSnapshot); // optional
218
+ * ```
219
+ * @param neatJSON Serialized meta (no population).
220
+ * @param fitnessFunction Fitness callback used to construct the new instance.
221
+ * @returns Fresh NEAT instance with restored innovation history.
222
+ */
223
+ export function fromJSONImpl(
224
+ this: any,
225
+ neatJSON: NeatMetaJSON,
226
+ fitnessFunction: (network: any) => number
227
+ ): any {
228
+ /** const alias for the constructor (class) this function is bound to */
229
+ const NeatClass = this as any;
230
+ // 1. Instantiate with stored IO sizes & options
231
+ const neatInstance = new NeatClass(
232
+ neatJSON.input,
233
+ neatJSON.output,
234
+ fitnessFunction,
235
+ neatJSON.options || {}
236
+ );
237
+ // 2. Restore generation index
238
+ neatInstance.generation = neatJSON.generation || 0;
239
+ // 3. Restore innovation maps when present
240
+ if (Array.isArray(neatJSON.nodeSplitInnovations))
241
+ neatInstance._nodeSplitInnovations = new Map(neatJSON.nodeSplitInnovations);
242
+ if (Array.isArray(neatJSON.connInnovations))
243
+ neatInstance._connInnovations = new Map(neatJSON.connInnovations);
244
+ // 4. Restore next innovation counter
245
+ if (typeof neatJSON.nextGlobalInnovation === 'number')
246
+ neatInstance._nextGlobalInnovation = neatJSON.nextGlobalInnovation;
247
+ // 5. Return reconstructed instance (empty population)
248
+ return neatInstance;
249
+ }
@@ -0,0 +1,235 @@
1
+ import { NeatLike } from './neat.types';
2
+ import Network from '../architecture/network';
3
+
4
+ /**
5
+ * Helper utilities that augment the core NEAT (NeuroEvolution of Augmenting Topologies)
6
+ * implementation. These functions are kept separate from the main class so they can
7
+ * be tree‑shaken when unused and independently documented for educational purposes.
8
+ *
9
+ * The helpers focus on three core lifecycle operations:
10
+ * 1. Spawning children from an existing parent genome with mutation ("sexual" reproduction not handled here).
11
+ * 2. Registering externally created genomes so lineage & invariants remain consistent.
12
+ * 3. Creating the initial population pool (bootstrapping evolution) either from a seed
13
+ * network or by synthesizing fresh minimal networks.
14
+ *
15
+ * All helpers expect to be invoked with a `this` context that matches `NeatLike`.
16
+ * They intentionally use defensive try/catch blocks to avoid aborting broader
17
+ * evolutionary runs when an individual genome operation fails; this mirrors the
18
+ * tolerant/robust nature of many historical NEAT library implementations.
19
+ */
20
+
21
+ /**
22
+ * Spawn (clone & mutate) a child genome from an existing parent genome.
23
+ *
24
+ * The returned child is intentionally NOT auto‑inserted into the population;
25
+ * call {@link addGenome} (or the class method wrapper) once you decide to
26
+ * keep it. This separation allows callers to perform custom validation or
27
+ * scoring heuristics before committing the child genome.
28
+ *
29
+ * Evolutionary rationale:
30
+ * - Cloning preserves the full topology & weights of the parent.
31
+ * - A configurable number of mutation passes are applied sequentially; each
32
+ * pass may alter structure (add/remove nodes / connections) or weights.
33
+ * - Lineage annotations (`_parents`, `_depth`) enable later analytics (e.g.,
34
+ * diversity statistics, genealogy visualization, pruning heuristics).
35
+ *
36
+ * Robustness philosophy: individual mutation failures are silently ignored so
37
+ * a single stochastic edge case (e.g., no valid structural mutation) does not
38
+ * derail evolutionary progress.
39
+ *
40
+ * @param this Bound NEAT instance (inferred when used as a method).
41
+ * @param parentGenome Parent genome/network to clone. Must implement either
42
+ * `clone()` OR a pair of `toJSON()` / static `fromJSON()` for deep copying.
43
+ * @param mutateCount Number of sequential mutation operations to attempt; each
44
+ * iteration chooses a mutation method using the instance's selection logic.
45
+ * Defaults to 1 for conservative structural drift.
46
+ * @returns A new genome (unregistered) whose score is reset and whose lineage
47
+ * metadata references the parent.
48
+ * @example
49
+ * ```ts
50
+ * // Assume `neat` is an instance implementing NeatLike and `parent` is a genome in neat.population
51
+ * const child = neat.spawnFromParent(parent, 3); // apply 3 mutation passes
52
+ * // Optionally inspect / filter the child before adding
53
+ * neat.addGenome(child, [parent._id]);
54
+ * ```
55
+ */
56
+ export function spawnFromParent(
57
+ this: NeatLike,
58
+ parentGenome: any,
59
+ mutateCount: number = 1
60
+ ) {
61
+ // Step 1: Deep clone the parent (prefer direct clone() for performance).
62
+ const clone = parentGenome.clone
63
+ ? parentGenome.clone()
64
+ : require('../architecture/network').default.fromJSON(
65
+ parentGenome.toJSON()
66
+ );
67
+
68
+ // Step 2: Reset evaluation state for the fresh offspring.
69
+ clone.score = undefined;
70
+ (clone as any)._reenableProb = (this as any).options.reenableProb;
71
+ (clone as any)._id = (this as any)._nextGenomeId++;
72
+
73
+ // Step 3: Record minimal lineage (single direct parent) and generation depth.
74
+ (clone as any)._parents = [(parentGenome as any)._id];
75
+ (clone as any)._depth = ((parentGenome as any)._depth ?? 0) + 1;
76
+
77
+ // Step 4: Enforce structural invariants (minimum hidden nodes, no dead ends).
78
+ (this as any).ensureMinHiddenNodes(clone);
79
+ (this as any).ensureNoDeadEnds(clone);
80
+
81
+ // Step 5: Apply the requested number of mutation passes.
82
+ for (let mutationIndex = 0; mutationIndex < mutateCount; mutationIndex++) {
83
+ try {
84
+ // Select a mutation operator; may return a single method or an array of candidates.
85
+ let selectedMutationMethod = (this as any).selectMutationMethod(
86
+ clone,
87
+ false
88
+ );
89
+ if (Array.isArray(selectedMutationMethod)) {
90
+ const candidateMutations = selectedMutationMethod as any[];
91
+ selectedMutationMethod =
92
+ candidateMutations[
93
+ Math.floor((this as any)._getRNG()() * candidateMutations.length)
94
+ ];
95
+ }
96
+ // Execute mutation if a valid operator with a name (convention) is present.
97
+ if (selectedMutationMethod && selectedMutationMethod.name) {
98
+ clone.mutate(selectedMutationMethod);
99
+ }
100
+ } catch {
101
+ // Intentionally ignore individual mutation failures to keep evolution moving.
102
+ }
103
+ }
104
+
105
+ // Step 6: Invalidate any cached compatibility / distance metrics tied to the genome.
106
+ (this as any)._invalidateGenomeCaches(clone);
107
+ return clone;
108
+ }
109
+
110
+ /**
111
+ * Register an externally constructed genome (e.g., deserialized, custom‑built,
112
+ * or imported from another run) into the active population. Ensures lineage
113
+ * metadata and structural invariants are consistent with internally spawned
114
+ * genomes.
115
+ *
116
+ * Defensive design: If invariant enforcement fails, the genome is still added
117
+ * (best effort) so experiments remain reproducible and do not abort mid‑run.
118
+ * Caller can optionally inspect or prune later during evaluation.
119
+ *
120
+ * @param this Bound NEAT instance.
121
+ * @param genome Genome / network object to insert. Mutated in place to add
122
+ * internal metadata fields (`_id`, `_parents`, `_depth`, `_reenableProb`).
123
+ * @param parents Optional explicit list of parent genome IDs (e.g., 2 parents
124
+ * for crossover). If omitted, lineage metadata is left empty.
125
+ * @example
126
+ * ```ts
127
+ * const imported = Network.fromJSON(saved);
128
+ * neat.addGenome(imported, [parentA._id, parentB._id]);
129
+ * ```
130
+ */
131
+ export function addGenome(this: NeatLike, genome: any, parents?: number[]) {
132
+ try {
133
+ // Step 1: Reset score so future evaluations are not biased by stale values.
134
+ genome.score = undefined;
135
+ (genome as any)._reenableProb = (this as any).options.reenableProb;
136
+ (genome as any)._id = (this as any)._nextGenomeId++;
137
+
138
+ // Step 2: Copy lineage from provided parent IDs (if any).
139
+ (genome as any)._parents = Array.isArray(parents) ? parents.slice() : [];
140
+ (genome as any)._depth = 0;
141
+ if ((genome as any)._parents.length) {
142
+ // Compute depth = (max parent depth) + 1 for genealogical layering.
143
+ const parentDepths = (genome as any)._parents
144
+ .map((pid: number) =>
145
+ (this as any).population.find((g: any) => g._id === pid)
146
+ )
147
+ .filter(Boolean)
148
+ .map((g: any) => g._depth ?? 0);
149
+ (genome as any)._depth = parentDepths.length
150
+ ? Math.max(...parentDepths) + 1
151
+ : 1;
152
+ }
153
+
154
+ // Step 3: Ensure structural invariants.
155
+ (this as any).ensureMinHiddenNodes(genome);
156
+ (this as any).ensureNoDeadEnds(genome);
157
+
158
+ // Step 4: Invalidate caches & persist.
159
+ (this as any)._invalidateGenomeCaches(genome);
160
+ (this as any).population.push(genome);
161
+ } catch (error) {
162
+ // Fallback: still add genome so the evolutionary run can continue.
163
+ (this as any).population.push(genome);
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Create (or reset) the initial population pool for a NEAT run.
169
+ *
170
+ * If a `seedNetwork` is supplied, every genome is a structural + weight clone
171
+ * of that seed. This is useful for transfer learning or continuing evolution
172
+ * from a known good architecture. When omitted, brand‑new minimal networks are
173
+ * synthesized using the configured input/output sizes (and optional minimum
174
+ * hidden layer size).
175
+ *
176
+ * Design notes:
177
+ * - Population size is derived from `options.popsize` (default 50).
178
+ * - Each genome gets a unique sequential `_id` for reproducible lineage.
179
+ * - When lineage tracking is enabled (`_lineageEnabled`), parent & depth fields
180
+ * are initialized for later analytics.
181
+ * - Structural invariant checks are best effort. A single failure should not
182
+ * prevent other genomes from being created, hence broad try/catch blocks.
183
+ *
184
+ * @param this Bound NEAT instance.
185
+ * @param seedNetwork Optional prototype network to clone for every initial genome.
186
+ * @example
187
+ * ```ts
188
+ * // Basic: create 50 fresh minimal networks
189
+ * neat.createPool(null);
190
+ *
191
+ * // Seeded: start with a known topology
192
+ * const seed = new Network(neat.input, neat.output, { minHidden: 4 });
193
+ * neat.createPool(seed);
194
+ * ```
195
+ */
196
+ export function createPool(this: NeatLike, seedNetwork: any | null) {
197
+ try {
198
+ // Step 1: Reset population container.
199
+ (this as any).population = [];
200
+ const poolSize = ((this as any).options?.popsize as number) || 50;
201
+
202
+ // Step 2: Generate each initial genome.
203
+ for (let genomeIndex = 0; genomeIndex < poolSize; genomeIndex++) {
204
+ // Clone from seed OR build a fresh network.
205
+ const genomeCopy = seedNetwork
206
+ ? Network.fromJSON(seedNetwork.toJSON())
207
+ : new Network((this as any).input, (this as any).output, {
208
+ minHidden: (this as any).options?.minHidden,
209
+ });
210
+
211
+ // Step 2a: Ensure no stale scoring information.
212
+ genomeCopy.score = undefined;
213
+
214
+ // Step 2b: Attempt structural invariant enforcement (best effort).
215
+ try {
216
+ (this as any).ensureNoDeadEnds(genomeCopy);
217
+ } catch {
218
+ // Ignored; genome may still be viable or corrected by later mutations.
219
+ }
220
+
221
+ // Step 2c: Annotate runtime metadata.
222
+ (genomeCopy as any)._reenableProb = (this as any).options.reenableProb;
223
+ (genomeCopy as any)._id = (this as any)._nextGenomeId++;
224
+ if ((this as any)._lineageEnabled) {
225
+ (genomeCopy as any)._parents = [];
226
+ (genomeCopy as any)._depth = 0;
227
+ }
228
+
229
+ // Step 2d: Insert into population.
230
+ (this as any).population.push(genomeCopy);
231
+ }
232
+ } catch {
233
+ // Swallow: partial population is acceptable; caller may decide to refill or continue.
234
+ }
235
+ }