@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
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import type Network from '../network';
|
|
2
|
+
import Node from '../node';
|
|
3
|
+
import Connection from '../connection';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Genetic operator: NEAT‑style crossover (legacy merge operator removed).
|
|
7
|
+
*
|
|
8
|
+
* This module now focuses solely on producing recombinant offspring via {@link crossOver}.
|
|
9
|
+
* The previous experimental Network.merge has been removed to reduce maintenance surface area
|
|
10
|
+
* and avoid implying a misleading “sequential composition” guarantee.
|
|
11
|
+
*
|
|
12
|
+
* @module network.genetic
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* NEAT-inspired crossover between two parent networks producing a single offspring.
|
|
17
|
+
*
|
|
18
|
+
* Simplifications relative to canonical NEAT:
|
|
19
|
+
* - Innovation ID is synthesized from (from.index, to.index) via Connection.innovationID instead of
|
|
20
|
+
* maintaining a global innovation number per mutation event.
|
|
21
|
+
* - Node alignment relies on current index ordering. This is weaker than historical innovation
|
|
22
|
+
* tracking, but adequate for many lightweight evolutionary experiments.
|
|
23
|
+
*
|
|
24
|
+
* High-level algorithm:
|
|
25
|
+
* 1. Validate that parents have identical I/O dimensionality (required for compatibility).
|
|
26
|
+
* 2. Decide offspring node array length:
|
|
27
|
+
* - If equal flag set or scores tied: random length in [minNodes, maxNodes].
|
|
28
|
+
* - Else: length of fitter parent.
|
|
29
|
+
* 3. For each index up to chosen size, pick a node gene from parents per rules:
|
|
30
|
+
* - Input indices: always from parent1 (assumes identical input interface).
|
|
31
|
+
* - Output indices (aligned from end): randomly choose if both present else take existing.
|
|
32
|
+
* - Hidden indices: if both present pick randomly; else inherit from fitter (or either if equal).
|
|
33
|
+
* 4. Reindex offspring nodes.
|
|
34
|
+
* 5. Collect connections (standard + self) from each parent into maps keyed by innovationID capturing
|
|
35
|
+
* weight, enabled flag, and gater index.
|
|
36
|
+
* 6. For overlapping genes (present in both), randomly choose one; if either disabled apply optional
|
|
37
|
+
* re-enable probability (reenableProb) to possibly re-activate.
|
|
38
|
+
* 7. For disjoint/excess genes, inherit only from fitter parent (or both if equal flag set / scores tied).
|
|
39
|
+
* 8. Materialize selected connection genes if their endpoints both exist in offspring; set weight & enabled state.
|
|
40
|
+
* 9. Reattach gating if gater node exists in offspring.
|
|
41
|
+
*
|
|
42
|
+
* Enabled reactivation probability:
|
|
43
|
+
* - Parents may carry disabled connections; offspring may re-enable them with a probability derived
|
|
44
|
+
* from parent-specific _reenableProb (or default 0.25). This allows dormant structures to resurface.
|
|
45
|
+
*
|
|
46
|
+
* @param network1 - First parent (ties resolved in its favor when scores equal and equal=false for some cases).
|
|
47
|
+
* @param network2 - Second parent.
|
|
48
|
+
* @param equal - Force symmetric treatment regardless of fitness (true => node count random between sizes and both parents equally contribute disjoint genes).
|
|
49
|
+
* @returns Offspring network instance.
|
|
50
|
+
* @throws If input/output sizes differ.
|
|
51
|
+
*/
|
|
52
|
+
export function crossOver(
|
|
53
|
+
network1: Network,
|
|
54
|
+
network2: Network,
|
|
55
|
+
equal = false
|
|
56
|
+
): Network {
|
|
57
|
+
if (network1.input !== network2.input || network1.output !== network2.output)
|
|
58
|
+
throw new Error(
|
|
59
|
+
'Parent networks must have the same input and output sizes for crossover.'
|
|
60
|
+
);
|
|
61
|
+
/** Offspring scaffold produced by recombination of parent networks. */
|
|
62
|
+
const offspring = new (require('../network').default)(
|
|
63
|
+
network1.input,
|
|
64
|
+
network1.output
|
|
65
|
+
) as Network;
|
|
66
|
+
/** Mutable list of standard (non self) connections assigned during reconstruction. */
|
|
67
|
+
(offspring as any).connections = [];
|
|
68
|
+
/** Ordered list of cloned node genes composing the offspring topology. */
|
|
69
|
+
(offspring as any).nodes = [];
|
|
70
|
+
/** Self–connections (loops) for offspring, rebuilt during connection materialization. */
|
|
71
|
+
(offspring as any).selfconns = [];
|
|
72
|
+
/** Collection of gated connections after inheritance. */
|
|
73
|
+
(offspring as any).gates = [];
|
|
74
|
+
/** Fitness (score) of parent 1 used for dominance decisions. */
|
|
75
|
+
const score1 = (network1 as any).score || 0;
|
|
76
|
+
/** Fitness (score) of parent 2 used for dominance decisions. */
|
|
77
|
+
const score2 = (network2 as any).score || 0;
|
|
78
|
+
/** Number of nodes in parent 1 (used to bound index-based selection). */
|
|
79
|
+
const n1Size = (network1 as any).nodes.length;
|
|
80
|
+
/** Number of nodes in parent 2 (used to bound index-based selection). */
|
|
81
|
+
const n2Size = (network2 as any).nodes.length;
|
|
82
|
+
// Decide offspring size based on equality / fitness.
|
|
83
|
+
/** Final number of node slots (including I/O) the offspring will contain. */
|
|
84
|
+
let size: number;
|
|
85
|
+
if (equal || score1 === score2) {
|
|
86
|
+
/** Upper bound on possible offspring node count when parents tied / equal mode. */
|
|
87
|
+
const max = Math.max(n1Size, n2Size);
|
|
88
|
+
/** Lower bound on possible offspring node count when parents tied / equal mode. */
|
|
89
|
+
const min = Math.min(n1Size, n2Size);
|
|
90
|
+
/** Random length chosen uniformly in [min, max]. */
|
|
91
|
+
size = Math.floor(Math.random() * (max - min + 1) + min);
|
|
92
|
+
} else size = score1 > score2 ? n1Size : n2Size;
|
|
93
|
+
/** Number of output nodes (shared by both parents). */
|
|
94
|
+
const outputSize = network1.output;
|
|
95
|
+
// Assign indices for deterministic innovation mapping later.
|
|
96
|
+
(network1 as any).nodes.forEach((n: any, i: number) => (n.index = i));
|
|
97
|
+
(network2 as any).nodes.forEach((n: any, i: number) => (n.index = i));
|
|
98
|
+
// Node gene selection loop.
|
|
99
|
+
for (let i = 0; i < size; i++) {
|
|
100
|
+
/** Chosen parent node gene for this index (if any). */
|
|
101
|
+
let chosen: any;
|
|
102
|
+
/** Parent 1 node gene at current index (undefined if beyond parent size). */
|
|
103
|
+
const node1 = i < n1Size ? (network1 as any).nodes[i] : undefined;
|
|
104
|
+
/** Parent 2 node gene at current index (undefined if beyond parent size). */
|
|
105
|
+
const node2 = i < n2Size ? (network2 as any).nodes[i] : undefined;
|
|
106
|
+
if (i < network1.input) chosen = node1;
|
|
107
|
+
// Always preserve consistent input interface.
|
|
108
|
+
else if (i >= size - outputSize) {
|
|
109
|
+
// Output region aligned from tail.
|
|
110
|
+
/** Index of candidate output node in parent 1 derived from tail alignment. */
|
|
111
|
+
const o1 = n1Size - (size - i);
|
|
112
|
+
/** Index of candidate output node in parent 2 derived from tail alignment. */
|
|
113
|
+
const o2 = n2Size - (size - i);
|
|
114
|
+
/** Parent 1 output node at aligned slot (if valid). */
|
|
115
|
+
const n1o =
|
|
116
|
+
o1 >= network1.input && o1 < n1Size
|
|
117
|
+
? (network1 as any).nodes[o1]
|
|
118
|
+
: undefined;
|
|
119
|
+
/** Parent 2 output node at aligned slot (if valid). */
|
|
120
|
+
const n2o =
|
|
121
|
+
o2 >= network2.input && o2 < n2Size
|
|
122
|
+
? (network2 as any).nodes[o2]
|
|
123
|
+
: undefined;
|
|
124
|
+
if (n1o && n2o)
|
|
125
|
+
chosen = ((network1 as any)._rand || Math.random)() >= 0.5 ? n1o : n2o;
|
|
126
|
+
else chosen = n1o || n2o;
|
|
127
|
+
} else {
|
|
128
|
+
// Hidden region.
|
|
129
|
+
if (node1 && node2)
|
|
130
|
+
chosen =
|
|
131
|
+
((network1 as any)._rand || Math.random)() >= 0.5 ? node1 : node2;
|
|
132
|
+
else if (node1 && (score1 >= score2 || equal)) chosen = node1;
|
|
133
|
+
else if (node2 && (score2 >= score1 || equal)) chosen = node2;
|
|
134
|
+
}
|
|
135
|
+
if (chosen) {
|
|
136
|
+
// Clone structural gene (bias + activation function / squash) but do not copy connections yet.
|
|
137
|
+
const nn: any = new Node(chosen.type);
|
|
138
|
+
nn.bias = chosen.bias;
|
|
139
|
+
nn.squash = chosen.squash;
|
|
140
|
+
(offspring as any).nodes.push(nn);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Reassign indices after constructing node list.
|
|
144
|
+
(offspring as any).nodes.forEach((n: any, i: number) => (n.index = i));
|
|
145
|
+
// Gather connection genes from both parents (including self connections) keyed by innovation id.
|
|
146
|
+
/** Map from innovation ID -> connection gene extracted from parent 1 (includes self connections). */
|
|
147
|
+
const n1conns: Record<string, any> = {};
|
|
148
|
+
/** Map from innovation ID -> connection gene extracted from parent 2 (includes self connections). */
|
|
149
|
+
const n2conns: Record<string, any> = {};
|
|
150
|
+
(network1 as any).connections
|
|
151
|
+
.concat((network1 as any).selfconns)
|
|
152
|
+
.forEach((c: any) => {
|
|
153
|
+
if (typeof c.from.index === 'number' && typeof c.to.index === 'number')
|
|
154
|
+
n1conns[Connection.innovationID(c.from.index, c.to.index)] = {
|
|
155
|
+
weight: c.weight,
|
|
156
|
+
from: c.from.index,
|
|
157
|
+
to: c.to.index,
|
|
158
|
+
gater: c.gater ? c.gater.index : -1,
|
|
159
|
+
enabled: (c as any).enabled !== false,
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
(network2 as any).connections
|
|
163
|
+
.concat((network2 as any).selfconns)
|
|
164
|
+
.forEach((c: any) => {
|
|
165
|
+
if (typeof c.from.index === 'number' && typeof c.to.index === 'number')
|
|
166
|
+
n2conns[Connection.innovationID(c.from.index, c.to.index)] = {
|
|
167
|
+
weight: c.weight,
|
|
168
|
+
from: c.from.index,
|
|
169
|
+
to: c.to.index,
|
|
170
|
+
gater: c.gater ? c.gater.index : -1,
|
|
171
|
+
enabled: (c as any).enabled !== false,
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
// Select connection genes: iterate parent1's map, handle overlaps, then optionally add remaining parent2 genes.
|
|
175
|
+
/** Accumulated list of chosen connection gene descriptors to materialize in offspring. */
|
|
176
|
+
const chosenConns: any[] = [];
|
|
177
|
+
/** Array of innovation IDs originating from parent 1 (iteration order). */
|
|
178
|
+
const keys1 = Object.keys(n1conns);
|
|
179
|
+
keys1.forEach((k) => {
|
|
180
|
+
/** Connection gene from parent 1 under current innovation ID. */
|
|
181
|
+
const c1 = n1conns[k];
|
|
182
|
+
if (n2conns[k]) {
|
|
183
|
+
// Matching gene.
|
|
184
|
+
/** Corresponding connection gene from parent 2 for matching innovation ID. */
|
|
185
|
+
const c2 = n2conns[k];
|
|
186
|
+
/** Selected gene (either c1 or c2) retained in offspring. */
|
|
187
|
+
const pick = ((network1 as any)._rand || Math.random)() >= 0.5 ? c1 : c2; // Randomly select weight / flags from one parent.
|
|
188
|
+
if (c1.enabled === false || c2.enabled === false) {
|
|
189
|
+
// If either disabled, chance to re-enable.
|
|
190
|
+
/** Probability threshold to re-enable a previously disabled matching connection. */
|
|
191
|
+
const rp =
|
|
192
|
+
(network1 as any)._reenableProb ??
|
|
193
|
+
(network2 as any)._reenableProb ??
|
|
194
|
+
0.25;
|
|
195
|
+
pick.enabled = Math.random() < rp;
|
|
196
|
+
}
|
|
197
|
+
chosenConns.push(pick);
|
|
198
|
+
delete n2conns[k]; // Remove from second map to mark consumed.
|
|
199
|
+
} else if (score1 >= score2 || equal) {
|
|
200
|
+
// Disjoint/excess gene from fitter or equal mode.
|
|
201
|
+
if (c1.enabled === false) {
|
|
202
|
+
/** Re-enable probability for a disabled disjoint/excess gene from parent1. */
|
|
203
|
+
const rp = (network1 as any)._reenableProb ?? 0.25;
|
|
204
|
+
c1.enabled = Math.random() < rp;
|
|
205
|
+
}
|
|
206
|
+
chosenConns.push(c1);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
// Remaining genes from parent2 if it is fitter (or equal mode).
|
|
210
|
+
if (score2 >= score1 || equal)
|
|
211
|
+
Object.keys(n2conns).forEach((k) => {
|
|
212
|
+
const d = n2conns[k];
|
|
213
|
+
if (d.enabled === false) {
|
|
214
|
+
/** Re-enable probability for parent2 disjoint/excess gene. */ const rp =
|
|
215
|
+
(network2 as any)._reenableProb ?? 0.25;
|
|
216
|
+
d.enabled = Math.random() < rp;
|
|
217
|
+
}
|
|
218
|
+
chosenConns.push(d);
|
|
219
|
+
});
|
|
220
|
+
/** Number of nodes copied into offspring; used to validate endpoint indices of connection genes. */
|
|
221
|
+
const nodeCount = (offspring as any).nodes.length;
|
|
222
|
+
// Materialize connection genes in offspring network (skip if endpoint nodes not present due to size truncation).
|
|
223
|
+
chosenConns.forEach((cd) => {
|
|
224
|
+
if (cd.from < nodeCount && cd.to < nodeCount) {
|
|
225
|
+
const from = (offspring as any).nodes[cd.from];
|
|
226
|
+
const to = (offspring as any).nodes[cd.to];
|
|
227
|
+
// Always enforce feed-forward ordering for crossover offspring: skip any backward or self-loop
|
|
228
|
+
// edges (self loops handled elsewhere) to satisfy structural invariants expected by tests.
|
|
229
|
+
if (cd.from >= cd.to) return; // skip backward / non feed-forward edge
|
|
230
|
+
if (!from.isProjectingTo(to)) {
|
|
231
|
+
/** Newly constructed connection edge within offspring (first element of connect array). */ const conn = (offspring as any).connect(
|
|
232
|
+
from,
|
|
233
|
+
to
|
|
234
|
+
)[0];
|
|
235
|
+
if (conn) {
|
|
236
|
+
conn.weight = cd.weight;
|
|
237
|
+
(conn as any).enabled = cd.enabled !== false;
|
|
238
|
+
if (cd.gater !== -1 && cd.gater < nodeCount)
|
|
239
|
+
(offspring as any).gate((offspring as any).nodes[cd.gater], conn);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
return offspring;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export default { crossOver };
|