@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,499 @@
|
|
|
1
|
+
import { methods } from '../../../src/neataptic';
|
|
2
|
+
import Network from '../../../src/architecture/network'; // Correct import for Network
|
|
3
|
+
import {
|
|
4
|
+
tiny,
|
|
5
|
+
spiralSmall,
|
|
6
|
+
spiral,
|
|
7
|
+
small,
|
|
8
|
+
medium,
|
|
9
|
+
medium2,
|
|
10
|
+
large,
|
|
11
|
+
minotaur,
|
|
12
|
+
} from './mazes';
|
|
13
|
+
import { colors } from './colors';
|
|
14
|
+
import { DashboardManager } from './dashboardManager';
|
|
15
|
+
import { TerminalUtility } from './terminalUtility';
|
|
16
|
+
import { IDashboardManager } from './interfaces';
|
|
17
|
+
import { EvolutionEngine } from './evolutionEngine';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Forces console output for this test by writing directly to stdout/stderr.
|
|
21
|
+
* This is necessary to ensure that log messages are visible during test execution,
|
|
22
|
+
* especially in environments where `console.log` might be mocked or redirected.
|
|
23
|
+
* @param args - The arguments to log, which will be joined into a single string.
|
|
24
|
+
*/
|
|
25
|
+
const forceLog = (...args: any[]): void => {
|
|
26
|
+
// Step 1: Join all arguments into a single string, separated by spaces, and add a newline character.
|
|
27
|
+
const message = args.join(' ') + '\n';
|
|
28
|
+
// Step 2: Write the formatted message directly to the standard output stream.
|
|
29
|
+
process.stdout.write(message);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Manages the dashboard for visualizing and logging the progress of the neuro-evolution process.
|
|
34
|
+
* It uses a terminal clearer to create animations and the `forceLog` function to output progress.
|
|
35
|
+
* @type {IDashboardManager}
|
|
36
|
+
*/
|
|
37
|
+
const dashboardManagerInstance: IDashboardManager = new DashboardManager(
|
|
38
|
+
TerminalUtility.createTerminalClearer(),
|
|
39
|
+
forceLog
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
jest.setTimeout(3600000);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Educational Note: This example demonstrates a powerful combination of neuro-evolution and supervised learning.
|
|
46
|
+
*
|
|
47
|
+
* 1. **Neuro-evolution (NEAT)**: The primary mechanism for discovering a solution. The network's topology (neurons and connections)
|
|
48
|
+
* and weights are evolved over generations to solve the maze. This is excellent for exploration and finding novel solutions
|
|
49
|
+
* in complex problem spaces where the ideal network structure is unknown.
|
|
50
|
+
*
|
|
51
|
+
* 2. **Input Encoding**: The agent's senses are encoded into the network's input neurons.
|
|
52
|
+
* - The first neuron receives a "compass" value (0=N, 0.25=E, 0.5=S, 0.75=W), indicating the general direction of the exit.
|
|
53
|
+
* - The next four neurons represent whether the path is open in each of the four cardinal directions.
|
|
54
|
+
* - The final neuron represents the change in progress towards the exit, rewarding forward movement.
|
|
55
|
+
*
|
|
56
|
+
* 3. **Output Interpretation**: The network has four output neurons, one for each cardinal direction. A `softmax` activation function
|
|
57
|
+
* is used on the output layer, which converts the raw outputs into a probability distribution. This means the outputs sum to 1,
|
|
58
|
+
* representing the network's confidence in each direction. The `argmax` of these probabilities is chosen as the agent's action.
|
|
59
|
+
*
|
|
60
|
+
* 4. **Supervised Refinement**: After a successful network (a "winner") is found via evolution, it is further refined using
|
|
61
|
+
* backpropagation (a supervised learning algorithm). This "fine-tuning" process uses a small, high-quality dataset of
|
|
62
|
+
* ideal inputs and outputs to solidify the learned rules (e.g., "if the path is only open to the North, move North").
|
|
63
|
+
*
|
|
64
|
+
* This synergistic approach allows the agent to first discover a working strategy through evolution and then perfect it through
|
|
65
|
+
* targeted training, leading to a more robust and efficient solution.
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Refines a winning neural network using backpropagation.
|
|
70
|
+
*
|
|
71
|
+
* This function takes a neural network that has successfully solved a maze (a "winner" from
|
|
72
|
+
* neuro-evolution) and further trains it using a supervised learning approach. The goal is to
|
|
73
|
+
* reinforce the associations between specific sensory inputs and the desired motor outputs (actions).
|
|
74
|
+
*
|
|
75
|
+
* The training dataset is predefined and maps idealized sensory states (representing clear environmental
|
|
76
|
+
* perceptions like "path open to the North") to the corresponding optimal action (e.g., "move North").
|
|
77
|
+
* This supervised refinement helps to solidify the network's decision-making logic, potentially
|
|
78
|
+
* improving its robustness and ability to generalize to new, unseen maze configurations or serve
|
|
79
|
+
* as a better starting point for evolving solutions to subsequent, more complex mazes.
|
|
80
|
+
*
|
|
81
|
+
* @param winner - The `Network` instance that previously succeeded in a task, to be refined.
|
|
82
|
+
* @returns A new `Network` instance that is a clone of the winner, further trained via backpropagation.
|
|
83
|
+
* @throws Error if no `winner` network is provided.
|
|
84
|
+
*/
|
|
85
|
+
function refineWinnerWithBackprop(winner?: Network) {
|
|
86
|
+
// Step 1: Validate that a winning network was provided.
|
|
87
|
+
if (!winner) {
|
|
88
|
+
throw new Error('No winning network provided for refinement.');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Step 2: Ensure all nodes in the network have a valid activation function.
|
|
92
|
+
// Backpropagation requires a differentiable activation function to work correctly.
|
|
93
|
+
// The logistic function is a common and suitable choice.
|
|
94
|
+
winner.nodes.forEach((n) => {
|
|
95
|
+
if (typeof n.squash !== 'function') {
|
|
96
|
+
// Assign the logistic activation function if one is not already set.
|
|
97
|
+
n.squash = methods.Activation.logistic;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @const {Array<Object>} trainingSet - A supervised training dataset for refining the network.
|
|
103
|
+
* This set contains idealized scenarios where there is only one clear path to the exit.
|
|
104
|
+
* The goal is to teach the network the fundamental rule: "If there is only one open direction that leads to progress, take it."
|
|
105
|
+
* Each entry has an `input` array and a corresponding `output` array.
|
|
106
|
+
*
|
|
107
|
+
* Inputs: `[compassScalar, openN, openE, openS, openW, progressDelta]`
|
|
108
|
+
* Outputs: An array of probabilities, with the target direction having a high value (0.92) and others a low value (0.02).
|
|
109
|
+
*/
|
|
110
|
+
const trainingSet: { input: number[]; output: number[] }[] = [];
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A helper function to create the target output array for the training set.
|
|
114
|
+
* @param {number} d - The index of the correct direction (0:N, 1:E, 2:S, 3:W).
|
|
115
|
+
* @returns {number[]} An array of probabilities for the output layer.
|
|
116
|
+
*/
|
|
117
|
+
const OUT = (d: number) => [0, 1, 2, 3].map((i) => (i === d ? 0.92 : 0.02));
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* A helper function to add a new entry to the training set.
|
|
121
|
+
* @param {number[]} i - The input vector.
|
|
122
|
+
* @param {number} d - The index of the target output direction.
|
|
123
|
+
*/
|
|
124
|
+
const add = (i: number[], d: number) =>
|
|
125
|
+
trainingSet.push({ input: i, output: OUT(d) });
|
|
126
|
+
|
|
127
|
+
// Step 3: Populate the training set with clear, unambiguous examples.
|
|
128
|
+
// For each cardinal direction, add cases where it's the only open path.
|
|
129
|
+
// Vary the `progressDelta` to ensure the network doesn't overfit to a specific progress value.
|
|
130
|
+
|
|
131
|
+
// North-only open scenarios
|
|
132
|
+
add([0, 1, 0, 0, 0, 0.85], 0);
|
|
133
|
+
add([0, 1, 0, 0, 0, 0.65], 0);
|
|
134
|
+
add([0, 1, 0, 0, 0, 0.55], 0); // Case with milder progress
|
|
135
|
+
|
|
136
|
+
// East-only open scenarios
|
|
137
|
+
add([0.25, 0, 1, 0, 0, 0.85], 1);
|
|
138
|
+
add([0.25, 0, 1, 0, 0, 0.65], 1);
|
|
139
|
+
add([0.25, 0, 1, 0, 0, 0.55], 1);
|
|
140
|
+
|
|
141
|
+
// South-only open scenarios
|
|
142
|
+
add([0.5, 0, 0, 1, 0, 0.85], 2);
|
|
143
|
+
add([0.5, 0, 0, 1, 0, 0.65], 2);
|
|
144
|
+
add([0.5, 0, 0, 1, 0, 0.55], 2);
|
|
145
|
+
|
|
146
|
+
// West-only open scenarios
|
|
147
|
+
add([0.75, 0, 0, 0, 1, 0.85], 3);
|
|
148
|
+
add([0.75, 0, 0, 0, 1, 0.65], 3);
|
|
149
|
+
add([0.75, 0, 0, 0, 1, 0.55], 3);
|
|
150
|
+
|
|
151
|
+
// Add examples with near-neutral progress to cover more situations.
|
|
152
|
+
add([0, 1, 0, 0, 0, 0.5], 0);
|
|
153
|
+
add([0.25, 0, 1, 0, 0, 0.5], 1);
|
|
154
|
+
add([0.5, 0, 0, 1, 0, 0.5], 2);
|
|
155
|
+
add([0.75, 0, 0, 0, 1, 0.5], 3);
|
|
156
|
+
|
|
157
|
+
// This line can be uncommented for deep debugging to analyze the network's state before training.
|
|
158
|
+
// analizeWinner(winner.clone(), trainingSet);
|
|
159
|
+
|
|
160
|
+
// Step 4: Train the network using the prepared training set.
|
|
161
|
+
// The `train` method uses backpropagation to adjust the network's weights
|
|
162
|
+
// to minimize the difference between its output and the target output.
|
|
163
|
+
winner.train(trainingSet, {
|
|
164
|
+
iterations: 220, // The maximum number of training iterations.
|
|
165
|
+
error: 0.005, // The target error threshold to stop training.
|
|
166
|
+
rate: 0.001, // The learning rate, controlling the step size of weight adjustments.
|
|
167
|
+
momentum: 0.1, // Momentum helps to avoid local minima and speed up convergence.
|
|
168
|
+
batchSize: 8, // The number of samples to process before updating weights.
|
|
169
|
+
cost: methods.Cost.softmaxCrossEntropy, // A cost function suitable for classification with softmax output.
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Step 5: Return a clone of the refined network.
|
|
173
|
+
// Cloning ensures that the original object from the evolution process is not mutated,
|
|
174
|
+
// which is good practice for maintaining a clear data flow.
|
|
175
|
+
return winner.clone();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Analyzes the weights and outputs of a network before and after training.
|
|
180
|
+
* This function is a powerful debugging tool to understand how a network's internal state
|
|
181
|
+
* changes during the supervised training process. It logs connection weights and eligibility
|
|
182
|
+
* traces at each iteration, providing a detailed view of the learning dynamics.
|
|
183
|
+
*
|
|
184
|
+
* @param winner - The network to analyze.
|
|
185
|
+
* @param trainingSet - The training data to use for supervised learning.
|
|
186
|
+
*/
|
|
187
|
+
function analizeWinner(
|
|
188
|
+
winner: Network,
|
|
189
|
+
trainingSet: { input: number[]; output: number[] }[]
|
|
190
|
+
) {
|
|
191
|
+
// Step 1: Log the network's connection weights before training to establish a baseline.
|
|
192
|
+
console.log(
|
|
193
|
+
'Winner weights before training:',
|
|
194
|
+
winner.connections.map((c) => c.weight)
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// Step 2: Train the network with a highly detailed logging schedule.
|
|
198
|
+
// The `schedule` option allows executing a function at specified iterations.
|
|
199
|
+
winner.train(trainingSet, {
|
|
200
|
+
iterations: 2000,
|
|
201
|
+
recurrent: true,
|
|
202
|
+
error: 0.001,
|
|
203
|
+
log: 100, // Log training progress every 100 iterations.
|
|
204
|
+
rate: 0.0001,
|
|
205
|
+
batchSize: 10,
|
|
206
|
+
schedule: {
|
|
207
|
+
iterations: 2000, // The schedule will run for all iterations.
|
|
208
|
+
function: ({ iteration }: { iteration: number }) => {
|
|
209
|
+
// For each node in the network, log the details of its incoming and outgoing connections.
|
|
210
|
+
// This includes the weight, eligibility trace, and total delta weight, which are key
|
|
211
|
+
// metrics in understanding how the network is learning.
|
|
212
|
+
winner.nodes.forEach((n, idx) => {
|
|
213
|
+
n.connections.in.forEach((c, cidx) => {
|
|
214
|
+
console.log(
|
|
215
|
+
`Iter ${iteration}: Node[${idx}] InConn[${cidx}] from Node ${c.from.index} weight=${c.weight} elig=${c.eligibility} tDeltaW=${c.totalDeltaWeight}`
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
n.connections.out.forEach((c, cidx) => {
|
|
219
|
+
console.log(
|
|
220
|
+
`Iter ${iteration}: Node[${idx}] OutConn[${cidx}] to Node ${c.to.index} weight=${c.weight} elig=${c.eligibility} tDeltaW=${c.totalDeltaWeight}`
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Step 3: Log the network's connection weights after training to see the changes.
|
|
229
|
+
console.log(
|
|
230
|
+
'Winner weights after training:',
|
|
231
|
+
winner.connections.map((c) => c.weight)
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Step 4: Test the network's response to the primary input patterns after training.
|
|
235
|
+
// This helps to verify that the network has learned the correct associations.
|
|
236
|
+
console.log(
|
|
237
|
+
'Test output for North-only scenario [0,1,0,0,0,0.7]:',
|
|
238
|
+
winner.activate([0, 1, 0, 0, 0, 0.7])
|
|
239
|
+
);
|
|
240
|
+
console.log(
|
|
241
|
+
'Test output for East-only scenario [0.25,0,1,0,0,0.7]:',
|
|
242
|
+
winner.activate([0.25, 0, 1, 0, 0, 0.7])
|
|
243
|
+
);
|
|
244
|
+
console.log(
|
|
245
|
+
'Test output for South-only scenario [0.5,0,0,1,0,0.7]:',
|
|
246
|
+
winner.activate([0.5, 0, 0, 1, 0, 0.7])
|
|
247
|
+
);
|
|
248
|
+
console.log(
|
|
249
|
+
'Test output for West-only scenario [0.75,0,0,0,1,0.7]:',
|
|
250
|
+
winner.activate([0.75, 0, 0, 0, 1, 0.7])
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// Step 5: Pause execution for manual inspection in a debugging environment.
|
|
254
|
+
debugger;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
describe('ASCII Maze Solver using Neuro-Evolution', () => {
|
|
258
|
+
/**
|
|
259
|
+
* This `beforeAll` block runs once before any of the tests in this suite.
|
|
260
|
+
* It sets up the environment for clear and informative logging.
|
|
261
|
+
*/
|
|
262
|
+
beforeAll(() => {
|
|
263
|
+
// Override the global console.log function to ensure output is always visible,
|
|
264
|
+
// even in test environments that might otherwise suppress it.
|
|
265
|
+
console.log = jest.fn((...args) => {
|
|
266
|
+
// A small filter to avoid logging specific debug messages from the dashboard.
|
|
267
|
+
if (args.some((a) => typeof a === 'string' && a.includes('[DPDBG-')))
|
|
268
|
+
return;
|
|
269
|
+
// Write directly to standard output.
|
|
270
|
+
process.stdout.write(args.join(' ') + '\n');
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Store the original console.debug implementation.
|
|
274
|
+
const origDebug = console.debug;
|
|
275
|
+
// Override console.debug to filter out specific dashboard messages.
|
|
276
|
+
console.debug = (...args: any[]) => {
|
|
277
|
+
if (args.some((a) => typeof a === 'string' && a.includes('[DPDBG-')))
|
|
278
|
+
return;
|
|
279
|
+
// Call the original debug function if it exists.
|
|
280
|
+
origDebug?.(...args);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// Print a header with instructions for the user on how to see detailed logs.
|
|
284
|
+
console.log('\n');
|
|
285
|
+
console.log('═══════════════════════════════════════════════════');
|
|
286
|
+
console.log(' To see all ASCII maze visualization and logs, run');
|
|
287
|
+
console.log(` ${colors.cyan}npm run test:e2e:logs${colors.reset}`);
|
|
288
|
+
console.log('═══════════════════════════════════════════════════');
|
|
289
|
+
console.log('\n');
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// --- Main Test Case ---
|
|
293
|
+
it('Evolve agent: curriculum learning in multiple steps from a tiny maze to the minotaur maze', async () => {
|
|
294
|
+
// Educational Note on Curriculum Learning:
|
|
295
|
+
// This test implements a strategy called "curriculum learning," where the agent is trained
|
|
296
|
+
// on a sequence of increasingly difficult tasks (mazes). It starts with a very simple maze
|
|
297
|
+
// and gradually moves to more complex ones. The winning network from one maze is used as the
|
|
298
|
+
// starting point for the next, allowing the agent to build upon its knowledge. This is often
|
|
299
|
+
// more effective than trying to solve the most difficult task from scratch.
|
|
300
|
+
|
|
301
|
+
// --- Phase 1: Tiny Maze ---
|
|
302
|
+
// The simplest maze to establish a baseline behavior.
|
|
303
|
+
const tinyResult = await EvolutionEngine.runMazeEvolution({
|
|
304
|
+
mazeConfig: { maze: tiny },
|
|
305
|
+
agentSimConfig: { maxSteps: 100 },
|
|
306
|
+
evolutionAlgorithmConfig: {
|
|
307
|
+
allowRecurrent: true,
|
|
308
|
+
popSize: 40,
|
|
309
|
+
maxStagnantGenerations: 200,
|
|
310
|
+
minProgressToPass: 99,
|
|
311
|
+
maxGenerations: 200,
|
|
312
|
+
lamarckianIterations: 5,
|
|
313
|
+
lamarckianSampleSize: 16,
|
|
314
|
+
},
|
|
315
|
+
reportingConfig: {
|
|
316
|
+
dashboardManager: dashboardManagerInstance,
|
|
317
|
+
logEvery: 1,
|
|
318
|
+
label: 'tiny',
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Refine the winner from the tiny maze using backpropagation.
|
|
323
|
+
const tinyRefined = refineWinnerWithBackprop(
|
|
324
|
+
tinyResult?.bestNetwork as Network
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
// --- Phase 2: Small Spiral Maze ---
|
|
328
|
+
// Introduces the challenge of a spiral, requiring the agent to make a long sequence of turns.
|
|
329
|
+
const spiralSmallResult = await EvolutionEngine.runMazeEvolution({
|
|
330
|
+
mazeConfig: { maze: spiralSmall },
|
|
331
|
+
agentSimConfig: { maxSteps: 100 },
|
|
332
|
+
evolutionAlgorithmConfig: {
|
|
333
|
+
allowRecurrent: true,
|
|
334
|
+
popSize: 40,
|
|
335
|
+
maxStagnantGenerations: 200,
|
|
336
|
+
minProgressToPass: 99,
|
|
337
|
+
maxGenerations: 200,
|
|
338
|
+
lamarckianIterations: 5,
|
|
339
|
+
lamarckianSampleSize: 16,
|
|
340
|
+
initialBestNetwork: tinyRefined, // Start with the refined network from the previous phase.
|
|
341
|
+
},
|
|
342
|
+
reportingConfig: {
|
|
343
|
+
dashboardManager: dashboardManagerInstance,
|
|
344
|
+
logEvery: 1,
|
|
345
|
+
label: 'spiralSmall',
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
const spiralSmallRefined = refineWinnerWithBackprop(
|
|
349
|
+
spiralSmallResult?.bestNetwork as Network
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
// --- Phase 3: Spiral Maze ---
|
|
353
|
+
// A larger spiral to test the agent's ability to generalize its turning behavior.
|
|
354
|
+
const spiralResult = await EvolutionEngine.runMazeEvolution({
|
|
355
|
+
mazeConfig: { maze: spiral },
|
|
356
|
+
agentSimConfig: { maxSteps: 150 },
|
|
357
|
+
evolutionAlgorithmConfig: {
|
|
358
|
+
allowRecurrent: true,
|
|
359
|
+
popSize: 40,
|
|
360
|
+
maxStagnantGenerations: 200,
|
|
361
|
+
minProgressToPass: 99,
|
|
362
|
+
maxGenerations: 300,
|
|
363
|
+
lamarckianIterations: 5,
|
|
364
|
+
lamarckianSampleSize: 16,
|
|
365
|
+
initialBestNetwork: spiralSmallRefined,
|
|
366
|
+
},
|
|
367
|
+
reportingConfig: {
|
|
368
|
+
dashboardManager: dashboardManagerInstance,
|
|
369
|
+
logEvery: 1,
|
|
370
|
+
label: 'spiral',
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
const spiralRefined = refineWinnerWithBackprop(
|
|
374
|
+
spiralResult.bestNetwork as Network
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
// --- Phase 4: Small Maze ---
|
|
378
|
+
// A more traditional maze with branches and dead ends.
|
|
379
|
+
const smallResult = await EvolutionEngine.runMazeEvolution({
|
|
380
|
+
mazeConfig: { maze: small },
|
|
381
|
+
agentSimConfig: { maxSteps: 50 },
|
|
382
|
+
evolutionAlgorithmConfig: {
|
|
383
|
+
allowRecurrent: true,
|
|
384
|
+
popSize: 40,
|
|
385
|
+
maxStagnantGenerations: 200,
|
|
386
|
+
minProgressToPass: 99,
|
|
387
|
+
maxGenerations: 300,
|
|
388
|
+
lamarckianIterations: 5,
|
|
389
|
+
lamarckianSampleSize: 16,
|
|
390
|
+
initialBestNetwork: spiralRefined,
|
|
391
|
+
},
|
|
392
|
+
reportingConfig: {
|
|
393
|
+
dashboardManager: dashboardManagerInstance,
|
|
394
|
+
logEvery: 1,
|
|
395
|
+
label: 'small',
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
const smallRefined = refineWinnerWithBackprop(
|
|
399
|
+
smallResult?.bestNetwork as Network
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
// --- Phase 5: Medium Maze ---
|
|
403
|
+
// Increases the size and complexity, requiring more steps and better navigation.
|
|
404
|
+
const mediumResult = await EvolutionEngine.runMazeEvolution({
|
|
405
|
+
mazeConfig: { maze: medium },
|
|
406
|
+
agentSimConfig: { maxSteps: 250 },
|
|
407
|
+
evolutionAlgorithmConfig: {
|
|
408
|
+
allowRecurrent: true,
|
|
409
|
+
popSize: 40,
|
|
410
|
+
maxStagnantGenerations: 200,
|
|
411
|
+
minProgressToPass: 99,
|
|
412
|
+
maxGenerations: 400,
|
|
413
|
+
lamarckianIterations: 5,
|
|
414
|
+
lamarckianSampleSize: 16,
|
|
415
|
+
initialBestNetwork: smallRefined,
|
|
416
|
+
},
|
|
417
|
+
reportingConfig: {
|
|
418
|
+
dashboardManager: dashboardManagerInstance,
|
|
419
|
+
logEvery: 1,
|
|
420
|
+
label: 'medium',
|
|
421
|
+
},
|
|
422
|
+
});
|
|
423
|
+
const mediumRefined = refineWinnerWithBackprop(
|
|
424
|
+
mediumResult?.bestNetwork as Network
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
// --- Phase 6: Medium 2 Maze ---
|
|
428
|
+
// A different medium-sized maze to test generalization.
|
|
429
|
+
const medium2Result = await EvolutionEngine.runMazeEvolution({
|
|
430
|
+
mazeConfig: { maze: medium2 },
|
|
431
|
+
agentSimConfig: { maxSteps: 300 },
|
|
432
|
+
evolutionAlgorithmConfig: {
|
|
433
|
+
allowRecurrent: true,
|
|
434
|
+
popSize: 40,
|
|
435
|
+
maxStagnantGenerations: 200,
|
|
436
|
+
minProgressToPass: 99,
|
|
437
|
+
maxGenerations: 400,
|
|
438
|
+
lamarckianIterations: 5,
|
|
439
|
+
lamarckianSampleSize: 16,
|
|
440
|
+
initialBestNetwork: mediumRefined,
|
|
441
|
+
},
|
|
442
|
+
reportingConfig: {
|
|
443
|
+
dashboardManager: dashboardManagerInstance,
|
|
444
|
+
logEvery: 1,
|
|
445
|
+
label: 'medium2',
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
const medium2Refined = refineWinnerWithBackprop(
|
|
449
|
+
medium2Result?.bestNetwork as Network
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
// --- Phase 7: Large Maze ---
|
|
453
|
+
// A significant step up in difficulty, with a much larger search space.
|
|
454
|
+
const largeResult = await EvolutionEngine.runMazeEvolution({
|
|
455
|
+
mazeConfig: { maze: large },
|
|
456
|
+
agentSimConfig: { maxSteps: 400 },
|
|
457
|
+
evolutionAlgorithmConfig: {
|
|
458
|
+
allowRecurrent: true,
|
|
459
|
+
popSize: 40,
|
|
460
|
+
maxStagnantGenerations: 200,
|
|
461
|
+
minProgressToPass: 99,
|
|
462
|
+
maxGenerations: 500,
|
|
463
|
+
lamarckianIterations: 5,
|
|
464
|
+
lamarckianSampleSize: 16,
|
|
465
|
+
initialBestNetwork: medium2Refined,
|
|
466
|
+
},
|
|
467
|
+
reportingConfig: {
|
|
468
|
+
dashboardManager: dashboardManagerInstance,
|
|
469
|
+
logEvery: 1,
|
|
470
|
+
label: 'large',
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
const largeRefined = refineWinnerWithBackprop(
|
|
474
|
+
largeResult?.bestNetwork as Network
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
// --- Final Phase: Minotaur Maze ---
|
|
478
|
+
// The most complex maze, requiring the agent to have learned a robust and general navigation strategy.
|
|
479
|
+
await EvolutionEngine.runMazeEvolution({
|
|
480
|
+
mazeConfig: { maze: minotaur },
|
|
481
|
+
agentSimConfig: { maxSteps: 700 },
|
|
482
|
+
evolutionAlgorithmConfig: {
|
|
483
|
+
allowRecurrent: true,
|
|
484
|
+
popSize: 40,
|
|
485
|
+
maxStagnantGenerations: 200,
|
|
486
|
+
minProgressToPass: 99,
|
|
487
|
+
maxGenerations: 600,
|
|
488
|
+
lamarckianIterations: 5,
|
|
489
|
+
lamarckianSampleSize: 16,
|
|
490
|
+
initialBestNetwork: largeRefined,
|
|
491
|
+
},
|
|
492
|
+
reportingConfig: {
|
|
493
|
+
dashboardManager: dashboardManagerInstance,
|
|
494
|
+
logEvery: 1,
|
|
495
|
+
label: 'minotaur',
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
}, 0);
|
|
499
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ASCII Maze Example - Main file
|
|
3
|
+
*
|
|
4
|
+
* This module brings together all components of the ASCII Maze solving system,
|
|
5
|
+
* providing the core functionality for the neuroevolution-based maze solver.
|
|
6
|
+
*
|
|
7
|
+
* The system demonstrates how a neural network can be evolved to solve
|
|
8
|
+
* increasingly complex mazes through reinforcement learning principles.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Re-export components from specialized modules as classes for external use.
|
|
12
|
+
/** Utility functions for maze manipulation and analysis. */
|
|
13
|
+
export { MazeUtils } from './mazeUtils';
|
|
14
|
+
/** Provides sensory input (vision) for the agent in the maze. */
|
|
15
|
+
export { MazeVision } from './mazeVision';
|
|
16
|
+
/** Handles agent movement logic within the maze. */
|
|
17
|
+
export { MazeMovement } from './mazeMovement';
|
|
18
|
+
/** Visualization utilities for rendering the maze and agent progress. */
|
|
19
|
+
export { MazeVisualization } from './mazeVisualization';
|
|
20
|
+
/** Manages dashboard output and progress reporting. */
|
|
21
|
+
export { DashboardManager } from './dashboardManager';
|
|
22
|
+
/** Terminal utilities for clearing and formatting output. */
|
|
23
|
+
export { TerminalUtility } from './terminalUtility';
|
|
24
|
+
|
|
25
|
+
// Export interfaces and maze collection for external configuration and extension.
|
|
26
|
+
/** All shared interfaces for the ASCII Maze system. */
|
|
27
|
+
export * from './interfaces';
|
|
28
|
+
/** Color constants for terminal output. */
|
|
29
|
+
export { colors } from './colors';
|
|
30
|
+
/** Collection of available maze definitions. */
|
|
31
|
+
export * as mazes from './mazes';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Main entry point for the ASCII Maze example.
|
|
35
|
+
* If this file is executed directly, it runs the enhanced demonstration environment.
|
|
36
|
+
*/
|
|
37
|
+
if (require.main === module) {
|
|
38
|
+
// If this file is being run directly (not imported), run the enhanced demo.
|
|
39
|
+
// This sets up the demonstration environment for the ASCII Maze solver.
|
|
40
|
+
require('./enhancedMazeDemo');
|
|
41
|
+
}
|