@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 CHANGED
@@ -103,8 +103,7 @@ import { Neat } from '@reicek/neataptic-ts';
103
103
 
104
104
  const neatMo = new Neat(2, 1, fitness, {
105
105
  popsize: 60,
106
- multiObjective: { enabled: true, complexityMetric: 'nodes' },
107
- novelty: { enabled: true, descriptor: g => [g.nodes.length, g.connections.length], k: 8, blendFactor: 0.25 },
106
+ multiObjective: { enabled: true },
108
107
  seed: 7,
109
108
  });
110
109
  await neatMo.evaluate();
@@ -191,10 +190,12 @@ Principle: add one lever at a time and measure its *telemetry delta* (front size
191
190
  Fast iteration recipe:
192
191
 
193
192
  ```ts
193
+ import { cpus } from 'node:os';
194
+
194
195
  const neat = new Neat(inp, out, fit, {
195
196
  popsize: 120,
196
197
  fastMode: true,
197
- threads: (require('os').cpus().length - 1),
198
+ threads: cpus().length > 1 ? cpus().length - 1 : 1,
198
199
  adaptiveMutation: { enabled: true, strategy: 'twoTier' },
199
200
  telemetry: { enabled: true, performance: true, complexity: true }
200
201
  });
@@ -221,7 +222,10 @@ Evolve topology first, then fine‑tune weights:
221
222
 
222
223
  ```ts
223
224
  const best = neat.getBest();
224
- await best?.train(data, { iterations: 500, rate: 0.01, optimizer: 'adam',
225
+ await best?.train(data, {
226
+ iterations: 500,
227
+ rate: 0.01,
228
+ optimizer: 'adam',
225
229
  gradientClip: { mode: 'norm', maxNorm: 1 },
226
230
  movingAverageWindow: 5,
227
231
  });
@@ -238,7 +242,7 @@ await best?.train(data, {
238
242
  optimizer: 'adamw',
239
243
  gradientClip: { mode: 'norm', maxNorm: 1 },
240
244
  movingAverageWindow: 7,
241
- metricsHook: m => console.log(m.iteration, m.error, m.gradNorm)
245
+ metricsHook: m => console.log(m.iteration, m.error, m.gradNorm),
242
246
  });
243
247
  ```
244
248
 
@@ -351,7 +355,7 @@ This section summarizes practical knobs to accelerate evolution while preserving
351
355
 
352
356
  ### Structural Complexity Caching
353
357
 
354
- `network.evolve` caches per-genome complexity metrics (nodes / connections / gates). This avoids recomputing counts each evaluation when unchanged, reducing overhead for large populations or deep architectures.
358
+ `neat.evolve` caches per-genome complexity metrics (nodes / connections / gates). This avoids recomputing counts each evaluation when unchanged, reducing overhead for large populations or deep architectures.
355
359
 
356
360
  ### Profiling
357
361
 
@@ -380,8 +384,6 @@ const neat = new Neat(inputs, outputs, fitness, {
380
384
  popsize: 120,
381
385
  threads: 8,
382
386
  telemetry: { enabled: true, performance: true },
383
- telemetrySelect: ['performance', 'complexity'],
384
- adaptiveMutation: { enabled: true, strategy: 'twoTier' },
385
387
  fastMode: true,
386
388
  });
387
389
  ```
@@ -427,7 +429,7 @@ When enabled BEFORE constructing / mutating a `Network`:
427
429
  Intended Usage:
428
430
 
429
431
  ```ts
430
- import { config } from 'neataptic';
432
+ import { config, methods } from '@reicek/neataptic-ts';
431
433
  config.deterministicChainMode = true; // enable
432
434
  const net = new Network(1, 1);
433
435
  for (let i = 0; i < 5; i++) net.mutate(methods.mutation.ADD_NODE); // guaranteed 5 hidden chain
@@ -524,9 +526,7 @@ const neat = new Neat(4, 2, fitnessFn, {
524
526
  multiObjective: { enabled: true, complexityMetric: 'nodes' },
525
527
  });
526
528
  // Add structural entropy (maximize)
527
- neat.registerObjective('entropy', 'max', (g) =>
528
- (neat as any)._structuralEntropy(g)
529
- );
529
+ neat.registerObjective('entropy', 'max', g => (neat as any)._structuralEntropy(g));
530
530
  await neat.evaluate();
531
531
  await neat.evolve();
532
532
  console.log(neat.getObjectives()); // [{key:'fitness',...},{key:'complexity',...},{key:'entropy',...}]
@@ -583,16 +583,16 @@ Configure an adaptive schedule that expands limits when improvement slope is pos
583
583
 
584
584
  ```ts
585
585
  complexityBudget: {
586
- enabled:true,
587
- mode:'adaptive',
588
- maxNodesStart: input+output+2,
589
- maxNodesEnd: (input+output+2)*6,
590
- improvementWindow: 8,
591
- increaseFactor:1.15,
592
- stagnationFactor:0.93,
593
- minNodes: input+output+2,
594
- maxConnsStart: 40,
595
- maxConnsEnd: 400
586
+ enabled: true,
587
+ mode: 'adaptive',
588
+ maxNodesStart: 4,
589
+ maxNodesEnd: 24,
590
+ improvementWindow: 8,
591
+ increaseFactor: 1.15,
592
+ stagnationFactor: 0.93,
593
+ minNodes: 4,
594
+ maxConnsStart: 40,
595
+ maxConnsEnd: 400,
596
596
  }
597
597
  ```
598
598
 
@@ -669,11 +669,11 @@ diversityPressure:{ enabled:true, motifSample:25, penaltyStrength:0.05 }
669
669
  Adaptive novelty threshold targeting an archive insertion rate:
670
670
 
671
671
  ```ts
672
- novelty:{
673
- enabled:true,
674
- descriptor:g=>[g.nodes.length, g.connections.length],
675
- archiveAddThreshold:0.5,
676
- dynamicThreshold:{ enabled:true, targetRate:0.15, adjust:0.1, min:0.01, max:10 }
672
+ novelty: {
673
+ enabled: true,
674
+ descriptor: g => [g.nodes.length, g.connections.length],
675
+ archiveAddThreshold: 0.5,
676
+ dynamicThreshold: { enabled: true, targetRate: 0.15, adjust: 0.1, min: 0.01, max: 10 },
677
677
  }
678
678
  ```
679
679
 
@@ -684,13 +684,13 @@ After each evaluation the threshold is nudged up/down so the fraction of inserte
684
684
  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:
685
685
 
686
686
  ```ts
687
- multiObjective:{
688
- enabled:true,
689
- objectives:[
690
- { key:'fitness', direction:'max', accessor: g => g.score },
691
- { key:'novelty', direction:'max', accessor: g => (g as any)._novelty }
692
- ],
693
- pruneInactive:{ enabled:true, window:5, rangeEps:1e-9, protect:['fitness'] }
687
+ multiObjective: {
688
+ enabled: true,
689
+ objectives: [
690
+ { key: 'fitness', direction: 'max', accessor: g => g.score },
691
+ { key: 'novelty', direction: 'max', accessor: g => (g as any)._novelty },
692
+ ],
693
+ pruneInactive: { enabled: true, window: 5, rangeEps: 1e-9, protect: ['fitness'] },
694
694
  }
695
695
  ```
696
696
 
@@ -742,13 +742,13 @@ Use these to detect genealogical stagnation (both remaining near zero) vs broad
742
742
  Apply score adjustments based on lineage structure (depth dispersion) or penalize inbreeding (high ancestor overlap):
743
743
 
744
744
  ```ts
745
- lineagePressure:{
746
- enabled:true,
747
- mode:'antiInbreeding', // 'penalizeDeep' | 'rewardShallow' | 'spread' | 'antiInbreeding'
748
- strength:0.02, // generic scaling for depth modes
749
- ancestorWindow:4, // generations to look back when computing ancestor sets
750
- inbreedingPenalty:0.04, // override penalty scaling (defaults to strength*2)
751
- diversityBonus:0.02 // bonus scaling for very distinct parent lineages
745
+ lineagePressure: {
746
+ enabled: true,
747
+ mode: 'antiInbreeding', // 'penalizeDeep' | 'rewardShallow' | 'spread' | 'antiInbreeding'
748
+ strength: 0.02, // generic scaling for depth modes
749
+ ancestorWindow: 4, // generations to look back when computing ancestor sets
750
+ inbreedingPenalty: 0.04, // override penalty scaling (defaults to strength*2)
751
+ diversityBonus: 0.02, // bonus scaling for very distinct parent lineages
752
752
  }
753
753
  ```
754
754
 
@@ -791,7 +791,7 @@ Operator adaptation vs bandit:
791
791
  Per-genome mutation rate/amount adapt each generation under strategies (`twoTier`, `exploreLow`, `anneal`). Use:
792
792
 
793
793
  ```ts
794
- adaptiveMutation:{ enabled:true, strategy:'twoTier', sigma:0.08, adaptAmount:true }
794
+ adaptiveMutation: { enabled: true, strategy: 'twoTier', sigma: 0.08, adaptAmount: true }
795
795
  ```
796
796
 
797
797
  Operator success statistics (bandit + weighting):
@@ -875,7 +875,7 @@ const csv = neat.exportSpeciesHistoryCSV();
875
875
 
876
876
  These APIs are evolving; consult source `src/neat.ts` for full option surfaces while docs finalize.
877
877
 
878
- Full option & telemetry reference: [docs/API.md](./docs/API.md)
878
+ Full option & telemetry reference: See the generated documentation in the `docs/` directory.
879
879
 
880
880
  # Network Constructor Update
881
881
 
@@ -888,6 +888,7 @@ new Network(input: number, output: number, options?: { minHidden?: number })
888
888
  - `input`: Number of input nodes (required)
889
889
  - `output`: Number of output nodes (required)
890
890
  - `options.minHidden`: (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.
891
+ - `options.seed`: (optional) A numeric seed for the random number generator to ensure reproducible initial weights and biases.
891
892
 
892
893
  **Example:**
893
894
 
@@ -895,21 +896,25 @@ new Network(input: number, output: number, options?: { minHidden?: number })
895
896
  // Standard 1-1 network (no hidden nodes)
896
897
  const net = new Network(1, 1);
897
898
 
898
- // Enforce at least 3 hidden nodes
899
- const netWithHidden = new Network(2, 1, { minHidden: 3 });
899
+ // Enforce at least 3 hidden nodes and set a seed
900
+ const netWithHidden = new Network(2, 1, { minHidden: 3, seed: 123 });
900
901
  ```
901
902
 
902
- # Neat Evolution minHidden Option
903
+ # Neat Evolution Options
903
904
 
904
- The `minHidden` option can also be passed to the `Neat` class to enforce a minimum number of hidden nodes in all evolved networks:
905
+ The `Neat` class constructor accepts an options object that includes all `Network` options (`minHidden`, `seed`) plus evolution-specific settings.
905
906
 
906
907
  ```ts
907
- import Neat from './src/neat';
908
- const neat = new Neat(2, 1, fitnessFn, { popsize: 50, minHidden: 5 });
908
+ import { Neat } from '@reicek/neataptic-ts';
909
+ const neat = new Neat(2, 1, fitnessFn, {
910
+ popsize: 50,
911
+ minHidden: 5, // Passed to Network constructor
912
+ seed: 4242, // Passed to both Neat and Network constructors
913
+ });
909
914
  ```
910
915
 
911
916
  - All networks created by the evolutionary process will have at least 5 hidden nodes.
912
- - This is useful for ensuring a minimum network complexity during neuro-evolution.
917
+ - The `seed` ensures that the entire evolutionary process, including initial population creation, is reproducible.
913
918
 
914
919
  See tests in `test/neat.ts` for usage and verification.
915
920
 
@@ -922,7 +927,7 @@ Interoperability layer for exchanging strictly layered MLP (and experimental rec
922
927
  ### Basic Usage
923
928
 
924
929
  ```ts
925
- import { exportToONNX, importFromONNX } from './src/architecture/onnx';
930
+ import { exportToONNX, importFromONNX } from '@reicek/neataptic-ts';
926
931
 
927
932
  // Export
928
933
  const onnxModel = exportToONNX(network, { includeMetadata: true });
@@ -1088,7 +1093,7 @@ Notes:
1088
1093
  ### Learning Rate Scheduler Usage
1089
1094
 
1090
1095
  ```ts
1091
- import methods from './src/methods/methods';
1096
+ import { methods } from '@reicek/neataptic-ts';
1092
1097
  const ratePolicy = methods.Rate.cosineAnnealingWarmRestarts(200, 1e-5, 2);
1093
1098
  net.train(data, { iterations: 1000, rate: 0.1, ratePolicy });
1094
1099
  ```
@@ -1330,8 +1335,7 @@ Optimizer reference (choose based on signal quality & overfitting risk):
1330
1335
  | adamax | beta1, beta2, eps | Infinity norm (u) instead of v |
1331
1336
  | nadam | beta1, beta2, eps | Nesterov variant of Adam |
1332
1337
  | radam | beta1, beta2, eps | Rectifies variance early in training |
1333
- | lion | beta1, beta2 | Direction = sign(beta1*m + beta2*m2) |
1334
- | adabelief | beta1, beta2, eps | Second moment of (g - m) (gradient surprise) |
1338
+ | AdaBelief | beta1, beta2, eps | Second moment of (g - m) (gradient surprise) |
1335
1339
  | lookahead | baseType, la_k, la_alpha | Interpolates toward slow weights every k steps |
1336
1340
 
1337
1341
  General guidance:
@@ -1658,92 +1662,3 @@ const neat = new Neat(2, 1, fitness, {
1658
1662
  ```
1659
1663
 
1660
1664
  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.
1661
-
1662
- ```
1663
-
1664
- #### Adaptive Sharing
1665
-
1666
- If `adaptiveSharing.enabled` the system adjusts `sharingSigma` each generation:
1667
-
1668
- ```
1669
-
1670
- sigma += step \* sign(fragmentation - target)
1671
-
1672
- ```
1673
-
1674
- within `[minSigma,maxSigma]`.
1675
-
1676
- #### Multi-Objective Notes & Strategy {#multi-objective-notes--strategy}
1677
-
1678
- 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:
1679
-
1680
- - Start single-objective until baseline performance plateaus, then enable `multiObjective.enabled` with `complexityMetric:'nodes'`.
1681
- - If early search stagnates due to premature parsimony, delay complexity with `multiObjective.dynamic.addComplexityAt`.
1682
- - Use `autoEntropy` to seed a third diversity proxy objective only when structural collapse is observed (few species, low ancestor uniqueness).
1683
- - Monitor front size; if it grows too large relative to population, enable `adaptiveEpsilon` to tighten dominance criteria.
1684
-
1685
- Planned (future): faster dominance (divide-and-conquer), richer motif diversity pressure, automated compatibility coefficient tuning.
1686
-
1687
- ## ASCII Maze Example: 6‑Input Long-Range Vision (MazeVision)
1688
-
1689
- The ASCII maze example uses a compact 6‑input perception schema ("MazeVision") with long‑range lookahead via a precomputed distance map. Inputs (order fixed):
1690
-
1691
- 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.
1692
- 2. openN
1693
- 3. openE
1694
- 4. openS
1695
- 5. openW
1696
- 6. progressDelta: Normalized recent progress signal around 0.5 ( >0.5 improving, <0.5 regressing ).
1697
-
1698
- ### Openness Semantics (openN/E/S/W) {#openness-semantics}
1699
-
1700
- 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.
1701
-
1702
- Value encoding:
1703
-
1704
- - 1: Direction(s) whose total path length Ldir is minimal among all strictly improving neighbors (ties allowed; multiple 1s possible).
1705
- - 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.
1706
- - 0: Wall, unreachable cell, dead end, or any non‑improving move (neighbor distance >= current distance) – all treated uniformly.
1707
- - 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]).
1708
-
1709
- Rules / Notes:
1710
-
1711
- - Strict improvement filter: Only neighbors whose distanceMap value is strictly less than the current cell distance are considered for 1 or ratio values.
1712
- - Horizon clipping: Paths with Ldir > H are treated as unreachable (value 0) to bound search cost.
1713
- - Multiple bests: Corridors that fork into equivalently short routes produce multiple 1s, encouraging neutrality across equally optimal choices.
1714
- - Backtrack marker is intentionally very small (0.001) so evolution distinguishes "retreat only" states from true walls without overweighting them.
1715
- - 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.
1716
-
1717
- ### progressDelta
1718
-
1719
- 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.
1720
-
1721
- ### Debugging
1722
-
1723
- 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.
1724
-
1725
- ### Quick Reference
1726
-
1727
- | Signal | Meaning |
1728
- | ------ | --------------------------------------------------- |
1729
- | 1 | Best strictly improving path(s) (minimal Ldir) |
1730
- | (0,1) | Longer but improving path (ratio Lmin/Ldir) |
1731
- | 0.001 | Only backtrack available (opposite of prior action) |
1732
- | 0 | Wall / dead end / non‑improving / unreachable |
1733
-
1734
- Implementation: `test/examples/asciiMaze/mazeVision.ts` (function `MazeVision.buildInputs6`).
1735
-
1736
- 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.
1737
-
1738
- ## Roadmap / Backlog
1739
-
1740
- Planned or partially designed enhancements not yet merged:
1741
-
1742
- - Structural motif diversity pressure: penalize over-represented connection patterns (entropy-based sharing) to sustain innovation.
1743
- - Automated compatibility coefficient tuning: search or adapt excess/disjoint/weight coefficients to stabilize species counts without manual calibration.
1744
- - Faster Pareto sorting: divide-and-conquer or incremental dominance maintenance to reduce O(N^2) overhead for large populations.
1745
- - Connection complexity budget (current budget targets nodes only) and dual-objective weighting option.
1746
- - Diversity-aware parent selection leveraging motif entropy and archive dispersion.
1747
- - Extended novelty descriptors helper utilities (e.g. built-in graph metrics: depth, feedforwardness, clustering).
1748
- - Visualization hooks (species lineage graph, archive embedding projection) for diagnostics.
1749
- ```