@reicek/neataptic-ts 0.1.13 → 0.1.15
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/README.md +58 -143
- package/docs/README.md +58 -143
- package/docs/assets/ascii-maze.bundle.js +1 -1
- package/docs/assets/ascii-maze.bundle.js.map +1 -1
- package/docs/examples/asciiMaze/index.html +2 -2
- package/docs/index.html +58 -141
- package/package.json +1 -1
- package/plans/HyperMorphoNEAT.md +525 -751
- package/plans/Memory_Optimization.md +195 -112
- package/plans/ONNX_EXPORT_PLAN.md +7 -9
- package/test/examples/asciiMaze/index.html +2 -2
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
<div id="ascii-maze-status" aria-live="polite" style="margin-top:8px;color:#f88"></div>
|
|
27
27
|
<!-- Deterministic script path (test environment -> docs assets) -->
|
|
28
28
|
<script
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
src="../../assets/ascii-maze.bundle.js"
|
|
30
|
+
data-ascii-maze-root="../../assets/"
|
|
31
31
|
defer
|
|
32
32
|
onload="(window.asciiMazeStart ? window.asciiMazeStart('ascii-maze-output') : (window.asciiMaze && window.asciiMaze.start && window.asciiMaze.start()))"
|
|
33
33
|
onerror="document.getElementById('ascii-maze-status').textContent='Failed to load ascii-maze.bundle.js';"
|
package/docs/index.html
CHANGED
|
@@ -112,8 +112,7 @@ console.log('best score', neat.getBest()?.score);
|
|
|
112
112
|
|
|
113
113
|
const neatMo = new Neat(2, 1, fitness, {
|
|
114
114
|
popsize: 60,
|
|
115
|
-
multiObjective: { enabled: true
|
|
116
|
-
novelty: { enabled: true, descriptor: g => [g.nodes.length, g.connections.length], k: 8, blendFactor: 0.25 },
|
|
115
|
+
multiObjective: { enabled: true },
|
|
117
116
|
seed: 7,
|
|
118
117
|
});
|
|
119
118
|
await neatMo.evaluate();
|
|
@@ -267,10 +266,12 @@ writeFileSync('telemetry.csv', csv);
|
|
|
267
266
|
</tbody></table>
|
|
268
267
|
<hr>
|
|
269
268
|
<h2 id="performance-shorthand">Performance shorthand</h2><p>Fast iteration recipe:</p>
|
|
270
|
-
<pre><code class="language-ts">
|
|
269
|
+
<pre><code class="language-ts">import { cpus } from 'node:os';
|
|
270
|
+
|
|
271
|
+
const neat = new Neat(inp, out, fit, {
|
|
271
272
|
popsize: 120,
|
|
272
273
|
fastMode: true,
|
|
273
|
-
threads:
|
|
274
|
+
threads: cpus().length > 1 ? cpus().length - 1 : 1,
|
|
274
275
|
adaptiveMutation: { enabled: true, strategy: 'twoTier' },
|
|
275
276
|
telemetry: { enabled: true, performance: true, complexity: true }
|
|
276
277
|
});
|
|
@@ -310,7 +311,10 @@ writeFileSync('telemetry.csv', csv);
|
|
|
310
311
|
<hr>
|
|
311
312
|
<h2 id="training-optional-polish-phase">Training (optional polish phase)</h2><p>Evolve topology first, then fine‑tune weights:</p>
|
|
312
313
|
<pre><code class="language-ts">const best = neat.getBest();
|
|
313
|
-
await best?.train(data, {
|
|
314
|
+
await best?.train(data, {
|
|
315
|
+
iterations: 500,
|
|
316
|
+
rate: 0.01,
|
|
317
|
+
optimizer: 'adam',
|
|
314
318
|
gradientClip: { mode: 'norm', maxNorm: 1 },
|
|
315
319
|
movingAverageWindow: 5,
|
|
316
320
|
});
|
|
@@ -323,7 +327,7 @@ await best?.train(data, { iterations: 500, rate: 0.01, optimizer: 'adam'
|
|
|
323
327
|
optimizer: 'adamw',
|
|
324
328
|
gradientClip: { mode: 'norm', maxNorm: 1 },
|
|
325
329
|
movingAverageWindow: 7,
|
|
326
|
-
metricsHook: m => console.log(m.iteration, m.error, m.gradNorm)
|
|
330
|
+
metricsHook: m => console.log(m.iteration, m.error, m.gradNorm),
|
|
327
331
|
});
|
|
328
332
|
</code></pre>
|
|
329
333
|
<p>Training docs cover scheduler composition (e.g. warmup → plateau) & pruning schedules.</p>
|
|
@@ -461,7 +465,7 @@ const roundTrip = importFromONNX(bytes);
|
|
|
461
465
|
<td>Prevents runaway long runs during tuning passes.</td>
|
|
462
466
|
</tr>
|
|
463
467
|
</tbody></table>
|
|
464
|
-
<h3 id="structural-complexity-caching">Structural Complexity Caching</h3><p><code>
|
|
468
|
+
<h3 id="structural-complexity-caching">Structural Complexity Caching</h3><p><code>neat.evolve</code> caches per-genome complexity metrics (nodes / connections / gates). This avoids recomputing counts each evaluation when unchanged, reducing overhead for large populations or deep architectures.</p>
|
|
465
469
|
<h3 id="profiling">Profiling</h3><p>Enable <code>telemetry:{ performance:true }</code> to capture per-generation evaluation & evolve timings. Use these to:</p>
|
|
466
470
|
<ol>
|
|
467
471
|
<li>Identify evaluation bottlenecks (cost function vs structural overhead).</li>
|
|
@@ -480,8 +484,6 @@ const roundTrip = importFromONNX(bytes);
|
|
|
480
484
|
popsize: 120,
|
|
481
485
|
threads: 8,
|
|
482
486
|
telemetry: { enabled: true, performance: true },
|
|
483
|
-
telemetrySelect: ['performance', 'complexity'],
|
|
484
|
-
adaptiveMutation: { enabled: true, strategy: 'twoTier' },
|
|
485
487
|
fastMode: true,
|
|
486
488
|
});
|
|
487
489
|
</code></pre>
|
|
@@ -511,7 +513,7 @@ const roundTrip = importFromONNX(bytes);
|
|
|
511
513
|
<li>A direct input→output shortcut is removed once at least one hidden node exists, ensuring depth grows.</li>
|
|
512
514
|
</ul>
|
|
513
515
|
<p>Intended Usage:</p>
|
|
514
|
-
<pre><code class="language-ts">import { config } from '
|
|
516
|
+
<pre><code class="language-ts">import { config, methods } from '@reicek/neataptic-ts';
|
|
515
517
|
config.deterministicChainMode = true; // enable
|
|
516
518
|
const net = new Network(1, 1);
|
|
517
519
|
for (let i = 0; i < 5; i++) net.mutate(methods.mutation.ADD_NODE); // guaranteed 5 hidden chain
|
|
@@ -641,9 +643,7 @@ for each Fi: decrement dominanceCount of genomes they dominate; those reaching 0
|
|
|
641
643
|
multiObjective: { enabled: true, complexityMetric: 'nodes' },
|
|
642
644
|
});
|
|
643
645
|
// Add structural entropy (maximize)
|
|
644
|
-
neat.registerObjective('entropy', 'max',
|
|
645
|
-
(neat as any)._structuralEntropy(g)
|
|
646
|
-
);
|
|
646
|
+
neat.registerObjective('entropy', 'max', g => (neat as any)._structuralEntropy(g));
|
|
647
647
|
await neat.evaluate();
|
|
648
648
|
await neat.evolve();
|
|
649
649
|
console.log(neat.getObjectives()); // [{key:'fitness',...},{key:'complexity',...},{key:'entropy',...}]
|
|
@@ -722,16 +722,16 @@ console.log(neat.getTelemetry().slice(-1)[0].complexity); // { meanNodes, meanCo
|
|
|
722
722
|
</tbody></table>
|
|
723
723
|
<p>Configure an adaptive schedule that expands limits when improvement slope is positive and contracts during stagnation. This mirrors a common parsimony heuristic: permit complexification only while marginal fitness gain per added structural unit remains appreciable; otherwise bias toward consolidation to reduce evaluation cost and overfitting risk:</p>
|
|
724
724
|
<pre><code class="language-ts">complexityBudget: {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
725
|
+
enabled: true,
|
|
726
|
+
mode: 'adaptive',
|
|
727
|
+
maxNodesStart: 4,
|
|
728
|
+
maxNodesEnd: 24,
|
|
729
|
+
improvementWindow: 8,
|
|
730
|
+
increaseFactor: 1.15,
|
|
731
|
+
stagnationFactor: 0.93,
|
|
732
|
+
minNodes: 4,
|
|
733
|
+
maxConnsStart: 40,
|
|
734
|
+
maxConnsEnd: 400,
|
|
735
735
|
}
|
|
736
736
|
</code></pre>
|
|
737
737
|
<p>Linear schedule alternative: set <code>mode:'linear'</code> and optionally <code>horizon</code> (generations) to interpolate from <code>maxNodesStart</code> to <code>maxNodesEnd</code>.</p>
|
|
@@ -808,22 +808,22 @@ console.log(neat.getTelemetry().slice(-1)[0].complexity); // { meanNodes, meanCo
|
|
|
808
808
|
<pre><code class="language-ts">diversityPressure:{ enabled:true, motifSample:25, penaltyStrength:0.05 }
|
|
809
809
|
</code></pre>
|
|
810
810
|
<p>Adaptive novelty threshold targeting an archive insertion rate:</p>
|
|
811
|
-
<pre><code class="language-ts">novelty:{
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
811
|
+
<pre><code class="language-ts">novelty: {
|
|
812
|
+
enabled: true,
|
|
813
|
+
descriptor: g => [g.nodes.length, g.connections.length],
|
|
814
|
+
archiveAddThreshold: 0.5,
|
|
815
|
+
dynamicThreshold: { enabled: true, targetRate: 0.15, adjust: 0.1, min: 0.01, max: 10 },
|
|
816
816
|
}
|
|
817
817
|
</code></pre>
|
|
818
818
|
<p>After each evaluation the threshold is nudged up/down so the fraction of inserted descriptors approximates <code>targetRate</code>.</p>
|
|
819
819
|
<h3 id="inactive-objective-pruning">Inactive Objective Pruning</h3><p>If you experiment with many custom objectives it is common for some to become constant (providing no ranking discrimination). Enable automatic removal of such stagnant objectives:</p>
|
|
820
|
-
<pre><code class="language-ts">multiObjective:{
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
820
|
+
<pre><code class="language-ts">multiObjective: {
|
|
821
|
+
enabled: true,
|
|
822
|
+
objectives: [
|
|
823
|
+
{ key: 'fitness', direction: 'max', accessor: g => g.score },
|
|
824
|
+
{ key: 'novelty', direction: 'max', accessor: g => (g as any)._novelty },
|
|
825
|
+
],
|
|
826
|
+
pruneInactive: { enabled: true, window: 5, rangeEps: 1e-9, protect: ['fitness'] },
|
|
827
827
|
}
|
|
828
828
|
</code></pre>
|
|
829
829
|
<p>Mechanics:</p>
|
|
@@ -862,13 +862,13 @@ Telemetry <code>lineage</code> block now also includes:</li>
|
|
|
862
862
|
</ul>
|
|
863
863
|
<p>Use these to detect genealogical stagnation (both remaining near zero) vs broad exploration (rising pair distance).</p>
|
|
864
864
|
<h3 id="lineage-pressure-anti-inbreeding-optional">Lineage Pressure & Anti-Inbreeding (Optional)</h3><p>Apply score adjustments based on lineage structure (depth dispersion) or penalize inbreeding (high ancestor overlap):</p>
|
|
865
|
-
<pre><code class="language-ts">lineagePressure:{
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
865
|
+
<pre><code class="language-ts">lineagePressure: {
|
|
866
|
+
enabled: true,
|
|
867
|
+
mode: 'antiInbreeding', // 'penalizeDeep' | 'rewardShallow' | 'spread' | 'antiInbreeding'
|
|
868
|
+
strength: 0.02, // generic scaling for depth modes
|
|
869
|
+
ancestorWindow: 4, // generations to look back when computing ancestor sets
|
|
870
|
+
inbreedingPenalty: 0.04, // override penalty scaling (defaults to strength*2)
|
|
871
|
+
diversityBonus: 0.02, // bonus scaling for very distinct parent lineages
|
|
872
872
|
}
|
|
873
873
|
</code></pre>
|
|
874
874
|
<p>Modes:</p>
|
|
@@ -916,7 +916,7 @@ neat2.importRNGState(json);
|
|
|
916
916
|
</tr>
|
|
917
917
|
</tbody></table>
|
|
918
918
|
<p>Per-genome mutation rate/amount adapt each generation under strategies (<code>twoTier</code>, <code>exploreLow</code>, <code>anneal</code>). Use:</p>
|
|
919
|
-
<pre><code class="language-ts">adaptiveMutation:{ enabled:true, strategy
|
|
919
|
+
<pre><code class="language-ts">adaptiveMutation: { enabled: true, strategy: 'twoTier', sigma: 0.08, adaptAmount: true }
|
|
920
920
|
</code></pre>
|
|
921
921
|
<p>Operator success statistics (bandit + weighting):</p>
|
|
922
922
|
<pre><code class="language-ts">neat.getOperatorStats(); // [{ name, success, attempts }, ...]
|
|
@@ -978,7 +978,7 @@ const csv = neat.exportSpeciesHistoryCSV();
|
|
|
978
978
|
// Columns include generation plus dynamic keys: id,size,best,lastImproved,age,meanNodes,meanConns,meanScore,meanNovelty,meanCompat,meanEntropy,varNodes,varConns,deltaMeanNodes,deltaMeanConns,deltaBestScore,turnoverRate,meanInnovation,innovationRange,enabledRatio (when extendedHistory enabled)
|
|
979
979
|
</code></pre>
|
|
980
980
|
<p>These APIs are evolving; consult source <code>src/neat.ts</code> for full option surfaces while docs finalize.</p>
|
|
981
|
-
<p>Full option & telemetry reference: <
|
|
981
|
+
<p>Full option & telemetry reference: See the generated documentation in the <code>docs/</code> directory.</p>
|
|
982
982
|
<h1 id="network-constructor-update">Network Constructor Update</h1><p>The <code>Network</code> class constructor now supports an optional third parameter for configuration:</p>
|
|
983
983
|
<pre><code class="language-ts">new Network(input: number, output: number, options?: { minHidden?: number })
|
|
984
984
|
</code></pre>
|
|
@@ -986,26 +986,31 @@ const csv = neat.exportSpeciesHistoryCSV();
|
|
|
986
986
|
<li><code>input</code>: Number of input nodes (required)</li>
|
|
987
987
|
<li><code>output</code>: Number of output nodes (required)</li>
|
|
988
988
|
<li><code>options.minHidden</code>: (optional) If set, enforces a minimum number of hidden nodes. If omitted or 0, no minimum is enforced. This allows true 1-1 (input-output only) networks.</li>
|
|
989
|
+
<li><code>options.seed</code>: (optional) A numeric seed for the random number generator to ensure reproducible initial weights and biases.</li>
|
|
989
990
|
</ul>
|
|
990
991
|
<p><strong>Example:</strong></p>
|
|
991
992
|
<pre><code class="language-ts">// Standard 1-1 network (no hidden nodes)
|
|
992
993
|
const net = new Network(1, 1);
|
|
993
994
|
|
|
994
|
-
// Enforce at least 3 hidden nodes
|
|
995
|
-
const netWithHidden = new Network(2, 1, { minHidden: 3 });
|
|
995
|
+
// Enforce at least 3 hidden nodes and set a seed
|
|
996
|
+
const netWithHidden = new Network(2, 1, { minHidden: 3, seed: 123 });
|
|
996
997
|
</code></pre>
|
|
997
|
-
<h1 id="neat-evolution-
|
|
998
|
-
<pre><code class="language-ts">import Neat from '
|
|
999
|
-
const neat = new Neat(2, 1, fitnessFn, {
|
|
998
|
+
<h1 id="neat-evolution-options">Neat Evolution Options</h1><p>The <code>Neat</code> class constructor accepts an options object that includes all <code>Network</code> options (<code>minHidden</code>, <code>seed</code>) plus evolution-specific settings.</p>
|
|
999
|
+
<pre><code class="language-ts">import { Neat } from '@reicek/neataptic-ts';
|
|
1000
|
+
const neat = new Neat(2, 1, fitnessFn, {
|
|
1001
|
+
popsize: 50,
|
|
1002
|
+
minHidden: 5, // Passed to Network constructor
|
|
1003
|
+
seed: 4242, // Passed to both Neat and Network constructors
|
|
1004
|
+
});
|
|
1000
1005
|
</code></pre>
|
|
1001
1006
|
<ul>
|
|
1002
1007
|
<li>All networks created by the evolutionary process will have at least 5 hidden nodes.</li>
|
|
1003
|
-
<li>
|
|
1008
|
+
<li>The <code>seed</code> ensures that the entire evolutionary process, including initial population creation, is reproducible.</li>
|
|
1004
1009
|
</ul>
|
|
1005
1010
|
<p>See tests in <code>test/neat.ts</code> for usage and verification.</p>
|
|
1006
1011
|
<hr>
|
|
1007
1012
|
<h1 id="onnx-import-export">ONNX Import/Export</h1><p>Interoperability layer for exchanging strictly layered MLP (and experimental recurrent) networks with ONNX tooling. Scope today: feed-forward layered perceptrons plus heuristic detection of simple recurrent gate groupings; arbitrary graphs not guaranteed.</p>
|
|
1008
|
-
<h3 id="basic-usage">Basic Usage</h3><pre><code class="language-ts">import { exportToONNX, importFromONNX } from '
|
|
1013
|
+
<h3 id="basic-usage">Basic Usage</h3><pre><code class="language-ts">import { exportToONNX, importFromONNX } from '@reicek/neataptic-ts';
|
|
1009
1014
|
|
|
1010
1015
|
// Export
|
|
1011
1016
|
const onnxModel = exportToONNX(network, { includeMetadata: true });
|
|
@@ -1224,7 +1229,7 @@ net.train(data, {
|
|
|
1224
1229
|
<li>Deterministic seeding: <code>new Network(4,2,{ seed:123 })</code> or later <code>net.setSeed(123)</code> ensures reproducible initial weights, biases, connection order, and mutation randomness for training (excluding certain static reconstruction paths). For NEAT evolution pass <code>seed</code> in <code>new Neat(...,{ seed:999 })</code>.</li>
|
|
1225
1230
|
<li>Overflow telemetry: during mixed precision training, <code>overflowCount</code> increments on detected NaN/Inf when unscaling gradients; <code>scaleUps</code> / <code>scaleDowns</code> count dynamic loss scale adjustments.</li>
|
|
1226
1231
|
</ul>
|
|
1227
|
-
<h3 id="learning-rate-scheduler-usage">Learning Rate Scheduler Usage</h3><pre><code class="language-ts">import methods from '
|
|
1232
|
+
<h3 id="learning-rate-scheduler-usage">Learning Rate Scheduler Usage</h3><pre><code class="language-ts">import { methods } from '@reicek/neataptic-ts';
|
|
1228
1233
|
const ratePolicy = methods.Rate.cosineAnnealingWarmRestarts(200, 1e-5, 2);
|
|
1229
1234
|
net.train(data, { iterations: 1000, rate: 0.1, ratePolicy });
|
|
1230
1235
|
</code></pre>
|
|
@@ -1543,12 +1548,7 @@ net.disableDropConnect();
|
|
|
1543
1548
|
<td>Rectifies variance early in training</td>
|
|
1544
1549
|
</tr>
|
|
1545
1550
|
<tr>
|
|
1546
|
-
<td>
|
|
1547
|
-
<td>beta1, beta2</td>
|
|
1548
|
-
<td>Direction = sign(beta1<em>m + beta2</em>m2)</td>
|
|
1549
|
-
</tr>
|
|
1550
|
-
<tr>
|
|
1551
|
-
<td>adabelief</td>
|
|
1551
|
+
<td>AdaBelief</td>
|
|
1552
1552
|
<td>beta1, beta2, eps</td>
|
|
1553
1553
|
<td>Second moment of (g - m) (gradient surprise)</td>
|
|
1554
1554
|
</tr>
|
|
@@ -1887,87 +1887,4 @@ neat.importRNGState(json); // restore from JSON
|
|
|
1887
1887
|
});
|
|
1888
1888
|
</code></pre>
|
|
1889
1889
|
<p>Use when raw search space contains huge numbers of trivial zero-score genomes (e.g. all-linear tiny nets). The filter prevents them from influencing speciation/dominance ordering; they still evolve structurally until criterion passes.</p>
|
|
1890
|
-
<pre><code>
|
|
1891
|
-
#### Adaptive Sharing
|
|
1892
|
-
|
|
1893
|
-
If `adaptiveSharing.enabled` the system adjusts `sharingSigma` each generation:
|
|
1894
|
-
</code></pre>
|
|
1895
|
-
<p>sigma += step * sign(fragmentation - target)</p>
|
|
1896
|
-
<pre><code>
|
|
1897
|
-
within `[minSigma,maxSigma]`.
|
|
1898
|
-
|
|
1899
|
-
#### Multi-Objective Notes & Strategy {#multi-objective-notes--strategy}
|
|
1900
|
-
|
|
1901
|
-
Implements a simplified NSGA-II style pass: fast non-dominated sort (O(N^2) current implementation) + crowding distance; final ordering uses (rank asc, crowding desc, fitness desc) before truncation. Practical guidance:
|
|
1902
|
-
|
|
1903
|
-
- Start single-objective until baseline performance plateaus, then enable `multiObjective.enabled` with `complexityMetric:'nodes'`.
|
|
1904
|
-
- If early search stagnates due to premature parsimony, delay complexity with `multiObjective.dynamic.addComplexityAt`.
|
|
1905
|
-
- Use `autoEntropy` to seed a third diversity proxy objective only when structural collapse is observed (few species, low ancestor uniqueness).
|
|
1906
|
-
- Monitor front size; if it grows too large relative to population, enable `adaptiveEpsilon` to tighten dominance criteria.
|
|
1907
|
-
|
|
1908
|
-
Planned (future): faster dominance (divide-and-conquer), richer motif diversity pressure, automated compatibility coefficient tuning.
|
|
1909
|
-
|
|
1910
|
-
## ASCII Maze Example: 6‑Input Long-Range Vision (MazeVision)
|
|
1911
|
-
|
|
1912
|
-
The ASCII maze example uses a compact 6‑input perception schema ("MazeVision") with long‑range lookahead via a precomputed distance map. Inputs (order fixed):
|
|
1913
|
-
|
|
1914
|
-
1. compassScalar: Encodes the direction of the globally best next step toward the exit as a discrete scalar in {0,0.25,0.5,0.75} corresponding to N,E,S,W. Uses an extended horizon (H_COMPASS=5000) so it can see deeper than openness ratios.
|
|
1915
|
-
2. openN
|
|
1916
|
-
3. openE
|
|
1917
|
-
4. openS
|
|
1918
|
-
5. openW
|
|
1919
|
-
6. progressDelta: Normalized recent progress signal around 0.5 ( >0.5 improving, <0.5 regressing ).
|
|
1920
|
-
|
|
1921
|
-
### Openness Semantics (openN/E/S/W) {#openness-semantics}
|
|
1922
|
-
|
|
1923
|
-
Each openness value describes the quality of the shortest path to the exit if the agent moves first in that direction, using a bounded lookahead horizon H=1000 over the distance map.
|
|
1924
|
-
|
|
1925
|
-
Value encoding:
|
|
1926
|
-
|
|
1927
|
-
- 1: Direction(s) whose total path length Ldir is minimal among all strictly improving neighbors (ties allowed; multiple 1s possible).
|
|
1928
|
-
- Ratio 0 < Lmin / Ldir < 1: Direction is a valid strictly improving path but longer than the best (Lmin is the shortest improving path cost; Ldir = 1 + distance of neighbor cell). This supplies graded preference rather than binary pruning.
|
|
1929
|
-
- 0: Wall, unreachable cell, dead end, or any non‑improving move (neighbor distance >= current distance) – all treated uniformly.
|
|
1930
|
-
- 0.001: Special back‑only escape marker. When all four openness values would otherwise be 0 but the opposite of the previous successful action is traversable, that single opposite direction is set to 0.001 to indicate a pure retreat (pattern e.g. [0,0,0,0.001]).
|
|
1931
|
-
|
|
1932
|
-
Rules / Notes:
|
|
1933
|
-
|
|
1934
|
-
- Strict improvement filter: Only neighbors whose distanceMap value is strictly less than the current cell distance are considered for 1 or ratio values.
|
|
1935
|
-
- Horizon clipping: Paths with Ldir > H are treated as unreachable (value 0) to bound search cost.
|
|
1936
|
-
- Multiple bests: Corridors that fork into equivalently short routes produce multiple 1s, encouraging neutrality across equally optimal choices.
|
|
1937
|
-
- Backtrack marker is intentionally very small (0.001) so evolution distinguishes "retreat only" states from true walls without overweighting them.
|
|
1938
|
-
- Supervised refinement dataset intentionally contains ONLY deterministic single‑path cases (exactly one openness=1, others 0) for clarity; richer ratio/backtrack patterns appear only in the Lamarckian / evolutionary phase.
|
|
1939
|
-
|
|
1940
|
-
### progressDelta
|
|
1941
|
-
|
|
1942
|
-
Computed from recent distance improvement: delta = prevDistance - currentDistance, clipped to [-2,2], then mapped to [0,1] as 0.5 + delta/4. Values >0.5 mean progress toward exit; <0.5 regression or stalling.
|
|
1943
|
-
|
|
1944
|
-
### Debugging
|
|
1945
|
-
|
|
1946
|
-
Set ASCII_VISION_DEBUG=1 to emit periodic vision lines: current position, compassScalar, input vector, and per‑direction distance/ratio breakdown for auditing mismatches between maze geometry and distance map.
|
|
1947
|
-
|
|
1948
|
-
### Quick Reference
|
|
1949
|
-
|
|
1950
|
-
| Signal | Meaning |
|
|
1951
|
-
| ------ | --------------------------------------------------- |
|
|
1952
|
-
| 1 | Best strictly improving path(s) (minimal Ldir) |
|
|
1953
|
-
| (0,1) | Longer but improving path (ratio Lmin/Ldir) |
|
|
1954
|
-
| 0.001 | Only backtrack available (opposite of prior action) |
|
|
1955
|
-
| 0 | Wall / dead end / non‑improving / unreachable |
|
|
1956
|
-
|
|
1957
|
-
Implementation: `test/examples/asciiMaze/mazeVision.ts` (function `MazeVision.buildInputs6`).
|
|
1958
|
-
|
|
1959
|
-
This design minimizes input size (6 vs earlier large encodings) while preserving directional discrimination and long‑range planning cues, aiding faster evolutionary convergence and avoiding overfitting to local dead‑end noise.
|
|
1960
|
-
|
|
1961
|
-
## Roadmap / Backlog
|
|
1962
|
-
|
|
1963
|
-
Planned or partially designed enhancements not yet merged:
|
|
1964
|
-
|
|
1965
|
-
- Structural motif diversity pressure: penalize over-represented connection patterns (entropy-based sharing) to sustain innovation.
|
|
1966
|
-
- Automated compatibility coefficient tuning: search or adapt excess/disjoint/weight coefficients to stabilize species counts without manual calibration.
|
|
1967
|
-
- Faster Pareto sorting: divide-and-conquer or incremental dominance maintenance to reduce O(N^2) overhead for large populations.
|
|
1968
|
-
- Connection complexity budget (current budget targets nodes only) and dual-objective weighting option.
|
|
1969
|
-
- Diversity-aware parent selection leveraging motif entropy and archive dispersion.
|
|
1970
|
-
- Extended novelty descriptors helper utilities (e.g. built-in graph metrics: depth, feedforwardness, clustering).
|
|
1971
|
-
- Visualization hooks (species lineage graph, archive embedding projection) for diagnostics.
|
|
1972
|
-
</code></pre>
|
|
1973
1890
|
<footer class="site-footer">Generated from source JSDoc • <a href="https://github.com/reicek/NeatapticTS">GitHub</a></footer></main><aside class="toc"></aside></div></body></html>
|
package/package.json
CHANGED