@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,589 @@
1
+ import Neat from '../../src/neat';
2
+ import Network from '../../src/architecture/network';
3
+ import * as methods from '../../src/methods/methods';
4
+ import Node from '../../src/architecture/node';
5
+ import Connection from '../../src/architecture/connection';
6
+
7
+ describe('Neat advanced coverage', () => {
8
+ describe('constructor', () => {
9
+ describe('when no options are provided', () => {
10
+ it('should set default popsize to 50', () => {
11
+ // Arrange
12
+ const fitness = jest.fn();
13
+ // Act
14
+ const neat = new Neat(2, 1, fitness);
15
+ // Assert
16
+ expect(neat.options.popsize).toBe(50);
17
+ });
18
+ });
19
+ describe('when options are provided', () => {
20
+ describe('popsize is set', () => {
21
+ it('should use provided popsize', () => {
22
+ // Arrange
23
+ const fitness = jest.fn();
24
+ // Act
25
+ const neat = new Neat(2, 1, fitness, { popsize: 10 });
26
+ // Assert
27
+ expect(neat.options.popsize).toBe(10);
28
+ });
29
+ });
30
+ describe('elitism is set', () => {
31
+ it('should use provided elitism', () => {
32
+ // Arrange
33
+ const fitness = jest.fn();
34
+ // Act
35
+ const neat = new Neat(2, 1, fitness, { elitism: 2 });
36
+ // Assert
37
+ expect(neat.options.elitism).toBe(2);
38
+ });
39
+ });
40
+ });
41
+ describe('when options is empty', () => {
42
+ it('should set default popsize to 50', () => {
43
+ // Arrange
44
+ const fitness = jest.fn();
45
+ // Act
46
+ const neat = new Neat(2, 1, fitness, {});
47
+ // Assert
48
+ expect(neat.options.popsize).toBe(50);
49
+ });
50
+ it('should set default equal to false', () => {
51
+ // Arrange
52
+ const fitness = jest.fn();
53
+ // Act
54
+ const neat = new Neat(2, 1, fitness, {});
55
+ // Assert
56
+ expect(neat.options.equal).toBe(false);
57
+ });
58
+ it('should set default clear to false', () => {
59
+ // Arrange
60
+ const fitness = jest.fn();
61
+ // Act
62
+ const neat = new Neat(2, 1, fitness, {});
63
+ // Assert
64
+ expect(neat.options.clear).toBe(false);
65
+ });
66
+ it('should set default elitism to 0', () => {
67
+ // Arrange
68
+ const fitness = jest.fn();
69
+ // Act
70
+ const neat = new Neat(2, 1, fitness, {});
71
+ // Assert
72
+ expect(neat.options.elitism).toBe(0);
73
+ });
74
+ it('should set default provenance to 0', () => {
75
+ // Arrange
76
+ const fitness = jest.fn();
77
+ // Act
78
+ const neat = new Neat(2, 1, fitness, {});
79
+ // Assert
80
+ expect(neat.options.provenance).toBe(0);
81
+ });
82
+ it('should set default mutationRate to 0.7', () => {
83
+ // Arrange
84
+ const fitness = jest.fn();
85
+ // Act
86
+ const neat = new Neat(2, 1, fitness, {});
87
+ // Assert
88
+ expect(neat.options.mutationRate).toBe(0.7);
89
+ });
90
+ it('should set default mutationAmount to 1', () => {
91
+ // Arrange
92
+ const fitness = jest.fn();
93
+ // Act
94
+ const neat = new Neat(2, 1, fitness, {});
95
+ // Assert
96
+ expect(neat.options.mutationAmount).toBe(1);
97
+ });
98
+ it('should set default fitnessPopulation to false', () => {
99
+ // Arrange
100
+ const fitness = jest.fn();
101
+ // Act
102
+ const neat = new Neat(2, 1, fitness, {});
103
+ // Assert
104
+ expect(neat.options.fitnessPopulation).toBe(false);
105
+ });
106
+ it('should set default selection to be defined', () => {
107
+ // Arrange
108
+ const fitness = jest.fn();
109
+ // Act
110
+ const neat = new Neat(2, 1, fitness, {});
111
+ // Assert
112
+ expect(neat.options.selection).toBeDefined();
113
+ });
114
+ it('should set default crossover to be defined', () => {
115
+ // Arrange
116
+ const fitness = jest.fn();
117
+ // Act
118
+ const neat = new Neat(2, 1, fitness, {});
119
+ // Assert
120
+ expect(neat.options.crossover).toBeDefined();
121
+ });
122
+ it('should set default mutation to be defined', () => {
123
+ // Arrange
124
+ const fitness = jest.fn();
125
+ // Act
126
+ const neat = new Neat(2, 1, fitness, {});
127
+ // Assert
128
+ expect(neat.options.mutation).toBeDefined();
129
+ });
130
+ it('should set default maxNodes to Infinity', () => {
131
+ // Arrange
132
+ const fitness = jest.fn();
133
+ // Act
134
+ const neat = new Neat(2, 1, fitness, {});
135
+ // Assert
136
+ expect(neat.options.maxNodes).toBe(Infinity);
137
+ });
138
+ it('should set default maxConns to Infinity', () => {
139
+ // Arrange
140
+ const fitness = jest.fn();
141
+ // Act
142
+ const neat = new Neat(2, 1, fitness, {});
143
+ // Assert
144
+ expect(neat.options.maxConns).toBe(Infinity);
145
+ });
146
+ it('should set default maxGates to Infinity', () => {
147
+ // Arrange
148
+ const fitness = jest.fn();
149
+ // Act
150
+ const neat = new Neat(2, 1, fitness, {});
151
+ // Assert
152
+ expect(neat.options.maxGates).toBe(Infinity);
153
+ });
154
+ });
155
+ });
156
+
157
+ describe('createPool', () => {
158
+ describe('when base network is provided', () => {
159
+ it('should create population with base network', () => {
160
+ // Arrange
161
+ const fitness = jest.fn();
162
+ const base = new Network(2, 1);
163
+ const neat = new Neat(2, 1, fitness, { popsize: 2, network: base });
164
+ // Act
165
+ neat.createPool(base);
166
+ // Assert
167
+ expect(neat.population.length).toBe(2);
168
+ });
169
+ });
170
+ describe('when base network is not provided', () => {
171
+ it('should create population without base network', () => {
172
+ // Arrange
173
+ const fitness = jest.fn();
174
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
175
+ // Act
176
+ neat.createPool(null);
177
+ // Assert
178
+ expect(neat.population.length).toBe(2);
179
+ });
180
+ });
181
+ });
182
+
183
+ describe('selectMutationMethod', () => {
184
+ describe('when ADD_NODE and maxNodes reached', () => {
185
+ it('should return null', () => {
186
+ // Arrange
187
+ const fitness = jest.fn();
188
+ const neat = new Neat(2, 1, fitness, {
189
+ mutation: [methods.mutation.ADD_NODE],
190
+ maxNodes: 1,
191
+ });
192
+ const genome = new Network(2, 1);
193
+ genome.nodes.push(new Node());
194
+ // Act
195
+ const result = neat.selectMutationMethod(genome);
196
+ // Assert
197
+ expect(result).toBeNull();
198
+ });
199
+ });
200
+ describe('when ADD_CONN and maxConns reached', () => {
201
+ it('should return null', () => {
202
+ // Arrange
203
+ const fitness = jest.fn();
204
+ const neat = new Neat(2, 1, fitness, {
205
+ mutation: [methods.mutation.ADD_CONN],
206
+ maxConns: 1,
207
+ });
208
+ const genome = new Network(2, 1);
209
+ genome.connections.push(new Connection(new Node(), new Node()));
210
+ // Act
211
+ const result = neat.selectMutationMethod(genome);
212
+ // Assert
213
+ expect(result).toBeNull();
214
+ });
215
+ });
216
+ describe('when ADD_GATE and maxGates reached', () => {
217
+ it('should return null', () => {
218
+ // Arrange
219
+ const fitness = jest.fn();
220
+ const neat = new Neat(2, 1, fitness, {
221
+ mutation: [methods.mutation.ADD_GATE],
222
+ maxGates: 1,
223
+ });
224
+ const genome = new Network(2, 1);
225
+ genome.gates.push(new Connection(new Node(), new Node()));
226
+ // Act
227
+ const result = neat.selectMutationMethod(genome);
228
+ // Assert
229
+ expect(result).toBeNull();
230
+ });
231
+ });
232
+ describe('when constraints not reached', () => {
233
+ it('should return mutation method', () => {
234
+ // Arrange
235
+ const fitness = jest.fn();
236
+ const neat = new Neat(2, 1, fitness, {
237
+ mutation: [methods.mutation.FFW],
238
+ });
239
+ const genome = new Network(2, 1);
240
+ // Act
241
+ const result = neat.selectMutationMethod(genome);
242
+ // Assert
243
+ expect(result).toBe(methods.mutation.FFW);
244
+ });
245
+ });
246
+ });
247
+
248
+ describe('getParent', () => {
249
+ describe('when selection is POWER', () => {
250
+ it('should return a parent from the population', () => {
251
+ // Arrange
252
+ const fitness = jest.fn();
253
+ const neat = new Neat(2, 1, fitness, {
254
+ selection: methods.selection.POWER,
255
+ });
256
+ neat.population = [new Network(2, 1), new Network(2, 1)];
257
+ neat.population[0].score = 2;
258
+ neat.population[1].score = 1;
259
+ // Act
260
+ const parent = neat.getParent();
261
+ // Assert
262
+ expect(neat.population).toContain(parent);
263
+ });
264
+ });
265
+ describe('when selection is FITNESS_PROPORTIONATE', () => {
266
+ it('should return a parent from the population', () => {
267
+ // Arrange
268
+ const fitness = jest.fn();
269
+ const neat = new Neat(2, 1, fitness, {
270
+ selection: methods.selection.FITNESS_PROPORTIONATE,
271
+ });
272
+ neat.population = [new Network(2, 1), new Network(2, 1)];
273
+ neat.population[0].score = 2;
274
+ neat.population[1].score = 1;
275
+ // Act
276
+ const parent = neat.getParent();
277
+ // Assert
278
+ expect(neat.population).toContain(parent);
279
+ });
280
+ });
281
+ describe('when selection is TOURNAMENT', () => {
282
+ it('should return a parent from the population', () => {
283
+ // Arrange
284
+ const fitness = jest.fn();
285
+ const neat = new Neat(2, 1, fitness, {
286
+ selection: {
287
+ ...methods.selection.TOURNAMENT,
288
+ size: 2,
289
+ probability: 1,
290
+ },
291
+ popsize: 2,
292
+ });
293
+ neat.population = [new Network(2, 1), new Network(2, 1)];
294
+ neat.population[0].score = 2;
295
+ neat.population[1].score = 1;
296
+ // Act
297
+ const parent = neat.getParent();
298
+ // Assert
299
+ expect(neat.population).toContain(parent);
300
+ });
301
+ it('should throw if tournament size is greater than population size', () => {
302
+ // Arrange
303
+ const fitness = jest.fn();
304
+ // popsize = 2, selection.size = 3
305
+ const neat = new Neat(2, 1, fitness, {
306
+ selection: {
307
+ ...methods.selection.TOURNAMENT,
308
+ size: 3,
309
+ probability: 1,
310
+ },
311
+ popsize: 2,
312
+ });
313
+ neat.population = [new Network(2, 1), new Network(2, 1)];
314
+ // Act & Assert
315
+ expect(() => {
316
+ neat.getParent();
317
+ }).toThrow('Tournament size must be less than population size.');
318
+ });
319
+ });
320
+ });
321
+
322
+ describe('evaluate', () => {
323
+ describe('when fitnessPopulation is false', () => {
324
+ it('should call fitness for each genome', async () => {
325
+ // Arrange
326
+ const fitness = jest.fn().mockReturnValue(1); // Spy
327
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
328
+ neat.population[0].score = undefined;
329
+ neat.population[1].score = undefined;
330
+ // Act
331
+ await neat.evaluate();
332
+ // Assert
333
+ expect(fitness).toHaveBeenCalledTimes(2);
334
+ });
335
+ });
336
+ describe('when fitnessPopulation is true', () => {
337
+ it('should call fitness once for population', async () => {
338
+ // Arrange
339
+ const fitness = jest.fn().mockReturnValue(1); // Spy
340
+ const neat = new Neat(2, 1, fitness, {
341
+ popsize: 2,
342
+ fitnessPopulation: true,
343
+ });
344
+ // Act
345
+ await neat.evaluate();
346
+ // Assert
347
+ expect(fitness).toHaveBeenCalledTimes(1);
348
+ });
349
+ it('should clear genomes if clear is true', async () => {
350
+ // Arrange
351
+ const fitness = jest.fn().mockReturnValue(1); // Spy
352
+ const neat = new Neat(2, 1, fitness, {
353
+ popsize: 2,
354
+ fitnessPopulation: true,
355
+ clear: true,
356
+ });
357
+ const clearSpy = jest.spyOn(neat.population[0], 'clear');
358
+ // Act
359
+ await neat.evaluate();
360
+ // Assert
361
+ expect(clearSpy).toHaveBeenCalled();
362
+ });
363
+ });
364
+ });
365
+
366
+ describe('evolve', () => {
367
+ describe('when last genome score is undefined', () => {
368
+ it('should call evaluate', async () => {
369
+ // Arrange
370
+ const fitness = jest.fn().mockReturnValue(1); // Spy
371
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
372
+ neat.population[1].score = undefined;
373
+ const evalSpy = jest.spyOn(neat, 'evaluate');
374
+ // Act
375
+ await neat.evolve();
376
+ // Assert
377
+ expect(evalSpy).toHaveBeenCalled();
378
+ });
379
+ });
380
+ describe('when all genome scores are defined', () => {
381
+ it('should increment generation', async () => {
382
+ // Arrange
383
+ const fitness = jest.fn().mockReturnValue(1); // Spy
384
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
385
+ neat.population[0].score = 1;
386
+ neat.population[1].score = 2;
387
+ // Act
388
+ await neat.evolve();
389
+ // Assert
390
+ expect(neat.generation).toBe(1);
391
+ });
392
+ });
393
+ });
394
+
395
+ describe('getOffspring', () => {
396
+ describe('when called', () => {
397
+ it('should call Network.crossOver with two parents', () => {
398
+ // Arrange
399
+ const fitness = jest.fn();
400
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
401
+ const crossSpy = jest
402
+ .spyOn(Network, 'crossOver')
403
+ .mockReturnValue(new Network(2, 1));
404
+ // Act
405
+ neat.getOffspring();
406
+ // Assert
407
+ expect(crossSpy).toHaveBeenCalled();
408
+ });
409
+ });
410
+ });
411
+
412
+ describe('mutate', () => {
413
+ describe('when mutationMethod is valid', () => {
414
+ it('should call mutate on genome', () => {
415
+ // Arrange
416
+ const fitness = jest.fn();
417
+ const neat = new Neat(2, 1, fitness, {
418
+ popsize: 1,
419
+ mutation: [methods.mutation.FFW],
420
+ mutationRate: 1,
421
+ });
422
+ const mutateSpy = jest.spyOn(neat.population[0], 'mutate');
423
+ // Act
424
+ neat.mutate();
425
+ // Assert
426
+ expect(mutateSpy).toHaveBeenCalled();
427
+ });
428
+ });
429
+ describe('when mutationMethod is null', () => {
430
+ it('should not call mutate', () => {
431
+ // Arrange
432
+ const fitness = jest.fn();
433
+ const neat = new Neat(2, 1, fitness, {
434
+ popsize: 1,
435
+ mutation: [methods.mutation.ADD_NODE],
436
+ mutationRate: 1,
437
+ maxNodes: 0,
438
+ });
439
+ const mutateSpy = jest.spyOn(neat.population[0], 'mutate');
440
+ // Force selectMutationMethod to always return null
441
+ jest.spyOn(neat, 'selectMutationMethod').mockReturnValue(null);
442
+ // Act
443
+ neat.mutate();
444
+ // Assert
445
+ expect(mutateSpy).not.toHaveBeenCalled();
446
+ });
447
+ });
448
+ });
449
+
450
+ describe('import/export', () => {
451
+ describe('when exporting and importing population', () => {
452
+ it('should export and import population', () => {
453
+ // Arrange
454
+ const fitness = jest.fn();
455
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
456
+ neat.population[0].score = 1;
457
+ neat.population[1].score = 2;
458
+ // Act
459
+ const exported = neat.export();
460
+ neat.import(exported);
461
+ // Assert
462
+ expect(neat.population.length).toBe(2);
463
+ });
464
+ });
465
+ describe('when exporting empty population', () => {
466
+ it('should export empty population as empty array', () => {
467
+ // Arrange
468
+ const fitness = jest.fn();
469
+ const neat = new Neat(2, 1, fitness, { popsize: 0 });
470
+ neat.population = [];
471
+ // Act
472
+ const exported = neat.export();
473
+ // Assert
474
+ expect(exported).toEqual([]);
475
+ });
476
+ });
477
+ describe('when importing empty array', () => {
478
+ it('should import empty array as empty population', () => {
479
+ // Arrange
480
+ const fitness = jest.fn();
481
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
482
+ // Act
483
+ neat.import([]);
484
+ // Assert
485
+ expect(neat.population.length).toBe(0);
486
+ });
487
+ });
488
+ });
489
+
490
+ describe('sort', () => {
491
+ describe('when population has scores', () => {
492
+ it('should sort population by score descending', () => {
493
+ // Arrange
494
+ const fitness = jest.fn();
495
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
496
+ neat.population[0].score = 1;
497
+ neat.population[1].score = 2;
498
+ // Act
499
+ neat.sort();
500
+ // Assert
501
+ expect(neat.population[0].score).toBe(2);
502
+ });
503
+ });
504
+ describe('when population is empty', () => {
505
+ it('should handle empty population', () => {
506
+ // Arrange
507
+ const fitness = jest.fn();
508
+ const neat = new Neat(2, 1, fitness, { popsize: 0 });
509
+ neat.population = [];
510
+ // Act
511
+ neat.sort();
512
+ // Assert
513
+ expect(neat.population).toEqual([]);
514
+ });
515
+ });
516
+ });
517
+
518
+ describe('getFittest', () => {
519
+ describe('when population has scores', () => {
520
+ it('should return fittest genome', () => {
521
+ // Arrange
522
+ const fitness = jest.fn();
523
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
524
+ neat.population[0].score = 1;
525
+ neat.population[1].score = 2;
526
+ // Act
527
+ const fittest = neat.getFittest();
528
+ // Assert
529
+ expect(fittest.score).toBe(2);
530
+ });
531
+ });
532
+ describe('when last genome score is undefined', () => {
533
+ it('should call evaluate', () => {
534
+ // Arrange
535
+ const fitness = jest.fn().mockReturnValue(1); // Spy
536
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
537
+ neat.population[1].score = undefined;
538
+ const evalSpy = jest.spyOn(neat, 'evaluate');
539
+ // Act
540
+ neat.getFittest();
541
+ // Assert
542
+ expect(evalSpy).toHaveBeenCalled();
543
+ });
544
+ });
545
+ describe('when population[1] exists and population[0] is less fit', () => {
546
+ it('should call sort', () => {
547
+ // Arrange
548
+ const fitness = jest.fn();
549
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
550
+ neat.population[0].score = 1;
551
+ neat.population[1].score = 2;
552
+ const sortSpy = jest.spyOn(neat, 'sort');
553
+ // Act
554
+ neat.getFittest();
555
+ // Assert
556
+ expect(sortSpy).toHaveBeenCalled();
557
+ });
558
+ });
559
+ });
560
+
561
+ describe('getAverage', () => {
562
+ describe('when population has scores', () => {
563
+ it('should return average score', () => {
564
+ // Arrange
565
+ const fitness = jest.fn();
566
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
567
+ neat.population[0].score = 1;
568
+ neat.population[1].score = 3;
569
+ // Act
570
+ const avg = neat.getAverage();
571
+ // Assert
572
+ expect(avg).toBe(2);
573
+ });
574
+ });
575
+ describe('when last genome score is undefined', () => {
576
+ it('should call evaluate', () => {
577
+ // Arrange
578
+ const fitness = jest.fn().mockReturnValue(1); // Spy
579
+ const neat = new Neat(2, 1, fitness, { popsize: 2 });
580
+ neat.population[1].score = undefined;
581
+ const evalSpy = jest.spyOn(neat, 'evaluate');
582
+ // Act
583
+ neat.getAverage();
584
+ // Assert
585
+ expect(evalSpy).toHaveBeenCalled();
586
+ });
587
+ });
588
+ });
589
+ });
@@ -0,0 +1,47 @@
1
+ import Neat from '../../src/neat';
2
+ import Network from '../../src/architecture/network';
3
+
4
+ describe('diversityPressure & autoCompatTuning', () => {
5
+ const fitness = (net: Network) => {
6
+ // Simple fitness proportional to hidden node count to create motif similarity pressure
7
+ const hidden = (net as any).nodes.filter((n: any) => n.type === 'H').length;
8
+ return hidden;
9
+ };
10
+ test('diversity pressure adjusts scores (no crash)', async () => {
11
+ const neat = new Neat(2, 1, fitness, {
12
+ popsize: 30,
13
+ speciation: true,
14
+ diversityPressure: {
15
+ enabled: true,
16
+ motifSample: 10,
17
+ penaltyStrength: 0.05,
18
+ },
19
+ targetSpecies: 5,
20
+ autoCompatTuning: { enabled: false },
21
+ });
22
+ await neat.evolve();
23
+ // Evaluate the newly created generation so all genomes have scores
24
+ await neat.evaluate();
25
+ const pop = neat.population;
26
+ expect(pop.every((g) => typeof g.score === 'number')).toBe(true);
27
+ });
28
+ test('auto compatibility tuning nudges coefficients', async () => {
29
+ const neat = new Neat(2, 1, fitness, {
30
+ popsize: 40,
31
+ speciation: true,
32
+ targetSpecies: 6,
33
+ autoCompatTuning: {
34
+ enabled: true,
35
+ adjustRate: 0.05,
36
+ minCoeff: 0.2,
37
+ maxCoeff: 3,
38
+ },
39
+ });
40
+ const startExcess = neat.options.excessCoeff!;
41
+ // run a few generations to allow adjustment
42
+ for (let i = 0; i < 5; i++) await neat.evolve();
43
+ const endExcess = neat.options.excessCoeff!;
44
+ // Coefficient should have moved (unless perfectly matched already which is unlikely)
45
+ expect(endExcess).not.toBe(startExcess);
46
+ });
47
+ });
@@ -0,0 +1,21 @@
1
+ import Neat from '../../src/neat';
2
+ import Network from '../../src/architecture/network';
3
+
4
+ describe('Diversity metrics', () => {
5
+ test('computes diversity snapshot with compatibility and entropy stats', async () => {
6
+ const neat = new Neat(4, 2, (n: Network) => n.connections.length, {
7
+ popsize: 25,
8
+ seed: 77,
9
+ speciation: true,
10
+ diversityMetrics: { enabled: true, pairSample: 30, graphletSample: 40 },
11
+ });
12
+ await neat.evaluate();
13
+ await neat.evolve();
14
+ const stats = neat.getDiversityStats();
15
+ expect(stats).toBeTruthy();
16
+ expect(typeof stats.meanCompat).toBe('number');
17
+ expect(typeof stats.graphletEntropy).toBe('number');
18
+ const tel = neat.getTelemetry().slice(-1)[0];
19
+ expect(tel.diversity).toBeTruthy();
20
+ });
21
+ });
@@ -0,0 +1,44 @@
1
+ import { computeDiversityStats } from '../../src/neat/neat.diversity';
2
+
3
+ describe('computeDiversityStats', () => {
4
+ function mockGenome(nodes: number, conns: number, depth?: number) {
5
+ return {
6
+ nodes: new Array(nodes)
7
+ .fill(0)
8
+ .map((_, i) => ({ id: i, connections: { out: [] as any[] } })),
9
+ connections: new Array(conns).fill(0).map((_, i) => ({ id: i })),
10
+ _depth: depth,
11
+ } as any;
12
+ }
13
+ const compat = {
14
+ _compatibilityDistance(a: any, b: any) {
15
+ return (
16
+ Math.abs(a.nodes.length - b.nodes.length) +
17
+ Math.abs(a.connections.length - b.connections.length)
18
+ );
19
+ },
20
+ };
21
+
22
+ it('returns undefined for empty population', () => {
23
+ expect(computeDiversityStats([], compat)).toBeUndefined();
24
+ });
25
+
26
+ it('computes expected keys', () => {
27
+ const pop = [mockGenome(3, 2, 1), mockGenome(5, 4, 2), mockGenome(4, 3, 4)];
28
+ const stats = computeDiversityStats(pop, compat)!;
29
+ // Keys required by telemetry/tests
30
+ const keys = [
31
+ 'lineageMeanDepth',
32
+ 'lineageMeanPairDist',
33
+ 'meanNodes',
34
+ 'meanConns',
35
+ 'nodeVar',
36
+ 'connVar',
37
+ 'meanCompat',
38
+ 'graphletEntropy',
39
+ 'population',
40
+ ];
41
+ for (const k of keys) expect(stats).toHaveProperty(k);
42
+ expect(stats.population).toBe(pop.length);
43
+ });
44
+ });