@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
package/README.md ADDED
@@ -0,0 +1,1173 @@
1
+ # NeatapticTS
2
+
3
+ [![License: MIT](https://img.shields.io/badge/license-MIT-2c3963.svg)](LICENSE) [![Docs](https://img.shields.io/badge/docs-generated-2c3963.svg)](docs/index.html)
4
+
5
+ <img src="https://cdn-images-1.medium.com/max/800/1*THG2__H9YHxYIt2sulzlTw.png" width="500px"/>
6
+
7
+ NeatapticTS is an educational TypeScript library for building and evolving neural networks.
8
+ It provides a flexible, minimal API for networks, neuro-evolution (NEAT-style operators),
9
+ multi-objective selection, and performance telemetry — with defaults suitable for learning,
10
+ research prototypes, and teaching.
11
+
12
+ This repository contains both the library source (in `src/`) and an auto-generated set of
13
+ documentation pages (in `docs/`) produced from the project's JSDoc comments. The per-folder
14
+ README files inside `src/` are regenerated from source JSDoc so GitHub shows up-to-date
15
+ API summaries.
16
+
17
+ Why use NeatapticTS?
18
+ - Small, readable TypeScript codebase aimed at education and research.
19
+ - Built-in neuro-evolution primitives (mutation, crossover, speciation).
20
+ - Optional multi-objective and Pareto evolution utilities.
21
+ - Designed for easy experimentation with runtime telemetry and parallel evaluation.
22
+
23
+ ## Origins & comparison
24
+ NeatapticTS is inspired by and based on the original Neataptic project by Thomas Wagenaar (GitHub: `wagenaartje`). The original Neataptic (site: https://wagenaartje.github.io/neataptic/, repo: https://github.com/wagenaartje/neataptic) is a popular JavaScript library (many stars and examples) that offered fast neuro-evolution and backpropagation for browser and Node.js usage.
25
+
26
+ Notable points about the original Neataptic
27
+ - Author / repo: Thomas Wagenaar (`wagenaartje`) — project site and docs at https://wagenaartje.github.io/neataptic/ and source at https://github.com/wagenaartje/neataptic.
28
+ - Designed for in-browser demos and learning: playgrounds, neuroevolution examples, and articles accompany the code.
29
+ - Uses the Instinct neuro-evolution approach in many examples and claims very fast backpropagation and neural training performance compared with contemporaries.
30
+ - Built-in architect helpers (Perceptron, LSTM, GRU, etc.), low-level Node/Group/Layer APIs, and many examples and demos.
31
+ - License and status: the original repo ships with a LICENSE (see upstream) and has historically been widely used; at times it has been marked `unmaintained` in issues — consult the upstream repo for current status.
32
+
33
+ How NeatapticTS compares and what it adds
34
+ - TypeScript refactor: strongly-typed sources, clearer contracts, and easier maintenance for educational use.
35
+ - Parallel evaluation: worker-based multi-threaded evaluation to speed up population scoring on multi-core machines.
36
+ - Multi-objective & Pareto utilities: native support for Pareto-based evolution, hypervolume proxies, and runtime objective management.
37
+ - Adaptive complexity & pruning: runtime complexity budgets, adaptive pruning, and structural-entropy utilities to manage bloat during evolution.
38
+ - Performance ergonomics: activation array pooling, configurable activation precision, and other throughput options.
39
+ - Rich telemetry: extended per-generation telemetry (fronts, operator stats, lineage, complexity) to aid analysis and teaching.
40
+ - Documentation workflow: per-folder `README.md` auto-generated from JSDoc, plus rendered HTML site under `docs/` for easy browsing and publishing.
41
+ - Educational focus: conservative defaults, clearer examples, and documentation geared toward learners and researchers.
42
+
43
+ Notes
44
+ - This project builds on the ideas and API style of the original Neataptic, but it is a TypeScript-first rework with some API differences. It is not guaranteed to be a drop-in replacement; consult the generated per-folder docs in `src/*/README.md` or `docs/` for exact signatures and behaviors.
45
+ - For the canonical original project, demos, and historical context see:
46
+
47
+ - Neataptic site & docs: https://wagenaartje.github.io/neataptic/
48
+ - Neataptic GitHub: https://github.com/wagenaartje/neataptic
49
+
50
+ Quick links
51
+ - Documentation (auto-generated): `docs/` (open `docs/index.html` in a browser)
52
+ - Live API summaries mirrored into source: `src/*/README.md` (auto-generated)
53
+ - Tests & examples: `test/` and `test/examples/`
54
+
55
+ ## Table of contents
56
+ - [Installation](#installation)
57
+ - [Minimal example](#minimal-example)
58
+ - [Documentation & API](#documentation--api)
59
+ - [Performance & tuning](#performance--tuning)
60
+ - [Contributing](#contributing)
61
+ - [License](#license)
62
+ - [Need help?](#need-help)
63
+
64
+ Installation
65
+
66
+ This project is primarily intended as a library you develop from source. To install the
67
+ published package (when available) or run locally:
68
+
69
+ 1. Clone the repository and install dev dependencies:
70
+
71
+ ```powershell
72
+ git clone https://github.com/reicek/NeatapticTS.git
73
+ cd NeatapticTS
74
+ npm install
75
+ ```
76
+
77
+ 2. Build and run the docs generator (the repo keeps generated READMEs inside `src/`):
78
+
79
+ ```powershell
80
+ npm run docs
81
+ ```
82
+ Minimal example
83
+
84
+ The example below shows a tiny setup that creates a `Neat` population and runs evaluate + evolve
85
+ for a few iterations. See per-folder READMEs in `src/` for detailed API surface.
86
+
87
+ ```ts
88
+ import { Neat, Network } from './src/neat';
89
+
90
+ // simple fitness: maximize negative squared error for x->2x mapping
91
+ const fitness = (net: any) => {
92
+ const out = net.activate([1])[0];
93
+ return -Math.pow(out - 2, 2);
94
+ };
95
+
96
+ const neat = new Neat(1, 1, fitness, { popsize: 30, fastMode: true });
97
+
98
+ async function run() {
99
+ await neat.evaluate();
100
+ await neat.evolve();
101
+ console.log('best', neat.getBest());
102
+ }
103
+
104
+ run();
105
+ ```
106
+
107
+ Documentation & API
108
+
109
+ - The `docs/` folder contains rendered HTML pages (generated from the same READMEs) for
110
+ browsing locally or publishing as static pages. See `docs/index.html`.
111
+ - Per-file and per-folder API summaries are kept inside `src/*/README.md` and are regenerated
112
+ using the JSDoc comments in the TypeScript sources. Run `npm run docs` to refresh them.
113
+
114
+ Note about helper APIs
115
+ ---------------------
116
+
117
+ The `Neat` class exposes two small helpers intended for safe programmatic genome
118
+ management:
119
+
120
+ - `spawnFromParent(parent, mutateCount?)` — clone a single parent, apply a small
121
+ number of mutations, and preserve lineage fields. The function returns an
122
+ unregistered child (it does NOT add the child to `neat.population`).
123
+ - `addGenome(genome, parents?)` — register an externally created genome into the
124
+ population while assigning `_id`, estimating `_depth` from provided parents,
125
+ enforcing structural invariants, and invalidating caches.
126
+
127
+ These helpers are documented inline via JSDoc in `src/neat.ts`; keep those
128
+ comments updated so the generated per-folder READMEs and HTML docs include the
129
+ public contract and usage notes.
130
+
131
+ Performance & tuning
132
+
133
+ NeatapticTS includes telemetry and knobs to trade throughput vs fidelity (threads, Lamarckian
134
+ refinement, mutation scheduling, complexity budgets). See `docs/src/README.md` and the
135
+ generated per-folder READMEs for the `Neat`, `Network`, and telemetry option surfaces.
136
+
137
+ Contributing
138
+
139
+ - This is a public, educational project. Contributions that improve clarity, add examples,
140
+ or simplify the learning path are especially welcome.
141
+ - Please follow the repository's coding conventions and include tests for behavioral changes.
142
+ - For docs changes, update JSDoc comments in `src/` and run `npm run docs` to regenerate the
143
+ markdown/html outputs.
144
+
145
+ License
146
+
147
+ This project is released under the MIT License — see the `LICENSE` file.
148
+
149
+ Attribution
150
+
151
+ - Core ideas and some code are derived from Neataptic (Thomas Wagenaar, `wagenaartje`) and Synaptic (Juan Cazala). See `LICENSE` for details and original copyrights.
152
+
153
+ Need help?
154
+
155
+ If something in the API is unclear, open an issue describing what you were trying to do and
156
+ which part of the documentation could have helped. We prioritize documentation improvements
157
+ and small example additions for educational clarity.
158
+
159
+ Enjoy learning and experimenting with neuro-evolution!
160
+
161
+ # NeatapticTS
162
+
163
+ NeatapticTS offers flexible neural networks; neurons and synapses can be removed with a single line of code. No fixed architecture is required for neural networks to function at all. This flexibility allows networks to be shaped for your dataset through neuro-evolution, which is done using multiple threads.
164
+
165
+ ## Performance & Parallelism Tuning
166
+
167
+ This section summarizes practical knobs to accelerate evolution while preserving solution quality.
168
+
169
+ ### Core Levers
170
+ | Lever | Effect | Guidance |
171
+ |-------|--------|----------|
172
+ | `threads` | Parallel genome evaluation | Increase until CPU saturation (watch diminishing returns > physical cores). Falls back to single-thread if workers unavailable. |
173
+ | `growth` | Structural penalty strength | Higher discourages bloating (faster eval, may limit innovation). Tune 5e-5..5e-4. |
174
+ | `mutationRate` / `mutationAmount` | Exploration breadth | For small populations (<=10) library enforces higher defaults. Reduce when convergence noisy. |
175
+ | `fastMode` | Lower sampling overhead | Use for CI or rapid iteration; disables some heavy sampling defaults. |
176
+ | `adaptiveMutation` | Dynamic operator pressure | Stabilizes search; can reduce wasted evaluations. |
177
+ | `telemetrySelect` | Reduce telemetry overhead | Keep only necessary blocks (e.g. ['performance']). |
178
+ | `lamarckianIterations` / `lamarckianSampleSize` | Local refinement vs throughput | Lower for diversity, raise for precision on stable plateaus. |
179
+ | `maxGenerations` (asciiMaze engine) | Safety cap | Prevents runaway long runs during tuning passes. |
180
+
181
+ ### Structural Complexity Caching
182
+ `network.evolve` caches per-genome complexity metrics (nodes / connections / gates). This avoids recomputing counts each evaluation when unchanged, reducing overhead for large populations or deep architectures.
183
+
184
+ ### Profiling
185
+ Enable `telemetry:{ performance:true }` to capture per-generation evaluation & evolve timings. Use these to:
186
+ 1. Identify evaluation bottlenecks (cost function vs structural overhead).
187
+ 2. Compare single vs multi-thread scaling (expect near-linear until memory bandwidth limits).
188
+ 3. Decide when to lower Lamarckian refinement iterations.
189
+
190
+ ### Suggested Workflow
191
+ 1. Start with `threads = physicalCores - 1` to leave OS headroom.
192
+ 2. Enable performance telemetry and run a short benchmark (e.g., 30 generations).
193
+ 3. Inspect mean `evalMs` / `evolveMs`; if `evalMs` dominates and scaling <70% ideal, reduce Lamarckian refinement & complexity penalty if innovation stalls.
194
+ 4. Prune telemetry (`telemetrySelect`) to omit heavy blocks during large sweeps.
195
+ 5. Lock seed and record baseline; adjust one lever at a time.
196
+
197
+ ### Determinism Caveat
198
+ Parallel evaluation introduces nondeterministic ordering of floating-point accumulation if your cost function is stateful. For strict reproducibility benchmark with `threads=1`.
199
+
200
+ ### Example Minimal Perf Config
201
+ ```ts
202
+ const neat = new Neat(inputs, outputs, fitness, {
203
+ popsize: 120,
204
+ threads: 8,
205
+ telemetry:{ enabled:true, performance:true },
206
+ telemetrySelect:['performance','complexity'],
207
+ adaptiveMutation:{ enabled:true, strategy:'twoTier' },
208
+ fastMode:true
209
+ });
210
+ ```
211
+
212
+ Monitor `getTelemetry().slice(-1)[0].perf` for current timings.
213
+
214
+ ### Memory Optimizations (Activation Pooling & Precision)
215
+ `Network` constructor now accepts:
216
+ ```ts
217
+ new Network(input, output, {
218
+ activationPrecision: 'f32', // default 'f64'; use float32 activations
219
+ reuseActivationArrays: true, // reuse a pooled output buffer each forward pass
220
+ returnTypedActivations: true // return the pooled Float32Array/Float64Array directly (no Array clone)
221
+ });
222
+ ```
223
+ Guidelines:
224
+ * Use `activationPrecision:'f32'` for large populations or inference batches where 1e-7 precision loss is acceptable.
225
+ * Enable `reuseActivationArrays` to eliminate per-call allocation of output arrays (avoid mutating returned array between passes if reusing!).
226
+ * Set `returnTypedActivations:false` (default) if consumer code expects a plain JS array; this will clone when pooling is on.
227
+ * For maximum throughput (e.g., evaluation workers), combine all three.
228
+
229
+ These options are conservative by default to preserve existing test expectations.
230
+
231
+ ### Future Improvements
232
+ Planned: micro-batch evaluation, worker task stealing, SIMD/WASM kernels, adaptive Lamarckian schedules.
233
+
234
+ ### Deterministic Chain Mode (Test Utility)
235
+ `config.deterministicChainMode` (default: `false`) enables a simplified deterministic variant of the `ADD_NODE` structural mutation used strictly for tests that must guarantee a predictable deep linear chain.
236
+
237
+ When enabled BEFORE constructing / mutating a `Network`:
238
+ * Each `ADD_NODE` splits the terminal connection of the current single input→…→output chain (following the first outgoing edge each step).
239
+ * The original connection is replaced by two new ones (from→newHidden, newHidden→to).
240
+ * Any alternate outgoing edges encountered along the chain are pruned to preserve strict linearity.
241
+ * A direct input→output shortcut is removed once at least one hidden node exists, ensuring depth grows.
242
+
243
+ Intended Usage:
244
+ ```ts
245
+ import { config } from 'neataptic';
246
+ config.deterministicChainMode = true; // enable
247
+ const net = new Network(1,1);
248
+ for (let i=0;i<5;i++) net.mutate(methods.mutation.ADD_NODE); // guaranteed 5 hidden chain
249
+ config.deterministicChainMode = false; // restore (recommended)
250
+ ```
251
+ Rationale:
252
+ Normal evolutionary heuristics are stochastic and may add branches; some legacy tests asserted an exact depth progression. The flag isolates that legacy requirement without constraining typical evolutionary runs. Avoid using this mode in production evolution— it suppresses beneficial structural diversity.
253
+
254
+ Invariants (enforced after each deterministic `ADD_NODE`):
255
+ 1. Exactly one outgoing forward edge per non-output node along the primary chain.
256
+ 2. No direct input→output edge after the first hidden node is inserted.
257
+ 3. Hidden node count increments by 1 per `ADD_NODE` call (barring impossible edge cases like empty connection sets).
258
+
259
+ If you need to debug chain growth without enabling global warnings, temporarily set a bespoke flag (e.g. `config.debugDeepPath`) and add localized logging; persistent debug output has been removed to keep test noise low.
260
+
261
+
262
+ ## New Evolution Enhancements
263
+
264
+ Key advanced NEAT features now included:
265
+ * Multi-objective Pareto evolution (fast non-dominated sort) with pluggable objectives.
266
+ * Runtime objective registration: add/remove custom objectives without rewriting core sorter.
267
+ * Hypervolume proxy + Pareto front size telemetry (`getTelemetry()` now includes `fronts`, optional `hv`).
268
+ * Structural entropy proxy (degree-distribution entropy) available for custom objectives.
269
+ * Adaptive complexity budget (nodes & connections) with slope‑aware growth / contraction and diversity modulation.
270
+ * Species extended history (mean complexity, novelty, compatibility, entropy) when `speciesAllocation.extendedHistory=true`.
271
+ * Diversity pressure: motif frequency rarity bonus; novelty archive blending & optional sparse pruning.
272
+ * Self-adaptive mutation rates & amounts (strategies: twoTier, exploreLow, anneal) plus operator bandit selection.
273
+ * Persistent Pareto archive snapshots (first front) retrievable via `getParetoArchive()`.
274
+ * Performance profiling (evaluation & evolution durations) opt-in via `telemetry:{ performance:true }`.
275
+ * Complexity telemetry block (mean/max nodes/connections, enabled ratio, growth deltas) via `telemetry:{ complexity:true }`.
276
+ * Optional hypervolume scalar (`hv`) for first front via `telemetry:{ hypervolume:true }`.
277
+ * Lineage tracking: telemetry `lineage` block now includes `{ parents:[id1,id2], depthBest, meanDepth, inbreeding }` when `lineageTracking` (default true). Depth accumulates as max-parent-depth+1; `inbreeding` counts self-matings in last reproduction phase.
278
+ * Auto entropy objective: enable `multiObjective:{ enabled:true, autoEntropy:true }` to add a structural entropy maximization objective automatically.
279
+ * Adaptive dominance epsilon: `multiObjective:{ adaptiveEpsilon:{ enabled:true, targetFront:~sqrt(pop), adjust:0.002 } }` tunes `dominanceEpsilon` to stabilize first-front size.
280
+ * Reference-point hypervolume (optional): supply `multiObjective.refPoint` (array or `'auto'`) for improved HV scalar (overwrites proxy when `telemetry.hypervolume` set).
281
+ * Pareto front objective vectors export via `exportParetoFrontJSONL()`.
282
+ * Extended species metrics: variance, innovation range, enabled ratio, turnover, delta complexity & score.
283
+ * Adaptive novelty archive insertion threshold (`novelty.dynamicThreshold`) to target insertion rate.
284
+ * Inactive objective pruning: `multiObjective.pruneInactive` automatically removes stagnant objectives (zero range) after a window.
285
+ * Fast mode auto-tuning: `fastMode:true` reduces heavy sampling defaults (diversity, novelty) for faster iteration.
286
+ * Adaptive target species: `adaptiveTargetSpecies` maps structural entropy to a dynamic `targetSpecies` value (feeds compatibility controller).
287
+ * Auto distance coefficient tuning: `autoDistanceCoeffTuning` adjusts excess/disjoint coefficients based on entropy deviation.
288
+ * Adaptive pruning: `adaptivePruning` gently increases sparsity toward target using live complexity metrics.
289
+ * Objective importance telemetry (`objImportance`) with per-generation range & variance per objective.
290
+ * Objective lifecycle events (`objEvents`) and ages (`objAges`).
291
+
292
+ ### Multi-Objective Usage
293
+
294
+ Default objectives (if `multiObjective.enabled`): maximize fitness (your score) + minimize complexity (`nodes` or `connections`).
295
+
296
+ Register additional objectives at runtime:
297
+ ```ts
298
+ const neat = new Neat(4,2, fitnessFn, { popsize: 50, multiObjective:{ enabled:true, complexityMetric:'nodes' } });
299
+ // Add structural entropy (maximize)
300
+ neat.registerObjective('entropy','max', g => (neat as any)._structuralEntropy(g));
301
+ await neat.evaluate();
302
+ await neat.evolve();
303
+ console.log(neat.getObjectives()); // [{key:'fitness',...},{key:'complexity',...},{key:'entropy',...}]
304
+ const fronts = neat.getParetoFronts(); // Array<Network[]> for leading fronts
305
+ ```
306
+ Remove custom objectives:
307
+ ```ts
308
+ neat.clearObjectives(); // reverts to default pair (fitness + complexity)
309
+ ```
310
+ Inspect per-genome metrics:
311
+ ```ts
312
+ neat.getMultiObjectiveMetrics(); // [{ rank, crowding, score, nodes, connections }, ...]
313
+ ```
314
+
315
+ Telemetry front sizes & hypervolume proxy:
316
+ ```ts
317
+ neat.getTelemetry().slice(-1)[0]; // { gen, best, species, hyper, fronts:[f0,f1,...] }
318
+ ```
319
+ Enable complexity + hypervolume fields:
320
+ ```ts
321
+ const neat = new Neat(4,2, fit, { telemetry:{ enabled:true, complexity:true, hypervolume:true } });
322
+ await neat.evolve();
323
+ console.log(neat.getTelemetry().slice(-1)[0].complexity); // { meanNodes, meanConns, ... }
324
+ ```
325
+
326
+ ### Adaptive Complexity Budget
327
+ Configure an adaptive schedule that expands limits when improvement slope is positive and contracts during stagnation:
328
+ ```ts
329
+ complexityBudget: {
330
+ enabled:true,
331
+ mode:'adaptive',
332
+ maxNodesStart: input+output+2,
333
+ maxNodesEnd: (input+output+2)*6,
334
+ improvementWindow: 8,
335
+ increaseFactor:1.15,
336
+ stagnationFactor:0.93,
337
+ minNodes: input+output+2,
338
+ maxConnsStart: 40,
339
+ maxConnsEnd: 400
340
+ }
341
+ ```
342
+ Linear schedule alternative: set `mode:'linear'` and optionally `horizon` (generations) to interpolate from `maxNodesStart` to `maxNodesEnd`.
343
+
344
+ ### Species Extended History
345
+ If `speciesAllocation.extendedHistory:true`, each generation stores per-species stats:
346
+ ```ts
347
+ [{ generation, stats:[ { id, size, best, lastImproved, age, meanNodes, meanConns, meanScore, meanNovelty, meanCompat, meanEntropy, varNodes, varConns, deltaMeanNodes, deltaMeanConns, deltaBestScore, turnoverRate, meanInnovation, innovationRange, enabledRatio } ] }]
348
+ ```
349
+ Key new fields:
350
+ - `age`: generations since species creation.
351
+ - `varNodes/varConns`: intra-species variance of nodes/connections.
352
+ - `deltaMean*` & `deltaBestScore`: per-generation change indicators.
353
+ - `turnoverRate`: fraction of members new vs previous generation.
354
+ - `innovationRange`: spread of innovation IDs inside species.
355
+ - `enabledRatio`: enabled / total connections ratio (structural activation health).
356
+
357
+ ### Diversity & Novelty
358
+ Enable novelty search blending:
359
+ ```ts
360
+ novelty:{ enabled:true, descriptor: g => g.nodes.map(n=>n.bias).slice(0,8), k:10, blendFactor:0.3 }
361
+ ```
362
+ Enable motif rarity pressure:
363
+ ```ts
364
+ diversityPressure:{ enabled:true, motifSample:25, penaltyStrength:0.05 }
365
+ ```
366
+ Adaptive novelty threshold targeting an archive insertion rate:
367
+ ```ts
368
+ novelty:{
369
+ enabled:true,
370
+ descriptor:g=>[g.nodes.length, g.connections.length],
371
+ archiveAddThreshold:0.5,
372
+ dynamicThreshold:{ enabled:true, targetRate:0.15, adjust:0.1, min:0.01, max:10 }
373
+ }
374
+ ```
375
+ After each evaluation the threshold is nudged up/down so the fraction of inserted descriptors approximates `targetRate`.
376
+
377
+ ### Inactive Objective Pruning
378
+
379
+ If you experiment with many custom objectives it is common for some to become constant (providing no ranking discrimination). Enable automatic removal of such stagnant objectives:
380
+
381
+ ```ts
382
+ multiObjective:{
383
+ enabled:true,
384
+ objectives:[
385
+ { key:'fitness', direction:'max', accessor: g => g.score },
386
+ { key:'novelty', direction:'max', accessor: g => (g as any)._novelty }
387
+ ],
388
+ pruneInactive:{ enabled:true, window:5, rangeEps:1e-9, protect:['fitness'] }
389
+ }
390
+ ```
391
+
392
+ Mechanics:
393
+ * Each generation the range (max-min) for every objective is computed.
394
+ * If the range stays below `rangeEps` for `window` consecutive generations the objective is removed.
395
+ * Keys listed in `protect` are never removed (even if stagnant).
396
+ * Telemetry will reflect the pruned objective list from the following generation.
397
+
398
+ Use this to keep the Pareto sorter focused on informative axes.
399
+
400
+ ### Fast Mode Auto-Tuning
401
+
402
+ When iterating quickly or running inside tests you can set `fastMode:true` to scale down expensive sampling defaults:
403
+
404
+ ```ts
405
+ const neat = new Neat(8,3, fitFn, {
406
+ popsize: 80,
407
+ fastMode: true,
408
+ diversityMetrics:{ enabled:true }, // pairSample / graphletSample auto-lowered
409
+ novelty:{ enabled:true, descriptor: g=>[g.nodes.length,g.connections.length] } // k auto-lowered if not explicitly set
410
+ });
411
+ ```
412
+
413
+ Auto adjustments (only if corresponding option fields are undefined):
414
+ * `diversityMetrics.pairSample`: 40 -> 20
415
+ * `diversityMetrics.graphletSample`: 60 -> 30
416
+ * `novelty.k`: lowered to 5
417
+
418
+ The tuning runs once on first diversity stats computation. Override by explicitly supplying your own values.
419
+
420
+ Lineage depth diversity (auto when lineageTracking + diversityMetrics):
421
+ `diversity` object gains:
422
+ * `lineageMeanDepth`: mean `_depth` over population.
423
+ * `lineageMeanPairDist`: sampled mean absolute depth difference between genome pairs (structure ancestry dispersion).
424
+ Telemetry `lineage` block now also includes:
425
+ * `ancestorUniq`: average Jaccard distance (0..1) between ancestor sets of sampled genome pairs within a small depth window (higher = more genealogical diversification).
426
+
427
+ Use these to detect genealogical stagnation (both remaining near zero) vs broad exploration (rising pair distance).
428
+
429
+ ### Lineage Pressure & Anti-Inbreeding (Optional)
430
+ Apply score adjustments based on lineage structure (depth dispersion) or penalize inbreeding (high ancestor overlap):
431
+ ```ts
432
+ lineagePressure:{
433
+ enabled:true,
434
+ mode:'antiInbreeding', // 'penalizeDeep' | 'rewardShallow' | 'spread' | 'antiInbreeding'
435
+ strength:0.02, // generic scaling for depth modes
436
+ ancestorWindow:4, // generations to look back when computing ancestor sets
437
+ inbreedingPenalty:0.04, // override penalty scaling (defaults to strength*2)
438
+ diversityBonus:0.02 // bonus scaling for very distinct parent lineages
439
+ }
440
+ ```
441
+ Modes:
442
+ * `penalizeDeep`: subtracts proportional penalty `-(depth-target)*strength` when depth exceeds target.
443
+ * `rewardShallow`: adds proportional bonus for depths below target.
444
+ * `spread`: encourages dispersion around mean depth (boost far-from-mean, cap excessive depth).
445
+ * `antiInbreeding`: computes Jaccard overlap of ancestor sets of both parents (including parents) within `ancestorWindow` generations. Penalizes high overlap (>0.75) and rewards very distinct ancestry (<0.25). Penalty/bonus magnitude scales with overlap distance and the configured penalty/bonus parameters.
446
+
447
+ Runs after evaluation/speciation before sorting so it integrates with all other mechanisms.
448
+
449
+ ### RNG State Snapshot & Reproducibility
450
+ When a numeric `seed` is provided, a fast deterministic PRNG drives stochastic choices. You can snapshot/restore mid-run:
451
+ ```ts
452
+ const neat = new Neat(3,1, fit, { seed:1234 });
453
+ await neat.evolve();
454
+ const snap = neat.snapshotRNGState(); // { state }
455
+ // ... later
456
+ neat.restoreRNGState(snap);
457
+ // Serialize
458
+ const json = neat.exportRNGState();
459
+ // New instance (even without seed) can resume deterministic sequence
460
+ const neat2 = new Neat(3,1, fit, {});
461
+ neat2.importRNGState(json);
462
+ ```
463
+ Restoring installs the internal deterministic generator if it wasn't active.
464
+
465
+ ### Operator Adaptation & Mutation Self-Adaptation
466
+ Per-genome mutation rate/amount adapt each generation under strategies (`twoTier`, `exploreLow`, `anneal`). Use:
467
+ ```ts
468
+ adaptiveMutation:{ enabled:true, strategy:'twoTier', sigma:0.08, adaptAmount:true }
469
+ ```
470
+ Operator success statistics (bandit + weighting):
471
+ ```ts
472
+ neat.getOperatorStats(); // [{ name, success, attempts }, ...]
473
+ ```
474
+ Telemetry now also records an `ops` array each generation with lightweight success/attempt counts.
475
+
476
+ ### Objective Lifetime & Species Allocation Telemetry
477
+ Each telemetry entry now may include:
478
+ * `objectives`: array of active objective keys that generation (already used internally for auditing dynamic scheduling).
479
+ * `objAges`: object mapping objective key -> consecutive generations active. When an objective is removed (e.g. via pruning or dynamic delay) its age resets to 0 if later reintroduced.
480
+ * `speciesAlloc`: array of `{ id, alloc }` giving number of offspring allocated to each species in the reproduction phase that produced the current generation. Useful for diagnosing allocation fairness / starvation.
481
+ * `objEvents`: array of `{ type:'add'|'remove', key }` describing objective lifecycle changes that occurred for that generation (emitted when dynamic scheduling or pruning alters the active set).
482
+
483
+ Example:
484
+ ```ts
485
+ const neat = new Neat(4,2, fit, { popsize:40, multiObjective:{ enabled:true, autoEntropy:true, dynamic:{ enabled:true, addComplexityAt:2, addEntropyAt:5 } }, telemetry:{ enabled:true } });
486
+ for (let g=0; g<10; g++) { await neat.evaluate(); await neat.evolve(); }
487
+ const last = neat.getTelemetry().slice(-1)[0];
488
+ console.log(last.objectives); // ['fitness','complexity','entropy']
489
+ console.log(last.objAges); // { fitness:10, complexity:9, entropy:5 }
490
+ console.log(last.speciesAlloc); // [{ id:1, alloc:7 }, { id:2, alloc:6 }, ...]
491
+ ```
492
+
493
+ ### CSV Export Columns (Extended)
494
+ `exportTelemetryCSV()` now conditionally includes the following columns when present:
495
+ * `rng` (deterministic RNG state snapshot if `rngState:true` + seed supplied)
496
+ * `ops` (JSON array of operator stats)
497
+ * `objectives` (JSON array of active objective keys)
498
+ * `objAges` (JSON object of key->age)
499
+ * `speciesAlloc` (JSON array of per-species offspring allocations)
500
+ * `objEvents` (JSON array of add/remove objective lifecycle events)
501
+ Existing flattened sections remain: `complexity.*`, `perf.*`, `lineage.*`, `diversity.lineageMeanDepth`, `diversity.lineageMeanPairDist`, and `fronts`.
502
+
503
+ Performance profiling telemetry (optional):
504
+ ```ts
505
+ const neat = new Neat(4,2, fitnessFn, { telemetry:{ enabled:true, performance:true } });
506
+ await neat.evaluate(); await neat.evolve();
507
+ const last = neat.getTelemetry().slice(-1)[0];
508
+ console.log(last.perf); // { evalMs, evolveMs }
509
+
510
+ Export telemetry:
511
+ ```ts
512
+ neat.exportTelemetryJSONL(); // JSON Lines
513
+ neat.exportTelemetryCSV(); // CSV (flattened complexity/perf)
514
+ ```
515
+
516
+ Pareto archive snapshots:
517
+ ```ts
518
+ neat.getParetoArchive(); // [{ gen, size, genomes:[{ id, score, nodes, connections }] }, ...]
519
+ ```
520
+
521
+ Species history (per-species longitudinal metrics) CSV export:
522
+ ```ts
523
+ // Last ~200 generations (configurable)
524
+ const csv = neat.exportSpeciesHistoryCSV();
525
+ // Columns include generation plus dynamic keys: id,size,best,lastImproved,age,meanNodes,meanConns,meanScore,meanNovelty,meanCompat,meanEntropy,varNodes,varConns,deltaMeanNodes,deltaMeanConns,deltaBestScore,turnoverRate,meanInnovation,innovationRange,enabledRatio (when extendedHistory enabled)
526
+ ```
527
+
528
+ These APIs are evolving; consult source `src/neat.ts` for full option surfaces while docs finalize.
529
+
530
+ Full option & telemetry reference: [docs/API.md](./docs/API.md)
531
+
532
+ # Network Constructor Update
533
+
534
+ The `Network` class constructor now supports an optional third parameter for configuration:
535
+
536
+ ```ts
537
+ new Network(input: number, output: number, options?: { minHidden?: number })
538
+ ```
539
+ - `input`: Number of input nodes (required)
540
+ - `output`: Number of output nodes (required)
541
+ - `options.minHidden`: (optional) If set, enforces a minimum number of hidden nodes. If omitted or 0, no minimum is enforced. This allows true 1-1 (input-output only) networks.
542
+
543
+ **Example:**
544
+ ```ts
545
+ // Standard 1-1 network (no hidden nodes)
546
+ const net = new Network(1, 1);
547
+
548
+ // Enforce at least 3 hidden nodes
549
+ const netWithHidden = new Network(2, 1, { minHidden: 3 });
550
+ ```
551
+
552
+ # Neat Evolution minHidden Option
553
+
554
+ The `minHidden` option can also be passed to the `Neat` class to enforce a minimum number of hidden nodes in all evolved networks:
555
+
556
+ ```ts
557
+ import Neat from './src/neat';
558
+ const neat = new Neat(2, 1, fitnessFn, { popsize: 50, minHidden: 5 });
559
+ ```
560
+ - All networks created by the evolutionary process will have at least 5 hidden nodes.
561
+ - This is useful for ensuring a minimum network complexity during neuro-evolution.
562
+
563
+ See tests in `test/neat.ts` for usage and verification.
564
+
565
+ ---
566
+
567
+ # ONNX Import/Export
568
+
569
+ NeatapticTS now supports exporting to and importing from ONNX format for strictly layered MLPs. This allows interoperability with other machine learning frameworks.
570
+
571
+ - Use `exportToONNX(network)` to export a network to ONNX.
572
+ - Use `importFromONNX(onnxModel)` to import a compatible ONNX model as a `Network` instance.
573
+
574
+ See tests in `test/network/onnx.export.test.ts` and `test/network/onnx.import.test.ts` for usage examples.
575
+
576
+ ---
577
+
578
+ ### Further notices
579
+
580
+ NeatapticTS is based on [Neataptic](https://github.com/wagenaartje/neataptic). Parts of [Synaptic](https://github.com/cazala/synaptic) were used to develop Neataptic.
581
+
582
+ The neuro-evolution algorithm used is the [Instinct](https://medium.com/@ThomasWagenaar/neuro-evolution-on-steroids-82bd14ddc2f6) algorithm.
583
+
584
+ ##### Original [repository](https://github.com/wagenaartje/neataptic) in now [unmaintained](https://github.com/wagenaartje/neataptic/issues/112)
585
+
586
+ ---
587
+
588
+ ## Added Training Features
589
+
590
+ - Learning rate schedulers: fixed, step, exp, inv, cosine annealing, cosine annealing w/ warm restarts, linear warmup+decay, reduce-on-plateau.
591
+ - Regularization (L1, L2, custom function), dropout, DropConnect.
592
+ - Per-iteration `metricsHook` exposing `{ iteration, error, gradNorm }`.
593
+ - Checkpointing (`best`, `last`) via `checkpoint.save` callback.
594
+ - Advanced optimizers: `sgd`, `rmsprop`, `adagrad`, `adam`, `adamw`, `amsgrad`, `adamax`, `nadam`, `radam`, `lion`, `adabelief`, and `lookahead` wrapper.
595
+ - Gradient improvements: per-call gradient clipping (global / layerwise, norm or percentile), micro-batch gradient accumulation (`accumulationSteps`) independent of data `batchSize`, optional mixed precision (loss-scaled with dynamic scaling) training.
596
+
597
+ ### Gradient Improvements
598
+
599
+ Gradient clipping (optional):
600
+ ```ts
601
+ net.train(data, { iterations:500, rate:0.01, optimizer:'adam', gradientClip:{ mode:'norm', maxNorm:1 } });
602
+ net.train(data, { iterations:500, rate:0.01, optimizer:'adam', gradientClip:{ mode:'percentile', percentile:99 } });
603
+ // Layerwise variants: 'layerwiseNorm' | 'layerwisePercentile'
604
+ ```
605
+
606
+ Micro-batch accumulation (simulate larger effective batch without increasing memory):
607
+ ```ts
608
+ // Process 1 sample at a time, accumulate 8 micro-batches, then apply one optimizer step
609
+ net.train(data, { iterations:100, rate:0.005, batchSize:1, accumulationSteps:8, optimizer:'adam' });
610
+ ```
611
+ If `accumulationSteps > 1`, gradients are averaged before the optimizer step so results match a single larger batch (deterministic given same sample order).
612
+
613
+ Mixed precision (simulated FP16 gradients with FP32 master weights + dynamic loss scaling):
614
+ ```ts
615
+ net.train(data, { iterations:300, rate:0.01, optimizer:'adam', mixedPrecision:{ lossScale:1024 } });
616
+ ```
617
+ Behavior:
618
+ * Stores master FP32 copies of weights/biases (`_fp32Weight`, `_fp32Bias`).
619
+ * Scales gradients during accumulation; unscales before clipping / optimizer update; adjusts `lossScale` down on overflow (NaN/Inf), attempts periodic doubling after sustained stable steps (configurable via `mixedPrecision.dynamic`).
620
+ * Raw gradient norm (pre-optimizer, post-scaling/clipping) exposed via metrics hook as `gradNormRaw` (legacy post-update norm still `gradNorm`).
621
+ * Pure JS numbers remain 64-bit; this is a functional simulation for stability and future WASM/GPU backends.
622
+
623
+ Clipping modes:
624
+ | mode | scope | description |
625
+ |------|-------|-------------|
626
+ | norm | global | Clip global L2 gradient norm to `maxNorm`. |
627
+ | percentile | global | Clamp individual gradients above given percentile magnitude. |
628
+ | layerwiseNorm | per layer | Apply norm clipping per architectural layer (fallback per node if no layer info). Optionally splits weight vs bias groups via `gradientClip.separateBias`. |
629
+ | layerwisePercentile | per layer | Percentile clamp per architectural layer (fallback per node). Supports `separateBias`. |
630
+
631
+ Notes:
632
+ * Provide either `{ mode, maxNorm? , percentile? }` or shorthand `{ maxNorm }` / `{ percentile }`.
633
+ * Percentile ranking is magnitude-based.
634
+ * Accumulation averages gradients; to sum instead (rare) scale `rate` accordingly.
635
+ * Dynamic loss scaling heuristics: halves on detected overflow, doubles after configurable stable steps (default 200) within bounds `[minScale,maxScale]`.
636
+ * Config: `mixedPrecision:{ lossScale:1024, dynamic:{ minScale:1, maxScale:131072, increaseEvery:300 } }`.
637
+ * Accumulation reduction: default averages gradients; specify `accumulationReduction:'sum'` to sum instead (then adjust learning rate manually, e.g. multiply by 1/accumulationSteps if you want equivalent averaging semantics).
638
+ * Layerwise clipping: set `gradientClip:{ mode:'layerwiseNorm', maxNorm:1, separateBias:true }` to treat biases separately from weights.
639
+ * Two gradient norms tracked: raw (pre-update) and legacy (post-update deltas). Future APIs may expose both formally.
640
+ * Access stats: `net.getTrainingStats()` -> `{ gradNorm, gradNormRaw, lossScale, optimizerStep, mp:{ good, bad, overflowCount, scaleUps, scaleDowns, lastOverflowStep } }`.
641
+ * Test hook (not for production): `net.testForceOverflow()` forces the next mixed-precision step to register an overflow (used in unit tests to validate telemetry paths).
642
+ * Gradient clip grouping count: `net.getLastGradClipGroupCount()` (useful to verify separateBias effect).
643
+ * Rate helper for accumulation: `const adjRate = Network.adjustRateForAccumulation(rate, accumulationSteps, accumulationReduction)`.
644
+ * Deterministic seeding: `new Network(4,2,{ seed:123 })` or later `net.setSeed(123)` ensures reproducible initial weights, biases, connection order, and mutation randomness for training (excluding certain static reconstruction paths). For NEAT evolution pass `seed` in `new Neat(...,{ seed:999 })`.
645
+ * Overflow telemetry: during mixed precision training, `overflowCount` increments on detected NaN/Inf when unscaling gradients; `scaleUps` / `scaleDowns` count dynamic loss scale adjustments.
646
+
647
+ ### Learning Rate Scheduler Usage
648
+
649
+ ```ts
650
+ import methods from './src/methods/methods';
651
+ const net = new Network(2,1);
652
+ const ratePolicy = methods.Rate.cosineAnnealingWarmRestarts(200, 1e-5, 2);
653
+ net.train(data, { iterations: 1000, rate: 0.1, ratePolicy });
654
+ ```
655
+
656
+ Reduce-on-plateau automatically receives current error because `train` detects a 3-arg scheduler:
657
+ ```ts
658
+ const rop = methods.Rate.reduceOnPlateau({ patience: 20, factor: 0.5, minRate: 1e-5 });
659
+ net.train(data, { iterations: 5000, rate: 0.05, ratePolicy: rop });
660
+ ```
661
+
662
+ #### Early Stopping Extensions
663
+
664
+ You can enable moving-average smoothing and independent early-stop patience separate from any scheduler plateau logic. You can also give the learning-rate scheduler (e.g. reduce-on-plateau) its OWN smoothing configuration if you want it to react differently than early stopping:
665
+
666
+ ```ts
667
+ net.train(data, {
668
+ rate: 0.05,
669
+ iterations: 10000,
670
+ error: 0.02, // target threshold (checked on SMOOTHED early-stop error)
671
+ movingAverageType: 'median',
672
+ movingAverageWindow: 7, // EARLY STOP smoothing (robust to spikes)
673
+ earlyStopPatience: 25,
674
+ earlyStopMinDelta: 0.0005,
675
+
676
+ ---
677
+ // Separate plateau smoothing: scheduler sees a faster EMA over shorter horizon
678
+ plateauMovingAverageType: 'ema',
679
+ plateauMovingAverageWindow: 3,
680
+ plateauEmaAlpha: 0.6, // (otherwise defaults to 2/(N+1))
681
+ ratePolicy: methods.Rate.reduceOnPlateau({ patience: 10, factor: 0.5, minRate: 1e-5 })
682
+ });
683
+ ```
684
+
685
+ Behavior details:
686
+ Smoothing types (set `movingAverageType`):
687
+
688
+ | Type | Description | Key Params | When to Use |
689
+ |------|-------------|------------|-------------|
690
+ | sma | Simple moving average over window | movingAverageWindow | General mild smoothing |
691
+ | ema | Exponential moving average | emaAlpha (default 2/(N+1)) | Faster reaction than SMA |
692
+ | adaptive-ema | Dual-track variance-adaptive EMA (takes min of baseline & adaptive for guaranteed non-worse smoothing) | emaAlpha, adaptiveAlphaMin/Max, adaptiveVarianceFactor | Volatile early phase responsiveness with stability guarantee |
693
+ | wma | Linear weighted (recent heavier) | movingAverageWindow | Slightly more responsive than SMA |
694
+ | median | Moving median | movingAverageWindow | Spike / outlier robustness |
695
+ | trimmed | Trimmed mean (drops extremes) | trimmedRatio (0-0.5) | Moderate outliers; keep efficiency |
696
+ | gaussian | Gaussian-weighted window (recent emphasized smoothly) | gaussianSigma (default N/3) | Want smooth taper vs linear weights |
697
+
698
+ Additional options:
699
+ * trimmedRatio: fraction trimmed from each side for 'trimmed' (default 0.1)
700
+ * gaussianSigma: width for 'gaussian' (default window/3)
701
+ * trackVariance: true to compute running variance & min (exposed via metricsHook)
702
+ * adaptiveAlphaMin / adaptiveAlphaMax / adaptiveVarianceFactor for adaptive-ema control
703
+
704
+ Metrics hook additions when smoothing active: `rawError`, `runningVariance`, `runningMin` (if trackVariance true) alongside smoothed `error`.
705
+ * Target `error` comparison and improvement tracking both use the smoothed value.
706
+ * `earlyStopPatience` counts iterations since the last smoothed improvement > `earlyStopMinDelta`.
707
+ * Plateau smoothing: provide any of `plateauMovingAverageWindow`, `plateauMovingAverageType`, `plateauEmaAlpha`, `plateauTrimmedRatio`, `plateauGaussianSigma`, `plateauAdaptiveAlphaMin/Max`, `plateauAdaptiveVarianceFactor`.
708
+ - If none are supplied the scheduler reuses the early-stop smoothing.
709
+ - If supplied, metricsHook receives an extra field `plateauError` (the separately smoothed value supplied to the scheduler), while `error` remains the early-stop smoothed value.
710
+ - Plateau adaptive-ema uses the same dual-track min(adaptive, baseline) logic.
711
+ * This does not interfere with `Rate.reduceOnPlateau` patience; they are independent.
712
+
713
+
714
+ #### Scheduler Reference
715
+
716
+ | Scheduler | Factory Call | Key Params | Behavior |
717
+ |-----------|--------------|------------|----------|
718
+ | fixed | `Rate.fixed()` | – | Constant learning rate. |
719
+ | step | `Rate.step(gamma?, stepSize?)` | gamma (default 0.9), stepSize (default 100) | Multiplies rate by `gamma` every `stepSize` iterations. |
720
+ | exp | `Rate.exp(gamma?)` | gamma (default 0.999) | Exponential decay: `rate * gamma^t`. |
721
+ | inv | `Rate.inv(gamma?, power?)` | gamma (0.001), power (2) | Inverse time decay: `rate / (1 + γ * t^p)`. |
722
+ | cosine annealing | `Rate.cosineAnnealing(period?, minRate?)` | period (1000), minRate (0) | Cosine decay from base to `minRate` each period. |
723
+ | cosine warm restarts | `Rate.cosineAnnealingWarmRestarts(initialPeriod, minRate?, tMult?)` | initialPeriod, minRate (0), tMult (2) | Cosine cycles with period multiplied by `tMult` after each restart. |
724
+ | linear warmup + decay | `Rate.linearWarmupDecay(totalSteps, warmupSteps?, endRate?)` | totalSteps, warmupSteps (auto 10%), endRate (0) | Linear ramp to base, then linear decay to `endRate`. |
725
+ | reduce on plateau | `Rate.reduceOnPlateau(opts)` | patience, factor, minRate, threshold | Monitors error; reduces current rate by `factor` after `patience` non-improving iterations. |
726
+
727
+ Notes:
728
+ * All scheduler factories return a function `(baseRate, iteration)` except `reduceOnPlateau`, which returns `(baseRate, iteration, error)`; `train` auto-detects and supplies `error` if the function arity is 3.
729
+ * You can wrap or compose schedulers—see below for a composition pattern.
730
+
731
+ #### Evolution Warning
732
+
733
+ `evolve()` now always emits a warning `"Evolution completed without finding a valid best genome"` when no suitable genome is selected (including zero-iteration runs) to aid testability and user feedback.
734
+
735
+ #### Composing Schedulers (Warmup then Plateau)
736
+
737
+ You can combine policies by writing a small delegator that switches logic after warmup completes and still passes error when required:
738
+
739
+ ```ts
740
+ import methods from './src/methods/methods';
741
+
742
+ const warmup = methods.Rate.linearWarmupDecay(500, 100, 0.1); // only use warmup phase portion
743
+ const plateau = methods.Rate.reduceOnPlateau({ patience: 15, factor: 0.5, minRate: 1e-5 });
744
+
745
+ // Hybrid policy: first 100 steps use linear ramp; afterward delegate to plateau (needs error)
746
+ const hybrid = (base: number, t: number, err?: number) => {
747
+ if (t <= 100) return warmup(base, t); // ignore decay tail by cutting early
748
+ // plateau expects error (3-arg); train will pass it because we define length >= 3 when we use 'err'
749
+ return plateau(base, t - 100, err!); // shift iteration so plateau's patience focuses on post-warmup
750
+ };
751
+
752
+ net.train(data, { iterations: 2000, rate: 0.05, ratePolicy: hybrid });
753
+ ```
754
+
755
+ For more elaborate chaining (e.g., staged cosine cycles then plateau), follow the same pattern: evaluate `t`, decide which inner policy to call, adjust `t` relative to that stage, and pass along `error` if the target policy needs it.
756
+
757
+
758
+ ### Metrics Hook & Checkpoints
759
+
760
+ ```ts
761
+ net.train(data, {
762
+ iterations: 800,
763
+ rate: 0.05,
764
+ metricsHook: ({ iteration, error, gradNorm }) => console.log(iteration, error, gradNorm),
765
+ checkpoint: {
766
+ best: true,
767
+ last: true,
768
+ save: ({ type, iteration, error, network }) => {/* persist */}
769
+ }
770
+ });
771
+ ```
772
+
773
+ ### DropConnect
774
+
775
+ ```ts
776
+ net.enableDropConnect(0.3);
777
+ net.train(data, { iterations: 300, rate: 0.05 });
778
+ net.disableDropConnect();
779
+ ```
780
+
781
+ ### Advanced Optimizers
782
+
783
+ Supply `optimizer` to `train` as a simple string (uses defaults) or a config object.
784
+
785
+ Basic (defaults):
786
+ ```ts
787
+ net.train(data, { iterations: 200, rate: 0.01, optimizer: 'adam' });
788
+ ```
789
+
790
+ Custom AdamW:
791
+ ```ts
792
+ net.train(data, {
793
+ iterations: 500,
794
+ rate: 0.005,
795
+ optimizer: { type: 'adamw', beta1: 0.9, beta2: 0.999, eps: 1e-8, weightDecay: 0.01 }
796
+ });
797
+ ```
798
+
799
+ Lion (sign-based update):
800
+ ```ts
801
+ net.train(data, { iterations: 300, rate: 0.001, optimizer: { type: 'lion', beta1: 0.9, beta2: 0.99 } });
802
+ ```
803
+
804
+ Adamax (robust to sparse large gradients):
805
+ ```ts
806
+ net.train(data, { iterations: 300, rate: 0.002, optimizer: { type: 'adamax', beta1: 0.9, beta2: 0.999 } });
807
+ ```
808
+
809
+ NAdam (Nesterov momentum style lookahead on first moment):
810
+ ```ts
811
+ net.train(data, { iterations: 300, rate: 0.001, optimizer: { type: 'nadam', beta1: 0.9, beta2: 0.999 } });
812
+ ```
813
+
814
+ RAdam (more stable early training variance):
815
+ ```ts
816
+ net.train(data, { iterations: 300, rate: 0.001, optimizer: { type: 'radam', beta1: 0.9, beta2: 0.999 } });
817
+ ```
818
+
819
+ AdaBelief (faster convergence / better generalization via surprise-based variance):
820
+ ```ts
821
+ net.train(data, { iterations: 300, rate: 0.001, optimizer: { type: 'adabelief', beta1: 0.9, beta2: 0.999 } });
822
+ ```
823
+
824
+ Lookahead (wraps a base optimizer; performs k fast steps then interpolates):
825
+ ```ts
826
+ net.train(data, {
827
+ iterations: 400,
828
+ rate: 0.01,
829
+ optimizer: { type: 'lookahead', baseType: 'adam', la_k: 5, la_alpha: 0.5 }
830
+ });
831
+ ```
832
+
833
+ Optimizer reference:
834
+
835
+ | Optimizer | Key Params (in object) | Notes |
836
+ |-------------|------------------------|-------|
837
+ | sgd | momentum | Nesterov momentum internally in propagate when update=true |
838
+ | rmsprop | eps | Uses fixed decay 0.9 / 0.1 split for cache |
839
+ | adagrad | eps | Cache accumulates squared grads (monotonic) |
840
+ | adam | beta1, beta2, eps | Standard bias correction |
841
+ | adamw | beta1, beta2, eps, weightDecay | Decoupled weight decay applied after adaptive step |
842
+ | amsgrad | beta1, beta2, eps | Maintains max second-moment vhat |
843
+ | adamax | beta1, beta2, eps | Infinity norm (u) instead of v |
844
+ | nadam | beta1, beta2, eps | Nesterov variant of Adam |
845
+ | radam | beta1, beta2, eps | Rectifies variance early in training |
846
+ | lion | beta1, beta2 | Direction = sign(beta1*m + beta2*m2) |
847
+ | adabelief | beta1, beta2, eps | Second moment of (g - m) (gradient surprise) |
848
+ | lookahead | baseType, la_k, la_alpha | Interpolates toward slow weights every k steps |
849
+
850
+ General notes:
851
+ * Step counter resets each `train` call (t starts at 1) for reproducibility.
852
+ * `lookahead.baseType` defaults to `adam` and cannot itself be `lookahead`.
853
+ * Only AdamW applies decoupled `weightDecay`; for others combine regularization if needed.
854
+ * Adamax may help with sparse or bursty gradients (uses infinity norm).\
855
+ * NAdam can yield slightly faster early progress due to lookahead on m.\
856
+ * RAdam mitigates the need for warmup; behaves like Adam after variance rectification threshold.\
857
+ * AdaBelief can reduce over-adaptation to noisy gradients by modeling belief deviation.\
858
+ * Lion performs well in some large-scale settings due to sign-based memory efficiency.
859
+
860
+ ### Label Smoothing
861
+
862
+ Cross-entropy with label smoothing discourages over-confident predictions.
863
+
864
+ ```ts
865
+ import methods from './src/methods/methods';
866
+ const loss = methods.Cost.labelSmoothing([1,0,0],[0.8,0.1,0.1],0.1);
867
+ ```
868
+
869
+ ### Weight Noise
870
+
871
+ Adds zero-mean Gaussian noise to weights on each *training* forward pass (inference unaffected). Original weights are restored immediately after the pass (noise is ephemeral / non-destructive).
872
+
873
+ Basic usage:
874
+ ```ts
875
+ net.enableWeightNoise(0.05); // global stdDev = 0.05
876
+ net.train(data, { iterations: 100, rate:0.05 });
877
+ net.disableWeightNoise();
878
+ ```
879
+
880
+ Per-hidden-layer std deviations (layered networks only):
881
+ ```ts
882
+ const layered = Architect.perceptron(4, 32, 16, 8, 2); // input, 3 hidden, output
883
+ layered.enableWeightNoise({ perHiddenLayer: [0.05, 0.02, 0.0] }); // third hidden layer noiseless
884
+ ```
885
+
886
+ Dynamic schedule (e.g. cosine decay) for global std:
887
+ ```ts
888
+ net.enableWeightNoise(0.1); // initial value (will be overridden by schedule each step)
889
+ net.setWeightNoiseSchedule(step => 0.1 * Math.cos(step / 500));
890
+ ```
891
+
892
+ Deterministic seeding / custom RNG (affects dropout, dropconnect, stochastic depth, weight noise sampling):
893
+ ```ts
894
+ net.setSeed(42); // reproducible stochastic regularization
895
+ // or provide a custom RNG
896
+ net.setRandom(() => myDeterministicGenerator.next());
897
+ ```
898
+
899
+ Diagnostics:
900
+ * Each connection temporarily stores last noise in `connection._wnLast` (for test / inspection).
901
+ * Global training forward pass count: `net.trainingStep`.
902
+ * Last skipped layers (stochastic depth): `net.lastSkippedLayers`.
903
+ * Regularization statistics (after any forward): `net.getRegularizationStats()` returns
904
+ `{ droppedHiddenNodes, totalHiddenNodes, droppedConnections, totalConnections, skippedLayers, weightNoise: { count, sumAbs, maxAbs, meanAbs } }`.
905
+ * To combine with DropConnect / Dropout the sampling is independent (noise applied before masking).
906
+
907
+ Gotchas:
908
+ * Per-layer noise ignores global schedule (schedule currently applies only when using a single global std). If you need both, emulate by updating `enableWeightNoise` each epoch.
909
+ * Very large noise (> weight scale) can destabilize gradients; start small (e.g. 1-10% of typical weight magnitude).
910
+
911
+ ### Stochastic Depth (Layer Drop)
912
+
913
+ Randomly skip (drop) entire hidden layers during training for deeper layered networks to reduce effective depth and encourage resilient representations.
914
+
915
+ ```ts
916
+ const deep = Architect.perceptron(8,16,16,16,4,2); // input + 4 hidden + output
917
+ deep.setSeed(123); // reproducible skipping
918
+ deep.setStochasticDepth([0.9,0.85,0.8,0.75]); // survival probabilities per hidden layer
919
+ deep.train(data, { iterations:500, rate:0.01 });
920
+ deep.disableStochasticDepth(); // inference uses full depth
921
+ ```
922
+
923
+ Runtime info:
924
+ * Recently skipped layer indices available via `(deep as any)._lastSkippedLayers` (for test / debugging).
925
+ * Surviving layer outputs are scaled by `1/p` to preserve expectation (like inverted dropout).
926
+ * Dynamic scheduling: `deep.setStochasticDepthSchedule((step, current) => current.map((p,i)=> Math.max(0.5, p - 0.0005*step)))` to slowly reduce survival probabilities (example).
927
+
928
+ Rules:
929
+ * Provide exactly one probability per hidden layer (input & output excluded).
930
+ * Probabilities must be in (0,1]. Use 1.0 to always keep a layer.
931
+ * A layer only skips if its input activation vector size equals its own size (simple identity passthrough). Otherwise it is forced to run to avoid shape mismatch.
932
+ * Stochastic depth and node-level dropout can co-exist; skipping occurs before dropout at deeper layers.
933
+ * Clear schedule: `deep.clearStochasticDepthSchedule()`.
934
+
935
+ Example combining schedule + stats capture:
936
+ ```ts
937
+ deep.setSeed(2025);
938
+ deep.setStochasticDepth([0.9,0.85,0.8]);
939
+ deep.setStochasticDepthSchedule((step, probs) => probs.map(p => Math.max(0.7, p - 0.0001*step)));
940
+ for (let i=0;i<10;i++) {
941
+ deep.activate(sampleInput, true);
942
+ console.log(deep.getRegularizationStats());
943
+ }
944
+ ```
945
+
946
+ ### DropConnect (recap)
947
+ Already supported: randomly drops individual connections per training pass.
948
+
949
+ ```ts
950
+ net.enableDropConnect(0.2);
951
+ net.train(data, { iterations:200, rate:0.02 });
952
+ net.disableDropConnect();
953
+ ```
954
+
955
+ ## Architecture & Evolution
956
+
957
+ ```
958
+ Structural pruning: magnitude-based & SNIP-style (|w * grad| saliency) prune+optional regrow, scheduled window.
959
+ Connection sparsification: progressive schedule toward target sparsity.
960
+ Neuroevolution: speciation (compatibility distance: excess, disjoint, weight diff), dynamic threshold (optional targetSpecies), kernel fitness sharing (quadratic within-species), stagnation injection (global refresh after N stagnant generations).
961
+ Innovation tracking: per-connection monotonically increasing counter (serialized); fallback Cantor pairing for missing.
962
+ Acyclic enforcement: optional; topological order cache for forward pass; cycle prevention via reachability test.
963
+ ```
964
+
965
+ Disabled gene handling:
966
+ Connections now carry an `enabled` flag (classic NEAT). Structural mutations or pruning routines may disable a connection instead of deleting it. During crossover:
967
+ * Matching genes inherit enabled state; if either parent disabled the gene, it remains disabled unless re-enabled probabilistically.
968
+ * Disjoint/excess genes retain their parent's state.
969
+ Re-enable probability is controlled by `reenableProb` (default 0.25). This allows temporarily silenced structure to be revived later, preserving historical innovation without immediate loss.
970
+
971
+ SNIP usage example:
972
+ ```ts
973
+ net.configurePruning({
974
+ start: 100,
975
+ end: 1000,
976
+ targetSparsity: 0.8,
977
+ frequency: 10,
978
+ regrowFraction: 0.1,
979
+ method: 'snip' // use saliency |w * grad|, falls back to |w| if no grad yet
980
+ });
981
+ ```
982
+
983
+ Notes:
984
+ * Saliency uses last accumulated gradient proxy (totalDeltaWeight or previousDeltaWeight) gathered during training steps.
985
+ * If gradients are zero/unused early, ranking gracefully reverts to pure magnitude.
986
+ * Regrow explores random new connections (respecting acyclic constraint) to maintain exploration.
987
+ * Pruning currently applies during gradient-based training, not inside the NEAT evolutionary loop (future option possible).
988
+ * Disabled genes are still serialized (`enabled:false`) and restored on load.
989
+
990
+ ### Evolution Options (selected)
991
+
992
+ | Option | Description | Default |
993
+ |--------|-------------|---------|
994
+ | `targetSpecies` | Desired number of species used by dynamic compatibility threshold steering | 8 |
995
+ | `sharingSigma` | Radius parameter for quadratic kernel fitness sharing | 3.0 |
996
+ | `globalStagnationGenerations` | Generations without global improvement before injecting fresh genomes (0 disables) | 30 |
997
+ | `reenableProb` | Probability a disabled connection gene is re-enabled during crossover | 0.25 |
998
+ | `evolutionPruning.startGeneration` | Generation index to begin structural pruning across genomes | – |
999
+ | `evolutionPruning.interval` | Apply pruning every N generations (default 1) | 1 |
1000
+ | `evolutionPruning.targetSparsity` | Final sparsity fraction (e.g. 0.8 keeps 20% connections) | – |
1001
+ | `evolutionPruning.rampGenerations` | Generations to linearly ramp 0 -> target sparsity | 0 (immediate) |
1002
+ | `evolutionPruning.method` | Prune ranking: 'magnitude' or 'snip' | magnitude |
1003
+ | `targetSpecies` | Desired species count for dynamic compatibility threshold controller | 8 |
1004
+ | `compatAdjust.kp` | Proportional gain for threshold adjustment | 0.3 |
1005
+ | `compatAdjust.ki` | Integral gain (slow corrective action) | 0.02 |
1006
+ | `compatAdjust.smoothingWindow` | EMA window for species count smoothing | 5 |
1007
+ | `compatAdjust.minThreshold` | Lower clamp for compatibility threshold | 0.5 |
1008
+ | `compatAdjust.maxThreshold` | Upper clamp for compatibility threshold | 10 |
1009
+ | `compatAdjust.decay` | Integral decay factor (anti-windup) | 0.95 |
1010
+ | `sharingSigma` | If > 0 enables kernel fitness sharing (score_i /= sum_j (1-(d_ij/σ)^2)) | 0 (off) |
1011
+ | `globalStagnationGenerations` | If >0 replace worst 20% genomes after N stagnant generations | 0 (off) |
1012
+
1013
+ ### Advanced Evolution Extensions
1014
+
1015
+ The TypeScript refactor adds several research-grade evolutionary controls. All are optional and off unless noted.
1016
+
1017
+ | Option Group | Key Fields | Purpose / Behavior | Default |
1018
+ |--------------|-----------|--------------------|---------|
1019
+ | `adaptiveMutation` | `{ enabled, initialRate, sigma, minRate, maxRate, adaptAmount, amountSigma, strategy, adaptEvery }` | Enhanced per-genome adaptive mutation; strategies: twoTier (top half down, bottom up), exploreLow (boost weakest), anneal (global decay); optional simultaneous adaptation of mutation amount. | disabled |
1020
+ | `novelty` | `{ descriptor(g), k, addThreshold, archiveLimit, blendFactor }` | Novelty search scaffold blending kNN novelty with fitness; maintains archive | disabled |
1021
+ | `speciesAgeBonus` | `{ youngGenerations, youngMultiplier, oldGenerations, oldPenalty }` | Temporary boost for young species, penalty for very old | `{ youngGenerations:5, youngMultiplier:1.2, oldGenerations:30, oldPenalty:0.3 }` |
1022
+ | `speciesAgeProtection` | `{ grace, oldPenalty }` | Protect very young species from early elimination; apply fitness scale to very old | `{ grace:3, oldPenalty:0.5 }` |
1023
+ | `crossSpeciesMatingProb` | number | Chance to select second parent from another species (maintains diversity) | 0 |
1024
+ | `adaptiveSharing` | `{ enabled, targetFragmentation, adjustStep, minSigma, maxSigma }` | Auto-adjust kernel fitness sharing `sharingSigma` toward target fragmentation (#species/pop) | disabled |
1025
+ | `minimalCriterion` | `(network)=>boolean` | Filters genomes prior to speciation; failing genomes get score 0 (minimal criterion novelty style) | undefined |
1026
+ | `operatorAdaptation` | `{ enabled, window, boost, decay }` | Tracks mutation operator success; weights selection probability by recent success | disabled |
1027
+ | `phasedComplexity` | `{ enabled, phaseLength, simplifyFraction }` | Alternates complexify vs simplify phases; simplify prunes fraction of weakest connections | disabled |
1028
+ | `complexityBudget` | `{ enabled, maxNodesStart, maxNodesEnd, horizon }` | Linear schedule for max allowed nodes; prevents premature bloat | disabled |
1029
+ | `multiObjective` | `{ enabled, complexityMetric }` | Pareto non-dominated sorting (fitness maximize, complexity minimize). `complexityMetric`=`'nodes'|'connections'` | disabled |
1030
+ | `reenableProb` (adaptive) | (base option) | Disabled gene reactivation probability; internally adaptively adjusted based on success when adaptive features on | 0.25 |
1031
+ | `diversityPressure` | `{ enabled, motifSample, penaltyStrength }` | Penalizes over-represented small connection motif signatures to promote structural variety | disabled |
1032
+ | `autoCompatTuning` | `{ enabled, target, adjustRate, minCoeff, maxCoeff }` | Automatically scales excess/disjoint coefficients to approach target species count | disabled |
1033
+ | `speciesAllocation` | `{ minOffspring, extendedHistory }` | Guarantees minimum offspring per species (if capacity) and optionally records extended per-species metrics (compatibility, mean complexity, novelty) | `{ minOffspring:1, extendedHistory:true }` |
1034
+ | `minimalCriterionAdaptive` | `{ enabled, initialThreshold, targetAcceptance, adjustRate, metric }` | Dynamically adjusts acceptance threshold to maintain target pass rate before fitness ranking | disabled |
1035
+ | `complexityBudget` (adaptive) | `{ enabled, mode:'adaptive', maxNodesStart, maxNodesEnd, improvementWindow, increaseFactor, stagnationFactor, maxConnsStart, maxConnsEnd }` | Adaptive complexity caps grow on improvement, shrink on stagnation | disabled |
1036
+ | `operatorBandit` | `{ enabled, c, minAttempts }` | UCB1-style multi-armed bandit weighting for mutation operator selection | disabled |
1037
+ | `novelty.pruneStrategy` | `'fifo'|'sparse'` | Sparse pruning removes closest archive pair iteratively to keep diversity | fifo |
1038
+ | `telemetry` | `{ enabled, logEvery, performance, complexity, hypervolume, rngState }` | Per-generation summary: base { gen,best,species,hyper,fronts }; optional perf timings, complexity block, hv scalar; if `lineageTracking` then `lineage:{ parents, depthBest, meanDepth, inbreeding, ancestorUniq }`; `rngState` embeds deterministic PRNG state (seeded runs) | disabled |
1039
+ | `lineageTracking` | `boolean` | Track parent genome ids & depth; adds `_parents`/`_depth` on genomes and enriched telemetry lineage block | true |
1040
+ | `multiObjective.autoEntropy` | `boolean` | Automatically append entropy (structure diversity proxy) objective (maximize) | false |
1041
+ | `multiObjective.dynamic` | `{ enabled, addComplexityAt, addEntropyAt, dropEntropyOnStagnation, readdEntropyAfter }` | Dynamic scheduling of complexity/entropy objectives: delay adding complexity & entropy to let pure fitness search early; temporarily drop entropy during stagnation then re-add after cooldown. | disabled |
1042
+ #### Example: Entropy-guided sharing sigma & ancestor uniqueness adaptive tuning
1043
+ ```ts
1044
+ const neat = new Neat(inputs, outputs, fitness, {
1045
+ seed: 42,
1046
+ sharingSigma: 3.0,
1047
+ entropySharingTuning: { enabled:true, targetEntropyVar:0.25, adjustRate:0.1, minSigma:0.5, maxSigma:6 },
1048
+ ancestorUniqAdaptive: { enabled:true, mode:'epsilon', lowThreshold:0.25, highThreshold:0.6, adjust:0.01, cooldown:4 },
1049
+ multiObjective: { enabled:true, autoEntropy:true, adaptiveEpsilon:{ enabled:true, targetFront: Math.floor(Math.sqrt( popSize )) } },
1050
+ telemetry: { enabled:true, rngState:true },
1051
+ lineageTracking: true
1052
+ });
1053
+ ```
1054
+ `entropySharingTuning` shrinks `sharingSigma` when structural entropy variance is too low (increasing local competition) and widens it when variance is high (reducing over-fragmentation). `entropyCompatTuning` dynamically nudges `compatibilityThreshold` based on mean structural entropy to balance species fragmentation. `ancestorUniqAdaptive` boosts diversity pressure (or relaxes dominance if using mode `epsilon`) when genealogical ancestorUniq metric drops below a threshold, and dials it back when uniqueness is already high. CSV export now includes `ops` operator stats and `objectives` active objective keys per generation when available.
1055
+
1056
+ | `multiObjective.adaptiveEpsilon` | `{ enabled,targetFront,adjust,min,max,cooldown }` | Auto-tune dominance epsilon toward target first-front size | disabled |
1057
+ | `multiObjective.refPoint` | `number[] | 'auto'` | Reference point for hypervolume (when telemetry.hypervolume true) | auto (slightly >1) |
1058
+ | `exportParetoFrontJSONL()` | method | Export recent Pareto objective vectors JSONL (for external analysis) | - |
1059
+ | `lineagePressure` | `{ enabled, mode, targetMeanDepth, strength }` | Post-eval score adjustment using lineage depth (`penalizeDeep` / `rewardShallow` / `spread`) | disabled |
1060
+
1061
+ Helper getters:
1062
+ ```ts
1063
+ neat.getSpeciesHistory(); // rolling snapshots (size, age, best score)
1064
+ neat.getNoveltyArchiveSize(); // current novelty archive length
1065
+ neat.getMultiObjectiveMetrics(); // per-genome { rank, crowding, score, nodes, connections }
1066
+ neat.getOperatorStats(); // operator adaptation/bandit stats
1067
+ neat.getTelemetry(); // evolution telemetry log (recent)
1068
+ neat.exportTelemetryCSV(); // flattened telemetry (includes lineage.* & diversity.lineage* if present)
1069
+ neat.snapshotRNGState(); // { state } deterministic PRNG snapshot (if seeded)
1070
+ neat.restoreRNGState(snap); // restore snapshot
1071
+ neat.exportRNGState(); // JSON string of RNG state
1072
+ neat.importRNGState(json); // restore from JSON
1073
+ ```
1074
+ const neat = new Neat(4,2, fit, { telemetry:{ enabled:true, complexity:true, hypervolume:true }, lineageTracking:true, multiObjective:{ enabled:true, autoEntropy:true } });
1075
+ #### Example: Enabling Novelty + Multi-Objective
1076
+ ```ts
1077
+ const neat = new Neat(4, 2, fitness, {
1078
+ popsize: 100,
1079
+ novelty: {
1080
+ descriptor: net => net.nodes.filter(n=>n.type==='H').map(h=>h.index%2), // toy descriptor
1081
+ k: 10,
1082
+ addThreshold: 0.9,
1083
+ archiveLimit: 200,
1084
+ blendFactor: 0.3
1085
+ },
1086
+ multiObjective: { enabled: true, complexityMetric: 'nodes' },
1087
+ adaptiveMutation: { enabled: true }
1088
+ });
1089
+ ```
1090
+
1091
+ #### Example: Phased Complexity + Operator Adaptation
1092
+ ```ts
1093
+ const neat = new Neat(3,1, fitness, {
1094
+ phasedComplexity: { enabled:true, phaseLength: 5, simplifyFraction: 0.15 },
1095
+ operatorAdaptation: { enabled:true, window: 30, boost: 2.0, decay: 0.9 }
1096
+ });
1097
+ ```
1098
+
1099
+ #### Minimal Criterion
1100
+ ```ts
1101
+ const neat = new Neat(2,1, fitness, {
1102
+ minimalCriterion: net => net.nodes.length >= 5 // require some complexity before scoring
1103
+ });
1104
+ ```
1105
+
1106
+ #### Adaptive Sharing
1107
+ If `adaptiveSharing.enabled` the system adjusts `sharingSigma` each generation:
1108
+ ```
1109
+ sigma += step * sign(fragmentation - target)
1110
+ ```
1111
+ within `[minSigma,maxSigma]`.
1112
+
1113
+ #### Multi-Objective Notes
1114
+ Implements a simplified NSGA-II style pass: fast non-dominated sort (O(N^2) current implementation) + crowding distance; final ordering uses (rank asc, crowding desc, fitness desc) before truncation to next generation size.
1115
+
1116
+ Planned (future): faster dominance (divide-and-conquer), structural motif diversity pressure, automated compatibility coefficient tuning.
1117
+
1118
+
1119
+ ## ASCII Maze Example: 6‑Input Long-Range Vision (MazeVision)
1120
+
1121
+ The ASCII maze example uses a compact 6‑input perception schema ("MazeVision") with long‑range lookahead via a precomputed distance map. Inputs (order fixed):
1122
+
1123
+ 1. compassScalar: Encodes the direction of the globally best next step toward the exit as a discrete scalar in {0,0.25,0.5,0.75} corresponding to N,E,S,W. Uses an extended horizon (H_COMPASS=5000) so it can see deeper than openness ratios.
1124
+ 2. openN
1125
+ 3. openE
1126
+ 4. openS
1127
+ 5. openW
1128
+ 6. progressDelta: Normalized recent progress signal around 0.5 ( >0.5 improving, <0.5 regressing ).
1129
+
1130
+ ### Openness Semantics (openN/E/S/W)
1131
+
1132
+ Each openness value describes the quality of the shortest path to the exit if the agent moves first in that direction, using a bounded lookahead horizon H=1000 over the distance map.
1133
+
1134
+ Value encoding:
1135
+ * 1: Direction(s) whose total path length Ldir is minimal among all strictly improving neighbors (ties allowed; multiple 1s possible).
1136
+ * Ratio 0 < Lmin / Ldir < 1: Direction is a valid strictly improving path but longer than the best (Lmin is the shortest improving path cost; Ldir = 1 + distance of neighbor cell). This supplies graded preference rather than binary pruning.
1137
+ * 0: Wall, unreachable cell, dead end, or any non‑improving move (neighbor distance >= current distance) – all treated uniformly.
1138
+ * 0.001: Special back‑only escape marker. When all four openness values would otherwise be 0 but the opposite of the previous successful action is traversable, that single opposite direction is set to 0.001 to indicate a pure retreat (pattern e.g. [0,0,0,0.001]).
1139
+
1140
+ Rules / Notes:
1141
+ * Strict improvement filter: Only neighbors whose distanceMap value is strictly less than the current cell distance are considered for 1 or ratio values.
1142
+ * Horizon clipping: Paths with Ldir > H are treated as unreachable (value 0) to bound search cost.
1143
+ * Multiple bests: Corridors that fork into equivalently short routes produce multiple 1s, encouraging neutrality across equally optimal choices.
1144
+ * Backtrack marker is intentionally very small (0.001) so evolution distinguishes "retreat only" states from true walls without overweighting them.
1145
+ * Supervised refinement dataset intentionally contains ONLY deterministic single‑path cases (exactly one openness=1, others 0) for clarity; richer ratio/backtrack patterns appear only in the Lamarckian / evolutionary phase.
1146
+
1147
+ ### progressDelta
1148
+ Computed from recent distance improvement: delta = prevDistance - currentDistance, clipped to [-2,2], then mapped to [0,1] as 0.5 + delta/4. Values >0.5 mean progress toward exit; <0.5 regression or stalling.
1149
+
1150
+ ### Debugging
1151
+ Set ASCII_VISION_DEBUG=1 to emit periodic vision lines: current position, compassScalar, input vector, and per‑direction distance/ratio breakdown for auditing mismatches between maze geometry and distance map.
1152
+
1153
+ ### Quick Reference
1154
+ | Signal | Meaning |
1155
+ |--------|---------|
1156
+ | 1 | Best strictly improving path(s) (minimal Ldir) |
1157
+ | (0,1) | Longer but improving path (ratio Lmin/Ldir) |
1158
+ | 0.001 | Only backtrack available (opposite of prior action) |
1159
+ | 0 | Wall / dead end / non‑improving / unreachable |
1160
+
1161
+ Implementation: `test/examples/asciiMaze/mazeVision.ts` (function `MazeVision.buildInputs6`).
1162
+
1163
+ This design minimizes input size (6 vs earlier large encodings) while preserving directional discrimination and long‑range planning cues, aiding faster evolutionary convergence and avoiding overfitting to local dead‑end noise.
1164
+
1165
+ ## Roadmap / Backlog
1166
+ Planned or partially designed enhancements not yet merged:
1167
+ * Structural motif diversity pressure: penalize over-represented connection patterns (entropy-based sharing) to sustain innovation.
1168
+ * Automated compatibility coefficient tuning: search or adapt excess/disjoint/weight coefficients to stabilize species counts without manual calibration.
1169
+ * Faster Pareto sorting: divide-and-conquer or incremental dominance maintenance to reduce O(N^2) overhead for large populations.
1170
+ * Connection complexity budget (current budget targets nodes only) and dual-objective weighting option.
1171
+ * Diversity-aware parent selection leveraging motif entropy and archive dispersion.
1172
+ * Extended novelty descriptors helper utilities (e.g. built-in graph metrics: depth, feedforwardness, clustering).
1173
+ * Visualization hooks (species lineage graph, archive embedding projection) for diagnostics.