@reicek/neataptic-ts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
- package/.github/workflows/ci.yml +41 -0
- package/.github/workflows/deploy-pages.yml +29 -0
- package/.github/workflows/manual_release_pipeline.yml +62 -0
- package/.github/workflows/publish.yml +85 -0
- package/.github/workflows/release_dispatch.yml +38 -0
- package/.travis.yml +5 -0
- package/CONTRIBUTING.md +92 -0
- package/LICENSE +24 -0
- package/ONNX_EXPORT.md +87 -0
- package/README.md +1173 -0
- package/RELEASE.md +54 -0
- package/dist-docs/package.json +1 -0
- package/dist-docs/scripts/generate-docs.d.ts +2 -0
- package/dist-docs/scripts/generate-docs.d.ts.map +1 -0
- package/dist-docs/scripts/generate-docs.js +536 -0
- package/dist-docs/scripts/generate-docs.js.map +1 -0
- package/dist-docs/scripts/render-docs-html.d.ts +2 -0
- package/dist-docs/scripts/render-docs-html.d.ts.map +1 -0
- package/dist-docs/scripts/render-docs-html.js +148 -0
- package/dist-docs/scripts/render-docs-html.js.map +1 -0
- package/docs/FOLDERS.md +14 -0
- package/docs/README.md +1173 -0
- package/docs/architecture/README.md +1391 -0
- package/docs/architecture/index.html +938 -0
- package/docs/architecture/network/README.md +1210 -0
- package/docs/architecture/network/index.html +908 -0
- package/docs/assets/ascii-maze.bundle.js +16542 -0
- package/docs/assets/ascii-maze.bundle.js.map +7 -0
- package/docs/index.html +1419 -0
- package/docs/methods/README.md +670 -0
- package/docs/methods/index.html +477 -0
- package/docs/multithreading/README.md +274 -0
- package/docs/multithreading/index.html +215 -0
- package/docs/multithreading/workers/README.md +23 -0
- package/docs/multithreading/workers/browser/README.md +39 -0
- package/docs/multithreading/workers/browser/index.html +70 -0
- package/docs/multithreading/workers/index.html +57 -0
- package/docs/multithreading/workers/node/README.md +33 -0
- package/docs/multithreading/workers/node/index.html +66 -0
- package/docs/neat/README.md +1284 -0
- package/docs/neat/index.html +906 -0
- package/docs/src/README.md +2659 -0
- package/docs/src/index.html +1579 -0
- package/jest.config.ts +32 -0
- package/package.json +99 -0
- package/plans/HyperMorphoNEAT.md +293 -0
- package/plans/ONNX_EXPORT_PLAN.md +46 -0
- package/scripts/generate-docs.ts +486 -0
- package/scripts/render-docs-html.ts +138 -0
- package/scripts/types.d.ts +2 -0
- package/src/README.md +2659 -0
- package/src/architecture/README.md +1391 -0
- package/src/architecture/activationArrayPool.ts +135 -0
- package/src/architecture/architect.ts +635 -0
- package/src/architecture/connection.ts +148 -0
- package/src/architecture/group.ts +406 -0
- package/src/architecture/layer.ts +804 -0
- package/src/architecture/network/README.md +1210 -0
- package/src/architecture/network/network.activate.ts +223 -0
- package/src/architecture/network/network.connect.ts +157 -0
- package/src/architecture/network/network.deterministic.ts +167 -0
- package/src/architecture/network/network.evolve.ts +426 -0
- package/src/architecture/network/network.gating.ts +186 -0
- package/src/architecture/network/network.genetic.ts +247 -0
- package/src/architecture/network/network.mutate.ts +624 -0
- package/src/architecture/network/network.onnx.ts +463 -0
- package/src/architecture/network/network.prune.ts +216 -0
- package/src/architecture/network/network.remove.ts +96 -0
- package/src/architecture/network/network.serialize.ts +309 -0
- package/src/architecture/network/network.slab.ts +262 -0
- package/src/architecture/network/network.standalone.ts +246 -0
- package/src/architecture/network/network.stats.ts +59 -0
- package/src/architecture/network/network.topology.ts +86 -0
- package/src/architecture/network/network.training.ts +1278 -0
- package/src/architecture/network.ts +1302 -0
- package/src/architecture/node.ts +1288 -0
- package/src/architecture/onnx.ts +3 -0
- package/src/config.ts +83 -0
- package/src/methods/README.md +670 -0
- package/src/methods/activation.ts +372 -0
- package/src/methods/connection.ts +31 -0
- package/src/methods/cost.ts +347 -0
- package/src/methods/crossover.ts +63 -0
- package/src/methods/gating.ts +43 -0
- package/src/methods/methods.ts +8 -0
- package/src/methods/mutation.ts +300 -0
- package/src/methods/rate.ts +257 -0
- package/src/methods/selection.ts +65 -0
- package/src/multithreading/README.md +274 -0
- package/src/multithreading/multi.ts +339 -0
- package/src/multithreading/workers/README.md +23 -0
- package/src/multithreading/workers/browser/README.md +39 -0
- package/src/multithreading/workers/browser/testworker.ts +99 -0
- package/src/multithreading/workers/node/README.md +33 -0
- package/src/multithreading/workers/node/testworker.ts +72 -0
- package/src/multithreading/workers/node/worker.ts +70 -0
- package/src/multithreading/workers/workers.ts +22 -0
- package/src/neat/README.md +1284 -0
- package/src/neat/neat.adaptive.ts +544 -0
- package/src/neat/neat.compat.ts +164 -0
- package/src/neat/neat.constants.ts +20 -0
- package/src/neat/neat.diversity.ts +217 -0
- package/src/neat/neat.evaluate.ts +328 -0
- package/src/neat/neat.evolve.ts +1026 -0
- package/src/neat/neat.export.ts +249 -0
- package/src/neat/neat.helpers.ts +235 -0
- package/src/neat/neat.lineage.ts +220 -0
- package/src/neat/neat.multiobjective.ts +260 -0
- package/src/neat/neat.mutation.ts +718 -0
- package/src/neat/neat.objectives.ts +157 -0
- package/src/neat/neat.pruning.ts +190 -0
- package/src/neat/neat.selection.ts +269 -0
- package/src/neat/neat.speciation.ts +460 -0
- package/src/neat/neat.species.ts +151 -0
- package/src/neat/neat.telemetry.exports.ts +469 -0
- package/src/neat/neat.telemetry.ts +933 -0
- package/src/neat/neat.types.ts +275 -0
- package/src/neat.ts +1042 -0
- package/src/neataptic.ts +10 -0
- package/test/architecture/activationArrayPool.capacity.test.ts +19 -0
- package/test/architecture/activationArrayPool.test.ts +46 -0
- package/test/architecture/connection.test.ts +290 -0
- package/test/architecture/group.test.ts +950 -0
- package/test/architecture/layer.test.ts +1535 -0
- package/test/architecture/network.pruning.test.ts +65 -0
- package/test/architecture/node.test.ts +1602 -0
- package/test/examples/asciiMaze/asciiMaze.e2e.test.ts +499 -0
- package/test/examples/asciiMaze/asciiMaze.ts +41 -0
- package/test/examples/asciiMaze/browser-entry.ts +164 -0
- package/test/examples/asciiMaze/browserLogger.ts +221 -0
- package/test/examples/asciiMaze/browserTerminalUtility.ts +48 -0
- package/test/examples/asciiMaze/colors.ts +119 -0
- package/test/examples/asciiMaze/dashboardManager.ts +968 -0
- package/test/examples/asciiMaze/evolutionEngine.ts +1248 -0
- package/test/examples/asciiMaze/fitness.ts +136 -0
- package/test/examples/asciiMaze/index.html +128 -0
- package/test/examples/asciiMaze/index.ts +26 -0
- package/test/examples/asciiMaze/interfaces.ts +235 -0
- package/test/examples/asciiMaze/mazeMovement.ts +996 -0
- package/test/examples/asciiMaze/mazeUtils.ts +278 -0
- package/test/examples/asciiMaze/mazeVision.ts +402 -0
- package/test/examples/asciiMaze/mazeVisualization.ts +585 -0
- package/test/examples/asciiMaze/mazes.ts +245 -0
- package/test/examples/asciiMaze/networkRefinement.ts +76 -0
- package/test/examples/asciiMaze/networkVisualization.ts +901 -0
- package/test/examples/asciiMaze/terminalUtility.ts +73 -0
- package/test/methods/activation.test.ts +1142 -0
- package/test/methods/connection.test.ts +146 -0
- package/test/methods/cost.test.ts +1123 -0
- package/test/methods/crossover.test.ts +202 -0
- package/test/methods/gating.test.ts +144 -0
- package/test/methods/mutation.test.ts +451 -0
- package/test/methods/optimizers.advanced.test.ts +80 -0
- package/test/methods/optimizers.behavior.test.ts +105 -0
- package/test/methods/optimizers.formula.test.ts +89 -0
- package/test/methods/rate.cosineWarmRestarts.test.ts +44 -0
- package/test/methods/rate.linearWarmupDecay.test.ts +41 -0
- package/test/methods/rate.reduceOnPlateau.test.ts +45 -0
- package/test/methods/rate.test.ts +684 -0
- package/test/methods/selection.test.ts +245 -0
- package/test/multithreading/activations.functions.test.ts +54 -0
- package/test/multithreading/multi.test.ts +290 -0
- package/test/multithreading/worker.node.process.test.ts +39 -0
- package/test/multithreading/workers.coverage.test.ts +36 -0
- package/test/multithreading/workers.dynamic.import.test.ts +8 -0
- package/test/neat/neat.adaptive.complexityBudget.test.ts +34 -0
- package/test/neat/neat.adaptive.criterion.complexity.test.ts +50 -0
- package/test/neat/neat.adaptive.mutation.strategy.test.ts +37 -0
- package/test/neat/neat.adaptive.operator.decay.test.ts +31 -0
- package/test/neat/neat.adaptive.phasedComplexity.test.ts +25 -0
- package/test/neat/neat.adaptive.pruning.test.ts +25 -0
- package/test/neat/neat.adaptive.targetSpecies.test.ts +43 -0
- package/test/neat/neat.additional.coverage.test.ts +126 -0
- package/test/neat/neat.advanced.enhancements.test.ts +85 -0
- package/test/neat/neat.advanced.test.ts +589 -0
- package/test/neat/neat.diversity.autocompat.test.ts +47 -0
- package/test/neat/neat.diversity.metrics.test.ts +21 -0
- package/test/neat/neat.diversity.stats.test.ts +44 -0
- package/test/neat/neat.enhancements.test.ts +79 -0
- package/test/neat/neat.entropy.ancestorAdaptive.test.ts +133 -0
- package/test/neat/neat.entropy.compat.csv.test.ts +108 -0
- package/test/neat/neat.evolution.pruning.test.ts +39 -0
- package/test/neat/neat.fastmode.autotune.test.ts +42 -0
- package/test/neat/neat.innovation.test.ts +134 -0
- package/test/neat/neat.lineage.antibreeding.test.ts +35 -0
- package/test/neat/neat.lineage.entropy.test.ts +56 -0
- package/test/neat/neat.lineage.inbreeding.test.ts +49 -0
- package/test/neat/neat.lineage.pressure.test.ts +29 -0
- package/test/neat/neat.multiobjective.adaptive.test.ts +57 -0
- package/test/neat/neat.multiobjective.dynamic.schedule.test.ts +46 -0
- package/test/neat/neat.multiobjective.dynamic.test.ts +31 -0
- package/test/neat/neat.multiobjective.fastsort.delegation.test.ts +51 -0
- package/test/neat/neat.multiobjective.prune.test.ts +39 -0
- package/test/neat/neat.multiobjective.test.ts +21 -0
- package/test/neat/neat.mutation.undefined.pool.test.ts +24 -0
- package/test/neat/neat.objective.events.test.ts +26 -0
- package/test/neat/neat.objective.importance.test.ts +21 -0
- package/test/neat/neat.objective.lifetimes.test.ts +33 -0
- package/test/neat/neat.offspring.allocation.test.ts +22 -0
- package/test/neat/neat.operator.bandit.test.ts +17 -0
- package/test/neat/neat.operator.phases.test.ts +38 -0
- package/test/neat/neat.pruneInactive.behavior.test.ts +54 -0
- package/test/neat/neat.reenable.adaptation.test.ts +18 -0
- package/test/neat/neat.rng.state.test.ts +22 -0
- package/test/neat/neat.spawn.add.test.ts +123 -0
- package/test/neat/neat.speciation.test.ts +96 -0
- package/test/neat/neat.species.allocation.telemetry.test.ts +26 -0
- package/test/neat/neat.species.history.csv.test.ts +24 -0
- package/test/neat/neat.telemetry.advanced.test.ts +226 -0
- package/test/neat/neat.telemetry.csv.lineage.test.ts +19 -0
- package/test/neat/neat.telemetry.parity.test.ts +42 -0
- package/test/neat/neat.telemetry.stream.test.ts +19 -0
- package/test/neat/neat.telemetry.test.ts +16 -0
- package/test/neat/neat.test.ts +422 -0
- package/test/neat/neat.utilities.test.ts +44 -0
- package/test/network/__suppress_console.ts +9 -0
- package/test/network/acyclic.topoorder.test.ts +17 -0
- package/test/network/checkpoint.metricshook.test.ts +36 -0
- package/test/network/error.handling.test.ts +581 -0
- package/test/network/evolution.test.ts +285 -0
- package/test/network/genetic.test.ts +208 -0
- package/test/network/learning.capability.test.ts +244 -0
- package/test/network/mutation.effects.test.ts +492 -0
- package/test/network/network.activate.test.ts +115 -0
- package/test/network/network.activateBatch.test.ts +30 -0
- package/test/network/network.deterministic.test.ts +64 -0
- package/test/network/network.evolve.branches.test.ts +75 -0
- package/test/network/network.evolve.multithread.branches.test.ts +83 -0
- package/test/network/network.evolve.test.ts +100 -0
- package/test/network/network.gating.removal.test.ts +93 -0
- package/test/network/network.mutate.additional.test.ts +145 -0
- package/test/network/network.mutate.edgecases.test.ts +101 -0
- package/test/network/network.mutate.test.ts +101 -0
- package/test/network/network.prune.earlyexit.test.ts +38 -0
- package/test/network/network.remove.errors.test.ts +45 -0
- package/test/network/network.slab.fallbacks.test.ts +22 -0
- package/test/network/network.stats.test.ts +45 -0
- package/test/network/network.training.advanced.test.ts +149 -0
- package/test/network/network.training.basic.test.ts +228 -0
- package/test/network/network.training.helpers.test.ts +183 -0
- package/test/network/onnx.export.test.ts +310 -0
- package/test/network/onnx.import.test.ts +129 -0
- package/test/network/pruning.topology.test.ts +282 -0
- package/test/network/regularization.determinism.test.ts +83 -0
- package/test/network/regularization.dropconnect.test.ts +17 -0
- package/test/network/regularization.dropconnect.validation.test.ts +18 -0
- package/test/network/regularization.stochasticdepth.test.ts +27 -0
- package/test/network/regularization.test.ts +843 -0
- package/test/network/regularization.weightnoise.test.ts +30 -0
- package/test/network/setupTests.ts +2 -0
- package/test/network/standalone.test.ts +332 -0
- package/test/network/structure.serialization.test.ts +660 -0
- package/test/training/training.determinism.mixed-precision.test.ts +134 -0
- package/test/training/training.earlystopping.test.ts +91 -0
- package/test/training/training.edge-cases.test.ts +91 -0
- package/test/training/training.extensions.test.ts +47 -0
- package/test/training/training.gradient.features.test.ts +110 -0
- package/test/training/training.gradient.refinements.test.ts +170 -0
- package/test/training/training.gradient.separate-bias.test.ts +41 -0
- package/test/training/training.optimizer.test.ts +48 -0
- package/test/training/training.plateau.smoothing.test.ts +58 -0
- package/test/training/training.smoothing.types.test.ts +174 -0
- package/test/training/training.train.options.coverage.test.ts +52 -0
- package/test/utils/console-helper.ts +76 -0
- package/test/utils/jest-setup.ts +60 -0
- package/test/utils/test-helpers.ts +175 -0
- package/tsconfig.docs.json +12 -0
- package/tsconfig.json +21 -0
- package/webpack.config.js +49 -0
package/src/neat.ts
ADDED
|
@@ -0,0 +1,1042 @@
|
|
|
1
|
+
import Network from './architecture/network';
|
|
2
|
+
import type {
|
|
3
|
+
TelemetryEntry,
|
|
4
|
+
ObjectiveDescriptor,
|
|
5
|
+
SpeciesHistoryEntry,
|
|
6
|
+
OperatorStatsRecord,
|
|
7
|
+
} from './neat/neat.types';
|
|
8
|
+
import * as methods from './methods/methods';
|
|
9
|
+
import { selection as selectionMethods } from './methods/selection';
|
|
10
|
+
import NodeType from './architecture/node'; // Import the Node type with a different name to avoid conflicts
|
|
11
|
+
// Static imports (post-migration from runtime require delegates)
|
|
12
|
+
import {
|
|
13
|
+
ensureMinHiddenNodes,
|
|
14
|
+
selectMutationMethod,
|
|
15
|
+
ensureNoDeadEnds,
|
|
16
|
+
mutate,
|
|
17
|
+
mutateAddNodeReuse,
|
|
18
|
+
mutateAddConnReuse,
|
|
19
|
+
} from './neat/neat.mutation';
|
|
20
|
+
import { evolve } from './neat/neat.evolve';
|
|
21
|
+
import { evaluate } from './neat/neat.evaluate';
|
|
22
|
+
import { createPool, spawnFromParent, addGenome } from './neat/neat.helpers';
|
|
23
|
+
import {
|
|
24
|
+
_getObjectives,
|
|
25
|
+
registerObjective,
|
|
26
|
+
clearObjectives,
|
|
27
|
+
} from './neat/neat.objectives';
|
|
28
|
+
import {
|
|
29
|
+
computeDiversityStats,
|
|
30
|
+
structuralEntropy,
|
|
31
|
+
} from './neat/neat.diversity';
|
|
32
|
+
import { fastNonDominated } from './neat/neat.multiobjective';
|
|
33
|
+
import { _fallbackInnov, _compatibilityDistance } from './neat/neat.compat';
|
|
34
|
+
import {
|
|
35
|
+
_speciate,
|
|
36
|
+
_applyFitnessSharing,
|
|
37
|
+
_sortSpeciesMembers,
|
|
38
|
+
_updateSpeciesStagnation,
|
|
39
|
+
} from './neat/neat.speciation';
|
|
40
|
+
import { getSpeciesStats, getSpeciesHistory } from './neat/neat.species';
|
|
41
|
+
import {
|
|
42
|
+
exportTelemetryJSONL,
|
|
43
|
+
exportTelemetryCSV,
|
|
44
|
+
exportSpeciesHistoryCSV,
|
|
45
|
+
} from './neat/neat.telemetry.exports';
|
|
46
|
+
import { sort, getParent, getFittest, getAverage } from './neat/neat.selection';
|
|
47
|
+
import {
|
|
48
|
+
exportPopulation,
|
|
49
|
+
importPopulation,
|
|
50
|
+
exportState,
|
|
51
|
+
importStateImpl,
|
|
52
|
+
toJSONImpl,
|
|
53
|
+
fromJSONImpl,
|
|
54
|
+
} from './neat/neat.export';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Configuration options for Neat evolutionary runs.
|
|
58
|
+
*
|
|
59
|
+
* Each property is optional and the class applies sensible defaults when a
|
|
60
|
+
* field is not provided. Options control population size, mutation rates,
|
|
61
|
+
* compatibility coefficients, selection strategy and other behavioral knobs.
|
|
62
|
+
*
|
|
63
|
+
* Example:
|
|
64
|
+
* const opts: NeatOptions = { popsize: 100, mutationRate: 0.5 };
|
|
65
|
+
* const neat = new Neat(3, 1, fitnessFn, opts);
|
|
66
|
+
*
|
|
67
|
+
* Note: this type is intentionally permissive to support staged migration and
|
|
68
|
+
* legacy callers; prefer providing a typed options object where possible.
|
|
69
|
+
*/
|
|
70
|
+
type Options = { [k: string]: any };
|
|
71
|
+
// Public re-export for library consumers
|
|
72
|
+
export type NeatOptions = Options;
|
|
73
|
+
export default class Neat {
|
|
74
|
+
input: number;
|
|
75
|
+
output: number;
|
|
76
|
+
fitness: (network: Network) => number;
|
|
77
|
+
options: Options;
|
|
78
|
+
population: Network[] = [];
|
|
79
|
+
generation: number = 0;
|
|
80
|
+
// Deterministic RNG state (lazy init)
|
|
81
|
+
/**
|
|
82
|
+
* Internal numeric state for the deterministic xorshift RNG when no user RNG
|
|
83
|
+
* is provided. Stored as a 32-bit unsigned integer.
|
|
84
|
+
*/
|
|
85
|
+
private _rngState?: number;
|
|
86
|
+
/**
|
|
87
|
+
* Cached RNG function; created lazily and seeded from `_rngState` when used.
|
|
88
|
+
*/
|
|
89
|
+
private _rng?: () => number;
|
|
90
|
+
// Internal bookkeeping and caches (kept permissive during staggered migration)
|
|
91
|
+
/** Array of current species (internal representation). */
|
|
92
|
+
private _species: any[] = [];
|
|
93
|
+
/** Operator statistics used by adaptive operator selection. */
|
|
94
|
+
private _operatorStats: Map<string, OperatorStatsRecord> = new Map();
|
|
95
|
+
/** Map of node-split innovations used to reuse innovation ids for node splits. */
|
|
96
|
+
private _nodeSplitInnovations: Map<string, any> = new Map();
|
|
97
|
+
/** Map of connection innovations keyed by a string identifier. */
|
|
98
|
+
private _connInnovations: Map<string, number> = new Map();
|
|
99
|
+
/** Counter for issuing global innovation numbers when explicit numbers are used. */
|
|
100
|
+
private _nextGlobalInnovation: number = 1;
|
|
101
|
+
/** Counter for assigning unique genome ids. */
|
|
102
|
+
private _nextGenomeId: number = 1;
|
|
103
|
+
/** Whether lineage metadata should be recorded on genomes. */
|
|
104
|
+
private _lineageEnabled: boolean = false;
|
|
105
|
+
/** Last observed count of inbreeding (used for detecting excessive cloning). */
|
|
106
|
+
private _lastInbreedingCount: number = 0;
|
|
107
|
+
/** Previous inbreeding count snapshot. */
|
|
108
|
+
private _prevInbreedingCount: number = 0;
|
|
109
|
+
/** Optional phase marker for multi-stage experiments. */
|
|
110
|
+
private _phase?: string;
|
|
111
|
+
/** Telemetry buffer storing diagnostic snapshots per generation. */
|
|
112
|
+
private _telemetry: any[] = [];
|
|
113
|
+
/** Map of species id -> set of member genome ids from previous generation. */
|
|
114
|
+
private _prevSpeciesMembers: Map<number, Set<number>> = new Map();
|
|
115
|
+
/** Last recorded stats per species id. */
|
|
116
|
+
private _speciesLastStats: Map<number, any> = new Map();
|
|
117
|
+
/** Time-series history of species stats (for exports/telemetry). */
|
|
118
|
+
private _speciesHistory: any[] = [];
|
|
119
|
+
/** Archive of Pareto front metadata for multi-objective tracking. */
|
|
120
|
+
private _paretoArchive: any[] = [];
|
|
121
|
+
/** Archive storing Pareto objectives snapshots. */
|
|
122
|
+
private _paretoObjectivesArchive: any[] = [];
|
|
123
|
+
/** Novelty archive used by novelty search (behavior representatives). */
|
|
124
|
+
private _noveltyArchive: any[] = [];
|
|
125
|
+
/** Map tracking stale counts for objectives by key. */
|
|
126
|
+
private _objectiveStale: Map<string, number> = new Map();
|
|
127
|
+
/** Map tracking ages for objectives by key. */
|
|
128
|
+
private _objectiveAges: Map<string, number> = new Map();
|
|
129
|
+
/** Queue of recent objective activation/deactivation events for telemetry. */
|
|
130
|
+
private _objectiveEvents: any[] = [];
|
|
131
|
+
/** Pending objective keys to add during safe phases. */
|
|
132
|
+
private _pendingObjectiveAdds: string[] = [];
|
|
133
|
+
/** Pending objective keys to remove during safe phases. */
|
|
134
|
+
private _pendingObjectiveRemoves: string[] = [];
|
|
135
|
+
/** Last allocated offspring set (used by adaptive allocators). */
|
|
136
|
+
private _lastOffspringAlloc?: any[];
|
|
137
|
+
/** Adaptive prune level for complexity control (optional). */
|
|
138
|
+
private _adaptivePruneLevel?: number;
|
|
139
|
+
/** Duration of the last evaluation run (ms). */
|
|
140
|
+
private _lastEvalDuration?: number;
|
|
141
|
+
/** Duration of the last evolve run (ms). */
|
|
142
|
+
private _lastEvolveDuration?: number;
|
|
143
|
+
/** Cached diversity metrics (computed lazily). */
|
|
144
|
+
private _diversityStats?: any;
|
|
145
|
+
/** Cached list of registered objectives. */
|
|
146
|
+
private _objectivesList?: any[];
|
|
147
|
+
/** Generation index where the last global improvement occurred. */
|
|
148
|
+
private _lastGlobalImproveGeneration: number = 0;
|
|
149
|
+
/** Best score observed in the last generation (used for improvement detection). */
|
|
150
|
+
private _bestScoreLastGen?: number;
|
|
151
|
+
// Speciation controller state
|
|
152
|
+
/** Map of speciesId -> creation generation for bookkeeping. */
|
|
153
|
+
private _speciesCreated: Map<number, number> = new Map();
|
|
154
|
+
/** Exponential moving average for compatibility threshold (adaptive speciation). */
|
|
155
|
+
private _compatSpeciesEMA?: number;
|
|
156
|
+
/** Integral accumulator used by adaptive compatibility controllers. */
|
|
157
|
+
private _compatIntegral: number = 0;
|
|
158
|
+
/** Generation when epsilon compatibility was last adjusted. */
|
|
159
|
+
private _lastEpsilonAdjustGen: number = -Infinity;
|
|
160
|
+
/** Generation when ancestor uniqueness adjustment was last applied. */
|
|
161
|
+
private _lastAncestorUniqAdjustGen: number = -Infinity;
|
|
162
|
+
// Adaptive minimal criterion & complexity
|
|
163
|
+
/** Adaptive minimal criterion threshold (optional). */
|
|
164
|
+
private _mcThreshold?: number;
|
|
165
|
+
|
|
166
|
+
// Lightweight RNG accessor used throughout migrated modules
|
|
167
|
+
private _getRNG(): () => number {
|
|
168
|
+
if (!this._rng) {
|
|
169
|
+
// Allow user-provided RNG in options for deterministic tests
|
|
170
|
+
const optRng = (this.options as any)?.rng;
|
|
171
|
+
if (typeof optRng === 'function') this._rng = optRng;
|
|
172
|
+
else {
|
|
173
|
+
// Deterministic xorshift32 seeded by _rngState; if absent initialize lazily
|
|
174
|
+
if (this._rngState === undefined) {
|
|
175
|
+
// initialize with a non-zero seed derived from time & population length for variability
|
|
176
|
+
let seed =
|
|
177
|
+
(Date.now() ^ ((this.population.length + 1) * 0x9e3779b1)) >>> 0;
|
|
178
|
+
if (seed === 0) seed = 0x1a2b3c4d;
|
|
179
|
+
this._rngState = seed >>> 0;
|
|
180
|
+
}
|
|
181
|
+
this._rng = () => {
|
|
182
|
+
// xorshift32
|
|
183
|
+
let x = this._rngState! >>> 0;
|
|
184
|
+
x ^= x << 13;
|
|
185
|
+
x >>>= 0;
|
|
186
|
+
x ^= x >> 17;
|
|
187
|
+
x >>>= 0;
|
|
188
|
+
x ^= x << 5;
|
|
189
|
+
x >>>= 0;
|
|
190
|
+
this._rngState = x >>> 0;
|
|
191
|
+
return (x >>> 0) / 0xffffffff;
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return this._rng!;
|
|
196
|
+
}
|
|
197
|
+
// Delegate ensureMinHiddenNodes to migrated mutation helper for smaller class surface
|
|
198
|
+
/**
|
|
199
|
+
* Ensure a network has the minimum number of hidden nodes according to
|
|
200
|
+
* configured policy. Delegates to migrated helper implementation.
|
|
201
|
+
*
|
|
202
|
+
* @param network Network instance to adjust.
|
|
203
|
+
* @param multiplierOverride Optional multiplier to override configured policy.
|
|
204
|
+
*/
|
|
205
|
+
ensureMinHiddenNodes(network: Network, multiplierOverride?: number) {
|
|
206
|
+
return ensureMinHiddenNodes.call(this as any, network, multiplierOverride);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Construct a new Neat instance.
|
|
210
|
+
* Kept permissive during staged migration; accepts the same signature tests expect.
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* // Create a neat instance for 3 inputs and 1 output with default options
|
|
214
|
+
* const neat = new Neat(3, 1, (net) => evaluateFitness(net));
|
|
215
|
+
*/
|
|
216
|
+
constructor(
|
|
217
|
+
input?: number,
|
|
218
|
+
output?: number,
|
|
219
|
+
fitness?: any,
|
|
220
|
+
options: any = {}
|
|
221
|
+
) {
|
|
222
|
+
// Assign basic fields; other internals are initialized above as class fields
|
|
223
|
+
this.input = input ?? 0;
|
|
224
|
+
this.output = output ?? 0;
|
|
225
|
+
this.fitness = fitness ?? ((n: Network) => 0);
|
|
226
|
+
this.options = options || {};
|
|
227
|
+
// --- Default option hydration (only assign when undefined to respect caller overrides) ---
|
|
228
|
+
const opts: any = this.options;
|
|
229
|
+
// Core sizes / rates
|
|
230
|
+
if (opts.popsize === undefined) opts.popsize = 50;
|
|
231
|
+
if (opts.elitism === undefined) opts.elitism = 0;
|
|
232
|
+
if (opts.provenance === undefined) opts.provenance = 0;
|
|
233
|
+
if (opts.mutationRate === undefined) opts.mutationRate = 0.7; // tests expect 0.7
|
|
234
|
+
if (opts.mutationAmount === undefined) opts.mutationAmount = 1;
|
|
235
|
+
if (opts.fitnessPopulation === undefined) opts.fitnessPopulation = false;
|
|
236
|
+
if (opts.clear === undefined) opts.clear = false;
|
|
237
|
+
if (opts.equal === undefined) opts.equal = false;
|
|
238
|
+
if (opts.compatibilityThreshold === undefined)
|
|
239
|
+
opts.compatibilityThreshold = 3;
|
|
240
|
+
// Structural caps
|
|
241
|
+
if (opts.maxNodes === undefined) opts.maxNodes = Infinity;
|
|
242
|
+
if (opts.maxConns === undefined) opts.maxConns = Infinity;
|
|
243
|
+
if (opts.maxGates === undefined) opts.maxGates = Infinity;
|
|
244
|
+
// Compatibility distance coefficients
|
|
245
|
+
if (opts.excessCoeff === undefined) opts.excessCoeff = 1;
|
|
246
|
+
if (opts.disjointCoeff === undefined) opts.disjointCoeff = 1;
|
|
247
|
+
if (opts.weightDiffCoeff === undefined) opts.weightDiffCoeff = 0.5;
|
|
248
|
+
// Mutation list default (shallow copy so tests can check identity scenarios)
|
|
249
|
+
if (opts.mutation === undefined)
|
|
250
|
+
opts.mutation = methods.mutation.ALL
|
|
251
|
+
? methods.mutation.ALL.slice()
|
|
252
|
+
: methods.mutation.FFW
|
|
253
|
+
? [methods.mutation.FFW]
|
|
254
|
+
: [];
|
|
255
|
+
// Selection method defaults
|
|
256
|
+
if (opts.selection === undefined) {
|
|
257
|
+
// prefer dedicated selection module; fallback to methods.selection if legacy export
|
|
258
|
+
opts.selection =
|
|
259
|
+
(selectionMethods && selectionMethods.TOURNAMENT) ||
|
|
260
|
+
(methods as any).selection?.TOURNAMENT ||
|
|
261
|
+
selectionMethods.FITNESS_PROPORTIONATE;
|
|
262
|
+
}
|
|
263
|
+
if (opts.crossover === undefined)
|
|
264
|
+
opts.crossover = methods.crossover
|
|
265
|
+
? methods.crossover.SINGLE_POINT
|
|
266
|
+
: undefined;
|
|
267
|
+
// Novelty archive defaults
|
|
268
|
+
if (opts.novelty === undefined) opts.novelty = { enabled: false };
|
|
269
|
+
// Diversity metrics container
|
|
270
|
+
if (opts.diversityMetrics === undefined)
|
|
271
|
+
opts.diversityMetrics = { enabled: true };
|
|
272
|
+
// fastMode auto defaults
|
|
273
|
+
if (opts.fastMode && opts.diversityMetrics) {
|
|
274
|
+
if (opts.diversityMetrics.pairSample == null)
|
|
275
|
+
opts.diversityMetrics.pairSample = 20;
|
|
276
|
+
if (opts.diversityMetrics.graphletSample == null)
|
|
277
|
+
opts.diversityMetrics.graphletSample = 30;
|
|
278
|
+
if (opts.novelty?.enabled && opts.novelty.k == null) opts.novelty.k = 5;
|
|
279
|
+
}
|
|
280
|
+
// Initialize novelty archive backing array for size accessor
|
|
281
|
+
(this as any)._noveltyArchive = [];
|
|
282
|
+
// Speciation defaults
|
|
283
|
+
if (opts.speciation === undefined) opts.speciation = false;
|
|
284
|
+
// Objective system container
|
|
285
|
+
if (
|
|
286
|
+
opts.multiObjective &&
|
|
287
|
+
opts.multiObjective.enabled &&
|
|
288
|
+
!Array.isArray(opts.multiObjective.objectives)
|
|
289
|
+
)
|
|
290
|
+
opts.multiObjective.objectives = [];
|
|
291
|
+
// Ensure population initialization consistent with original behavior
|
|
292
|
+
this.population = this.population || [];
|
|
293
|
+
// If a network or population seed provided, create initial pool
|
|
294
|
+
try {
|
|
295
|
+
if ((this.options as any).network !== undefined)
|
|
296
|
+
this.createPool((this.options as any).network);
|
|
297
|
+
else if ((this.options as any).popsize) this.createPool(null);
|
|
298
|
+
} catch {}
|
|
299
|
+
// Enable lineage tracking if requested via options
|
|
300
|
+
if (
|
|
301
|
+
(this.options as any).lineage?.enabled ||
|
|
302
|
+
(this.options as any).provenance > 0
|
|
303
|
+
)
|
|
304
|
+
this._lineageEnabled = true;
|
|
305
|
+
// Backwards compat: some tests use `lineageTracking` boolean option
|
|
306
|
+
if ((this.options as any).lineageTracking === true)
|
|
307
|
+
this._lineageEnabled = true;
|
|
308
|
+
if (options.lineagePressure?.enabled && this._lineageEnabled !== true) {
|
|
309
|
+
// lineagePressure requires lineage metadata
|
|
310
|
+
this._lineageEnabled = true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Evolves the population by selecting, mutating, and breeding genomes.
|
|
315
|
+
* This method is delegated to `src/neat/neat.evolve.ts` during the migration.
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* // Run a single evolution step (async)
|
|
319
|
+
* await neat.evolve();
|
|
320
|
+
*/
|
|
321
|
+
async evolve(): Promise<Network> {
|
|
322
|
+
return evolve.call(this as any);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async evaluate(): Promise<any> {
|
|
326
|
+
return evaluate.call(this as any);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Create initial population pool. Delegates to helpers if present.
|
|
331
|
+
*/
|
|
332
|
+
createPool(network: Network | null): void {
|
|
333
|
+
try {
|
|
334
|
+
if (createPool && typeof createPool === 'function')
|
|
335
|
+
return createPool.call(this as any, network);
|
|
336
|
+
} catch {}
|
|
337
|
+
// Fallback basic implementation
|
|
338
|
+
this.population = [];
|
|
339
|
+
/**
|
|
340
|
+
* Size of the initial pool to create when seeding the population. Taken
|
|
341
|
+
* from options.popsize with a sensible default for backward compatibility.
|
|
342
|
+
*/
|
|
343
|
+
const poolSize = this.options.popsize || 50;
|
|
344
|
+
for (let idx = 0; idx < poolSize; idx++) {
|
|
345
|
+
// Clone or create a fresh genome for the pool
|
|
346
|
+
const genomeCopy = network
|
|
347
|
+
? Network.fromJSON((network as any).toJSON())
|
|
348
|
+
: new Network(this.input, this.output, {
|
|
349
|
+
minHidden: this.options.minHidden,
|
|
350
|
+
});
|
|
351
|
+
// Clear any serialized score so newly-created genomes start unevaluated
|
|
352
|
+
genomeCopy.score = undefined;
|
|
353
|
+
try {
|
|
354
|
+
this.ensureNoDeadEnds(genomeCopy);
|
|
355
|
+
} catch {}
|
|
356
|
+
(genomeCopy as any)._reenableProb = this.options.reenableProb;
|
|
357
|
+
(genomeCopy as any)._id = this._nextGenomeId++;
|
|
358
|
+
if (this._lineageEnabled) {
|
|
359
|
+
(genomeCopy as any)._parents = [];
|
|
360
|
+
(genomeCopy as any)._depth = 0;
|
|
361
|
+
}
|
|
362
|
+
this.population.push(genomeCopy);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// RNG snapshot / restore helpers used by tests
|
|
367
|
+
/**
|
|
368
|
+
* Return the current opaque RNG numeric state used by the instance.
|
|
369
|
+
* Useful for deterministic test replay and debugging.
|
|
370
|
+
*/
|
|
371
|
+
snapshotRNGState() {
|
|
372
|
+
return this._rngState;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Restore a previously-snapshotted RNG state. This restores the internal
|
|
376
|
+
* seed but does not re-create the RNG function until next use.
|
|
377
|
+
*
|
|
378
|
+
* @param state Opaque numeric RNG state produced by `snapshotRNGState()`.
|
|
379
|
+
*/
|
|
380
|
+
restoreRNGState(state: any) {
|
|
381
|
+
// Restore numeric RNG state (opaque to callers)
|
|
382
|
+
this._rngState = state;
|
|
383
|
+
// invalidate RNG so next call re-reads seed
|
|
384
|
+
this._rng = undefined;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Import an RNG state (alias for restore; kept for compatibility).
|
|
388
|
+
* @param state Numeric RNG state.
|
|
389
|
+
*/
|
|
390
|
+
importRNGState(state: any) {
|
|
391
|
+
this._rngState = state;
|
|
392
|
+
this._rng = undefined;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Export the current RNG state for external persistence or tests.
|
|
396
|
+
*/
|
|
397
|
+
exportRNGState() {
|
|
398
|
+
return this._rngState;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Generates an offspring by crossing over two parent networks.
|
|
402
|
+
* Uses the crossover method described in the Instinct algorithm.
|
|
403
|
+
* @returns A new network created from two parents.
|
|
404
|
+
* @see {@link https://medium.com/data-science/neuro-evolution-on-steroids-82bd14ddc2f6 Instinct: neuro-evolution on steroids by Thomas Wagenaar}
|
|
405
|
+
*/
|
|
406
|
+
getOffspring(): Network {
|
|
407
|
+
let parent1: Network;
|
|
408
|
+
let parent2: Network;
|
|
409
|
+
try {
|
|
410
|
+
parent1 = this.getParent();
|
|
411
|
+
} catch {
|
|
412
|
+
parent1 = this.population[0];
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
parent2 = this.getParent();
|
|
416
|
+
} catch {
|
|
417
|
+
parent2 =
|
|
418
|
+
this.population[
|
|
419
|
+
Math.floor(this._getRNG()() * this.population.length)
|
|
420
|
+
] || this.population[0];
|
|
421
|
+
}
|
|
422
|
+
const offspring = Network.crossOver(
|
|
423
|
+
parent1,
|
|
424
|
+
parent2,
|
|
425
|
+
this.options.equal || false
|
|
426
|
+
);
|
|
427
|
+
(offspring as any)._reenableProb = this.options.reenableProb;
|
|
428
|
+
(offspring as any)._id = this._nextGenomeId++;
|
|
429
|
+
if (this._lineageEnabled) {
|
|
430
|
+
(offspring as any)._parents = [
|
|
431
|
+
(parent1 as any)._id,
|
|
432
|
+
(parent2 as any)._id,
|
|
433
|
+
];
|
|
434
|
+
const depth1 = (parent1 as any)._depth ?? 0;
|
|
435
|
+
const depth2 = (parent2 as any)._depth ?? 0;
|
|
436
|
+
(offspring as any)._depth = 1 + Math.max(depth1, depth2);
|
|
437
|
+
if ((parent1 as any)._id === (parent2 as any)._id)
|
|
438
|
+
this._lastInbreedingCount++;
|
|
439
|
+
}
|
|
440
|
+
// Ensure the offspring has the minimum required hidden nodes
|
|
441
|
+
this.ensureMinHiddenNodes(offspring);
|
|
442
|
+
this.ensureNoDeadEnds(offspring); // Ensure no dead ends or blind I/O
|
|
443
|
+
return offspring;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** Emit a standardized warning when evolution loop finds no valid best genome (test hook). */
|
|
447
|
+
_warnIfNoBestGenome() {
|
|
448
|
+
try {
|
|
449
|
+
console.warn(
|
|
450
|
+
'Evolution completed without finding a valid best genome (no fitness improvements recorded).'
|
|
451
|
+
);
|
|
452
|
+
} catch {}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Spawn a new genome derived from a single parent while preserving Neat bookkeeping.
|
|
457
|
+
*
|
|
458
|
+
* This helper performs a canonical "clone + slight mutation" workflow while
|
|
459
|
+
* keeping `Neat`'s internal invariants intact. It is intended for callers that
|
|
460
|
+
* want a child genome derived from a single parent but do not want to perform the
|
|
461
|
+
* bookkeeping and registration steps manually. The function deliberately does NOT
|
|
462
|
+
* add the returned child to `this.population` so callers are free to inspect or
|
|
463
|
+
* further modify the child and then register it via `addGenome()` (or push it
|
|
464
|
+
* directly if they understand the consequences).
|
|
465
|
+
*
|
|
466
|
+
* Behavior summary:
|
|
467
|
+
* - Clone the provided `parent` (`parent.clone()` when available, else JSON round-trip).
|
|
468
|
+
* - Clear fitness/score on the child and assign a fresh unique `_id`.
|
|
469
|
+
* - If lineage tracking is enabled, set `(child as any)._parents = [parent._id]`
|
|
470
|
+
* and `(child as any)._depth = (parent._depth ?? 0) + 1`.
|
|
471
|
+
* - Enforce structural invariants by calling `ensureMinHiddenNodes(child)` and
|
|
472
|
+
* `ensureNoDeadEnds(child)` so the child is valid for subsequent mutation/evaluation.
|
|
473
|
+
* - Apply `mutateCount` mutations selected via `selectMutationMethod` and driven by
|
|
474
|
+
* the instance RNG (`_getRNG()`); mutation exceptions are caught and ignored to
|
|
475
|
+
* preserve best-effort behavior during population seeding/expansion.
|
|
476
|
+
* - Invalidate per-genome caches with `_invalidateGenomeCaches(child)` before return.
|
|
477
|
+
*
|
|
478
|
+
* Important: the returned child is not registered in `Neat.population` — call
|
|
479
|
+
* `addGenome(child, [parentId])` to insert it and keep telemetry/lineage consistent.
|
|
480
|
+
*
|
|
481
|
+
* @param parent - Source genome to derive from. Must be a `Network` instance.
|
|
482
|
+
* @param mutateCount - Number of mutation operations to apply to the spawned child (default: 1).
|
|
483
|
+
* @returns A new `Network` instance derived from `parent`. The child is unregistered.
|
|
484
|
+
*/
|
|
485
|
+
spawnFromParent(parent: Network, mutateCount: number = 1): Network {
|
|
486
|
+
return spawnFromParent.call(this as any, parent, mutateCount);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Register an externally-created genome into the `Neat` population.
|
|
491
|
+
*
|
|
492
|
+
* Use this method when code constructs or mutates a `Network` outside of the
|
|
493
|
+
* usual reproduction pipeline and needs to insert it into `neat.population`
|
|
494
|
+
* while preserving lineage, id assignment, and structural invariants. The
|
|
495
|
+
* method performs best-effort safety actions and falls back to pushing the
|
|
496
|
+
* genome even if invariant enforcement throws, which mirrors the forgiving
|
|
497
|
+
* behavior used in dynamic population expansion.
|
|
498
|
+
*
|
|
499
|
+
* Behavior summary:
|
|
500
|
+
* - Clears the genome's `score` and assigns `_id` using Neat's counter.
|
|
501
|
+
* - When lineage is enabled, attaches the provided `parents` array (copied)
|
|
502
|
+
* and estimates `_depth` as `max(parent._depth) + 1` when parent ids are
|
|
503
|
+
* resolvable from the current population.
|
|
504
|
+
* - Enforces structural invariants (`ensureMinHiddenNodes` and
|
|
505
|
+
* `ensureNoDeadEnds`) and invalidates caches via
|
|
506
|
+
* `_invalidateGenomeCaches(genome)`.
|
|
507
|
+
* - Pushes the genome into `this.population`.
|
|
508
|
+
*
|
|
509
|
+
* Note: Because depth estimation requires parent objects to be discoverable
|
|
510
|
+
* in `this.population`, callers that generate intermediate parent genomes
|
|
511
|
+
* should register them via `addGenome` before relying on automatic depth
|
|
512
|
+
* estimation for their children.
|
|
513
|
+
*
|
|
514
|
+
* @param genome - The external `Network` to add.
|
|
515
|
+
* @param parents - Optional array of parent ids to record on the genome.
|
|
516
|
+
*/
|
|
517
|
+
addGenome(genome: Network, parents?: number[]): void {
|
|
518
|
+
return addGenome.call(this as any, genome as any, parents as any);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Selects a mutation method for a given genome based on constraints.
|
|
523
|
+
* Ensures that the mutation respects the maximum nodes, connections, and gates.
|
|
524
|
+
* @param genome - The genome to mutate.
|
|
525
|
+
* @returns The selected mutation method or null if no valid method is available.
|
|
526
|
+
*/
|
|
527
|
+
selectMutationMethod(genome: Network, rawReturnForTest: boolean = true): any {
|
|
528
|
+
try {
|
|
529
|
+
return selectMutationMethod.call(this as any, genome, rawReturnForTest);
|
|
530
|
+
} catch {
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/** Delegate ensureNoDeadEnds to mutation module (added for backward compat). */
|
|
536
|
+
ensureNoDeadEnds(network: Network) {
|
|
537
|
+
try {
|
|
538
|
+
return ensureNoDeadEnds.call(this as any, network);
|
|
539
|
+
} catch {
|
|
540
|
+
return; // silent fail (used defensively in seeding paths)
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/** Minimum hidden size considering explicit minHidden or multiplier policy. */
|
|
545
|
+
getMinimumHiddenSize(multiplierOverride?: number): number {
|
|
546
|
+
const o: any = this.options;
|
|
547
|
+
if (typeof o.minHidden === 'number') return o.minHidden;
|
|
548
|
+
const mult = multiplierOverride ?? o.minHiddenMultiplier;
|
|
549
|
+
if (typeof mult === 'number' && isFinite(mult)) {
|
|
550
|
+
return Math.max(0, Math.round(mult * (this.input + this.output)));
|
|
551
|
+
}
|
|
552
|
+
return 0;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/** Produce `count` deterministic random samples using instance RNG. */
|
|
556
|
+
sampleRandom(count: number): number[] {
|
|
557
|
+
const rng = this._getRNG();
|
|
558
|
+
const arr: number[] = [];
|
|
559
|
+
for (let i = 0; i < count; i++) arr.push(rng());
|
|
560
|
+
return arr;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/** Internal: return cached objective descriptors, building if stale. */
|
|
564
|
+
private _getObjectives(): ObjectiveDescriptor[] {
|
|
565
|
+
return _getObjectives.call(this as any) as ObjectiveDescriptor[];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/** Public helper returning just the objective keys (tests rely on). */
|
|
569
|
+
getObjectiveKeys(): string[] {
|
|
570
|
+
// Map objective descriptors to their key strings
|
|
571
|
+
return (this._getObjectives() as ObjectiveDescriptor[]).map(
|
|
572
|
+
(obj) => obj.key
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/** Invalidate per-genome caches (compatibility distance, forward pass, etc.). */
|
|
577
|
+
private _invalidateGenomeCaches(genome: any) {
|
|
578
|
+
if (!genome || typeof genome !== 'object') return;
|
|
579
|
+
delete genome._compatCache;
|
|
580
|
+
// Network forward cache fields (best-effort, ignore if absent)
|
|
581
|
+
delete genome._outputCache;
|
|
582
|
+
delete genome._traceCache;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/** Compute and cache diversity statistics used by telemetry & tests. */
|
|
586
|
+
private _computeDiversityStats() {
|
|
587
|
+
this._diversityStats = computeDiversityStats(this.population, this);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Removed thin wrappers _structuralEntropy and _fastNonDominated; modules used directly where needed.
|
|
591
|
+
/** Compatibility wrapper retained for tests that reference (neat as any)._structuralEntropy */
|
|
592
|
+
private _structuralEntropy(genome: Network): number {
|
|
593
|
+
return structuralEntropy(genome);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Applies mutations to the population based on the mutation rate and amount.
|
|
598
|
+
* Each genome is mutated using the selected mutation methods.
|
|
599
|
+
* Slightly increases the chance of ADD_CONN mutation for more connectivity.
|
|
600
|
+
*/
|
|
601
|
+
mutate(): void {
|
|
602
|
+
return mutate.call(this as any);
|
|
603
|
+
}
|
|
604
|
+
// Perform ADD_NODE honoring global innovation reuse mapping
|
|
605
|
+
private _mutateAddNodeReuse(genome: Network) {
|
|
606
|
+
return mutateAddNodeReuse.call(this as any, genome);
|
|
607
|
+
}
|
|
608
|
+
private _mutateAddConnReuse(genome: Network) {
|
|
609
|
+
return mutateAddConnReuse.call(this as any, genome);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// --- Speciation helpers (properly scoped) ---
|
|
613
|
+
private _fallbackInnov(conn: any): number {
|
|
614
|
+
return _fallbackInnov.call(this as any, conn);
|
|
615
|
+
}
|
|
616
|
+
_compatibilityDistance(netA: Network, netB: Network): number {
|
|
617
|
+
return _compatibilityDistance.call(this as any, netA, netB);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Assign genomes into species based on compatibility distance and maintain species structures.
|
|
621
|
+
* This function creates new species for unassigned genomes and prunes empty species.
|
|
622
|
+
* It also records species-level history used for telemetry and adaptive controllers.
|
|
623
|
+
*/
|
|
624
|
+
private _speciate() {
|
|
625
|
+
return _speciate.call(this as any);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Apply fitness sharing within species. When `sharingSigma` > 0 this uses a kernel-based
|
|
629
|
+
* sharing; otherwise it falls back to classic per-species averaging. Sharing reduces
|
|
630
|
+
* effective fitness for similar genomes to promote diversity.
|
|
631
|
+
*/
|
|
632
|
+
private _applyFitnessSharing() {
|
|
633
|
+
return _applyFitnessSharing.call(this as any);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Sort members of a species in-place by descending score.
|
|
637
|
+
* @param sp - Species object with `members` array.
|
|
638
|
+
*/
|
|
639
|
+
private _sortSpeciesMembers(sp: { members: Network[] }) {
|
|
640
|
+
return _sortSpeciesMembers.call(this as any, sp);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Update species stagnation tracking and remove species that exceeded the allowed stagnation.
|
|
644
|
+
*/
|
|
645
|
+
private _updateSpeciesStagnation() {
|
|
646
|
+
return _updateSpeciesStagnation.call(this as any);
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Return a concise summary for each current species.
|
|
650
|
+
*
|
|
651
|
+
* Educational context: In NEAT, populations are partitioned into species based
|
|
652
|
+
* on genetic compatibility. Each species groups genomes that are similar so
|
|
653
|
+
* selection and reproduction can preserve diversity between groups. This
|
|
654
|
+
* accessor provides a lightweight view suitable for telemetry, visualization
|
|
655
|
+
* and teaching examples without exposing full genome objects.
|
|
656
|
+
*
|
|
657
|
+
* The returned array contains objects with these fields:
|
|
658
|
+
* - id: numeric species identifier
|
|
659
|
+
* - size: number of members currently assigned to the species
|
|
660
|
+
* - bestScore: the best observed fitness score for the species
|
|
661
|
+
* - lastImproved: generation index when the species last improved its best score
|
|
662
|
+
*
|
|
663
|
+
* Notes for learners:
|
|
664
|
+
* - Species sizes and lastImproved are typical signals used to detect
|
|
665
|
+
* stagnation and apply protective or penalizing measures.
|
|
666
|
+
* - This function intentionally avoids returning full member lists to
|
|
667
|
+
* prevent accidental mutation of internal state; use `getSpeciesHistory`
|
|
668
|
+
* for richer historical data.
|
|
669
|
+
*
|
|
670
|
+
* @returns An array of species summary objects.
|
|
671
|
+
*/
|
|
672
|
+
getSpeciesStats(): {
|
|
673
|
+
id: number;
|
|
674
|
+
size: number;
|
|
675
|
+
bestScore: number;
|
|
676
|
+
lastImproved: number;
|
|
677
|
+
}[] {
|
|
678
|
+
return getSpeciesStats.call(this as any);
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Returns the historical species statistics recorded each generation.
|
|
682
|
+
*
|
|
683
|
+
* Educational context: Species history captures per-generation snapshots
|
|
684
|
+
* of species-level metrics (size, best score, last improvement) and is
|
|
685
|
+
* useful for plotting trends, teaching about speciation dynamics, and
|
|
686
|
+
* driving adaptive controllers.
|
|
687
|
+
*
|
|
688
|
+
* The returned array contains entries with a `generation` index and a
|
|
689
|
+
* `stats` array containing per-species summaries recorded at that
|
|
690
|
+
* generation.
|
|
691
|
+
*
|
|
692
|
+
* @returns An array of generation-stamped species stat snapshots.
|
|
693
|
+
*/
|
|
694
|
+
getSpeciesHistory(): SpeciesHistoryEntry[] {
|
|
695
|
+
return getSpeciesHistory.call(this as any) as SpeciesHistoryEntry[];
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Returns the number of entries currently stored in the novelty archive.
|
|
699
|
+
*
|
|
700
|
+
* Educational context: The novelty archive stores representative behaviors
|
|
701
|
+
* used by behavior-based novelty search. Monitoring its size helps teach
|
|
702
|
+
* how behavioral diversity accumulates over time and can be used to
|
|
703
|
+
* throttle archive growth.
|
|
704
|
+
*
|
|
705
|
+
* @returns Number of archived behaviors.
|
|
706
|
+
*/
|
|
707
|
+
getNoveltyArchiveSize(): number {
|
|
708
|
+
return this._noveltyArchive ? this._noveltyArchive.length : 0;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Returns compact multi-objective metrics for each genome in the current
|
|
712
|
+
* population. The metrics include Pareto rank and crowding distance (if
|
|
713
|
+
* computed), along with simple size and score measures useful in
|
|
714
|
+
* instructional contexts.
|
|
715
|
+
*
|
|
716
|
+
* @returns Array of per-genome MO metric objects.
|
|
717
|
+
*/
|
|
718
|
+
getMultiObjectiveMetrics(): {
|
|
719
|
+
rank: number;
|
|
720
|
+
crowding: number;
|
|
721
|
+
score: number;
|
|
722
|
+
nodes: number;
|
|
723
|
+
connections: number;
|
|
724
|
+
}[] {
|
|
725
|
+
return this.population.map((genome) => ({
|
|
726
|
+
rank: (genome as any)._moRank ?? 0,
|
|
727
|
+
crowding: (genome as any)._moCrowd ?? 0,
|
|
728
|
+
score: genome.score || 0,
|
|
729
|
+
nodes: genome.nodes.length,
|
|
730
|
+
connections: genome.connections.length,
|
|
731
|
+
}));
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Returns a summary of mutation/operator statistics used by operator
|
|
735
|
+
* adaptation and bandit selection.
|
|
736
|
+
*
|
|
737
|
+
* Educational context: Operator statistics track how often mutation
|
|
738
|
+
* operators are attempted and how often they succeed. These counters are
|
|
739
|
+
* used by adaptation mechanisms to bias operator selection towards
|
|
740
|
+
* successful operators.
|
|
741
|
+
*
|
|
742
|
+
* @returns Array of { name, success, attempts } objects.
|
|
743
|
+
*/
|
|
744
|
+
getOperatorStats(): { name: string; success: number; attempts: number }[] {
|
|
745
|
+
return Array.from(this._operatorStats.entries()).map(
|
|
746
|
+
([operatorName, stats]) => ({
|
|
747
|
+
name: operatorName,
|
|
748
|
+
success: stats.success,
|
|
749
|
+
attempts: stats.attempts,
|
|
750
|
+
})
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Return the internal telemetry buffer.
|
|
755
|
+
*
|
|
756
|
+
* Telemetry entries are produced per-generation when telemetry is enabled
|
|
757
|
+
* and include diagnostic metrics (diversity, performance, lineage, etc.).
|
|
758
|
+
* This accessor returns the raw buffer for external inspection or export.
|
|
759
|
+
*
|
|
760
|
+
* @returns Array of telemetry snapshot objects.
|
|
761
|
+
*/
|
|
762
|
+
getTelemetry(): any[] {
|
|
763
|
+
return this._telemetry;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Export telemetry as JSON Lines (one JSON object per line).
|
|
767
|
+
*
|
|
768
|
+
* Useful for piping telemetry to external loggers or analysis tools.
|
|
769
|
+
*
|
|
770
|
+
* @returns A newline-separated string of JSON objects.
|
|
771
|
+
*/
|
|
772
|
+
exportTelemetryJSONL(): string {
|
|
773
|
+
return exportTelemetryJSONL.call(this as any);
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Export recent telemetry entries as CSV.
|
|
777
|
+
*
|
|
778
|
+
* The exporter attempts to flatten commonly-used nested fields (complexity,
|
|
779
|
+
* perf, lineage) into columns. This is a best-effort exporter intended for
|
|
780
|
+
* human inspection and simple ingestion.
|
|
781
|
+
*
|
|
782
|
+
* @param maxEntries Maximum number of recent telemetry entries to include.
|
|
783
|
+
* @returns CSV string (may be empty when no telemetry present).
|
|
784
|
+
*/
|
|
785
|
+
exportTelemetryCSV(maxEntries = 500): string {
|
|
786
|
+
return exportTelemetryCSV.call(this as any, maxEntries);
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Export telemetry as CSV with flattened columns for common nested fields.
|
|
790
|
+
*/
|
|
791
|
+
clearTelemetry() {
|
|
792
|
+
this._telemetry = [];
|
|
793
|
+
}
|
|
794
|
+
/** Clear all collected telemetry entries. */
|
|
795
|
+
getObjectives(): { key: string; direction: 'max' | 'min' }[] {
|
|
796
|
+
return (this._getObjectives() as ObjectiveDescriptor[]).map((o) => ({
|
|
797
|
+
key: o.key,
|
|
798
|
+
direction: o.direction,
|
|
799
|
+
}));
|
|
800
|
+
}
|
|
801
|
+
getObjectiveEvents(): { gen: number; type: 'add' | 'remove'; key: string }[] {
|
|
802
|
+
return this._objectiveEvents.slice();
|
|
803
|
+
}
|
|
804
|
+
/** Get recent objective add/remove events. */
|
|
805
|
+
getLineageSnapshot(limit = 20): { id: number; parents: number[] }[] {
|
|
806
|
+
return this.population.slice(0, limit).map((genome) => ({
|
|
807
|
+
id: (genome as any)._id ?? -1,
|
|
808
|
+
parents: Array.isArray((genome as any)._parents)
|
|
809
|
+
? (genome as any)._parents.slice()
|
|
810
|
+
: [],
|
|
811
|
+
}));
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Return an array of {id, parents} for the first `limit` genomes in population.
|
|
815
|
+
*/
|
|
816
|
+
exportSpeciesHistoryCSV(maxEntries = 200): string {
|
|
817
|
+
return exportSpeciesHistoryCSV.call(this as any, maxEntries);
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Export species history as CSV.
|
|
821
|
+
*
|
|
822
|
+
* Produces rows for each recorded per-species stat entry within the
|
|
823
|
+
* specified window. Useful for quick inspection or spreadsheet analysis.
|
|
824
|
+
*
|
|
825
|
+
* @param maxEntries Maximum history entries to include (default: 200).
|
|
826
|
+
* @returns CSV string (may be empty).
|
|
827
|
+
*/
|
|
828
|
+
getParetoFronts(maxFronts = 3): Network[][] {
|
|
829
|
+
if (!this.options.multiObjective?.enabled) return [[...this.population]];
|
|
830
|
+
// reconstruct fronts from stored ranks (avoids re-sorting again)
|
|
831
|
+
const fronts: Network[][] = [];
|
|
832
|
+
for (let frontIdx = 0; frontIdx < maxFronts; frontIdx++) {
|
|
833
|
+
const front = this.population.filter(
|
|
834
|
+
(genome) => ((genome as any)._moRank ?? 0) === frontIdx
|
|
835
|
+
);
|
|
836
|
+
if (!front.length) break;
|
|
837
|
+
fronts.push(front);
|
|
838
|
+
}
|
|
839
|
+
return fronts;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Return the latest cached diversity statistics.
|
|
843
|
+
*
|
|
844
|
+
* Educational context: diversity metrics summarize how genetically and
|
|
845
|
+
* behaviorally spread the population is. They can include lineage depth,
|
|
846
|
+
* pairwise genetic distances, and other aggregated measures used by
|
|
847
|
+
* adaptive controllers, novelty search, and telemetry. This accessor returns
|
|
848
|
+
* whatever precomputed diversity object the Neat instance holds (may be
|
|
849
|
+
* undefined if not computed for the current generation).
|
|
850
|
+
*
|
|
851
|
+
* @returns Arbitrary diversity summary object or undefined.
|
|
852
|
+
*/
|
|
853
|
+
getDiversityStats() {
|
|
854
|
+
return this._diversityStats;
|
|
855
|
+
}
|
|
856
|
+
registerObjective(
|
|
857
|
+
key: string,
|
|
858
|
+
direction: 'min' | 'max',
|
|
859
|
+
// Widen accessor parameter type to match underlying registerObjective expectation (GenomeLike)
|
|
860
|
+
accessor: (g: any) => number
|
|
861
|
+
) {
|
|
862
|
+
return registerObjective.call(this as any, key, direction, accessor);
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Register a custom objective for multi-objective optimization.
|
|
866
|
+
*
|
|
867
|
+
* Educational context: multi-objective optimization lets you optimize for
|
|
868
|
+
* multiple, potentially conflicting goals (e.g., maximize fitness while
|
|
869
|
+
* minimizing complexity). Each objective is identified by a unique key and
|
|
870
|
+
* an accessor function mapping a genome to a numeric score. Registering an
|
|
871
|
+
* objective makes it visible to the internal MO pipeline and clears any
|
|
872
|
+
* cached objective list so changes take effect immediately.
|
|
873
|
+
*
|
|
874
|
+
* @param key Unique objective key.
|
|
875
|
+
* @param direction 'min' or 'max' indicating optimization direction.
|
|
876
|
+
* @param accessor Function mapping a genome to a numeric objective value.
|
|
877
|
+
*/
|
|
878
|
+
/**
|
|
879
|
+
* Clear all registered multi-objective objectives.
|
|
880
|
+
*
|
|
881
|
+
* Removes any objectives configured for multi-objective optimization and
|
|
882
|
+
* clears internal caches. Useful for tests or when reconfiguring the MO
|
|
883
|
+
* setup at runtime.
|
|
884
|
+
*/
|
|
885
|
+
clearObjectives() {
|
|
886
|
+
return clearObjectives.call(this as any);
|
|
887
|
+
}
|
|
888
|
+
// Advanced archives & performance accessors
|
|
889
|
+
/**
|
|
890
|
+
* Get recent Pareto archive entries (meta information about archived fronts).
|
|
891
|
+
*
|
|
892
|
+
* Educational context: when performing multi-objective search we may store
|
|
893
|
+
* representative Pareto-front snapshots over time. This accessor returns the
|
|
894
|
+
* most recent archive entries up to the provided limit.
|
|
895
|
+
*
|
|
896
|
+
* @param maxEntries Maximum number of entries to return (default: 50).
|
|
897
|
+
* @returns Array of archived Pareto metadata entries.
|
|
898
|
+
*/
|
|
899
|
+
getParetoArchive(maxEntries = 50) {
|
|
900
|
+
return this._paretoArchive.slice(-maxEntries);
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Export Pareto front archive as JSON Lines for external analysis.
|
|
904
|
+
*
|
|
905
|
+
* Each line is a JSON object representing one archived Pareto snapshot.
|
|
906
|
+
*
|
|
907
|
+
* @param maxEntries Maximum number of entries to include (default: 100).
|
|
908
|
+
* @returns Newline-separated JSON objects.
|
|
909
|
+
*/
|
|
910
|
+
exportParetoFrontJSONL(maxEntries = 100): string {
|
|
911
|
+
const slice = this._paretoObjectivesArchive.slice(-maxEntries);
|
|
912
|
+
return slice.map((e) => JSON.stringify(e)).join('\n');
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Return recent performance statistics (durations in milliseconds) for the
|
|
916
|
+
* most recent evaluation and evolve operations.
|
|
917
|
+
*
|
|
918
|
+
* Provides wall-clock timing useful for profiling and teaching how runtime
|
|
919
|
+
* varies with network complexity or population settings.
|
|
920
|
+
*
|
|
921
|
+
* @returns Object with { lastEvalMs, lastEvolveMs }.
|
|
922
|
+
*/
|
|
923
|
+
getPerformanceStats() {
|
|
924
|
+
return {
|
|
925
|
+
lastEvalMs: this._lastEvalDuration,
|
|
926
|
+
lastEvolveMs: this._lastEvolveDuration,
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
// Utility exports / maintenance
|
|
930
|
+
/**
|
|
931
|
+
* Export species history as JSON Lines for storage and analysis.
|
|
932
|
+
*
|
|
933
|
+
* Each line is a JSON object containing a generation index and per-species
|
|
934
|
+
* stats recorded at that generation. Useful for long-term tracking.
|
|
935
|
+
*
|
|
936
|
+
* @param maxEntries Maximum history entries to include (default: 200).
|
|
937
|
+
* @returns Newline-separated JSON objects.
|
|
938
|
+
*/
|
|
939
|
+
exportSpeciesHistoryJSONL(maxEntries = 200): string {
|
|
940
|
+
const slice = this._speciesHistory.slice(-maxEntries);
|
|
941
|
+
return slice.map((e) => JSON.stringify(e)).join('\n');
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Reset the novelty archive (clear entries).
|
|
945
|
+
*
|
|
946
|
+
* The novelty archive is used to keep representative behaviors for novelty
|
|
947
|
+
* search. Clearing it removes stored behaviors.
|
|
948
|
+
*/
|
|
949
|
+
resetNoveltyArchive() {
|
|
950
|
+
this._noveltyArchive = [];
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Clear the Pareto archive.
|
|
954
|
+
*
|
|
955
|
+
* Removes any stored Pareto-front snapshots retained by the algorithm.
|
|
956
|
+
*/
|
|
957
|
+
clearParetoArchive() {
|
|
958
|
+
this._paretoArchive = [];
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Sorts the population in descending order of fitness scores.
|
|
963
|
+
* Ensures that the fittest genomes are at the start of the population array.
|
|
964
|
+
*/
|
|
965
|
+
sort(): void {
|
|
966
|
+
return sort.call(this as any);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* Selects a parent genome for breeding based on the selection method.
|
|
971
|
+
* Supports multiple selection strategies, including POWER, FITNESS_PROPORTIONATE, and TOURNAMENT.
|
|
972
|
+
* @returns The selected parent genome.
|
|
973
|
+
* @throws Error if tournament size exceeds population size.
|
|
974
|
+
*/
|
|
975
|
+
getParent(): Network {
|
|
976
|
+
return getParent.call(this as any);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Retrieves the fittest genome from the population.
|
|
981
|
+
* Ensures that the population is evaluated and sorted before returning the result.
|
|
982
|
+
* @returns The fittest genome in the population.
|
|
983
|
+
*/
|
|
984
|
+
getFittest(): Network {
|
|
985
|
+
return getFittest.call(this as any);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Calculates the average fitness score of the population.
|
|
990
|
+
* Ensures that the population is evaluated before calculating the average.
|
|
991
|
+
* @returns The average fitness score of the population.
|
|
992
|
+
*/
|
|
993
|
+
getAverage(): number {
|
|
994
|
+
return getAverage.call(this as any);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Exports the current population as an array of JSON objects.
|
|
999
|
+
* Useful for saving the state of the population for later use.
|
|
1000
|
+
* @returns An array of JSON representations of the population.
|
|
1001
|
+
*/
|
|
1002
|
+
export(): any[] {
|
|
1003
|
+
return exportPopulation.call(this as any);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Imports a population from an array of JSON objects.
|
|
1008
|
+
* Replaces the current population with the imported one.
|
|
1009
|
+
* @param json - An array of JSON objects representing the population.
|
|
1010
|
+
*/
|
|
1011
|
+
import(json: any[]): void {
|
|
1012
|
+
return importPopulation.call(this as any, json as any);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Convenience: export full evolutionary state (meta + population genomes).
|
|
1017
|
+
* Combines innovation registries and serialized genomes for easy persistence.
|
|
1018
|
+
*/
|
|
1019
|
+
exportState(): any {
|
|
1020
|
+
return exportState.call(this as any);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Convenience: restore full evolutionary state previously produced by exportState().
|
|
1025
|
+
* @param bundle Object with shape { neat, population }
|
|
1026
|
+
* @param fitness Fitness function to attach
|
|
1027
|
+
*/
|
|
1028
|
+
static importState(bundle: any, fitness: (n: Network) => number): Neat {
|
|
1029
|
+
return importStateImpl.call(Neat as any, bundle, fitness) as Neat;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Import a previously exported state bundle and rehydrate a Neat instance.
|
|
1033
|
+
*/
|
|
1034
|
+
// Serialize NEAT meta (without population) for persistence of innovation history
|
|
1035
|
+
toJSON(): any {
|
|
1036
|
+
return toJSONImpl.call(this as any);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
static fromJSON(json: any, fitness: (n: Network) => number): Neat {
|
|
1040
|
+
return fromJSONImpl.call(Neat as any, json, fitness) as Neat;
|
|
1041
|
+
}
|
|
1042
|
+
}
|