@reicek/neataptic-ts 0.1.26 → 0.1.27
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.
- package/.github/agents/boundary-mapper.agent.md +13 -8
- package/.github/agents/docs-scout.agent.md +14 -9
- package/.github/agents/plan-scout.agent.md +11 -6
- package/.github/agents/solid-split.agent.md +33 -17
- package/.github/copilot-instructions.md +100 -88
- package/.github/skills/educational-docs/SKILL.md +90 -40
- package/.github/skills/educational-docs/assets/mermaid-diagram-playbook.md +141 -141
- package/.github/skills/educational-docs/assets/visual-style-guide.md +1 -1
- package/.github/skills/plan-alignment/SKILL.md +1 -1
- package/.github/skills/solid-split/SKILL.md +54 -35
- package/.github/skills/solid-split/assets/docs-checklist.md +1 -1
- package/.github/skills/solid-split/assets/split-plan-template.md +41 -12
- package/.github/skills/solid-split/assets/split-workflow-checklist.md +4 -1
- package/.github/skills/test-fix-workflow/SKILL.md +12 -7
- package/.github/skills/trace-analyzer-extension/SKILL.md +1 -1
- package/.github/skills/trace-analyzer-extension/assets/extension-checklist.md +1 -1
- package/.github/skills/trace-analyzer-extension/references/analyzer-extension-workflow.md +1 -1
- package/.github/skills/trace-audit-reporting/SKILL.md +1 -1
- package/.github/skills/trace-audit-reporting/assets/performance-report-template.md +1 -1
- package/.github/skills/trace-audit-reporting/references/trace-analysis-workflow.md +1 -1
- package/.github/skills/tracker-handoff/SKILL.md +53 -5
- package/package.json +1 -1
- package/plans/Flappy_Bird_Folder_Documentation_Pass.logs.md +42 -0
- package/plans/Flappy_Bird_Folder_Documentation_Pass.md +22 -104
- package/plans/README.md +6 -2
- package/plans/Roadmap.md +35 -31
- package/plans/analyze-trace-solid-split.logs.md +35 -0
- package/plans/analyze-trace-solid-split.plans.md +21 -52
- package/plans/architecture-solid-split.logs.md +44 -0
- package/plans/architecture-solid-split.plans.md +20 -653
- package/plans/asciiMaze-typescript-repair.logs.md +35 -0
- package/plans/asciiMaze-typescript-repair.plans.md +30 -36
- package/plans/generate-docs-solid-split.logs.md +36 -0
- package/plans/generate-docs-solid-split.plans.md +21 -73
- package/plans/methods-docs.logs.md +37 -0
- package/plans/methods-docs.plans.md +22 -183
- package/plans/methods-solid-split.logs.md +41 -0
- package/plans/methods-solid-split.plans.md +23 -65
- package/plans/neat-docs.logs.md +38 -0
- package/plans/neat-docs.plans.md +21 -63
- package/plans/neat-test-surface-repair.logs.md +38 -0
- package/plans/neat-test-surface-repair.plans.md +24 -24
- package/plans/readme-first-section-pass.logs.md +63 -0
- package/plans/readme-first-section-pass.plans.md +42 -0
- package/plans/render-docs-html-solid-split.logs.md +36 -0
- package/plans/render-docs-html-solid-split.plans.md +20 -54
- package/plans/src-no-explicit-any-cleanup.logs.md +39 -0
- package/plans/src-no-explicit-any-cleanup.plans.md +23 -161
- package/plans/utils-docs.logs.md +37 -0
- package/plans/utils-docs.plans.md +20 -35
- package/src/README.md +88 -22
- package/src/architecture/README.md +94 -17
- package/src/architecture/activationArrayPool/README.md +53 -0
- package/src/architecture/activationArrayPool/activationArrayPool.ts +53 -0
- package/src/architecture/layer/README.md +94 -5
- package/src/architecture/layer/layer.ts +94 -5
- package/src/architecture/network/README.md +85 -14
- package/src/architecture/network/activate/README.md +301 -213
- package/src/architecture/network/activate/docs.order.json +9 -0
- package/src/architecture/network/activate/network.activate.utils.ts +90 -0
- package/src/architecture/network/connect/README.md +80 -3
- package/src/architecture/network/connect/docs.order.json +9 -0
- package/src/architecture/network/connect/network.connect.utils.ts +79 -0
- package/src/architecture/network/deterministic/README.md +96 -12
- package/src/architecture/network/deterministic/docs.order.json +10 -0
- package/src/architecture/network/deterministic/network.deterministic.utils.ts +90 -0
- package/src/architecture/network/evolve/README.md +286 -228
- package/src/architecture/network/evolve/docs.order.json +11 -0
- package/src/architecture/network/evolve/network.evolve.utils.ts +59 -0
- package/src/architecture/network/gating/README.md +58 -16
- package/src/architecture/network/gating/docs.order.json +9 -0
- package/src/architecture/network/gating/network.gating.utils.ts +43 -0
- package/src/architecture/network/genetic/README.md +75 -36
- package/src/architecture/network/genetic/docs.order.json +10 -0
- package/src/architecture/network/genetic/network.genetic.utils.ts +53 -17
- package/src/architecture/network/network.ts +91 -46
- package/src/architecture/network/standalone/README.md +309 -285
- package/src/architecture/network/standalone/docs.order.json +13 -0
- package/src/architecture/network/standalone/network.standalone.utils.ts +51 -28
- package/src/architecture/network.ts +94 -17
- package/src/architecture/nodePool/README.md +53 -0
- package/src/architecture/nodePool/nodePool.ts +53 -0
- package/src/methods/README.md +80 -34
- package/src/methods/methods.ts +80 -34
- package/src/methods/mutation/README.md +39 -93
- package/src/methods/mutation/mutation.ts +58 -0
- package/src/multithreading/README.md +114 -14
- package/src/multithreading/docs.order.json +5 -0
- package/src/multithreading/multi.ts +103 -7
- package/src/multithreading/types.ts +6 -1
- package/src/multithreading/workers/README.md +72 -2
- package/src/multithreading/workers/browser/README.md +39 -0
- package/src/multithreading/workers/browser/testworker.ts +40 -0
- package/src/multithreading/workers/node/README.md +50 -11
- package/src/multithreading/workers/node/docs.order.json +4 -0
- package/src/multithreading/workers/node/testworker.ts +43 -3
- package/src/multithreading/workers/workers.ts +36 -1
- package/src/neat/README.md +230 -152
- package/src/neat/adaptive/core/README.md +99 -42
- package/src/neat/adaptive/core/adaptive.core.ts +71 -14
- package/src/neat/adaptive/core/docs.order.json +8 -0
- package/src/neat/docs.order.json +9 -0
- package/src/neat/lineage/README.md +118 -16
- package/src/neat/lineage/lineage.ts +115 -13
- package/src/neat/neat.defaults.constants.ts +91 -13
- package/src/neat.ts +86 -20
- package/src/utils/README.md +71 -21
- package/src/utils/memory.ts +69 -19
- package/test/examples/asciiMaze/browser-entry/README.md +81 -8
- package/test/examples/asciiMaze/browser-entry/browser-entry.ts +81 -8
- package/test/examples/asciiMaze/dashboardManager/README.md +68 -4
- package/test/examples/asciiMaze/dashboardManager/dashboardManager.ts +68 -4
- package/test/examples/asciiMaze/evolutionEngine/README.md +1259 -1174
- package/test/examples/asciiMaze/evolutionEngine/docs.order.json +10 -0
- package/test/examples/asciiMaze/evolutionEngine/evolutionEngine.types.ts +87 -0
- package/test/examples/asciiMaze/mazeMovement/README.md +81 -4
- package/test/examples/asciiMaze/mazeMovement/mazeMovement.ts +81 -4
- package/test/examples/flappy_bird/browser-entry/README.md +64 -21
- package/test/examples/flappy_bird/browser-entry/browser-entry.ts +64 -21
- package/test/examples/flappy_bird/flappy-evolution-worker/README.md +98 -10
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.ts +98 -10
- package/test/examples/flappy_bird/simulation-shared/README.md +88 -4
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.types.ts +88 -4
|
@@ -1,62 +1,89 @@
|
|
|
1
1
|
# evolutionEngine
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
3
|
+
Population-level runtime contracts for the ASCII Maze evolution engine.
|
|
4
|
+
|
|
5
|
+
This folder is where one maze, one fitness story, and one NEAT controller
|
|
6
|
+
turn into a repeatable training program. The public `EvolutionEngine` facade
|
|
7
|
+
uses these contracts to normalize options, coordinate host callbacks, keep
|
|
8
|
+
deterministic state reproducible, and decide when a curriculum phase has
|
|
9
|
+
produced a winner worth carrying forward.
|
|
10
|
+
|
|
11
|
+
The important distinction is scale. `mazeMovement/` explains one agent run.
|
|
12
|
+
`fitness.ts` explains how that run is scored. `dashboardManager/` explains
|
|
13
|
+
how progress is shown to a human. `evolutionEngine/` explains how many runs
|
|
14
|
+
across many generations become one population-level search loop with stop
|
|
15
|
+
reasons, telemetry, warm starts, and phase outcomes that the next maze can
|
|
16
|
+
reuse.
|
|
17
|
+
|
|
18
|
+
This file is the right chapter opening because it names the public nouns of
|
|
19
|
+
that loop before the reader hits pooled scratch buffers or hot-path helpers.
|
|
20
|
+
It answers four questions quickly: what a caller can configure, what a host
|
|
21
|
+
may observe or interrupt, what result the engine returns, and which shared
|
|
22
|
+
runtime contexts keep the hot path allocation-light.
|
|
23
|
+
|
|
24
|
+
A useful mental model is to treat the engine as a control tower rather than
|
|
25
|
+
the aircraft itself. The engine does not move the agent through one maze cell
|
|
26
|
+
at a time. It schedules phases, batches generations, preserves deterministic
|
|
27
|
+
state, and hands structured outcomes back to browser or terminal hosts.
|
|
28
|
+
|
|
29
|
+
Read the chapter in three passes. Start here for the public contracts and the
|
|
30
|
+
meaning of a run result. Continue to `engineState.types.ts` when you want the
|
|
31
|
+
shared scratch and toggle state that keeps the loop cheap. Finish with
|
|
32
|
+
`evolutionLoop.ts`, `evolutionEngine.services.ts`, and `sampling.ts` when you
|
|
33
|
+
want the actual orchestration and telemetry mechanics.
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
flowchart LR
|
|
37
|
+
classDef base fill:#08131f,stroke:#1ea7ff,color:#dff6ff,stroke-width:1px;
|
|
38
|
+
classDef accent fill:#0f2233,stroke:#ffd166,color:#fff4cc,stroke-width:1.5px;
|
|
39
|
+
|
|
40
|
+
Caller["Caller or host"]:::base --> Options["IRunMazeEvolutionOptions\nrun inputs"]:::base
|
|
41
|
+
Options --> Engine["EvolutionEngine facade\npopulation-level control"]:::accent
|
|
42
|
+
Engine --> Loop["generation loop\nevaluate mutate telemetry"]:::base
|
|
43
|
+
Loop --> Result["MazeEvolutionRunResult\nbest network + exit reason"]:::base
|
|
44
|
+
Result --> Phase["MazeEvolutionCurriculumPhaseOutcome\ncarry winner forward"]:::base
|
|
45
|
+
Engine --> Host["EvolutionHostAdapter\npause and stop hooks"]:::base
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```mermaid
|
|
49
|
+
flowchart TD
|
|
50
|
+
classDef base fill:#08131f,stroke:#1ea7ff,color:#dff6ff,stroke-width:1px;
|
|
51
|
+
classDef accent fill:#0f2233,stroke:#ffd166,color:#fff4cc,stroke-width:1.5px;
|
|
52
|
+
|
|
53
|
+
EngineFolder["evolutionEngine/"]:::accent --> Contracts["evolutionEngine.types.ts\npublic run contracts"]:::base
|
|
54
|
+
EngineFolder --> Scratch["engineState.types.ts\nshared scratch and toggles"]:::base
|
|
55
|
+
EngineFolder --> Loop["evolutionLoop.ts\nmain generation orchestration"]:::base
|
|
56
|
+
EngineFolder --> Services["evolutionEngine.services.ts\nand helpers"]:::base
|
|
57
|
+
EngineFolder --> Sampling["sampling.ts\nand telemetry support"]:::base
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For background reading on the staged difficulty idea behind the browser and
|
|
61
|
+
curriculum-style runs, see Wikipedia contributors,
|
|
62
|
+
[Curriculum learning](https://en.wikipedia.org/wiki/Curriculum_learning),
|
|
63
|
+
which captures the broader teaching idea of solving easier tasks before
|
|
64
|
+
harder ones.
|
|
65
|
+
|
|
66
|
+
Example: describe the host adapter and reporting hooks the engine may call.
|
|
54
67
|
|
|
55
|
-
|
|
68
|
+
```ts
|
|
69
|
+
const hostAdapter: EvolutionHostAdapter = {
|
|
70
|
+
isPauseRequested: () => window.asciiMazePaused === true,
|
|
71
|
+
handleStop: ({ reason, completedGenerations }) => {
|
|
72
|
+
console.log(reason, completedGenerations);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
```
|
|
56
76
|
|
|
57
|
-
|
|
77
|
+
Example: sketch one engine run configuration before execution begins.
|
|
58
78
|
|
|
59
|
-
|
|
79
|
+
```ts
|
|
80
|
+
const runOptions: IRunMazeEvolutionOptions = {
|
|
81
|
+
mazeConfig: { maze },
|
|
82
|
+
agentSimConfig: { maxSteps: 160 },
|
|
83
|
+
evolutionAlgorithmConfig: { popSize: 120, deterministic: true },
|
|
84
|
+
reportingConfig: { dashboardManager, logEvery: 5 },
|
|
85
|
+
};
|
|
86
|
+
```
|
|
60
87
|
|
|
61
88
|
## evolutionEngine/evolutionEngine.types.ts
|
|
62
89
|
|
|
@@ -255,1507 +282,1638 @@ Network instance annotated with telemetry fields during a generation.
|
|
|
255
282
|
|
|
256
283
|
Training constants used by Lamarckian warm-start and refinement helpers.
|
|
257
284
|
|
|
258
|
-
## evolutionEngine/
|
|
285
|
+
## evolutionEngine/engineState.types.ts
|
|
259
286
|
|
|
260
|
-
|
|
287
|
+
Shared contracts for the ASCII Maze evolution engine subsystem.
|
|
261
288
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
289
|
+
The `evolutionEngine/` folder is where the public `EvolutionEngine` facade
|
|
290
|
+
fans out into the lower-level machinery that keeps long maze runs practical:
|
|
291
|
+
pooled scratch state, deterministic RNG caches, telemetry workspaces,
|
|
292
|
+
sampling helpers, warm-start support, and population-level recovery logic.
|
|
266
293
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
294
|
+
This file is the common footing for that subsystem. It defines the shared
|
|
295
|
+
state and scratch-buffer contracts that let the rest of the engine stay
|
|
296
|
+
allocation-light and orchestration-first instead of passing dozens of loose
|
|
297
|
+
arrays and counters through every hot-path helper.
|
|
270
298
|
|
|
271
|
-
###
|
|
299
|
+
### EngineProfilingState
|
|
272
300
|
|
|
273
|
-
|
|
274
|
-
getTail(
|
|
275
|
-
state: EngineState,
|
|
276
|
-
source: T[] | undefined,
|
|
277
|
-
count: number,
|
|
278
|
-
): T[]
|
|
279
|
-
```
|
|
301
|
+
Aggregated profiling configuration and accumulators shared across the evolution run.
|
|
280
302
|
|
|
281
|
-
|
|
303
|
+
### EngineScratchState
|
|
282
304
|
|
|
283
|
-
|
|
284
|
-
1. Validate the source array and clamp the requested tail length.
|
|
285
|
-
2. Grow the pooled tail buffer to the next power of two when necessary.
|
|
286
|
-
3. Copy the suffix into the pooled buffer and trim its logical length.
|
|
305
|
+
Centralised shared state for the ASCII maze evolution façade.
|
|
287
306
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
307
|
+
Responsibilities:
|
|
308
|
+
1. Define the scratch-buffer schema consumed by telemetry, population, and inspection helpers.
|
|
309
|
+
2. Expose runtime toggle state (`EngineToggleState`) that drives optional phases and telemetry density.
|
|
310
|
+
3. Provide factory and maintenance helpers (`createEngineState`, `initialiseTelemetryScratch`, `ensureVisitedHashCapacity`, `ensureRngCacheBatch`, `reseedRngState`) that size buffers and keep deterministic RNG state in sync.
|
|
311
|
+
4. Export the project-wide singleton `engineState` so extracted modules can share the façade’s pooled resources while still accepting injected state for testing.
|
|
292
312
|
|
|
293
|
-
|
|
313
|
+
Callers mutate the returned scratch instances in place to avoid per-generation allocations; higher-level modules should treat the helpers as the sole entry point for sizing or resetting shared buffers.
|
|
294
314
|
|
|
295
|
-
|
|
315
|
+
### EngineState
|
|
296
316
|
|
|
297
|
-
|
|
317
|
+
Shared engine state instance combining pooled scratch buffers with toggle flags.
|
|
298
318
|
|
|
299
|
-
###
|
|
319
|
+
### EngineToggleState
|
|
300
320
|
|
|
301
|
-
|
|
302
|
-
pushHistory(
|
|
303
|
-
buffer: T[] | undefined,
|
|
304
|
-
value: T,
|
|
305
|
-
maxLength: number,
|
|
306
|
-
): T[]
|
|
307
|
-
```
|
|
321
|
+
Runtime switches that adjust telemetry verbosity and optional training phases.
|
|
308
322
|
|
|
309
|
-
|
|
323
|
+
### RngCacheHandles
|
|
310
324
|
|
|
311
|
-
|
|
312
|
-
- `buffer` - Existing history buffer (may be undefined).
|
|
313
|
-
- `value` - Value to append to the buffer.
|
|
314
|
-
- `maxLength` - Maximum allowed buffer length.
|
|
325
|
+
Handles returned after ensuring the RNG cache is ready for consumption.
|
|
315
326
|
|
|
316
|
-
|
|
327
|
+
### RngCacheParameters
|
|
317
328
|
|
|
318
|
-
|
|
329
|
+
Parameters controlling the RNG cache refill process.
|
|
319
330
|
|
|
320
|
-
|
|
331
|
+
### TelemetryScratchHandles
|
|
321
332
|
|
|
322
|
-
|
|
333
|
+
Collection of scratch buffers handed back after initialisation for convenience.
|
|
323
334
|
|
|
324
|
-
|
|
325
|
-
sampleArray(
|
|
326
|
-
state: EngineState,
|
|
327
|
-
source: T[],
|
|
328
|
-
sampleCount: number,
|
|
329
|
-
): T[]
|
|
330
|
-
```
|
|
335
|
+
### TelemetryScratchRequest
|
|
331
336
|
|
|
332
|
-
|
|
337
|
+
Configuration describing which telemetry scratch buffers require capacity guarantees.
|
|
333
338
|
|
|
334
|
-
|
|
335
|
-
returned array reuses shared scratch storage, so callers should copy it first
|
|
336
|
-
if they need the contents to survive another helper call.
|
|
339
|
+
### VisitedHashScratchHandles
|
|
337
340
|
|
|
338
|
-
|
|
339
|
-
1. Validate the input array and normalise `sampleCount` to an integer.
|
|
340
|
-
2. Grow the shared `sampleResultBuffer` to the next power of two when capacity is insufficient.
|
|
341
|
-
3. Fill the pooled buffer using the shared fast RNG and truncate the logical length to `sampleCount`.
|
|
341
|
+
Handles exposed after ensuring the visited-coordinate hash table capacity.
|
|
342
342
|
|
|
343
|
-
|
|
344
|
-
- `state` - Shared engine state providing pooled scratch buffers.
|
|
345
|
-
- `source` - Source array to sample from.
|
|
346
|
-
- `sampleCount` - Number of samples to draw (with replacement).
|
|
343
|
+
## evolutionEngine/evolutionLoop.ts
|
|
347
344
|
|
|
348
|
-
|
|
345
|
+
Evolution Loop Module
|
|
349
346
|
|
|
350
|
-
|
|
347
|
+
Purpose:
|
|
348
|
+
-------
|
|
349
|
+
Provides utilities for running the main NEAT evolution loop, including
|
|
350
|
+
generation orchestration, cancellation checking, and loop helper preparation.
|
|
351
351
|
|
|
352
|
-
|
|
353
|
-
|
|
352
|
+
This module encapsulates:
|
|
353
|
+
- Cancellation detection (AbortSignal and legacy cancellation API)
|
|
354
|
+
- Loop helper preparation (frame flushing, persistence, logging)
|
|
355
|
+
- Generation execution and orchestration
|
|
356
|
+
- Stop condition evaluation
|
|
354
357
|
|
|
355
|
-
|
|
358
|
+
ES2023 Policy:
|
|
359
|
+
-------------
|
|
360
|
+
- Uses nullish coalescing `??` and optional chaining `?.`
|
|
361
|
+
- Descriptive variable names (no short identifiers)
|
|
362
|
+
- Async/await for generation loops
|
|
363
|
+
- Best-effort error handling (swallow non-fatal errors)
|
|
364
|
+
|
|
365
|
+
### checkCancellation
|
|
356
366
|
|
|
357
367
|
```ts
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
): number
|
|
368
|
+
checkCancellation(
|
|
369
|
+
options: EvolutionOptions,
|
|
370
|
+
bestResult: IMazeRunResult | undefined,
|
|
371
|
+
): string | undefined
|
|
363
372
|
```
|
|
364
373
|
|
|
365
|
-
|
|
374
|
+
Inspect cooperative cancellation sources and annotate the provided result when cancelled.
|
|
366
375
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
376
|
+
This function checks for cancellation from two sources in priority order:
|
|
377
|
+
1) Legacy cancellation object with `isCancelled()` method
|
|
378
|
+
2) Standard AbortSignal with `aborted` property
|
|
370
379
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
380
|
+
Design Rationale:
|
|
381
|
+
- Allocation-free (uses only short-lived local references)
|
|
382
|
+
- Best-effort error handling (swallow exceptions to avoid disrupting loop)
|
|
383
|
+
- Sets `exitReason` on result object for caller inspection
|
|
384
|
+
- Safe to call on hot paths (no scratch buffers or pooling needed)
|
|
375
385
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
386
|
+
Cancellation Priority:
|
|
387
|
+
1. Check `options.cancellation.isCancelled()` (legacy API)
|
|
388
|
+
2. Check `options.signal.aborted` (standard AbortSignal)
|
|
389
|
+
3. Return undefined if no cancellation detected
|
|
380
390
|
|
|
381
|
-
|
|
391
|
+
Parameters:
|
|
382
392
|
|
|
383
|
-
|
|
393
|
+
Parameters:
|
|
394
|
+
- `options` - - Optional run configuration which may contain `cancellation` and/or `signal`
|
|
395
|
+
- `bestResult` - - Optional mutable result object that will be annotated with `exitReason`
|
|
384
396
|
|
|
385
|
-
|
|
386
|
-
for (let index = 0; index < sampled; index++) {
|
|
387
|
-
processGenome(sharedState.scratch.samplePool[index]);
|
|
388
|
-
}
|
|
397
|
+
Returns: A reason string ('cancelled' | 'aborted') when cancellation is detected, otherwise `undefined`
|
|
389
398
|
|
|
390
|
-
|
|
399
|
+
Examples:
|
|
391
400
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
source: T[],
|
|
396
|
-
segmentStart: number,
|
|
397
|
-
sampleCount: number,
|
|
398
|
-
): number
|
|
399
|
-
```
|
|
401
|
+
// Check cancellation before starting next generation
|
|
402
|
+
const cancelReason = checkCancellation(opts, runResult);
|
|
403
|
+
if (cancelReason) return cancelReason;
|
|
400
404
|
|
|
401
|
-
|
|
405
|
+
// Check cancellation without result annotation
|
|
406
|
+
if (checkCancellation(opts)) {
|
|
407
|
+
console.log('User requested cancellation');
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
402
410
|
|
|
403
|
-
|
|
404
|
-
of a population without first allocating `source.slice(segmentStart)`.
|
|
411
|
+
### checkStopConditions
|
|
405
412
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
413
|
+
```ts
|
|
414
|
+
checkStopConditions(
|
|
415
|
+
bestResult: IMazeRunResult | undefined,
|
|
416
|
+
bestNetwork: default | null,
|
|
417
|
+
maze: string[],
|
|
418
|
+
completedGenerations: number,
|
|
419
|
+
neat: default,
|
|
420
|
+
dashboardManager: IDashboardManager | undefined,
|
|
421
|
+
flushToFrame: () => Promise<void>,
|
|
422
|
+
hostAdapter: EvolutionHostAdapter | undefined,
|
|
423
|
+
minProgressToPass: number,
|
|
424
|
+
autoPauseOnSolve: boolean,
|
|
425
|
+
stopOnlyOnSolve: boolean,
|
|
426
|
+
stagnantGenerations: number,
|
|
427
|
+
maxStagnantGenerations: number,
|
|
428
|
+
maxGenerations: number,
|
|
429
|
+
): Promise<string | undefined>
|
|
430
|
+
```
|
|
410
431
|
|
|
411
|
-
|
|
412
|
-
- `state` - Shared engine state providing pooled sampling buffers.
|
|
413
|
-
- `source` - Source array to sample from.
|
|
414
|
-
- `segmentStart` - Inclusive start index of the segment.
|
|
415
|
-
- `sampleCount` - Number of items to sample (with replacement).
|
|
432
|
+
Inspect common termination conditions and perform minimal, best-effort side-effects.
|
|
416
433
|
|
|
417
|
-
|
|
434
|
+
This function checks three canonical stop reasons in priority order:
|
|
435
|
+
1) **Solved**: Best result achieves minimum progress threshold
|
|
436
|
+
2) **Stagnation**: No improvement for maxStagnantGenerations
|
|
437
|
+
3) **MaxGenerations**: Absolute generation cap reached
|
|
418
438
|
|
|
419
|
-
|
|
439
|
+
Design Rationale:
|
|
440
|
+
- Allocation-light (uses only local references)
|
|
441
|
+
- Best-effort error handling (all side effects swallowed)
|
|
442
|
+
- Safe to call frequently on hot paths
|
|
443
|
+
- Updates dashboard and yields to host when stopping
|
|
444
|
+
- Emits optional 'asciiMazeSolved' event on solve (browser only)
|
|
420
445
|
|
|
421
|
-
|
|
422
|
-
|
|
446
|
+
Side Effects (Best-Effort):
|
|
447
|
+
- Updates dashboard manager when stopping
|
|
448
|
+
- Awaits flushToFrame to yield to host
|
|
449
|
+
- Reports stop events through the optional host adapter
|
|
450
|
+
- Annotates `bestResult.exitReason` with canonical reason string
|
|
423
451
|
|
|
424
|
-
|
|
452
|
+
Parameters:
|
|
425
453
|
|
|
426
|
-
|
|
454
|
+
Parameters:
|
|
455
|
+
- `bestResult` - - Mutable run summary object (may be mutated with `exitReason`)
|
|
456
|
+
- `bestNetwork` - - Network object associated with the best result (for dashboard)
|
|
457
|
+
- `maze` - - Maze descriptor passed to dashboard updates/events
|
|
458
|
+
- `completedGenerations` - - Current generation index (integer)
|
|
459
|
+
- `neat` - - NEAT driver instance (passed to dashboard update)
|
|
460
|
+
- `dashboardManager` - - Optional manager exposing `update(maze, result, network, gen, neat)`
|
|
461
|
+
- `flushToFrame` - - Async function to yield to host renderer (e.g. requestAnimationFrame)
|
|
462
|
+
- `hostAdapter` - - Optional host adapter that owns host-side stop behavior
|
|
463
|
+
- `minProgressToPass` - - Numeric threshold to consider a run 'solved'
|
|
464
|
+
- `autoPauseOnSolve` - - When truthy request host-side pause handling on solve
|
|
465
|
+
- `stopOnlyOnSolve` - - When true ignore stagnation/maxGenerations as stop reasons
|
|
466
|
+
- `stagnantGenerations` - - Current count of stagnant generations observed
|
|
467
|
+
- `maxStagnantGenerations` - - Max allowed stagnant generations before stopping
|
|
468
|
+
- `maxGenerations` - - Absolute generation cap after which the run stops
|
|
427
469
|
|
|
428
|
-
|
|
429
|
-
1. Define the scratch-buffer schema consumed by telemetry, population, and inspection helpers.
|
|
430
|
-
2. Expose runtime toggle state (`EngineToggleState`) that drives optional phases and telemetry density.
|
|
431
|
-
3. Provide factory and maintenance helpers (`createEngineState`, `initialiseTelemetryScratch`, `ensureVisitedHashCapacity`, `ensureRngCacheBatch`, `reseedRngState`) that size buffers and keep deterministic RNG state in sync.
|
|
432
|
-
4. Export the project-wide singleton `engineState` so extracted modules can share the façade’s pooled resources while still accepting injected state for testing.
|
|
470
|
+
Returns: A canonical reason string ('solved'|'stagnation'|'maxGenerations') when stopping, otherwise `undefined`
|
|
433
471
|
|
|
434
|
-
|
|
472
|
+
Example:
|
|
435
473
|
|
|
436
|
-
|
|
474
|
+
// Check stop conditions after each generation
|
|
475
|
+
const reason = await checkStopConditions(
|
|
476
|
+
bestResult, bestNet, maze, gen, neat, dashboard, flush,
|
|
477
|
+
95, true, false, stagnant, 500, 10000
|
|
478
|
+
);
|
|
479
|
+
if (reason) {
|
|
480
|
+
console.log('Stopping due to', reason);
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
### emitProfileSummary
|
|
437
485
|
|
|
438
486
|
```ts
|
|
439
|
-
|
|
487
|
+
emitProfileSummary(
|
|
488
|
+
engineState: EngineState,
|
|
489
|
+
safeWrite: (msg: string) => void,
|
|
490
|
+
completedGenerations: number,
|
|
491
|
+
totalEvolveMs: number,
|
|
492
|
+
totalLamarckMs: number,
|
|
493
|
+
totalSimMs: number,
|
|
494
|
+
isProfilingDetailsEnabledFn: (state: EngineState) => boolean,
|
|
495
|
+
getProfilingAccumulatorsFn: (state: EngineState) => ProfilingAccumulators,
|
|
496
|
+
): void
|
|
440
497
|
```
|
|
441
498
|
|
|
442
|
-
|
|
499
|
+
Emit a formatted profiling summary showing average per-generation timings.
|
|
443
500
|
|
|
444
|
-
|
|
501
|
+
This function prints a compact profiling summary with average millisecond timings
|
|
502
|
+
for the main evolution phases (evolve, Lamarckian training, simulation). If detailed
|
|
503
|
+
profiling is enabled, it also prints averages for telemetry, simplify, snapshot, and
|
|
504
|
+
prune operations.
|
|
445
505
|
|
|
446
|
-
|
|
506
|
+
Design Rationale:
|
|
507
|
+
- Allocation-free (reuses pooled Float64Array for intermediate calculations)
|
|
508
|
+
- Best-effort error handling (swallow all exceptions)
|
|
509
|
+
- Defensive numeric validation with divide-by-zero guards
|
|
510
|
+
- Conditional detailed profiling output
|
|
447
511
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
512
|
+
Calculation Steps:
|
|
513
|
+
1. Validate and normalize generation count (guard divide-by-zero)
|
|
514
|
+
2. Store totals in pooled scratch buffer (4-slot Float64Array)
|
|
515
|
+
3. Compute per-generation averages by dividing totals by generation count
|
|
516
|
+
4. Format numbers with 2 decimal places and print compact summary
|
|
517
|
+
5. If detailed profiling enabled, print averaged detail line
|
|
451
518
|
|
|
452
|
-
|
|
519
|
+
Parameters:
|
|
453
520
|
|
|
454
|
-
|
|
521
|
+
Parameters:
|
|
522
|
+
- `engineState` - - Shared engine state with scratch buffers and profiling accumulators
|
|
523
|
+
- `safeWrite` - - Safe logging function (best-effort, never throws)
|
|
524
|
+
- `completedGenerations` - - Number of completed generations (must be > 0)
|
|
525
|
+
- `totalEvolveMs` - - Total milliseconds spent in NEAT evolve() calls
|
|
526
|
+
- `totalLamarckMs` - - Total milliseconds spent in Lamarckian training
|
|
527
|
+
- `totalSimMs` - - Total milliseconds spent in simulation
|
|
528
|
+
- `isProfilingDetailsEnabledFn` - - Function to check if detailed profiling is enabled
|
|
529
|
+
- `getProfilingAccumulatorsFn` - - Function to get detailed profiling accumulators
|
|
455
530
|
|
|
456
531
|
Example:
|
|
457
532
|
|
|
458
|
-
|
|
459
|
-
|
|
533
|
+
// Print averages after a run that completed 100 generations
|
|
534
|
+
emitProfileSummary(
|
|
535
|
+
state, console.log, 100, 12000, 3000, 4500,
|
|
536
|
+
isProfilingDetailsEnabled, getProfilingAccumulators
|
|
537
|
+
);
|
|
460
538
|
|
|
461
|
-
###
|
|
539
|
+
### EvolutionLoopResult
|
|
462
540
|
|
|
463
|
-
|
|
464
|
-
createScratchState(): EngineScratchState
|
|
465
|
-
```
|
|
541
|
+
Evolution loop result
|
|
466
542
|
|
|
467
|
-
|
|
543
|
+
### GenerationOutcome
|
|
468
544
|
|
|
469
|
-
|
|
545
|
+
Generation outcome with profiling timings
|
|
470
546
|
|
|
471
|
-
|
|
547
|
+
### MutableMazeResult
|
|
472
548
|
|
|
473
|
-
|
|
474
|
-
console.log(scratch.exps.length); // 4
|
|
549
|
+
Mutable result object with exitReason field
|
|
475
550
|
|
|
476
|
-
###
|
|
551
|
+
### persistSnapshotIfNeeded
|
|
477
552
|
|
|
478
553
|
```ts
|
|
479
|
-
|
|
554
|
+
persistSnapshotIfNeeded(
|
|
555
|
+
engineState: EngineState,
|
|
556
|
+
fs: { writeFileSync?: ((path: string, data: string) => void) | undefined; } | null,
|
|
557
|
+
pathModule: { join?: ((...paths: string[]) => string) | undefined; } | null,
|
|
558
|
+
persistDir: string | undefined,
|
|
559
|
+
persistTopK: number,
|
|
560
|
+
completedGenerations: number,
|
|
561
|
+
persistEvery: number,
|
|
562
|
+
neat: default,
|
|
563
|
+
bestFitness: number,
|
|
564
|
+
simplifyMode: boolean,
|
|
565
|
+
plateauCounter: number,
|
|
566
|
+
scratchSnapshotObj: Record<string, unknown>,
|
|
567
|
+
scratchSnapshotTop: SnapshotEntry[],
|
|
568
|
+
collectTelemetryTailFn: (state: EngineState, neat: default, count: number) => unknown,
|
|
569
|
+
getSortedIndicesByScoreFn: (state: EngineState, population: default[]) => number[],
|
|
570
|
+
isProfilingDetailsEnabledFn: (state: EngineState) => boolean,
|
|
571
|
+
profilingStartTimestampFn: (state: EngineState) => number,
|
|
572
|
+
accumulateProfilingDurationFn: (state: EngineState, label: string, duration: number) => void,
|
|
573
|
+
): void
|
|
480
574
|
```
|
|
481
575
|
|
|
482
|
-
|
|
576
|
+
Persist a population snapshot to disk at the configured interval.
|
|
483
577
|
|
|
484
|
-
|
|
578
|
+
This function writes a JSON snapshot of the current generation state when the
|
|
579
|
+
generation cadence aligns with the configured persistence interval. It captures
|
|
580
|
+
top-K genomes, telemetry tail, and metadata for later analysis or resume.
|
|
485
581
|
|
|
486
|
-
|
|
582
|
+
Design Rationale:
|
|
583
|
+
- Best-effort semantics: swallows all errors to avoid disrupting evolution loop
|
|
584
|
+
- Reuses pooled scratch objects (SCRATCH_SNAPSHOT_OBJ, SCRATCH_SNAPSHOT_TOP) to minimize allocations
|
|
585
|
+
- Optional profiling when enabled (measures snapshot serialization time)
|
|
586
|
+
- Defensive validation to ensure FS/path modules are available
|
|
487
587
|
|
|
488
|
-
|
|
489
|
-
|
|
588
|
+
Scheduling Logic:
|
|
589
|
+
- Only persists when `completedGenerations % persistEvery === 0`
|
|
590
|
+
- Requires valid fs.writeFileSync and pathModule.join functions
|
|
591
|
+
- Requires non-empty population
|
|
490
592
|
|
|
491
|
-
|
|
593
|
+
Snapshot Structure:
|
|
594
|
+
- generation: completed generation index
|
|
595
|
+
- bestFitness: best fitness score this generation
|
|
596
|
+
- simplifyMode: whether simplification was active
|
|
597
|
+
- plateauCounter: current plateau detection counter
|
|
598
|
+
- timestamp: Date.now() when snapshot was created
|
|
599
|
+
- telemetryTail: last N telemetry entries
|
|
600
|
+
- top: top-K genomes with minimal metadata (idx, score, nodes, connections, json)
|
|
492
601
|
|
|
493
|
-
|
|
602
|
+
Parameters:
|
|
494
603
|
|
|
495
|
-
|
|
604
|
+
Parameters:
|
|
605
|
+
- `engineState` - - Shared engine state with scratch buffers and profiling
|
|
606
|
+
- `fs` - - Node.js fs module or compatible FS API
|
|
607
|
+
- `pathModule` - - Node.js path module or compatible path API
|
|
608
|
+
- `persistDir` - - Directory path where snapshots should be written
|
|
609
|
+
- `persistTopK` - - Number of top genomes to include in snapshot
|
|
610
|
+
- `completedGenerations` - - Current generation index
|
|
611
|
+
- `persistEvery` - - Generation interval for persistence (e.g., 25 = every 25th generation)
|
|
612
|
+
- `neat` - - NEAT instance with population
|
|
613
|
+
- `bestFitness` - - Best fitness score this generation
|
|
614
|
+
- `simplifyMode` - - Whether simplification mode is active
|
|
615
|
+
- `plateauCounter` - - Current plateau counter value
|
|
616
|
+
- `scratchSnapshotObj` - - Pooled snapshot object (reused across calls)
|
|
617
|
+
- `scratchSnapshotTop` - - Pooled top-K buffer (reused across calls)
|
|
618
|
+
- `collectTelemetryTailFn` - - Function to collect telemetry tail from state
|
|
619
|
+
- `getSortedIndicesByScoreFn` - - Function to get sorted genome indices
|
|
620
|
+
- `isProfilingDetailsEnabledFn` - - Function to check profiling state
|
|
621
|
+
- `profilingStartTimestampFn` - - Function to get profiling start time
|
|
622
|
+
- `accumulateProfilingDurationFn` - - Function to accumulate profiling duration
|
|
496
623
|
|
|
497
|
-
|
|
624
|
+
Example:
|
|
498
625
|
|
|
499
|
-
|
|
626
|
+
// Persist snapshot every 25 generations
|
|
627
|
+
persistSnapshotIfNeeded(
|
|
628
|
+
state, fs, path, './snapshots', 10, 50, 25, neat, 0.95, false, 3,
|
|
629
|
+
scratchObj, scratchTop, collectTail, getSorted, isProfilingEnabled, profileStart, profileAccum
|
|
630
|
+
);
|
|
500
631
|
|
|
501
|
-
|
|
632
|
+
### prepareLoopHelpers
|
|
502
633
|
|
|
503
|
-
|
|
634
|
+
```ts
|
|
635
|
+
prepareLoopHelpers(
|
|
636
|
+
opts: EvolutionOptions,
|
|
637
|
+
scratchBundle: ScratchBundle,
|
|
638
|
+
): LoopHelpers
|
|
639
|
+
```
|
|
504
640
|
|
|
505
|
-
|
|
641
|
+
Build lightweight helpers used inside the evolution loop.
|
|
506
642
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
643
|
+
This function assembles the helper utilities needed by the main evolution loop:
|
|
644
|
+
- Frame flushing for cooperative yielding
|
|
645
|
+
- Persistence handles for snapshot saving (Node.js only)
|
|
646
|
+
- Safe logging writer with fallback chain
|
|
647
|
+
- Scratch buffer warm-up (best-effort)
|
|
512
648
|
|
|
513
|
-
|
|
649
|
+
Design Rationale:
|
|
650
|
+
- All initialization is best-effort (failures swallowed)
|
|
651
|
+
- Warms up common scratch buffers to reduce first-use allocation spikes
|
|
652
|
+
- Returns simple POJO with utilities (no class coupling)
|
|
653
|
+
- Side effects isolated to scratch bundle parameter
|
|
514
654
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
### EngineToggleState
|
|
520
|
-
|
|
521
|
-
Runtime switches that adjust telemetry verbosity and optional training phases.
|
|
655
|
+
Scratch Buffer Warm-Up:
|
|
656
|
+
- samplePool: Array for population sampling
|
|
657
|
+
- profilingScratch: Float64Array(4) for timing accumulation
|
|
658
|
+
- exps: Float64Array(64) for exponential computations
|
|
522
659
|
|
|
523
|
-
|
|
660
|
+
Parameters:
|
|
524
661
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
state: EngineState,
|
|
529
|
-
): RngCacheHandles
|
|
530
|
-
```
|
|
662
|
+
Parameters:
|
|
663
|
+
- `opts` - - Normalized run options (contains persistDir and dashboardManager)
|
|
664
|
+
- `scratchBundle` - - Engine scratch state for optional buffer warm-up
|
|
531
665
|
|
|
532
|
-
|
|
666
|
+
Returns: Object containing:
|
|
667
|
+
- flushToFrame: Async function for cooperative yielding
|
|
668
|
+
- fs: Node.js fs module (null in browsers)
|
|
669
|
+
- path: Node.js path module (null in browsers)
|
|
670
|
+
- safeWrite: Resilient logging function with fallback chain
|
|
533
671
|
|
|
534
|
-
|
|
535
|
-
- `parameters` - Congruential generator parameters and cache batch size.
|
|
536
|
-
- `state` - Optional engine state container (defaults to the shared singleton).
|
|
672
|
+
Example:
|
|
537
673
|
|
|
538
|
-
|
|
674
|
+
// Prepare loop helpers with scratch buffer warm-up
|
|
675
|
+
const { flushToFrame, fs, path, safeWrite } = prepareLoopHelpers(opts, engineState.scratch);
|
|
676
|
+
safeWrite('Starting evolution...\n');
|
|
677
|
+
await flushToFrame(); // Yield to host
|
|
539
678
|
|
|
540
|
-
###
|
|
679
|
+
### runEvolutionLoop
|
|
541
680
|
|
|
542
681
|
```ts
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
682
|
+
runEvolutionLoop(
|
|
683
|
+
engineState: EngineState,
|
|
684
|
+
neat: default,
|
|
685
|
+
opts: EvolutionOptions,
|
|
686
|
+
lamarckianTrainingSet: { input: number[]; output: number[]; }[],
|
|
687
|
+
encodedMaze: number[][],
|
|
688
|
+
startPosition: readonly [number, number],
|
|
689
|
+
exitPosition: readonly [number, number],
|
|
690
|
+
distanceMap: number[][],
|
|
691
|
+
helpers: LoopHelpers,
|
|
692
|
+
doProfile: boolean,
|
|
693
|
+
runtimeContext: EvolutionLoopRuntimeContext,
|
|
694
|
+
initialRingState: LogitsRingState,
|
|
695
|
+
telemetryContext: EvolutionLoopTelemetryContext,
|
|
696
|
+
supportContext: EvolutionLoopSupportContext,
|
|
697
|
+
constants: TrainingConstants & { DEFAULT_TRAIN_BATCH_LARGE: number; FITTEST_TRAIN_ITERATIONS: number; TELEMETRY_MINIMAL: boolean; SATURATION_PRUNE_THRESHOLD: number; RECENT_WINDOW: number; REDUCED_TELEMETRY: boolean; DISABLE_BALDWIN: boolean; },
|
|
698
|
+
): Promise<EvolutionLoopResult>
|
|
547
699
|
```
|
|
548
700
|
|
|
549
|
-
|
|
701
|
+
Internal evolution loop that executes generations until a stop condition or cancellation.
|
|
550
702
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
703
|
+
Behaviour & contract:
|
|
704
|
+
- Runs generations in a resilient, best-effort manner; internal errors are swallowed
|
|
705
|
+
so a single failure cannot abort the whole run.
|
|
706
|
+
- When `doProfile` is truthy the loop accumulates timing into a pooled Float64Array
|
|
707
|
+
to avoid per-iteration allocations. The pooled buffer is reused across calls.
|
|
708
|
+
- The helper performs side-effects (dashboard updates, persistence) in a non-fatal
|
|
709
|
+
fashion and yields to the host when requested via `helpers.flushToFrame`.
|
|
555
710
|
|
|
556
711
|
Parameters:
|
|
557
|
-
- `
|
|
558
|
-
- `
|
|
712
|
+
- `engineState` - - Shared engine state with scratch buffers and configuration
|
|
713
|
+
- `neat` - - NEAT driver instance used to perform evolution and mutation operations
|
|
714
|
+
- `opts` - - Normalised run options (produced by normalizeRunOptions)
|
|
715
|
+
- `lamarckianTrainingSet` - - Optional supervised training cases used for Lamarckian warm-start
|
|
716
|
+
- `encodedMaze` - - Encoded maze representation consumed by simulators
|
|
717
|
+
- `startPosition` - - Start coordinates for the simulated agent
|
|
718
|
+
- `exitPosition` - - Exit coordinates for the simulated agent
|
|
719
|
+
- `distanceMap` - - Optional precomputed distance map to speed simulation
|
|
720
|
+
- `helpers` - - Helper utilities: { flushToFrame, fs, path, safeWrite }
|
|
721
|
+
- `doProfile` - - When truthy collect and return millisecond timings in the result
|
|
722
|
+
- `runtimeContext` - - Shared pooled ring buffers and limits for the hot path.
|
|
723
|
+
- `initialRingState` - - Current mutable ring state for this run.
|
|
724
|
+
- `telemetryContext` - - Telemetry thresholds and verbosity switches used during simulation.
|
|
725
|
+
- `supportContext` - - Shared scratch buffers and helper callbacks used by the loop.
|
|
726
|
+
- `constants` - - Object containing all engine constants (DEFAULT_TRAIN_ERROR, etc.)
|
|
559
727
|
|
|
560
|
-
Returns:
|
|
728
|
+
Returns: Promise resolving to an object:
|
|
729
|
+
{ bestNetwork, bestResult, neat, completedGenerations, totalEvolveMs, totalLamarckMs, totalSimMs, updatedRingState }
|
|
561
730
|
|
|
562
731
|
Example:
|
|
563
732
|
|
|
564
|
-
const
|
|
733
|
+
const runSummary = await runEvolutionLoop(
|
|
734
|
+
state, neat, opts, trainingSet, maze, start, exit, distMap, helpers, true, ...
|
|
735
|
+
);
|
|
565
736
|
|
|
566
|
-
###
|
|
737
|
+
### runGeneration
|
|
567
738
|
|
|
568
739
|
```ts
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
740
|
+
runGeneration(
|
|
741
|
+
engineState: EngineState,
|
|
742
|
+
neat: default,
|
|
743
|
+
doProfile: boolean,
|
|
744
|
+
lamarckianIterations: number,
|
|
745
|
+
lamarckianTrainingSet: { input: number[]; output: number[]; }[],
|
|
746
|
+
lamarckianSampleSize: number | undefined,
|
|
747
|
+
safeWrite: (msg: string) => void,
|
|
748
|
+
completedGenerations: number,
|
|
749
|
+
dynamicPopEnabled: boolean,
|
|
750
|
+
dynamicPopMax: number,
|
|
751
|
+
plateauGenerations: number,
|
|
752
|
+
plateauCounter: number,
|
|
753
|
+
dynamicPopExpandInterval: number,
|
|
754
|
+
dynamicPopExpandFactor: number,
|
|
755
|
+
dynamicPopPlateauSlack: number,
|
|
756
|
+
speciesHistoryRef: number[],
|
|
757
|
+
emptyVec: default[],
|
|
758
|
+
scratchNodeIdx: Int32Array<ArrayBufferLike>,
|
|
759
|
+
getNodeIndicesByType: (nodes: NetworkNode[], type: string) => number,
|
|
760
|
+
constants: TrainingConstants,
|
|
761
|
+
): Promise<GenerationOutcome>
|
|
573
762
|
```
|
|
574
763
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
Growth strategy:
|
|
578
|
-
- Float64Array pools grow geometrically (powers of two) and preserve previously written prefixes.
|
|
579
|
-
- String buffers are replaced with larger arrays (also geometric) to avoid repeated reallocations.
|
|
580
|
-
- Optional higher-moment buffers (M3/M4/kurtosis) are only created when {@link TelemetryScratchRequest.includeHigherMoments}
|
|
581
|
-
is truthy to keep reduced telemetry lightweight.
|
|
582
|
-
|
|
583
|
-
Parameters:
|
|
584
|
-
- `request` - Capacity hints for upcoming telemetry computations.
|
|
585
|
-
- `state` - Optional engine state target (defaults to the singleton {@link engineState} ).
|
|
586
|
-
|
|
587
|
-
Returns: Handles referencing the ensured scratch buffers for immediate use.
|
|
588
|
-
|
|
589
|
-
### reseedRngState
|
|
590
|
-
|
|
591
|
-
```ts
|
|
592
|
-
reseedRngState(
|
|
593
|
-
seed: number,
|
|
594
|
-
state: EngineState,
|
|
595
|
-
): number
|
|
596
|
-
```
|
|
764
|
+
Run one generation: evolve, ensure output identity, update species history, maybe expand population,
|
|
765
|
+
and run Lamarckian training if configured.
|
|
597
766
|
|
|
598
|
-
|
|
767
|
+
Behaviour & contract:
|
|
768
|
+
- Performs a single NEAT generation step in a best-effort, non-throwing manner.
|
|
769
|
+
- Measures profiling durations when `doProfile` is truthy. Profiling is optional and
|
|
770
|
+
kept allocation-free (uses local numeric temporaries only).
|
|
771
|
+
- Invokes the following steps in order (each step is wrapped in a try/catch so
|
|
772
|
+
the evolution loop remains resilient to per-stage failures):
|
|
773
|
+
1) `neat.evolve()` to produce the fittest network for this generation.
|
|
774
|
+
2) `ensureOutputIdentity` to normalise output activations for consumers.
|
|
775
|
+
3) `handleSpeciesHistory` to update species statistics and history.
|
|
776
|
+
4) `maybeExpandPopulation` to grow the population when configured and warranted.
|
|
777
|
+
5) Optional Lamarckian warm-start training via `applyLamarckianTraining`.
|
|
778
|
+
- The method is allocation-light and reuses engine helpers / pooled buffers where
|
|
779
|
+
appropriate. It never throws; internal errors are swallowed and optionally logged
|
|
780
|
+
via the provided `safeWrite` function.
|
|
599
781
|
|
|
600
782
|
Parameters:
|
|
601
|
-
- `
|
|
602
|
-
- `
|
|
783
|
+
- `engineState` - - Shared engine state with scratch buffers and RNG
|
|
784
|
+
- `neat` - - NEAT driver instance used for evolving the generation
|
|
785
|
+
- `doProfile` - - When truthy measure timing for the evolve step (ms) using engine clock
|
|
786
|
+
- `lamarckianIterations` - - Number of supervised training iterations to run per genome (0 to skip)
|
|
787
|
+
- `lamarckianTrainingSet` - - Array of supervised training cases used for warm-start (may be empty)
|
|
788
|
+
- `lamarckianSampleSize` - - Optional per-network sample size used by the warm-start routine
|
|
789
|
+
- `safeWrite` - - Safe logging function; used only for best-effort diagnostic messages
|
|
790
|
+
- `completedGenerations` - - Current generation index (used by expansion heuristics)
|
|
791
|
+
- `dynamicPopEnabled` - - Whether dynamic population expansion is enabled
|
|
792
|
+
- `dynamicPopMax` - - Upper bound on population size for expansion
|
|
793
|
+
- `plateauGenerations` - - Window size used by plateau detection
|
|
794
|
+
- `plateauCounter` - - Current plateau counter used by expansion heuristics
|
|
795
|
+
- `dynamicPopExpandInterval` - - Generation interval to attempt expansion
|
|
796
|
+
- `dynamicPopExpandFactor` - - Fractional growth factor used to compute new members
|
|
797
|
+
- `dynamicPopPlateauSlack` - - Minimum plateau ratio required to trigger expansion
|
|
798
|
+
- `speciesHistoryRef` - - Mutable array holding species history (maintained externally)
|
|
799
|
+
- `emptyVec` - - Empty array fallback to avoid ephemeral allocations
|
|
800
|
+
- `scratchNodeIdx` - - Pooled node index buffer (passed through to helpers)
|
|
801
|
+
- `getNodeIndicesByType` - - Helper function to collect node indices by type
|
|
802
|
+
- `constants` - - Object containing DEFAULT_TRAIN_ERROR, DEFAULT_TRAIN_RATE, DEFAULT_TRAIN_MOMENTUM, DEFAULT_TRAIN_BATCH_SMALL, DEFAULT_STD_SMALL, DEFAULT_STD_ADJUST_MULT
|
|
603
803
|
|
|
604
|
-
Returns:
|
|
804
|
+
Returns: An object shaped { fittest, tEvolve, tLamarck } where:
|
|
805
|
+
- `fittest` is the network returned by `neat.evolve()` (may be null on error),
|
|
806
|
+
- `tEvolve` is the measured evolve duration in milliseconds when `doProfile` is true (0 otherwise),
|
|
807
|
+
- `tLamarck` is the total time spent in Lamarckian training (0 when skipped)
|
|
605
808
|
|
|
606
|
-
|
|
809
|
+
Example:
|
|
607
810
|
|
|
608
|
-
|
|
811
|
+
// Run a single generation with profiling and optional Lamarckian warm-start
|
|
812
|
+
const { fittest, tEvolve, tLamarck } = await runGeneration(
|
|
813
|
+
engineState,
|
|
814
|
+
neatInstance,
|
|
815
|
+
true, // doProfile
|
|
816
|
+
5, // lamarckianIterations
|
|
817
|
+
trainingSet,
|
|
818
|
+
16, // lamarckianSampleSize
|
|
819
|
+
console.log,
|
|
820
|
+
genIndex,
|
|
821
|
+
true,
|
|
822
|
+
500,
|
|
823
|
+
10,
|
|
824
|
+
plateauCounter,
|
|
825
|
+
5,
|
|
826
|
+
0.1,
|
|
827
|
+
0.75,
|
|
828
|
+
speciesHistory,
|
|
829
|
+
[],
|
|
830
|
+
nodeIndexBuffer,
|
|
831
|
+
getNodeIndicesByTypeFn,
|
|
832
|
+
{ DEFAULT_TRAIN_ERROR: 0.01, ... }
|
|
833
|
+
);
|
|
609
834
|
|
|
610
|
-
###
|
|
835
|
+
### SimResultWithOutputs
|
|
611
836
|
|
|
612
|
-
|
|
837
|
+
Simulation result with step outputs
|
|
613
838
|
|
|
614
|
-
###
|
|
839
|
+
### simulateAndPostprocess
|
|
615
840
|
|
|
616
841
|
```ts
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
842
|
+
simulateAndPostprocess(
|
|
843
|
+
engineState: EngineState,
|
|
844
|
+
fittest: default,
|
|
845
|
+
encodedMaze: number[][],
|
|
846
|
+
startPosition: readonly [number, number],
|
|
847
|
+
exitPosition: readonly [number, number],
|
|
848
|
+
distanceMap: number[][],
|
|
849
|
+
maxSteps: number | undefined,
|
|
850
|
+
doProfile: boolean,
|
|
851
|
+
safeWrite: (msg: string) => void,
|
|
852
|
+
logEvery: number,
|
|
853
|
+
completedGenerations: number,
|
|
854
|
+
neat: default,
|
|
855
|
+
runtimeContext: EvolutionLoopRuntimeContext,
|
|
856
|
+
ringState: LogitsRingState,
|
|
857
|
+
telemetryContext: EvolutionLoopTelemetryContext,
|
|
858
|
+
loopSupportContext: Pick<EvolutionLoopSupportContext, "loopHelpers">,
|
|
859
|
+
): SimulationResult
|
|
620
860
|
```
|
|
621
861
|
|
|
622
|
-
|
|
862
|
+
Simulate the supplied `fittest` genome/network and perform allocation-light postprocessing.
|
|
623
863
|
|
|
624
|
-
|
|
625
|
-
-
|
|
864
|
+
Behaviour & contract:
|
|
865
|
+
- Runs the simulation via `MazeMovement.simulateAgent` and attaches compact telemetry
|
|
866
|
+
(saturation fraction, action entropy) directly onto the `fittest` object (in-place).
|
|
867
|
+
- When per-step logits are returned the helper attempts to copy them into the engine's pooled
|
|
868
|
+
ring buffers to avoid per-run allocations. Two copy modes are supported:
|
|
869
|
+
1) Shared SAB-backed flat Float32Array with an atomic Int32 write index (cross-worker safe).
|
|
870
|
+
2) Local in-process per-row Float32Array ring (`scratchLogitsRing`).
|
|
871
|
+
- Best-effort: all mutation and buffer-copy steps are guarded; failures are swallowed so the
|
|
872
|
+
evolution loop is not interrupted. Use `safeWrite` for optional diagnostic messages.
|
|
626
873
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
)
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
Toggle the reduced telemetry mode.
|
|
636
|
-
|
|
637
|
-
Parameters:
|
|
638
|
-
- `isEnabled` - When true, trims telemetry to the essential metrics only.
|
|
639
|
-
|
|
640
|
-
### setTelemetryMinimalFlag
|
|
641
|
-
|
|
642
|
-
```ts
|
|
643
|
-
setTelemetryMinimalFlag(
|
|
644
|
-
isMinimal: boolean,
|
|
645
|
-
): void
|
|
646
|
-
```
|
|
874
|
+
Steps (high level):
|
|
875
|
+
1) Run the simulator and capture wall-time when `doProfile` is truthy.
|
|
876
|
+
2) Attach compact telemetry fields to `fittest` and ensure legacy `_lastStepOutputs` exists.
|
|
877
|
+
3) If per-step logits are available, ensure ring capacity and copy them into the selected ring.
|
|
878
|
+
4) Optionally prune saturated hidden->output connections and emit telemetry via logGenerationTelemetry.
|
|
879
|
+
5) Return the raw simulation result and elapsed simulation time (ms when profiling enabled).
|
|
647
880
|
|
|
648
|
-
|
|
881
|
+
Notes on pooling / reentrancy:
|
|
882
|
+
- The local ring is not re-entrant; callers must avoid concurrent writes.
|
|
883
|
+
- When shared mode is true we prefer the SAB-backed path which uses Atomics and is safe
|
|
884
|
+
for cross-thread producers.
|
|
649
885
|
|
|
650
886
|
Parameters:
|
|
651
|
-
- `
|
|
887
|
+
- `engineState` - - Shared engine state with scratch buffers and ring configuration
|
|
888
|
+
- `fittest` - - Genome/network considered the generation's best; may be mutated with metadata
|
|
889
|
+
- `encodedMaze` - - Maze descriptor used by the simulator
|
|
890
|
+
- `startPosition` - - Start co-ordinates passed as-is to the simulator
|
|
891
|
+
- `exitPosition` - - Exit co-ordinates passed as-is to the simulator
|
|
892
|
+
- `distanceMap` - - Optional precomputed distance map consumed by the simulator
|
|
893
|
+
- `maxSteps` - - Optional maximum simulation steps; may be undefined to allow default
|
|
894
|
+
- `doProfile` - - When truthy measure and return the simulation time in milliseconds
|
|
895
|
+
- `safeWrite` - - Optional logger used for non-fatal diagnostic messages
|
|
896
|
+
- `logEvery` - - Emit telemetry every `logEvery` generations (0 disables periodic telemetry)
|
|
897
|
+
- `completedGenerations` - - Current generation index used for conditional telemetry
|
|
898
|
+
- `neat` - - NEAT driver instance passed to telemetry hooks
|
|
899
|
+
- `runtimeContext` - - Shared pooled ring buffers and limits for logits telemetry.
|
|
900
|
+
- `ringState` - - Current mutable ring state (capacity, shared-mode flag, write cursor).
|
|
901
|
+
- `telemetryContext` - - Telemetry thresholds and verbosity switches used after simulation.
|
|
902
|
+
- `loopSupportContext` - - Shared scratch buffers and helper callbacks used by the loop.
|
|
652
903
|
|
|
653
|
-
|
|
904
|
+
Returns: An object { generationResult, simTime, updatedRingState } where simTime is ms when profiling is enabled
|
|
654
905
|
|
|
655
|
-
|
|
906
|
+
Example:
|
|
656
907
|
|
|
657
|
-
|
|
908
|
+
const { generationResult, simTime, updatedRingState } = simulateAndPostprocess(
|
|
909
|
+
state, bestGenome, maze, start, exit, distMap, 1000, true, console.log, 10, genIdx, neat, ...
|
|
910
|
+
);
|
|
658
911
|
|
|
659
|
-
|
|
912
|
+
### SimulationOutcome
|
|
660
913
|
|
|
661
|
-
|
|
914
|
+
Simulation result with profiling and ring state
|
|
662
915
|
|
|
663
|
-
|
|
916
|
+
### updateDashboardAndMaybeFlush
|
|
664
917
|
|
|
665
|
-
|
|
918
|
+
```ts
|
|
919
|
+
updateDashboardAndMaybeFlush(
|
|
920
|
+
maze: string[],
|
|
921
|
+
result: IMazeRunResult | undefined,
|
|
922
|
+
network: default | null,
|
|
923
|
+
completedGenerations: number,
|
|
924
|
+
neat: default,
|
|
925
|
+
dashboardManager: IDashboardManager | undefined,
|
|
926
|
+
flushToFrame: (() => Promise<void>) | undefined,
|
|
927
|
+
): Promise<void>
|
|
928
|
+
```
|
|
666
929
|
|
|
667
|
-
|
|
930
|
+
Safely update a UI dashboard with the latest run state and optionally yield to the
|
|
931
|
+
host/frame via an awaited flush function.
|
|
668
932
|
|
|
669
|
-
|
|
933
|
+
Behaviour (best-effort):
|
|
934
|
+
1) If `dashboardManager.update` exists and is callable, call it with the stable
|
|
935
|
+
argument order (maze, result, network, completedGenerations, neat). Any exception
|
|
936
|
+
raised by the dashboard is swallowed to avoid interrupting the evolution loop.
|
|
937
|
+
2) If `flushToFrame` is supplied as an async function, await it to yield control to
|
|
938
|
+
the event loop or renderer (for example `() => new Promise(r => requestAnimationFrame(r))`).
|
|
939
|
+
3) The helper avoids heap allocations and relies on existing pooled scratch buffers in
|
|
940
|
+
the engine for heavy telemetry elsewhere; this method intentionally performs only
|
|
941
|
+
short-lived control flow and minimal work.
|
|
670
942
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
): void
|
|
677
|
-
```
|
|
943
|
+
Design Rationale:
|
|
944
|
+
- Allocation-free (no ephemeral objects or arrays created)
|
|
945
|
+
- Best-effort error handling (swallow all dashboard/flush errors)
|
|
946
|
+
- Stable argument order for dashboard implementations
|
|
947
|
+
- Async support for cooperative yielding to host scheduler
|
|
678
948
|
|
|
679
|
-
|
|
949
|
+
Parameters:
|
|
680
950
|
|
|
681
951
|
Parameters:
|
|
682
|
-
- `
|
|
683
|
-
- `
|
|
684
|
-
- `
|
|
952
|
+
- `maze` - - Maze instance or descriptor used by dashboard rendering.
|
|
953
|
+
- `result` - - Per-run result object (path, progress, telemetry, etc.).
|
|
954
|
+
- `network` - - Network or genome object that should be visualised.
|
|
955
|
+
- `completedGenerations` - - Integer index of the completed generation.
|
|
956
|
+
- `neat` - - NEAT manager instance (context passed to the dashboard update).
|
|
957
|
+
- `dashboardManager` - - Optional manager exposing `update(maze, result, network, gen, neat)`.
|
|
958
|
+
- `flushToFrame` - - Optional async function used to yield to the host/frame scheduler; may be omitted.
|
|
685
959
|
|
|
686
|
-
|
|
960
|
+
Example:
|
|
687
961
|
|
|
688
|
-
|
|
962
|
+
// Yield to the browser's next repaint after dashboard update:
|
|
963
|
+
await updateDashboardAndMaybeFlush(
|
|
964
|
+
maze, genResult, fittestNetwork, gen, neatInstance, dashboard, () => new Promise(r => requestAnimationFrame(r))
|
|
965
|
+
);
|
|
966
|
+
|
|
967
|
+
### updateDashboardPeriodic
|
|
689
968
|
|
|
690
969
|
```ts
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
970
|
+
updateDashboardPeriodic(
|
|
971
|
+
maze: string[],
|
|
972
|
+
bestResult: IMazeRunResult | undefined,
|
|
973
|
+
bestNetwork: default | null,
|
|
974
|
+
completedGenerations: number,
|
|
975
|
+
neat: default,
|
|
976
|
+
dashboardManager: IDashboardManager | undefined,
|
|
977
|
+
flushToFrame: (() => Promise<void>) | undefined,
|
|
978
|
+
): Promise<void>
|
|
694
979
|
```
|
|
695
980
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
Returns: void.
|
|
981
|
+
Periodic dashboard update used when the engine wants to refresh a non-primary
|
|
982
|
+
dashboard view (for example background or periodic reporting). This helper is
|
|
983
|
+
intentionally small, allocation-light and best-effort: dashboard errors are
|
|
984
|
+
swallowed so the evolution loop cannot be interrupted by UI issues.
|
|
702
985
|
|
|
703
|
-
|
|
986
|
+
Behavioural contract:
|
|
987
|
+
1) If `dashboardManager.update` is present and callable the method invokes it with
|
|
988
|
+
the stable argument order: (maze, bestResult, bestNetwork, completedGenerations, neat).
|
|
989
|
+
2) If `flushToFrame` is supplied the helper awaits it after the update to yield to
|
|
990
|
+
the host renderer (eg. requestAnimationFrame). Any exceptions raised by the
|
|
991
|
+
flush are swallowed.
|
|
992
|
+
3) The helper avoids creating ephemeral arrays/objects and therefore does not use
|
|
993
|
+
typed-array scratch buffers here — there is no hot numerical work to pool. Other
|
|
994
|
+
engine helpers already reuse class-level scratch buffers where appropriate.
|
|
704
995
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
)
|
|
710
|
-
```
|
|
996
|
+
Design Rationale:
|
|
997
|
+
- Fast-guard early when update cannot be performed
|
|
998
|
+
- Best-effort error handling (swallow all exceptions)
|
|
999
|
+
- Preserves dashboard `this` binding with `.call()`
|
|
1000
|
+
- Minimal allocations (no scratch buffers needed)
|
|
711
1001
|
|
|
712
|
-
|
|
1002
|
+
Steps / inline intent:
|
|
1003
|
+
1. Fast-guard when an update cannot be performed (missing manager, update method,
|
|
1004
|
+
or missing content to visualise).
|
|
1005
|
+
2. Call the dashboard update in a try/catch to preserve best-effort semantics.
|
|
1006
|
+
3. Optionally await the provided `flushToFrame` function to yield to the host.
|
|
713
1007
|
|
|
714
1008
|
Parameters:
|
|
715
|
-
- `state` - Shared engine state providing the RNG cache and seed.
|
|
716
|
-
- `parameters` - Congruential parameters forwarded to {@link ensureRngCacheBatch} .
|
|
717
1009
|
|
|
718
|
-
|
|
1010
|
+
Parameters:
|
|
1011
|
+
- `maze` - - Maze descriptor passed to the dashboard renderer.
|
|
1012
|
+
- `bestResult` - - Best-run result object used for display (may be falsy when not present).
|
|
1013
|
+
- `bestNetwork` - - Network or genome object to visualise (may be falsy when not present).
|
|
1014
|
+
- `completedGenerations` - - Completed generation index (number).
|
|
1015
|
+
- `neat` - - NEAT manager instance (passed through to dashboard update).
|
|
1016
|
+
- `dashboardManager` - - Optional manager exposing `update(maze, result, network, gen, neat)`.
|
|
1017
|
+
- `flushToFrame` - - Optional async function used to yield to the host/frame scheduler
|
|
1018
|
+
(for example: `() => new Promise(r => requestAnimationFrame(r))`).
|
|
719
1019
|
|
|
720
1020
|
Example:
|
|
721
1021
|
|
|
722
|
-
|
|
1022
|
+
// Safe periodic update and yield to next frame
|
|
1023
|
+
await updateDashboardPeriodic(
|
|
1024
|
+
maze, result, network, gen, neatInstance, dashboard, () => new Promise(r => requestAnimationFrame(r))
|
|
1025
|
+
);
|
|
723
1026
|
|
|
724
|
-
|
|
1027
|
+
## evolutionEngine/evolutionEngine.services.ts
|
|
1028
|
+
|
|
1029
|
+
### applyEvolutionEngineRingState
|
|
725
1030
|
|
|
726
1031
|
```ts
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
):
|
|
1032
|
+
applyEvolutionEngineRingState(
|
|
1033
|
+
updatedRingState: LogitsRingState,
|
|
1034
|
+
): void
|
|
730
1035
|
```
|
|
731
1036
|
|
|
732
|
-
|
|
1037
|
+
Apply the latest logits-ring runtime values returned by the evolution loop.
|
|
733
1038
|
|
|
734
1039
|
Parameters:
|
|
735
|
-
- `
|
|
736
|
-
|
|
737
|
-
Returns: Mutable record of profiling accumulators keyed by category name.
|
|
1040
|
+
- `updatedRingState` - - New ring-capacity, shared-mode, and write-cursor values.
|
|
738
1041
|
|
|
739
|
-
###
|
|
1042
|
+
### configureEvolutionEngineToggles
|
|
740
1043
|
|
|
741
1044
|
```ts
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1045
|
+
configureEvolutionEngineToggles(
|
|
1046
|
+
reducedTelemetry: boolean,
|
|
1047
|
+
telemetryMinimal: boolean,
|
|
1048
|
+
disableBaldwinPhase: boolean,
|
|
1049
|
+
): void
|
|
745
1050
|
```
|
|
746
1051
|
|
|
747
|
-
|
|
1052
|
+
Apply telemetry and Baldwin-phase toggles derived from one normalized run request.
|
|
748
1053
|
|
|
749
1054
|
Parameters:
|
|
750
|
-
- `
|
|
751
|
-
|
|
752
|
-
|
|
1055
|
+
- `reducedTelemetry` - - When true, keep only the essential telemetry metrics.
|
|
1056
|
+
- `telemetryMinimal` - - When true, disable verbose telemetry capture.
|
|
1057
|
+
- `disableBaldwinPhase` - - When true, skip the Baldwin refinement stage.
|
|
753
1058
|
|
|
754
|
-
###
|
|
1059
|
+
### getEvolutionEngineFacadeRuntimeState
|
|
755
1060
|
|
|
756
1061
|
```ts
|
|
757
|
-
|
|
758
|
-
state: EngineState,
|
|
759
|
-
): boolean
|
|
1062
|
+
getEvolutionEngineFacadeRuntimeState(): LogitsRingState
|
|
760
1063
|
```
|
|
761
1064
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
Parameters:
|
|
765
|
-
- `state` - Shared engine state providing profiling configuration.
|
|
1065
|
+
Read the mutable facade-owned logits-ring runtime state.
|
|
766
1066
|
|
|
767
|
-
Returns:
|
|
1067
|
+
Returns: Current ring-capacity, shared-mode, and write-cursor state.
|
|
768
1068
|
|
|
769
|
-
###
|
|
1069
|
+
### getEvolutionEngineMaxLogitsRingCapacity
|
|
770
1070
|
|
|
771
1071
|
```ts
|
|
772
|
-
|
|
1072
|
+
getEvolutionEngineMaxLogitsRingCapacity(): number
|
|
773
1073
|
```
|
|
774
1074
|
|
|
775
|
-
|
|
1075
|
+
Read the hard maximum ring capacity used by the public facade.
|
|
776
1076
|
|
|
777
|
-
Returns:
|
|
1077
|
+
Returns: Maximum ring capacity allowed for logits telemetry.
|
|
778
1078
|
|
|
779
|
-
###
|
|
1079
|
+
### getEvolutionEngineSharedState
|
|
780
1080
|
|
|
781
1081
|
```ts
|
|
782
|
-
|
|
1082
|
+
getEvolutionEngineSharedState(): EngineState
|
|
783
1083
|
```
|
|
784
1084
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
Returns: Timestamp in milliseconds, preferring `performance.now` when available.
|
|
1085
|
+
Return the shared engine singleton used by extracted engine modules.
|
|
788
1086
|
|
|
789
|
-
|
|
1087
|
+
The public facade now depends on the same owner as the rest of the engine
|
|
1088
|
+
boundary instead of creating a private duplicate singleton.
|
|
790
1089
|
|
|
791
|
-
|
|
1090
|
+
Returns: Shared engine state singleton.
|
|
792
1091
|
|
|
793
|
-
###
|
|
1092
|
+
### resetEvolutionEngineRingState
|
|
794
1093
|
|
|
795
1094
|
```ts
|
|
796
|
-
|
|
1095
|
+
resetEvolutionEngineRingState(): void
|
|
797
1096
|
```
|
|
798
1097
|
|
|
799
|
-
|
|
1098
|
+
Reset the facade-owned logits-ring runtime state to its baseline defaults.
|
|
800
1099
|
|
|
801
|
-
|
|
1100
|
+
## evolutionEngine/sampling.ts
|
|
802
1101
|
|
|
803
|
-
|
|
1102
|
+
Sampling and history helpers extracted from the ASCII maze evolution façade.
|
|
804
1103
|
|
|
805
|
-
|
|
1104
|
+
Responsibilities:
|
|
1105
|
+
1. Provide allocation-light array sampling utilities that reuse the shared `EngineState` scratch pools.
|
|
1106
|
+
2. Expose history helpers that mirror the façade behaviour while keeping pooled buffers centralised.
|
|
1107
|
+
3. Centralise RNG parameter resolution for sampling paths to keep behaviour deterministic under shared state.
|
|
806
1108
|
|
|
807
|
-
|
|
1109
|
+
These helpers look small, but they sit under several hot paths. The main job
|
|
1110
|
+
is not "random choice" in the abstract; it is random choice without quietly
|
|
1111
|
+
reintroducing per-generation array churn into long-running experiments.
|
|
1112
|
+
|
|
1113
|
+
### getTail
|
|
808
1114
|
|
|
809
1115
|
```ts
|
|
810
|
-
|
|
1116
|
+
getTail(
|
|
811
1117
|
state: EngineState,
|
|
812
|
-
|
|
813
|
-
|
|
1118
|
+
source: T[] | undefined,
|
|
1119
|
+
count: number,
|
|
1120
|
+
): T[]
|
|
814
1121
|
```
|
|
815
1122
|
|
|
816
|
-
|
|
1123
|
+
Extract the last `count` items from `source` into the shared tail buffer.
|
|
817
1124
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1125
|
+
Steps:
|
|
1126
|
+
1. Validate the source array and clamp the requested tail length.
|
|
1127
|
+
2. Grow the pooled tail buffer to the next power of two when necessary.
|
|
1128
|
+
3. Copy the suffix into the pooled buffer and trim its logical length.
|
|
821
1129
|
|
|
822
|
-
|
|
1130
|
+
Parameters:
|
|
1131
|
+
- `state` - Shared engine state providing the tail history buffer.
|
|
1132
|
+
- `source` - Source array reference.
|
|
1133
|
+
- `count` - Number of trailing items requested.
|
|
823
1134
|
|
|
824
|
-
|
|
1135
|
+
Returns: Pooled array containing the requested tail slice.
|
|
825
1136
|
|
|
826
|
-
|
|
1137
|
+
Example:
|
|
827
1138
|
|
|
828
|
-
|
|
829
|
-
shared {@link EngineState}. They keep the façade lean by encapsulating heuristics for logits
|
|
830
|
-
ring sizing, telemetry scratch sizing, and connection flag pooling.
|
|
1139
|
+
const recent = getTail(sharedState, telemetryLog, 40);
|
|
831
1140
|
|
|
832
|
-
###
|
|
1141
|
+
### pushHistory
|
|
833
1142
|
|
|
834
1143
|
```ts
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1144
|
+
pushHistory(
|
|
1145
|
+
buffer: T[] | undefined,
|
|
1146
|
+
value: T,
|
|
1147
|
+
maxLength: number,
|
|
1148
|
+
): T[]
|
|
839
1149
|
```
|
|
840
1150
|
|
|
841
|
-
|
|
1151
|
+
Proxy to {@link MazeUtils.pushHistory} for consistency with the façade API.
|
|
842
1152
|
|
|
843
1153
|
Parameters:
|
|
844
|
-
- `
|
|
845
|
-
- `
|
|
1154
|
+
- `buffer` - Existing history buffer (may be undefined).
|
|
1155
|
+
- `value` - Value to append to the buffer.
|
|
1156
|
+
- `maxLength` - Maximum allowed buffer length.
|
|
846
1157
|
|
|
847
|
-
Returns:
|
|
1158
|
+
Returns: Updated history buffer with `value` appended and trimmed to `maxLength`.
|
|
848
1159
|
|
|
849
|
-
|
|
1160
|
+
Example:
|
|
1161
|
+
|
|
1162
|
+
const history = pushHistory(existingHistory, snapshot, 20);
|
|
1163
|
+
|
|
1164
|
+
### sampleArray
|
|
850
1165
|
|
|
851
1166
|
```ts
|
|
852
|
-
|
|
1167
|
+
sampleArray(
|
|
853
1168
|
state: EngineState,
|
|
854
|
-
|
|
855
|
-
|
|
1169
|
+
source: T[],
|
|
1170
|
+
sampleCount: number,
|
|
1171
|
+
): T[]
|
|
856
1172
|
```
|
|
857
1173
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
Parameters:
|
|
861
|
-
- `state` - Shared engine state containing the pooled bitmap.
|
|
862
|
-
- `minimumCapacity` - Minimum number of entries required by the caller.
|
|
1174
|
+
Sample `sampleCount` items (with replacement) from `source` into the pooled scratch buffer.
|
|
863
1175
|
|
|
864
|
-
|
|
1176
|
+
This helper is for callers that want an ephemeral array view immediately. The
|
|
1177
|
+
returned array reuses shared scratch storage, so callers should copy it first
|
|
1178
|
+
if they need the contents to survive another helper call.
|
|
865
1179
|
|
|
866
|
-
|
|
1180
|
+
Steps:
|
|
1181
|
+
1. Validate the input array and normalise `sampleCount` to an integer.
|
|
1182
|
+
2. Grow the shared `sampleResultBuffer` to the next power of two when capacity is insufficient.
|
|
1183
|
+
3. Fill the pooled buffer using the shared fast RNG and truncate the logical length to `sampleCount`.
|
|
867
1184
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
)
|
|
872
|
-
```
|
|
1185
|
+
Parameters:
|
|
1186
|
+
- `state` - Shared engine state providing pooled scratch buffers.
|
|
1187
|
+
- `source` - Source array to sample from.
|
|
1188
|
+
- `sampleCount` - Number of samples to draw (with replacement).
|
|
873
1189
|
|
|
874
|
-
|
|
1190
|
+
Returns: Pooled ephemeral array containing the sampled elements.
|
|
875
1191
|
|
|
876
|
-
|
|
877
|
-
- `capacityRequest` - Parameters describing the requested resize.
|
|
1192
|
+
Example:
|
|
878
1193
|
|
|
879
|
-
|
|
1194
|
+
const picks = sampleArray(sharedState, population, 16);
|
|
1195
|
+
const safeCopy = [...picks];
|
|
880
1196
|
|
|
881
|
-
###
|
|
1197
|
+
### sampleIntoScratch
|
|
882
1198
|
|
|
883
1199
|
```ts
|
|
884
|
-
|
|
1200
|
+
sampleIntoScratch(
|
|
885
1201
|
state: EngineState,
|
|
886
|
-
|
|
887
|
-
|
|
1202
|
+
source: T[],
|
|
1203
|
+
sampleCount: number,
|
|
1204
|
+
): number
|
|
888
1205
|
```
|
|
889
1206
|
|
|
890
|
-
|
|
1207
|
+
Sample up to `sampleCount` items (with replacement) into the shared `samplePool` buffer.
|
|
1208
|
+
|
|
1209
|
+
Compared with `sampleArray`, this form is for callers that only need a count
|
|
1210
|
+
plus access to the pooled buffer on `state.scratch.samplePool`. That avoids
|
|
1211
|
+
one more logical array object on the hottest paths.
|
|
1212
|
+
|
|
1213
|
+
Steps:
|
|
1214
|
+
1. Validate inputs and ensure the pooled buffer exists.
|
|
1215
|
+
2. Grow the buffer by powers of two when more capacity is required.
|
|
1216
|
+
3. Fill the buffer with random selections and return the number of items written.
|
|
891
1217
|
|
|
892
1218
|
Parameters:
|
|
893
|
-
- `state` - Shared engine state
|
|
894
|
-
- `
|
|
1219
|
+
- `state` - Shared engine state providing the pooled buffer.
|
|
1220
|
+
- `source` - Source array to sample from.
|
|
1221
|
+
- `sampleCount` - Number of elements requested (floored to an integer).
|
|
895
1222
|
|
|
896
|
-
|
|
1223
|
+
Returns: Number of sampled elements written into the pooled buffer.
|
|
1224
|
+
|
|
1225
|
+
Example:
|
|
1226
|
+
|
|
1227
|
+
const sampled = sampleIntoScratch(sharedState, population, 24);
|
|
1228
|
+
for (let index = 0; index < sampled; index++) {
|
|
1229
|
+
processGenome(sharedState.scratch.samplePool[index]);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
### sampleSegmentIntoScratch
|
|
897
1233
|
|
|
898
1234
|
```ts
|
|
899
|
-
|
|
1235
|
+
sampleSegmentIntoScratch(
|
|
900
1236
|
state: EngineState,
|
|
901
|
-
|
|
902
|
-
|
|
1237
|
+
source: T[],
|
|
1238
|
+
segmentStart: number,
|
|
1239
|
+
sampleCount: number,
|
|
1240
|
+
): number
|
|
903
1241
|
```
|
|
904
1242
|
|
|
905
|
-
|
|
1243
|
+
Sample from a suffix of `source` starting at `segmentStart` into the pooled buffer.
|
|
1244
|
+
|
|
1245
|
+
The common use case is "sample from the non-elite tail" or some later slice
|
|
1246
|
+
of a population without first allocating `source.slice(segmentStart)`.
|
|
1247
|
+
|
|
1248
|
+
Steps:
|
|
1249
|
+
1. Clamp indices and ensure there is a non-empty segment.
|
|
1250
|
+
2. Ensure the pooled buffer can store the requested sample count.
|
|
1251
|
+
3. Fill the buffer via the shared RNG, returning the number of items written.
|
|
906
1252
|
|
|
907
1253
|
Parameters:
|
|
908
|
-
- `state` - Shared engine state
|
|
909
|
-
- `
|
|
1254
|
+
- `state` - Shared engine state providing pooled sampling buffers.
|
|
1255
|
+
- `source` - Source array to sample from.
|
|
1256
|
+
- `segmentStart` - Inclusive start index of the segment.
|
|
1257
|
+
- `sampleCount` - Number of items to sample (with replacement).
|
|
910
1258
|
|
|
911
|
-
Returns:
|
|
1259
|
+
Returns: Number of elements written into the pooled buffer.
|
|
912
1260
|
|
|
913
|
-
|
|
1261
|
+
Example:
|
|
914
1262
|
|
|
915
|
-
|
|
1263
|
+
const written = sampleSegmentIntoScratch(sharedState, population, elitismCount, 12);
|
|
1264
|
+
const genome = sharedState.scratch.samplePool[0];
|
|
916
1265
|
|
|
917
|
-
|
|
1266
|
+
## evolutionEngine/engineState.ts
|
|
918
1267
|
|
|
919
|
-
|
|
1268
|
+
Centralised shared state for the ASCII maze evolution façade.
|
|
920
1269
|
|
|
921
|
-
|
|
1270
|
+
Responsibilities:
|
|
1271
|
+
1. Define the scratch-buffer schema consumed by telemetry, population, and inspection helpers.
|
|
1272
|
+
2. Expose runtime toggle state (`EngineToggleState`) that drives optional phases and telemetry density.
|
|
1273
|
+
3. Provide factory and maintenance helpers (`createEngineState`, `initialiseTelemetryScratch`, `ensureVisitedHashCapacity`, `ensureRngCacheBatch`, `reseedRngState`) that size buffers and keep deterministic RNG state in sync.
|
|
1274
|
+
4. Export the project-wide singleton `engineState` so extracted modules can share the façade’s pooled resources while still accepting injected state for testing.
|
|
1275
|
+
|
|
1276
|
+
Callers mutate the returned scratch instances in place to avoid per-generation allocations; higher-level modules should treat the helpers as the sole entry point for sizing or resetting shared buffers.
|
|
1277
|
+
|
|
1278
|
+
### createEngineState
|
|
922
1279
|
|
|
923
1280
|
```ts
|
|
924
|
-
|
|
925
|
-
state: EngineState,
|
|
926
|
-
populationSize: number,
|
|
927
|
-
): void
|
|
1281
|
+
createEngineState(): EngineState
|
|
928
1282
|
```
|
|
929
1283
|
|
|
930
|
-
|
|
1284
|
+
Fabricates a new {@link EngineState} with pre-sized scratch buffers and default toggle values.
|
|
931
1285
|
|
|
932
|
-
|
|
933
|
-
- `state` - Shared engine state exposing scratch buffers.
|
|
934
|
-
- `populationSize` - Current population size used to derive shrink heuristics.
|
|
1286
|
+
Returns: Initialized engine state used by the maze evolution façade.
|
|
935
1287
|
|
|
936
|
-
###
|
|
1288
|
+
### createProfilingState
|
|
937
1289
|
|
|
938
|
-
|
|
1290
|
+
```ts
|
|
1291
|
+
createProfilingState(): EngineProfilingState
|
|
1292
|
+
```
|
|
939
1293
|
|
|
940
|
-
|
|
1294
|
+
Fabricates the profiling state bundle consumed by timing helpers.
|
|
941
1295
|
|
|
942
|
-
|
|
1296
|
+
Returns: Profiling configuration and accumulator state.
|
|
943
1297
|
|
|
944
|
-
|
|
1298
|
+
Example:
|
|
945
1299
|
|
|
946
|
-
|
|
1300
|
+
const profiling = createProfilingState();
|
|
1301
|
+
console.log(profiling.detailsEnabled); // false unless env flag set
|
|
947
1302
|
|
|
948
|
-
|
|
949
|
-
- Create cooperative frame-yielding helpers for async evolution loops.
|
|
950
|
-
- Initialize Node.js persistence helpers when available.
|
|
951
|
-
- Build resilient logging writers with dashboard and console fallbacks.
|
|
1303
|
+
### createScratchState
|
|
952
1304
|
|
|
953
|
-
|
|
1305
|
+
```ts
|
|
1306
|
+
createScratchState(): EngineScratchState
|
|
1307
|
+
```
|
|
954
1308
|
|
|
955
|
-
|
|
1309
|
+
Fabricates the default pooled scratch buffers shared by the evolution façade.
|
|
956
1310
|
|
|
957
|
-
|
|
1311
|
+
Returns: Scratch bundle sized for the baseline maze curriculum workload.
|
|
958
1312
|
|
|
959
|
-
|
|
1313
|
+
Example:
|
|
960
1314
|
|
|
961
|
-
|
|
1315
|
+
const scratch = createScratchState();
|
|
1316
|
+
console.log(scratch.exps.length); // 4
|
|
1317
|
+
|
|
1318
|
+
### createToggleState
|
|
962
1319
|
|
|
963
1320
|
```ts
|
|
964
|
-
|
|
965
|
-
persistDir: string | undefined,
|
|
966
|
-
): { fs: FilesystemModule | null; path: PathModule | null; }
|
|
1321
|
+
createToggleState(): EngineToggleState
|
|
967
1322
|
```
|
|
968
1323
|
|
|
969
|
-
|
|
970
|
-
directory exists. This helper intentionally does nothing in browser-like hosts.
|
|
1324
|
+
Fabricates the default runtime toggle bundle for the evolution façade.
|
|
971
1325
|
|
|
972
|
-
|
|
973
|
-
1. Detect whether a Node-like `require` is available and attempt to load `fs` and `path`
|
|
974
|
-
2. If both modules are available and `persistDir` is provided, ensure the directory exists
|
|
975
|
-
by creating it recursively when necessary
|
|
976
|
-
3. Return an object containing the (possibly null) `{ fs, path }` references for callers to use
|
|
1326
|
+
Returns: Toggle state with all optional phases enabled.
|
|
977
1327
|
|
|
978
|
-
|
|
979
|
-
- This helper deliberately performs defensive checks and swallows synchronous errors because
|
|
980
|
-
persistence is optional in many host environments (tests, browser demos)
|
|
981
|
-
- No large allocations are performed here; the function returns lightweight references
|
|
1328
|
+
Example:
|
|
982
1329
|
|
|
983
|
-
|
|
984
|
-
|
|
1330
|
+
const toggles = createToggleState();
|
|
1331
|
+
console.log(toggles.reducedTelemetry); // false
|
|
985
1332
|
|
|
986
|
-
|
|
1333
|
+
### DEFAULT_RNG_CACHE_BATCH_SIZE
|
|
987
1334
|
|
|
988
|
-
|
|
1335
|
+
Default RNG cache batch size mirroring the façade constant.
|
|
989
1336
|
|
|
990
|
-
|
|
991
|
-
if (fs && path) {
|
|
992
|
-
fs.writeFileSync(path.join(dir, 'snapshot.json'), data);
|
|
993
|
-
}
|
|
1337
|
+
### DEFAULT_VISITED_HASH_LOAD_FACTOR
|
|
994
1338
|
|
|
995
|
-
|
|
1339
|
+
Default load factor target for the visited coordinate hash table.
|
|
1340
|
+
|
|
1341
|
+
### EngineProfilingState
|
|
1342
|
+
|
|
1343
|
+
Aggregated profiling configuration and accumulators shared across the evolution run.
|
|
1344
|
+
|
|
1345
|
+
### EngineScratchState
|
|
1346
|
+
|
|
1347
|
+
Centralised shared state for the ASCII maze evolution façade.
|
|
1348
|
+
|
|
1349
|
+
Responsibilities:
|
|
1350
|
+
1. Define the scratch-buffer schema consumed by telemetry, population, and inspection helpers.
|
|
1351
|
+
2. Expose runtime toggle state (`EngineToggleState`) that drives optional phases and telemetry density.
|
|
1352
|
+
3. Provide factory and maintenance helpers (`createEngineState`, `initialiseTelemetryScratch`, `ensureVisitedHashCapacity`, `ensureRngCacheBatch`, `reseedRngState`) that size buffers and keep deterministic RNG state in sync.
|
|
1353
|
+
4. Export the project-wide singleton `engineState` so extracted modules can share the façade’s pooled resources while still accepting injected state for testing.
|
|
1354
|
+
|
|
1355
|
+
Callers mutate the returned scratch instances in place to avoid per-generation allocations; higher-level modules should treat the helpers as the sole entry point for sizing or resetting shared buffers.
|
|
1356
|
+
|
|
1357
|
+
### EngineState
|
|
1358
|
+
|
|
1359
|
+
Shared engine state instance combining pooled scratch buffers with toggle flags.
|
|
1360
|
+
|
|
1361
|
+
### EngineToggleState
|
|
1362
|
+
|
|
1363
|
+
Runtime switches that adjust telemetry verbosity and optional training phases.
|
|
1364
|
+
|
|
1365
|
+
### ensureRngCacheBatch
|
|
996
1366
|
|
|
997
1367
|
```ts
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1368
|
+
ensureRngCacheBatch(
|
|
1369
|
+
parameters: RngCacheParameters,
|
|
1370
|
+
state: EngineState,
|
|
1371
|
+
): RngCacheHandles
|
|
1001
1372
|
```
|
|
1002
1373
|
|
|
1003
|
-
|
|
1374
|
+
Ensure the RNG cache contains fresh samples before consumption.
|
|
1004
1375
|
|
|
1005
1376
|
Parameters:
|
|
1006
|
-
- `
|
|
1377
|
+
- `parameters` - Congruential generator parameters and cache batch size.
|
|
1378
|
+
- `state` - Optional engine state container (defaults to the shared singleton).
|
|
1007
1379
|
|
|
1008
|
-
Returns:
|
|
1380
|
+
Returns: Handles exposing the cache and its batch size.
|
|
1009
1381
|
|
|
1010
|
-
###
|
|
1382
|
+
### ensureVisitedHashCapacity
|
|
1011
1383
|
|
|
1012
1384
|
```ts
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1385
|
+
ensureVisitedHashCapacity(
|
|
1386
|
+
targetEntryCount: number,
|
|
1387
|
+
state: EngineState,
|
|
1388
|
+
): VisitedHashScratchHandles
|
|
1016
1389
|
```
|
|
1017
1390
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
Behaviour:
|
|
1021
|
-
- Prefers `requestAnimationFrame` when available (browser hosts)
|
|
1022
|
-
- Falls back to `setImmediate` when available (Node) or `setTimeout(...,0)` otherwise
|
|
1023
|
-
- Respects an optional host adapter pause callback by polling between ticks without busy-waiting
|
|
1024
|
-
- Resolves once a single new frame or tick is available and the host is not paused
|
|
1391
|
+
Ensure the visited-coordinate hash table can store the requested entry count.
|
|
1025
1392
|
|
|
1026
|
-
|
|
1027
|
-
1.
|
|
1028
|
-
2. When
|
|
1029
|
-
3.
|
|
1393
|
+
Growth policy:
|
|
1394
|
+
1. Tables expand geometrically (powers of two) while respecting the configured load factor.
|
|
1395
|
+
2. When the existing table is large enough, it is cleared in place to preserve the allocation.
|
|
1396
|
+
3. Load factors outside the (0,1) range fall back to {@link DEFAULT_VISITED_HASH_LOAD_FACTOR}.
|
|
1030
1397
|
|
|
1031
1398
|
Parameters:
|
|
1032
|
-
- `
|
|
1399
|
+
- `targetEntryCount` - Expected number of unique coordinates to store.
|
|
1400
|
+
- `state` - Optional engine state container (defaults to the shared singleton).
|
|
1033
1401
|
|
|
1034
|
-
Returns:
|
|
1402
|
+
Returns: Cleared table reference plus the slot mask for probing loops.
|
|
1035
1403
|
|
|
1036
1404
|
Example:
|
|
1037
1405
|
|
|
1038
|
-
const
|
|
1039
|
-
await flushToFrame(); // yields to next frame/tick
|
|
1406
|
+
const { table, slotMask } = ensureVisitedHashCapacity(256);
|
|
1040
1407
|
|
|
1041
|
-
###
|
|
1408
|
+
### initialiseTelemetryScratch
|
|
1042
1409
|
|
|
1043
1410
|
```ts
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1411
|
+
initialiseTelemetryScratch(
|
|
1412
|
+
request: TelemetryScratchRequest,
|
|
1413
|
+
state: EngineState,
|
|
1414
|
+
): TelemetryScratchHandles
|
|
1047
1415
|
```
|
|
1048
1416
|
|
|
1049
|
-
|
|
1050
|
-
dashboard logger, and finally `console.log` as a last resort.
|
|
1051
|
-
|
|
1052
|
-
Steps:
|
|
1053
|
-
1. If Node `process.stdout.write` is available, use it (no trailing newline forced)
|
|
1054
|
-
2. Else if `dashboardManager.logFunction` exists, call it
|
|
1055
|
-
3. Else fall back to `console.log` and trim the message
|
|
1417
|
+
Ensure telemetry-related scratch buffers allocate enough capacity for upcoming work.
|
|
1056
1418
|
|
|
1057
|
-
|
|
1058
|
-
-
|
|
1059
|
-
-
|
|
1060
|
-
|
|
1419
|
+
Growth strategy:
|
|
1420
|
+
- Float64Array pools grow geometrically (powers of two) and preserve previously written prefixes.
|
|
1421
|
+
- String buffers are replaced with larger arrays (also geometric) to avoid repeated reallocations.
|
|
1422
|
+
- Optional higher-moment buffers (M3/M4/kurtosis) are only created when {@link TelemetryScratchRequest.includeHigherMoments}
|
|
1423
|
+
is truthy to keep reduced telemetry lightweight.
|
|
1061
1424
|
|
|
1062
1425
|
Parameters:
|
|
1063
|
-
- `
|
|
1426
|
+
- `request` - Capacity hints for upcoming telemetry computations.
|
|
1427
|
+
- `state` - Optional engine state target (defaults to the singleton {@link engineState} ).
|
|
1064
1428
|
|
|
1065
|
-
Returns:
|
|
1429
|
+
Returns: Handles referencing the ensured scratch buffers for immediate use.
|
|
1066
1430
|
|
|
1067
|
-
|
|
1431
|
+
### reseedRngState
|
|
1068
1432
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1433
|
+
```ts
|
|
1434
|
+
reseedRngState(
|
|
1435
|
+
seed: number,
|
|
1436
|
+
state: EngineState,
|
|
1437
|
+
): number
|
|
1438
|
+
```
|
|
1071
1439
|
|
|
1072
|
-
|
|
1440
|
+
Persist a new RNG seed and schedule a cache refill on the next draw.
|
|
1073
1441
|
|
|
1074
|
-
|
|
1442
|
+
Parameters:
|
|
1443
|
+
- `seed` - Raw numeric seed requested by deterministic mode.
|
|
1444
|
+
- `state` - Optional engine state container (defaults to the shared singleton).
|
|
1075
1445
|
|
|
1076
|
-
|
|
1446
|
+
Returns: Unsigned seed stored in scratch state for diagnostics.
|
|
1077
1447
|
|
|
1078
|
-
|
|
1448
|
+
### RngCacheHandles
|
|
1079
1449
|
|
|
1080
|
-
|
|
1081
|
-
-------
|
|
1082
|
-
Provides utilities for running the main NEAT evolution loop, including
|
|
1083
|
-
generation orchestration, cancellation checking, and loop helper preparation.
|
|
1450
|
+
Handles returned after ensuring the RNG cache is ready for consumption.
|
|
1084
1451
|
|
|
1085
|
-
|
|
1086
|
-
- Cancellation detection (AbortSignal and legacy cancellation API)
|
|
1087
|
-
- Loop helper preparation (frame flushing, persistence, logging)
|
|
1088
|
-
- Generation execution and orchestration
|
|
1089
|
-
- Stop condition evaluation
|
|
1452
|
+
### RngCacheParameters
|
|
1090
1453
|
|
|
1091
|
-
|
|
1092
|
-
-------------
|
|
1093
|
-
- Uses nullish coalescing `??` and optional chaining `?.`
|
|
1094
|
-
- Descriptive variable names (no short identifiers)
|
|
1095
|
-
- Async/await for generation loops
|
|
1096
|
-
- Best-effort error handling (swallow non-fatal errors)
|
|
1454
|
+
Parameters controlling the RNG cache refill process.
|
|
1097
1455
|
|
|
1098
|
-
###
|
|
1456
|
+
### setBaldwinPhaseDisabledFlag
|
|
1099
1457
|
|
|
1100
1458
|
```ts
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
): string | undefined
|
|
1459
|
+
setBaldwinPhaseDisabledFlag(
|
|
1460
|
+
isDisabled: boolean,
|
|
1461
|
+
): void
|
|
1105
1462
|
```
|
|
1106
1463
|
|
|
1107
|
-
|
|
1464
|
+
Enable or disable the Baldwin-phase warm-start pipeline.
|
|
1108
1465
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
2) Standard AbortSignal with `aborted` property
|
|
1466
|
+
Parameters:
|
|
1467
|
+
- `isDisabled` - When true, skips the Lamarckian training stage.
|
|
1112
1468
|
|
|
1113
|
-
|
|
1114
|
-
- Allocation-free (uses only short-lived local references)
|
|
1115
|
-
- Best-effort error handling (swallow exceptions to avoid disrupting loop)
|
|
1116
|
-
- Sets `exitReason` on result object for caller inspection
|
|
1117
|
-
- Safe to call on hot paths (no scratch buffers or pooling needed)
|
|
1469
|
+
### setReducedTelemetryFlag
|
|
1118
1470
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1471
|
+
```ts
|
|
1472
|
+
setReducedTelemetryFlag(
|
|
1473
|
+
isEnabled: boolean,
|
|
1474
|
+
): void
|
|
1475
|
+
```
|
|
1123
1476
|
|
|
1124
|
-
|
|
1477
|
+
Toggle the reduced telemetry mode.
|
|
1125
1478
|
|
|
1126
1479
|
Parameters:
|
|
1127
|
-
- `
|
|
1128
|
-
- `bestResult` - - Optional mutable result object that will be annotated with `exitReason`
|
|
1480
|
+
- `isEnabled` - When true, trims telemetry to the essential metrics only.
|
|
1129
1481
|
|
|
1130
|
-
|
|
1482
|
+
### setTelemetryMinimalFlag
|
|
1131
1483
|
|
|
1132
|
-
|
|
1484
|
+
```ts
|
|
1485
|
+
setTelemetryMinimalFlag(
|
|
1486
|
+
isMinimal: boolean,
|
|
1487
|
+
): void
|
|
1488
|
+
```
|
|
1133
1489
|
|
|
1134
|
-
|
|
1135
|
-
const cancelReason = checkCancellation(opts, runResult);
|
|
1136
|
-
if (cancelReason) return cancelReason;
|
|
1490
|
+
Toggle the minimal telemetry mode for JSON output.
|
|
1137
1491
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
console.log('User requested cancellation');
|
|
1141
|
-
break;
|
|
1142
|
-
}
|
|
1492
|
+
Parameters:
|
|
1493
|
+
- `isMinimal` - When true, disables verbose telemetry capture.
|
|
1143
1494
|
|
|
1144
|
-
###
|
|
1495
|
+
### TelemetryScratchHandles
|
|
1145
1496
|
|
|
1146
|
-
|
|
1147
|
-
checkStopConditions(
|
|
1148
|
-
bestResult: IMazeRunResult | undefined,
|
|
1149
|
-
bestNetwork: default | null,
|
|
1150
|
-
maze: string[],
|
|
1151
|
-
completedGenerations: number,
|
|
1152
|
-
neat: default,
|
|
1153
|
-
dashboardManager: IDashboardManager | undefined,
|
|
1154
|
-
flushToFrame: () => Promise<void>,
|
|
1155
|
-
hostAdapter: EvolutionHostAdapter | undefined,
|
|
1156
|
-
minProgressToPass: number,
|
|
1157
|
-
autoPauseOnSolve: boolean,
|
|
1158
|
-
stopOnlyOnSolve: boolean,
|
|
1159
|
-
stagnantGenerations: number,
|
|
1160
|
-
maxStagnantGenerations: number,
|
|
1161
|
-
maxGenerations: number,
|
|
1162
|
-
): Promise<string | undefined>
|
|
1163
|
-
```
|
|
1497
|
+
Collection of scratch buffers handed back after initialisation for convenience.
|
|
1164
1498
|
|
|
1165
|
-
|
|
1499
|
+
### TelemetryScratchRequest
|
|
1166
1500
|
|
|
1167
|
-
|
|
1168
|
-
1) **Solved**: Best result achieves minimum progress threshold
|
|
1169
|
-
2) **Stagnation**: No improvement for maxStagnantGenerations
|
|
1170
|
-
3) **MaxGenerations**: Absolute generation cap reached
|
|
1501
|
+
Configuration describing which telemetry scratch buffers require capacity guarantees.
|
|
1171
1502
|
|
|
1172
|
-
|
|
1173
|
-
- Allocation-light (uses only local references)
|
|
1174
|
-
- Best-effort error handling (all side effects swallowed)
|
|
1175
|
-
- Safe to call frequently on hot paths
|
|
1176
|
-
- Updates dashboard and yields to host when stopping
|
|
1177
|
-
- Emits optional 'asciiMazeSolved' event on solve (browser only)
|
|
1503
|
+
### VisitedHashScratchHandles
|
|
1178
1504
|
|
|
1179
|
-
|
|
1180
|
-
- Updates dashboard manager when stopping
|
|
1181
|
-
- Awaits flushToFrame to yield to host
|
|
1182
|
-
- Reports stop events through the optional host adapter
|
|
1183
|
-
- Annotates `bestResult.exitReason` with canonical reason string
|
|
1505
|
+
Handles exposed after ensuring the visited-coordinate hash table capacity.
|
|
1184
1506
|
|
|
1185
|
-
|
|
1507
|
+
## evolutionEngine/rngAndTiming.ts
|
|
1186
1508
|
|
|
1187
|
-
|
|
1188
|
-
- `bestResult` - - Mutable run summary object (may be mutated with `exitReason`)
|
|
1189
|
-
- `bestNetwork` - - Network object associated with the best result (for dashboard)
|
|
1190
|
-
- `maze` - - Maze descriptor passed to dashboard updates/events
|
|
1191
|
-
- `completedGenerations` - - Current generation index (integer)
|
|
1192
|
-
- `neat` - - NEAT driver instance (passed to dashboard update)
|
|
1193
|
-
- `dashboardManager` - - Optional manager exposing `update(maze, result, network, gen, neat)`
|
|
1194
|
-
- `flushToFrame` - - Async function to yield to host renderer (e.g. requestAnimationFrame)
|
|
1195
|
-
- `hostAdapter` - - Optional host adapter that owns host-side stop behavior
|
|
1196
|
-
- `minProgressToPass` - - Numeric threshold to consider a run 'solved'
|
|
1197
|
-
- `autoPauseOnSolve` - - When truthy request host-side pause handling on solve
|
|
1198
|
-
- `stopOnlyOnSolve` - - When true ignore stagnation/maxGenerations as stop reasons
|
|
1199
|
-
- `stagnantGenerations` - - Current count of stagnant generations observed
|
|
1200
|
-
- `maxStagnantGenerations` - - Max allowed stagnant generations before stopping
|
|
1201
|
-
- `maxGenerations` - - Absolute generation cap after which the run stops
|
|
1509
|
+
RNG and timing helpers extracted from the evolution façade.
|
|
1202
1510
|
|
|
1203
|
-
|
|
1511
|
+
### accumulateProfilingDuration
|
|
1204
1512
|
|
|
1205
|
-
|
|
1513
|
+
```ts
|
|
1514
|
+
accumulateProfilingDuration(
|
|
1515
|
+
state: EngineState,
|
|
1516
|
+
category: string,
|
|
1517
|
+
deltaMs: number,
|
|
1518
|
+
): void
|
|
1519
|
+
```
|
|
1206
1520
|
|
|
1207
|
-
|
|
1208
|
-
const reason = await checkStopConditions(
|
|
1209
|
-
bestResult, bestNet, maze, gen, neat, dashboard, flush,
|
|
1210
|
-
95, true, false, stagnant, 500, 10000
|
|
1211
|
-
);
|
|
1212
|
-
if (reason) {
|
|
1213
|
-
console.log('Stopping due to', reason);
|
|
1214
|
-
break;
|
|
1215
|
-
}
|
|
1521
|
+
Accumulate a profiling duration under the supplied key when detail profiling is enabled.
|
|
1216
1522
|
|
|
1217
|
-
|
|
1523
|
+
Parameters:
|
|
1524
|
+
- `state` - Shared engine state containing profiling configuration.
|
|
1525
|
+
- `category` - Profiling segment key (for example `telemetry`).
|
|
1526
|
+
- `deltaMs` - Millisecond duration to add to the accumulator.
|
|
1527
|
+
|
|
1528
|
+
Returns: void.
|
|
1529
|
+
|
|
1530
|
+
### clearDeterministicMode
|
|
1218
1531
|
|
|
1219
1532
|
```ts
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
safeWrite: (msg: string) => void,
|
|
1223
|
-
completedGenerations: number,
|
|
1224
|
-
totalEvolveMs: number,
|
|
1225
|
-
totalLamarckMs: number,
|
|
1226
|
-
totalSimMs: number,
|
|
1227
|
-
isProfilingDetailsEnabledFn: (state: EngineState) => boolean,
|
|
1228
|
-
getProfilingAccumulatorsFn: (state: EngineState) => ProfilingAccumulators,
|
|
1533
|
+
clearDeterministicMode(
|
|
1534
|
+
state: EngineState,
|
|
1229
1535
|
): void
|
|
1230
1536
|
```
|
|
1231
1537
|
|
|
1232
|
-
|
|
1538
|
+
Disable deterministic RNG mode.
|
|
1233
1539
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
profiling is enabled, it also prints averages for telemetry, simplify, snapshot, and
|
|
1237
|
-
prune operations.
|
|
1540
|
+
Parameters:
|
|
1541
|
+
- `state` - Shared engine state where the deterministic flag is stored.
|
|
1238
1542
|
|
|
1239
|
-
|
|
1240
|
-
- Allocation-free (reuses pooled Float64Array for intermediate calculations)
|
|
1241
|
-
- Best-effort error handling (swallow all exceptions)
|
|
1242
|
-
- Defensive numeric validation with divide-by-zero guards
|
|
1243
|
-
- Conditional detailed profiling output
|
|
1543
|
+
Returns: void.
|
|
1244
1544
|
|
|
1245
|
-
|
|
1246
|
-
1. Validate and normalize generation count (guard divide-by-zero)
|
|
1247
|
-
2. Store totals in pooled scratch buffer (4-slot Float64Array)
|
|
1248
|
-
3. Compute per-generation averages by dividing totals by generation count
|
|
1249
|
-
4. Format numbers with 2 decimal places and print compact summary
|
|
1250
|
-
5. If detailed profiling enabled, print averaged detail line
|
|
1545
|
+
### drawFastRandom
|
|
1251
1546
|
|
|
1252
|
-
|
|
1547
|
+
```ts
|
|
1548
|
+
drawFastRandom(
|
|
1549
|
+
state: EngineState,
|
|
1550
|
+
parameters: RngCacheParameters,
|
|
1551
|
+
): number
|
|
1552
|
+
```
|
|
1553
|
+
|
|
1554
|
+
Generate a fast uniform random sample using the shared RNG cache.
|
|
1253
1555
|
|
|
1254
1556
|
Parameters:
|
|
1255
|
-
- `
|
|
1256
|
-
- `
|
|
1257
|
-
- `completedGenerations` - - Number of completed generations (must be > 0)
|
|
1258
|
-
- `totalEvolveMs` - - Total milliseconds spent in NEAT evolve() calls
|
|
1259
|
-
- `totalLamarckMs` - - Total milliseconds spent in Lamarckian training
|
|
1260
|
-
- `totalSimMs` - - Total milliseconds spent in simulation
|
|
1261
|
-
- `isProfilingDetailsEnabledFn` - - Function to check if detailed profiling is enabled
|
|
1262
|
-
- `getProfilingAccumulatorsFn` - - Function to get detailed profiling accumulators
|
|
1557
|
+
- `state` - Shared engine state providing the RNG cache and seed.
|
|
1558
|
+
- `parameters` - Congruential parameters forwarded to {@link ensureRngCacheBatch} .
|
|
1263
1559
|
|
|
1264
|
-
|
|
1560
|
+
Returns: Uniform sample in the range [0, 1).
|
|
1265
1561
|
|
|
1266
|
-
|
|
1267
|
-
emitProfileSummary(
|
|
1268
|
-
state, console.log, 100, 12000, 3000, 4500,
|
|
1269
|
-
isProfilingDetailsEnabled, getProfilingAccumulators
|
|
1270
|
-
);
|
|
1562
|
+
Example:
|
|
1271
1563
|
|
|
1272
|
-
|
|
1564
|
+
const sample = drawFastRandom(sharedState, rngParameters);
|
|
1273
1565
|
|
|
1274
|
-
|
|
1566
|
+
### getProfilingAccumulators
|
|
1275
1567
|
|
|
1276
|
-
|
|
1568
|
+
```ts
|
|
1569
|
+
getProfilingAccumulators(
|
|
1570
|
+
state: EngineState,
|
|
1571
|
+
): Record<string, number>
|
|
1572
|
+
```
|
|
1277
1573
|
|
|
1278
|
-
|
|
1574
|
+
Provide direct access to the profiling accumulator map.
|
|
1279
1575
|
|
|
1280
|
-
|
|
1576
|
+
Parameters:
|
|
1577
|
+
- `state` - Shared engine state containing the profiling accumulators.
|
|
1281
1578
|
|
|
1282
|
-
Mutable
|
|
1579
|
+
Returns: Mutable record of profiling accumulators keyed by category name.
|
|
1283
1580
|
|
|
1284
|
-
###
|
|
1581
|
+
### isDeterministicModeEnabled
|
|
1285
1582
|
|
|
1286
1583
|
```ts
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
pathModule: { join?: ((...paths: string[]) => string) | undefined; } | null,
|
|
1291
|
-
persistDir: string | undefined,
|
|
1292
|
-
persistTopK: number,
|
|
1293
|
-
completedGenerations: number,
|
|
1294
|
-
persistEvery: number,
|
|
1295
|
-
neat: default,
|
|
1296
|
-
bestFitness: number,
|
|
1297
|
-
simplifyMode: boolean,
|
|
1298
|
-
plateauCounter: number,
|
|
1299
|
-
scratchSnapshotObj: Record<string, unknown>,
|
|
1300
|
-
scratchSnapshotTop: SnapshotEntry[],
|
|
1301
|
-
collectTelemetryTailFn: (state: EngineState, neat: default, count: number) => unknown,
|
|
1302
|
-
getSortedIndicesByScoreFn: (state: EngineState, population: default[]) => number[],
|
|
1303
|
-
isProfilingDetailsEnabledFn: (state: EngineState) => boolean,
|
|
1304
|
-
profilingStartTimestampFn: (state: EngineState) => number,
|
|
1305
|
-
accumulateProfilingDurationFn: (state: EngineState, label: string, duration: number) => void,
|
|
1306
|
-
): void
|
|
1584
|
+
isDeterministicModeEnabled(
|
|
1585
|
+
state: EngineState,
|
|
1586
|
+
): boolean
|
|
1307
1587
|
```
|
|
1308
1588
|
|
|
1309
|
-
|
|
1589
|
+
Retrieve the deterministic RNG flag stored on the shared engine state.
|
|
1310
1590
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
top-K genomes, telemetry tail, and metadata for later analysis or resume.
|
|
1591
|
+
Parameters:
|
|
1592
|
+
- `state` - Shared engine state where the deterministic flag is stored.
|
|
1314
1593
|
|
|
1315
|
-
|
|
1316
|
-
- Best-effort semantics: swallows all errors to avoid disrupting evolution loop
|
|
1317
|
-
- Reuses pooled scratch objects (SCRATCH_SNAPSHOT_OBJ, SCRATCH_SNAPSHOT_TOP) to minimize allocations
|
|
1318
|
-
- Optional profiling when enabled (measures snapshot serialization time)
|
|
1319
|
-
- Defensive validation to ensure FS/path modules are available
|
|
1594
|
+
Returns: True when deterministic mode is active.
|
|
1320
1595
|
|
|
1321
|
-
|
|
1322
|
-
- Only persists when `completedGenerations % persistEvery === 0`
|
|
1323
|
-
- Requires valid fs.writeFileSync and pathModule.join functions
|
|
1324
|
-
- Requires non-empty population
|
|
1596
|
+
### isProfilingDetailsEnabled
|
|
1325
1597
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
- timestamp: Date.now() when snapshot was created
|
|
1332
|
-
- telemetryTail: last N telemetry entries
|
|
1333
|
-
- top: top-K genomes with minimal metadata (idx, score, nodes, connections, json)
|
|
1598
|
+
```ts
|
|
1599
|
+
isProfilingDetailsEnabled(
|
|
1600
|
+
state: EngineState,
|
|
1601
|
+
): boolean
|
|
1602
|
+
```
|
|
1334
1603
|
|
|
1335
|
-
|
|
1604
|
+
Determine whether detailed profiling accumulation is active.
|
|
1336
1605
|
|
|
1337
1606
|
Parameters:
|
|
1338
|
-
- `
|
|
1339
|
-
- `fs` - - Node.js fs module or compatible FS API
|
|
1340
|
-
- `pathModule` - - Node.js path module or compatible path API
|
|
1341
|
-
- `persistDir` - - Directory path where snapshots should be written
|
|
1342
|
-
- `persistTopK` - - Number of top genomes to include in snapshot
|
|
1343
|
-
- `completedGenerations` - - Current generation index
|
|
1344
|
-
- `persistEvery` - - Generation interval for persistence (e.g., 25 = every 25th generation)
|
|
1345
|
-
- `neat` - - NEAT instance with population
|
|
1346
|
-
- `bestFitness` - - Best fitness score this generation
|
|
1347
|
-
- `simplifyMode` - - Whether simplification mode is active
|
|
1348
|
-
- `plateauCounter` - - Current plateau counter value
|
|
1349
|
-
- `scratchSnapshotObj` - - Pooled snapshot object (reused across calls)
|
|
1350
|
-
- `scratchSnapshotTop` - - Pooled top-K buffer (reused across calls)
|
|
1351
|
-
- `collectTelemetryTailFn` - - Function to collect telemetry tail from state
|
|
1352
|
-
- `getSortedIndicesByScoreFn` - - Function to get sorted genome indices
|
|
1353
|
-
- `isProfilingDetailsEnabledFn` - - Function to check profiling state
|
|
1354
|
-
- `profilingStartTimestampFn` - - Function to get profiling start time
|
|
1355
|
-
- `accumulateProfilingDurationFn` - - Function to accumulate profiling duration
|
|
1356
|
-
|
|
1357
|
-
Example:
|
|
1607
|
+
- `state` - Shared engine state providing profiling configuration.
|
|
1358
1608
|
|
|
1359
|
-
|
|
1360
|
-
persistSnapshotIfNeeded(
|
|
1361
|
-
state, fs, path, './snapshots', 10, 50, 25, neat, 0.95, false, 3,
|
|
1362
|
-
scratchObj, scratchTop, collectTail, getSorted, isProfilingEnabled, profileStart, profileAccum
|
|
1363
|
-
);
|
|
1609
|
+
Returns: True when detail profiling is enabled.
|
|
1364
1610
|
|
|
1365
|
-
###
|
|
1611
|
+
### profilingStartTimestamp
|
|
1366
1612
|
|
|
1367
1613
|
```ts
|
|
1368
|
-
|
|
1369
|
-
opts: EvolutionOptions,
|
|
1370
|
-
scratchBundle: ScratchBundle,
|
|
1371
|
-
): LoopHelpers
|
|
1614
|
+
profilingStartTimestamp(): number
|
|
1372
1615
|
```
|
|
1373
1616
|
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
This function assembles the helper utilities needed by the main evolution loop:
|
|
1377
|
-
- Frame flushing for cooperative yielding
|
|
1378
|
-
- Persistence handles for snapshot saving (Node.js only)
|
|
1379
|
-
- Safe logging writer with fallback chain
|
|
1380
|
-
- Scratch buffer warm-up (best-effort)
|
|
1617
|
+
Return a profiling start timestamp that mirrors the historic `#PROFILE_T0` helper.
|
|
1381
1618
|
|
|
1382
|
-
|
|
1383
|
-
- All initialization is best-effort (failures swallowed)
|
|
1384
|
-
- Warms up common scratch buffers to reduce first-use allocation spikes
|
|
1385
|
-
- Returns simple POJO with utilities (no class coupling)
|
|
1386
|
-
- Side effects isolated to scratch bundle parameter
|
|
1619
|
+
Returns: Millisecond timestamp representing the profiling start time.
|
|
1387
1620
|
|
|
1388
|
-
|
|
1389
|
-
- samplePool: Array for population sampling
|
|
1390
|
-
- profilingScratch: Float64Array(4) for timing accumulation
|
|
1391
|
-
- exps: Float64Array(64) for exponential computations
|
|
1621
|
+
### readHighResolutionTime
|
|
1392
1622
|
|
|
1393
|
-
|
|
1623
|
+
```ts
|
|
1624
|
+
readHighResolutionTime(): number
|
|
1625
|
+
```
|
|
1394
1626
|
|
|
1395
|
-
|
|
1396
|
-
- `opts` - - Normalized run options (contains persistDir and dashboardManager)
|
|
1397
|
-
- `scratchBundle` - - Engine scratch state for optional buffer warm-up
|
|
1627
|
+
Obtain a monotonic-ish timestamp suitable for profiling.
|
|
1398
1628
|
|
|
1399
|
-
Returns:
|
|
1400
|
-
- flushToFrame: Async function for cooperative yielding
|
|
1401
|
-
- fs: Node.js fs module (null in browsers)
|
|
1402
|
-
- path: Node.js path module (null in browsers)
|
|
1403
|
-
- safeWrite: Resilient logging function with fallback chain
|
|
1629
|
+
Returns: Timestamp in milliseconds, preferring `performance.now` when available.
|
|
1404
1630
|
|
|
1405
1631
|
Example:
|
|
1406
1632
|
|
|
1407
|
-
|
|
1408
|
-
const { flushToFrame, fs, path, safeWrite } = prepareLoopHelpers(opts, engineState.scratch);
|
|
1409
|
-
safeWrite('Starting evolution...\n');
|
|
1410
|
-
await flushToFrame(); // Yield to host
|
|
1633
|
+
const timestamp = readHighResolutionTime();
|
|
1411
1634
|
|
|
1412
|
-
###
|
|
1635
|
+
### resolveRngParameters
|
|
1413
1636
|
|
|
1414
1637
|
```ts
|
|
1415
|
-
|
|
1416
|
-
engineState: EngineState,
|
|
1417
|
-
neat: default,
|
|
1418
|
-
opts: EvolutionOptions,
|
|
1419
|
-
lamarckianTrainingSet: { input: number[]; output: number[]; }[],
|
|
1420
|
-
encodedMaze: number[][],
|
|
1421
|
-
startPosition: readonly [number, number],
|
|
1422
|
-
exitPosition: readonly [number, number],
|
|
1423
|
-
distanceMap: number[][],
|
|
1424
|
-
helpers: LoopHelpers,
|
|
1425
|
-
doProfile: boolean,
|
|
1426
|
-
runtimeContext: EvolutionLoopRuntimeContext,
|
|
1427
|
-
initialRingState: LogitsRingState,
|
|
1428
|
-
telemetryContext: EvolutionLoopTelemetryContext,
|
|
1429
|
-
supportContext: EvolutionLoopSupportContext,
|
|
1430
|
-
constants: TrainingConstants & { DEFAULT_TRAIN_BATCH_LARGE: number; FITTEST_TRAIN_ITERATIONS: number; TELEMETRY_MINIMAL: boolean; SATURATION_PRUNE_THRESHOLD: number; RECENT_WINDOW: number; REDUCED_TELEMETRY: boolean; DISABLE_BALDWIN: boolean; },
|
|
1431
|
-
): Promise<EvolutionLoopResult>
|
|
1638
|
+
resolveRngParameters(): RngCacheParameters
|
|
1432
1639
|
```
|
|
1433
1640
|
|
|
1434
|
-
|
|
1641
|
+
Provide cached congruential parameters used by the shared fast RNG helper.
|
|
1435
1642
|
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1643
|
+
Returns: Immutable {@link RngCacheParameters} reference reused across draws.
|
|
1644
|
+
|
|
1645
|
+
Example:
|
|
1646
|
+
|
|
1647
|
+
const parameters = resolveRngParameters();
|
|
1648
|
+
|
|
1649
|
+
### setDeterministicMode
|
|
1650
|
+
|
|
1651
|
+
```ts
|
|
1652
|
+
setDeterministicMode(
|
|
1653
|
+
state: EngineState,
|
|
1654
|
+
seed: number | undefined,
|
|
1655
|
+
): void
|
|
1656
|
+
```
|
|
1657
|
+
|
|
1658
|
+
Enable deterministic RNG mode and optionally reseed the shared RNG state.
|
|
1443
1659
|
|
|
1444
1660
|
Parameters:
|
|
1445
|
-
- `
|
|
1446
|
-
- `
|
|
1447
|
-
- `opts` - - Normalised run options (produced by normalizeRunOptions)
|
|
1448
|
-
- `lamarckianTrainingSet` - - Optional supervised training cases used for Lamarckian warm-start
|
|
1449
|
-
- `encodedMaze` - - Encoded maze representation consumed by simulators
|
|
1450
|
-
- `startPosition` - - Start coordinates for the simulated agent
|
|
1451
|
-
- `exitPosition` - - Exit coordinates for the simulated agent
|
|
1452
|
-
- `distanceMap` - - Optional precomputed distance map to speed simulation
|
|
1453
|
-
- `helpers` - - Helper utilities: { flushToFrame, fs, path, safeWrite }
|
|
1454
|
-
- `doProfile` - - When truthy collect and return millisecond timings in the result
|
|
1455
|
-
- `runtimeContext` - - Shared pooled ring buffers and limits for the hot path.
|
|
1456
|
-
- `initialRingState` - - Current mutable ring state for this run.
|
|
1457
|
-
- `telemetryContext` - - Telemetry thresholds and verbosity switches used during simulation.
|
|
1458
|
-
- `supportContext` - - Shared scratch buffers and helper callbacks used by the loop.
|
|
1459
|
-
- `constants` - - Object containing all engine constants (DEFAULT_TRAIN_ERROR, etc.)
|
|
1661
|
+
- `state` - Shared engine state where the deterministic flag is stored.
|
|
1662
|
+
- `seed` - Optional deterministic seed. Finite numeric inputs are normalised to u32.
|
|
1460
1663
|
|
|
1461
|
-
Returns:
|
|
1462
|
-
{ bestNetwork, bestResult, neat, completedGenerations, totalEvolveMs, totalLamarckMs, totalSimMs, updatedRingState }
|
|
1664
|
+
Returns: void.
|
|
1463
1665
|
|
|
1464
|
-
|
|
1666
|
+
## evolutionEngine/scratchPools.ts
|
|
1465
1667
|
|
|
1466
|
-
|
|
1467
|
-
state, neat, opts, trainingSet, maze, start, exit, distMap, helpers, true, ...
|
|
1468
|
-
);
|
|
1668
|
+
Scratch pool management helpers extracted from the maze evolution façade.
|
|
1469
1669
|
|
|
1470
|
-
|
|
1670
|
+
These utilities centralise the logic that grows and shrinks pooled buffers attached to the
|
|
1671
|
+
shared {@link EngineState}. They keep the façade lean by encapsulating heuristics for logits
|
|
1672
|
+
ring sizing, telemetry scratch sizing, and connection flag pooling.
|
|
1673
|
+
|
|
1674
|
+
### allocateLogitsRing
|
|
1471
1675
|
|
|
1472
1676
|
```ts
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
lamarckianIterations: number,
|
|
1478
|
-
lamarckianTrainingSet: { input: number[]; output: number[]; }[],
|
|
1479
|
-
lamarckianSampleSize: number | undefined,
|
|
1480
|
-
safeWrite: (msg: string) => void,
|
|
1481
|
-
completedGenerations: number,
|
|
1482
|
-
dynamicPopEnabled: boolean,
|
|
1483
|
-
dynamicPopMax: number,
|
|
1484
|
-
plateauGenerations: number,
|
|
1485
|
-
plateauCounter: number,
|
|
1486
|
-
dynamicPopExpandInterval: number,
|
|
1487
|
-
dynamicPopExpandFactor: number,
|
|
1488
|
-
dynamicPopPlateauSlack: number,
|
|
1489
|
-
speciesHistoryRef: number[],
|
|
1490
|
-
emptyVec: default[],
|
|
1491
|
-
scratchNodeIdx: Int32Array<ArrayBufferLike>,
|
|
1492
|
-
getNodeIndicesByType: (nodes: NetworkNode[], type: string) => number,
|
|
1493
|
-
constants: TrainingConstants,
|
|
1494
|
-
): Promise<GenerationOutcome>
|
|
1677
|
+
allocateLogitsRing(
|
|
1678
|
+
capacity: number,
|
|
1679
|
+
actionDimension: number,
|
|
1680
|
+
): Float32Array<ArrayBufferLike>[]
|
|
1495
1681
|
```
|
|
1496
1682
|
|
|
1497
|
-
|
|
1498
|
-
and run Lamarckian training if configured.
|
|
1683
|
+
Build a non-shared logits ring sized to the requested capacity.
|
|
1499
1684
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1685
|
+
Parameters:
|
|
1686
|
+
- `capacity` - Number of rows to create.
|
|
1687
|
+
- `actionDimension` - Number of logits stored per row.
|
|
1688
|
+
|
|
1689
|
+
Returns: Array of typed rows representing the ring buffer.
|
|
1690
|
+
|
|
1691
|
+
### ensureConnFlagsCapacity
|
|
1692
|
+
|
|
1693
|
+
```ts
|
|
1694
|
+
ensureConnFlagsCapacity(
|
|
1695
|
+
state: EngineState,
|
|
1696
|
+
minimumCapacity: number,
|
|
1697
|
+
): Int8Array<ArrayBufferLike> | null
|
|
1698
|
+
```
|
|
1699
|
+
|
|
1700
|
+
Ensure the recurrent/gated detection bitmap has sufficient capacity.
|
|
1514
1701
|
|
|
1515
1702
|
Parameters:
|
|
1516
|
-
- `
|
|
1517
|
-
- `
|
|
1518
|
-
- `doProfile` - - When truthy measure timing for the evolve step (ms) using engine clock
|
|
1519
|
-
- `lamarckianIterations` - - Number of supervised training iterations to run per genome (0 to skip)
|
|
1520
|
-
- `lamarckianTrainingSet` - - Array of supervised training cases used for warm-start (may be empty)
|
|
1521
|
-
- `lamarckianSampleSize` - - Optional per-network sample size used by the warm-start routine
|
|
1522
|
-
- `safeWrite` - - Safe logging function; used only for best-effort diagnostic messages
|
|
1523
|
-
- `completedGenerations` - - Current generation index (used by expansion heuristics)
|
|
1524
|
-
- `dynamicPopEnabled` - - Whether dynamic population expansion is enabled
|
|
1525
|
-
- `dynamicPopMax` - - Upper bound on population size for expansion
|
|
1526
|
-
- `plateauGenerations` - - Window size used by plateau detection
|
|
1527
|
-
- `plateauCounter` - - Current plateau counter used by expansion heuristics
|
|
1528
|
-
- `dynamicPopExpandInterval` - - Generation interval to attempt expansion
|
|
1529
|
-
- `dynamicPopExpandFactor` - - Fractional growth factor used to compute new members
|
|
1530
|
-
- `dynamicPopPlateauSlack` - - Minimum plateau ratio required to trigger expansion
|
|
1531
|
-
- `speciesHistoryRef` - - Mutable array holding species history (maintained externally)
|
|
1532
|
-
- `emptyVec` - - Empty array fallback to avoid ephemeral allocations
|
|
1533
|
-
- `scratchNodeIdx` - - Pooled node index buffer (passed through to helpers)
|
|
1534
|
-
- `getNodeIndicesByType` - - Helper function to collect node indices by type
|
|
1535
|
-
- `constants` - - Object containing DEFAULT_TRAIN_ERROR, DEFAULT_TRAIN_RATE, DEFAULT_TRAIN_MOMENTUM, DEFAULT_TRAIN_BATCH_SMALL, DEFAULT_STD_SMALL, DEFAULT_STD_ADJUST_MULT
|
|
1703
|
+
- `state` - Shared engine state containing the pooled bitmap.
|
|
1704
|
+
- `minimumCapacity` - Minimum number of entries required by the caller.
|
|
1536
1705
|
|
|
1537
|
-
Returns:
|
|
1538
|
-
- `fittest` is the network returned by `neat.evolve()` (may be null on error),
|
|
1539
|
-
- `tEvolve` is the measured evolve duration in milliseconds when `doProfile` is true (0 otherwise),
|
|
1540
|
-
- `tLamarck` is the total time spent in Lamarckian training (0 when skipped)
|
|
1706
|
+
Returns: Int8Array bitmap or null when allocation failed.
|
|
1541
1707
|
|
|
1542
|
-
|
|
1708
|
+
### ensureLogitsRingCapacity
|
|
1543
1709
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
5, // lamarckianIterations
|
|
1550
|
-
trainingSet,
|
|
1551
|
-
16, // lamarckianSampleSize
|
|
1552
|
-
console.log,
|
|
1553
|
-
genIndex,
|
|
1554
|
-
true,
|
|
1555
|
-
500,
|
|
1556
|
-
10,
|
|
1557
|
-
plateauCounter,
|
|
1558
|
-
5,
|
|
1559
|
-
0.1,
|
|
1560
|
-
0.75,
|
|
1561
|
-
speciesHistory,
|
|
1562
|
-
[],
|
|
1563
|
-
nodeIndexBuffer,
|
|
1564
|
-
getNodeIndicesByTypeFn,
|
|
1565
|
-
{ DEFAULT_TRAIN_ERROR: 0.01, ... }
|
|
1566
|
-
);
|
|
1710
|
+
```ts
|
|
1711
|
+
ensureLogitsRingCapacity(
|
|
1712
|
+
capacityRequest: LogitsRingCapacityOptions,
|
|
1713
|
+
): LogitsRingCapacityResult
|
|
1714
|
+
```
|
|
1567
1715
|
|
|
1568
|
-
|
|
1716
|
+
Grow or shrink the logits ring to accomodate the requested recent-step budget.
|
|
1569
1717
|
|
|
1570
|
-
|
|
1718
|
+
Parameters:
|
|
1719
|
+
- `capacityRequest` - Parameters describing the requested resize.
|
|
1571
1720
|
|
|
1572
|
-
|
|
1721
|
+
Returns: The resulting capacity and whether shared-array mode stayed enabled.
|
|
1722
|
+
|
|
1723
|
+
### ensureScratchCapacity
|
|
1573
1724
|
|
|
1574
1725
|
```ts
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
startPosition: readonly [number, number],
|
|
1580
|
-
exitPosition: readonly [number, number],
|
|
1581
|
-
distanceMap: number[][],
|
|
1582
|
-
maxSteps: number | undefined,
|
|
1583
|
-
doProfile: boolean,
|
|
1584
|
-
safeWrite: (msg: string) => void,
|
|
1585
|
-
logEvery: number,
|
|
1586
|
-
completedGenerations: number,
|
|
1587
|
-
neat: default,
|
|
1588
|
-
runtimeContext: EvolutionLoopRuntimeContext,
|
|
1589
|
-
ringState: LogitsRingState,
|
|
1590
|
-
telemetryContext: EvolutionLoopTelemetryContext,
|
|
1591
|
-
loopSupportContext: Pick<EvolutionLoopSupportContext, "loopHelpers">,
|
|
1592
|
-
): SimulationResult
|
|
1726
|
+
ensureScratchCapacity(
|
|
1727
|
+
state: EngineState,
|
|
1728
|
+
request: ScratchCapacityRequest,
|
|
1729
|
+
): void
|
|
1593
1730
|
```
|
|
1594
1731
|
|
|
1595
|
-
|
|
1732
|
+
Grow pooled scratch buffers to conservative sizes for the upcoming run.
|
|
1596
1733
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
- When per-step logits are returned the helper attempts to copy them into the engine's pooled
|
|
1601
|
-
ring buffers to avoid per-run allocations. Two copy modes are supported:
|
|
1602
|
-
1) Shared SAB-backed flat Float32Array with an atomic Int32 write index (cross-worker safe).
|
|
1603
|
-
2) Local in-process per-row Float32Array ring (`scratchLogitsRing`).
|
|
1604
|
-
- Best-effort: all mutation and buffer-copy steps are guarded; failures are swallowed so the
|
|
1605
|
-
evolution loop is not interrupted. Use `safeWrite` for optional diagnostic messages.
|
|
1734
|
+
Parameters:
|
|
1735
|
+
- `state` - Shared engine state exposing scratch buffers.
|
|
1736
|
+
- `request` - Sizing request describing the evolution workload.
|
|
1606
1737
|
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1738
|
+
### initialiseSharedLogitsRing
|
|
1739
|
+
|
|
1740
|
+
```ts
|
|
1741
|
+
initialiseSharedLogitsRing(
|
|
1742
|
+
state: EngineState,
|
|
1743
|
+
config: SharedLogitsConfig,
|
|
1744
|
+
): boolean
|
|
1745
|
+
```
|
|
1746
|
+
|
|
1747
|
+
Attempt to allocate SharedArrayBuffer-backed logits ring storage.
|
|
1748
|
+
|
|
1749
|
+
Parameters:
|
|
1750
|
+
- `state` - Shared engine state containing the logits buffers.
|
|
1751
|
+
- `config` - Shared ring configuration (capacity and action dimension).
|
|
1752
|
+
|
|
1753
|
+
Returns: true when shared mode was activated successfully.
|
|
1754
|
+
|
|
1755
|
+
### LogitsRingCapacityOptions
|
|
1756
|
+
|
|
1757
|
+
Shape describing the parameters used when ensuring the logits ring capacity.
|
|
1758
|
+
|
|
1759
|
+
### LogitsRingCapacityResult
|
|
1760
|
+
|
|
1761
|
+
Result returned after resizing the logits ring.
|
|
1762
|
+
|
|
1763
|
+
### maybeShrinkScratch
|
|
1764
|
+
|
|
1765
|
+
```ts
|
|
1766
|
+
maybeShrinkScratch(
|
|
1767
|
+
state: EngineState,
|
|
1768
|
+
populationSize: number,
|
|
1769
|
+
): void
|
|
1770
|
+
```
|
|
1771
|
+
|
|
1772
|
+
Shrink oversized scratch buffers once the population size drops significantly.
|
|
1773
|
+
|
|
1774
|
+
Parameters:
|
|
1775
|
+
- `state` - Shared engine state exposing scratch buffers.
|
|
1776
|
+
- `populationSize` - Current population size used to derive shrink heuristics.
|
|
1777
|
+
|
|
1778
|
+
### ScratchCapacityRequest
|
|
1779
|
+
|
|
1780
|
+
Parameters describing the scratch sizing requirements for ensureScratchCapacity.
|
|
1781
|
+
|
|
1782
|
+
### SharedLogitsConfig
|
|
1783
|
+
|
|
1784
|
+
Parameters passed when attempting to initialise the shared logits ring buffers.
|
|
1785
|
+
|
|
1786
|
+
## evolutionEngine/setupHelpers.ts
|
|
1787
|
+
|
|
1788
|
+
Setup helpers for the ASCII maze evolution engine.
|
|
1789
|
+
|
|
1790
|
+
Responsibilities:
|
|
1791
|
+
- Create cooperative frame-yielding helpers for async evolution loops.
|
|
1792
|
+
- Initialize Node.js persistence helpers when available.
|
|
1793
|
+
- Build resilient logging writers with dashboard and console fallbacks.
|
|
1794
|
+
|
|
1795
|
+
### DashboardManagerLike
|
|
1796
|
+
|
|
1797
|
+
Dashboard manager shape for logging (optional log function).
|
|
1798
|
+
|
|
1799
|
+
### FilesystemModule
|
|
1800
|
+
|
|
1801
|
+
Minimal filesystem module shape for type safety (Node.js fs module subset).
|
|
1802
|
+
|
|
1803
|
+
### initPersistence
|
|
1804
|
+
|
|
1805
|
+
```ts
|
|
1806
|
+
initPersistence(
|
|
1807
|
+
persistDir: string | undefined,
|
|
1808
|
+
): { fs: FilesystemModule | null; path: PathModule | null; }
|
|
1809
|
+
```
|
|
1613
1810
|
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1811
|
+
Initialize persistence helpers (Node `fs` & `path`) when available and ensure the target
|
|
1812
|
+
directory exists. This helper intentionally does nothing in browser-like hosts.
|
|
1813
|
+
|
|
1814
|
+
Steps:
|
|
1815
|
+
1. Detect whether a Node-like `require` is available and attempt to load `fs` and `path`
|
|
1816
|
+
2. If both modules are available and `persistDir` is provided, ensure the directory exists
|
|
1817
|
+
by creating it recursively when necessary
|
|
1818
|
+
3. Return an object containing the (possibly null) `{ fs, path }` references for callers to use
|
|
1819
|
+
|
|
1820
|
+
Notes:
|
|
1821
|
+
- This helper deliberately performs defensive checks and swallows synchronous errors because
|
|
1822
|
+
persistence is optional in many host environments (tests, browser demos)
|
|
1823
|
+
- No large allocations are performed here; the function returns lightweight references
|
|
1618
1824
|
|
|
1619
1825
|
Parameters:
|
|
1620
|
-
- `
|
|
1621
|
-
- `fittest` - - Genome/network considered the generation's best; may be mutated with metadata
|
|
1622
|
-
- `encodedMaze` - - Maze descriptor used by the simulator
|
|
1623
|
-
- `startPosition` - - Start co-ordinates passed as-is to the simulator
|
|
1624
|
-
- `exitPosition` - - Exit co-ordinates passed as-is to the simulator
|
|
1625
|
-
- `distanceMap` - - Optional precomputed distance map consumed by the simulator
|
|
1626
|
-
- `maxSteps` - - Optional maximum simulation steps; may be undefined to allow default
|
|
1627
|
-
- `doProfile` - - When truthy measure and return the simulation time in milliseconds
|
|
1628
|
-
- `safeWrite` - - Optional logger used for non-fatal diagnostic messages
|
|
1629
|
-
- `logEvery` - - Emit telemetry every `logEvery` generations (0 disables periodic telemetry)
|
|
1630
|
-
- `completedGenerations` - - Current generation index used for conditional telemetry
|
|
1631
|
-
- `neat` - - NEAT driver instance passed to telemetry hooks
|
|
1632
|
-
- `runtimeContext` - - Shared pooled ring buffers and limits for logits telemetry.
|
|
1633
|
-
- `ringState` - - Current mutable ring state (capacity, shared-mode flag, write cursor).
|
|
1634
|
-
- `telemetryContext` - - Telemetry thresholds and verbosity switches used after simulation.
|
|
1635
|
-
- `loopSupportContext` - - Shared scratch buffers and helper callbacks used by the loop.
|
|
1826
|
+
- `persistDir` - - Optional directory to ensure exists. If falsy, no filesystem mutations are attempted.
|
|
1636
1827
|
|
|
1637
|
-
Returns:
|
|
1828
|
+
Returns: Object with `{ fs, path }` where each value may be `null` when unavailable.
|
|
1638
1829
|
|
|
1639
1830
|
Example:
|
|
1640
1831
|
|
|
1641
|
-
const {
|
|
1642
|
-
|
|
1643
|
-
);
|
|
1832
|
+
const { fs, path } = initPersistence('./snapshots');
|
|
1833
|
+
if (fs && path) {
|
|
1834
|
+
fs.writeFileSync(path.join(dir, 'snapshot.json'), data);
|
|
1835
|
+
}
|
|
1644
1836
|
|
|
1645
|
-
###
|
|
1837
|
+
### isPauseRequested
|
|
1646
1838
|
|
|
1647
|
-
|
|
1839
|
+
```ts
|
|
1840
|
+
isPauseRequested(
|
|
1841
|
+
hostAdapter: EvolutionHostAdapter | undefined,
|
|
1842
|
+
): boolean
|
|
1843
|
+
```
|
|
1648
1844
|
|
|
1649
|
-
|
|
1845
|
+
Read host-controlled pause state without letting host errors break the engine.
|
|
1846
|
+
|
|
1847
|
+
Parameters:
|
|
1848
|
+
- `hostAdapter` - - Optional host adapter implementing pause polling.
|
|
1849
|
+
|
|
1850
|
+
Returns: True when the host asks the engine to remain paused.
|
|
1851
|
+
|
|
1852
|
+
### makeFlushToFrame
|
|
1650
1853
|
|
|
1651
1854
|
```ts
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
network: default | null,
|
|
1656
|
-
completedGenerations: number,
|
|
1657
|
-
neat: default,
|
|
1658
|
-
dashboardManager: IDashboardManager | undefined,
|
|
1659
|
-
flushToFrame: (() => Promise<void>) | undefined,
|
|
1660
|
-
): Promise<void>
|
|
1855
|
+
makeFlushToFrame(
|
|
1856
|
+
hostAdapter: EvolutionHostAdapter | undefined,
|
|
1857
|
+
): () => Promise<void>
|
|
1661
1858
|
```
|
|
1662
1859
|
|
|
1663
|
-
|
|
1664
|
-
host/frame via an awaited flush function.
|
|
1860
|
+
Create a cooperative frame-yielding function used by the evolution loop.
|
|
1665
1861
|
|
|
1666
|
-
Behaviour
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
the event loop or renderer (for example `() => new Promise(r => requestAnimationFrame(r))`).
|
|
1672
|
-
3) The helper avoids heap allocations and relies on existing pooled scratch buffers in
|
|
1673
|
-
the engine for heavy telemetry elsewhere; this method intentionally performs only
|
|
1674
|
-
short-lived control flow and minimal work.
|
|
1862
|
+
Behaviour:
|
|
1863
|
+
- Prefers `requestAnimationFrame` when available (browser hosts)
|
|
1864
|
+
- Falls back to `setImmediate` when available (Node) or `setTimeout(...,0)` otherwise
|
|
1865
|
+
- Respects an optional host adapter pause callback by polling between ticks without busy-waiting
|
|
1866
|
+
- Resolves once a single new frame or tick is available and the host is not paused
|
|
1675
1867
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
- Async support for cooperative yielding to host scheduler
|
|
1868
|
+
Steps:
|
|
1869
|
+
1. Choose the preferred tick function based on the host runtime
|
|
1870
|
+
2. When called, await the preferred tick; if the host adapter reports pause, poll again after the tick
|
|
1871
|
+
3. Resolve once a tick passed while not paused
|
|
1681
1872
|
|
|
1682
1873
|
Parameters:
|
|
1874
|
+
- `hostAdapter` - - Optional host adapter that owns cooperative pause state.
|
|
1683
1875
|
|
|
1684
|
-
|
|
1685
|
-
- `maze` - - Maze instance or descriptor used by dashboard rendering.
|
|
1686
|
-
- `result` - - Per-run result object (path, progress, telemetry, etc.).
|
|
1687
|
-
- `network` - - Network or genome object that should be visualised.
|
|
1688
|
-
- `completedGenerations` - - Integer index of the completed generation.
|
|
1689
|
-
- `neat` - - NEAT manager instance (context passed to the dashboard update).
|
|
1690
|
-
- `dashboardManager` - - Optional manager exposing `update(maze, result, network, gen, neat)`.
|
|
1691
|
-
- `flushToFrame` - - Optional async function used to yield to the host/frame scheduler; may be omitted.
|
|
1876
|
+
Returns: A function that yields cooperatively to the next animation frame / tick.
|
|
1692
1877
|
|
|
1693
1878
|
Example:
|
|
1694
1879
|
|
|
1695
|
-
|
|
1696
|
-
await
|
|
1697
|
-
maze, genResult, fittestNetwork, gen, neatInstance, dashboard, () => new Promise(r => requestAnimationFrame(r))
|
|
1698
|
-
);
|
|
1880
|
+
const flushToFrame = makeFlushToFrame();
|
|
1881
|
+
await flushToFrame(); // yields to next frame/tick
|
|
1699
1882
|
|
|
1700
|
-
###
|
|
1883
|
+
### makeSafeWriter
|
|
1701
1884
|
|
|
1702
1885
|
```ts
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
bestNetwork: default | null,
|
|
1707
|
-
completedGenerations: number,
|
|
1708
|
-
neat: default,
|
|
1709
|
-
dashboardManager: IDashboardManager | undefined,
|
|
1710
|
-
flushToFrame: (() => Promise<void>) | undefined,
|
|
1711
|
-
): Promise<void>
|
|
1886
|
+
makeSafeWriter(
|
|
1887
|
+
dashboardManager: DashboardManagerLike | undefined,
|
|
1888
|
+
): (msg: string) => void
|
|
1712
1889
|
```
|
|
1713
1890
|
|
|
1714
|
-
|
|
1715
|
-
dashboard
|
|
1716
|
-
intentionally small, allocation-light and best-effort: dashboard errors are
|
|
1717
|
-
swallowed so the evolution loop cannot be interrupted by UI issues.
|
|
1718
|
-
|
|
1719
|
-
Behavioural contract:
|
|
1720
|
-
1) If `dashboardManager.update` is present and callable the method invokes it with
|
|
1721
|
-
the stable argument order: (maze, bestResult, bestNetwork, completedGenerations, neat).
|
|
1722
|
-
2) If `flushToFrame` is supplied the helper awaits it after the update to yield to
|
|
1723
|
-
the host renderer (eg. requestAnimationFrame). Any exceptions raised by the
|
|
1724
|
-
flush are swallowed.
|
|
1725
|
-
3) The helper avoids creating ephemeral arrays/objects and therefore does not use
|
|
1726
|
-
typed-array scratch buffers here — there is no hot numerical work to pool. Other
|
|
1727
|
-
engine helpers already reuse class-level scratch buffers where appropriate.
|
|
1891
|
+
Build a resilient writer that attempts to write to Node stdout, then a provided
|
|
1892
|
+
dashboard logger, and finally `console.log` as a last resort.
|
|
1728
1893
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
- Minimal allocations (no scratch buffers needed)
|
|
1894
|
+
Steps:
|
|
1895
|
+
1. If Node `process.stdout.write` is available, use it (no trailing newline forced)
|
|
1896
|
+
2. Else if `dashboardManager.logFunction` exists, call it
|
|
1897
|
+
3. Else fall back to `console.log` and trim the message
|
|
1734
1898
|
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
3. Optionally await the provided `flushToFrame` function to yield to the host.
|
|
1899
|
+
Notes:
|
|
1900
|
+
- Errors are swallowed; logging must never throw and disrupt the evolution loop
|
|
1901
|
+
- This factory is allocation-light; the returned function only creates a trimmed string
|
|
1902
|
+
when falling back to `console.log`
|
|
1740
1903
|
|
|
1741
1904
|
Parameters:
|
|
1905
|
+
- `dashboardManager` - - Optional manager exposing `logFunction(msg:string)` used in some UIs.
|
|
1742
1906
|
|
|
1743
|
-
|
|
1744
|
-
- `maze` - - Maze descriptor passed to the dashboard renderer.
|
|
1745
|
-
- `bestResult` - - Best-run result object used for display (may be falsy when not present).
|
|
1746
|
-
- `bestNetwork` - - Network or genome object to visualise (may be falsy when not present).
|
|
1747
|
-
- `completedGenerations` - - Completed generation index (number).
|
|
1748
|
-
- `neat` - - NEAT manager instance (passed through to dashboard update).
|
|
1749
|
-
- `dashboardManager` - - Optional manager exposing `update(maze, result, network, gen, neat)`.
|
|
1750
|
-
- `flushToFrame` - - Optional async function used to yield to the host/frame scheduler
|
|
1751
|
-
(for example: `() => new Promise(r => requestAnimationFrame(r))`).
|
|
1907
|
+
Returns: A function accepting a single string message to write.
|
|
1752
1908
|
|
|
1753
1909
|
Example:
|
|
1754
1910
|
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1911
|
+
const safeWrite = makeSafeWriter(dashboardManager);
|
|
1912
|
+
safeWrite('[INFO] Generation 42 complete\n');
|
|
1913
|
+
|
|
1914
|
+
### PathModule
|
|
1915
|
+
|
|
1916
|
+
Minimal path module shape for type safety (Node.js path module subset).
|
|
1759
1917
|
|
|
1760
1918
|
## evolutionEngine/curriculumPhase.ts
|
|
1761
1919
|
|
|
@@ -3586,79 +3744,6 @@ Minimum safe load factor applied when normalising visited-hash configuration.
|
|
|
3586
3744
|
|
|
3587
3745
|
Knuth-derived 32-bit constant used when seeding the RNG state.
|
|
3588
3746
|
|
|
3589
|
-
## evolutionEngine/evolutionEngine.services.ts
|
|
3590
|
-
|
|
3591
|
-
### applyEvolutionEngineRingState
|
|
3592
|
-
|
|
3593
|
-
```ts
|
|
3594
|
-
applyEvolutionEngineRingState(
|
|
3595
|
-
updatedRingState: LogitsRingState,
|
|
3596
|
-
): void
|
|
3597
|
-
```
|
|
3598
|
-
|
|
3599
|
-
Apply the latest logits-ring runtime values returned by the evolution loop.
|
|
3600
|
-
|
|
3601
|
-
Parameters:
|
|
3602
|
-
- `updatedRingState` - - New ring-capacity, shared-mode, and write-cursor values.
|
|
3603
|
-
|
|
3604
|
-
### configureEvolutionEngineToggles
|
|
3605
|
-
|
|
3606
|
-
```ts
|
|
3607
|
-
configureEvolutionEngineToggles(
|
|
3608
|
-
reducedTelemetry: boolean,
|
|
3609
|
-
telemetryMinimal: boolean,
|
|
3610
|
-
disableBaldwinPhase: boolean,
|
|
3611
|
-
): void
|
|
3612
|
-
```
|
|
3613
|
-
|
|
3614
|
-
Apply telemetry and Baldwin-phase toggles derived from one normalized run request.
|
|
3615
|
-
|
|
3616
|
-
Parameters:
|
|
3617
|
-
- `reducedTelemetry` - - When true, keep only the essential telemetry metrics.
|
|
3618
|
-
- `telemetryMinimal` - - When true, disable verbose telemetry capture.
|
|
3619
|
-
- `disableBaldwinPhase` - - When true, skip the Baldwin refinement stage.
|
|
3620
|
-
|
|
3621
|
-
### getEvolutionEngineFacadeRuntimeState
|
|
3622
|
-
|
|
3623
|
-
```ts
|
|
3624
|
-
getEvolutionEngineFacadeRuntimeState(): LogitsRingState
|
|
3625
|
-
```
|
|
3626
|
-
|
|
3627
|
-
Read the mutable facade-owned logits-ring runtime state.
|
|
3628
|
-
|
|
3629
|
-
Returns: Current ring-capacity, shared-mode, and write-cursor state.
|
|
3630
|
-
|
|
3631
|
-
### getEvolutionEngineMaxLogitsRingCapacity
|
|
3632
|
-
|
|
3633
|
-
```ts
|
|
3634
|
-
getEvolutionEngineMaxLogitsRingCapacity(): number
|
|
3635
|
-
```
|
|
3636
|
-
|
|
3637
|
-
Read the hard maximum ring capacity used by the public facade.
|
|
3638
|
-
|
|
3639
|
-
Returns: Maximum ring capacity allowed for logits telemetry.
|
|
3640
|
-
|
|
3641
|
-
### getEvolutionEngineSharedState
|
|
3642
|
-
|
|
3643
|
-
```ts
|
|
3644
|
-
getEvolutionEngineSharedState(): EngineState
|
|
3645
|
-
```
|
|
3646
|
-
|
|
3647
|
-
Return the shared engine singleton used by extracted engine modules.
|
|
3648
|
-
|
|
3649
|
-
The public facade now depends on the same owner as the rest of the engine
|
|
3650
|
-
boundary instead of creating a private duplicate singleton.
|
|
3651
|
-
|
|
3652
|
-
Returns: Shared engine state singleton.
|
|
3653
|
-
|
|
3654
|
-
### resetEvolutionEngineRingState
|
|
3655
|
-
|
|
3656
|
-
```ts
|
|
3657
|
-
resetEvolutionEngineRingState(): void
|
|
3658
|
-
```
|
|
3659
|
-
|
|
3660
|
-
Reset the facade-owned logits-ring runtime state to its baseline defaults.
|
|
3661
|
-
|
|
3662
3747
|
## evolutionEngine/evolutionEngine.constants.ts
|
|
3663
3748
|
|
|
3664
3749
|
Stable tuning values consumed by the ASCII maze evolution facade.
|