## L1 / L3 tests for the repaired Tsetlin gun. ## ## L1 (clause sparsity + nonzero correction): before the fix the gun's Type I ## feedback never conditioned on the clause output, so every frequently-true ## literal ratcheted toward Include with no counter-force. Measured on ## `energy-threshold-turner` the clauses saturated at mean ≈714 included ## literals/clause (max 747, 100/100 active) and the correction was nonzero on ## only 8 of 764 predict calls — i.e. `Tsetlin.vHits` was effectively `Linear`. ## ## L3 (divergence): with the corrected Granmo Table 2/3 rules, the learning core ## is seeded (`makeTsetlinDriver(seed=1)`) so these numbers are reproducible. ## ## Run: nim c -r common_libs/tests/test_tsetlin_gun.nim import std/strformat import gun_harness/offline_range import range_guns import guns/tsetlin import guns/linear var failures = 0 proc check(name: string, ok: bool) = if ok: echo "PASS: ", name else: echo "FAIL: ", name; inc failures proc main() = # ── L1 ──────────────────────────────────────────────────────────────────── block: let fx = synthesizeEnergyThresholdTurner() let (drv, gun) = makeTsetlinDriver(seed = 1) let reps = replayFixture(fx, @[drv], metric = bmPoint) let st = gun[].tmClauseStats() echo &"L1 energy-threshold-turner (ticks={fx.states.len}):" echo &" clauses: mean={st.meanIncluded:.1f} max={st.maxIncluded} " & &"active={st.nActive}/{st.nClauses} meanAll={st.meanIncludedAll:.1f}" echo &" correction nonzero on {gun[].correctionsNonzero}/{gun[].predictCalls} predict calls" echo &" trainedShots={gun[].trainedShots} traceMisses={gun[].traceMisses} hits={reps[0].hits}/{reps[0].shots}" check "L1: mean included literals/clause fell to a sparse regime (< 40; was ~714)", st.meanIncluded < 40.0 check "L1: at least some clauses are active (the TM is doing something)", st.nActive > 0 check "L1: the learned correction is nonzero", gun[].correctionsNonzero > 0 check "L1: correction is nonzero on most predicts", gun[].correctionsNonzero * 2 > gun[].predictCalls check "L1: trace pairing intact (trainedShots > 0, traceMisses == 0)", gun[].trainedShots > 0 and gun[].traceMisses == 0 echo "" # ── L3 ──────────────────────────────────────────────────────────────────── block: var diverged = false var energyT, energyL: GunReport echo "L3 divergence over synthetic range fixtures (seed=1):" for name in SyntheticFixtureNames: let fx = synthesizeByName(name) let (drv, gun) = makeTsetlinDriver(seed = 1) let reps = replayFixture(fx, @[drv, makeDriver("Linear", LinearGun())], metric = bmPoint) let t = reps[0] let l = reps[1] echo &" {name:<26} Tsetlin {t.hits:>4}/{t.shots:<4} Linear {l.hits:>4}/{l.shots:<4} " & &"nzCorr={gun[].correctionsNonzero}/{gun[].predictCalls}" if t.hits != l.hits: diverged = true if name == "energy-threshold-turner": energyT = t energyL = l echo "" echo &"L3 energy-threshold-turner: Tsetlin {energyT.hits}/{energyT.shots} " & &"({energyT.hitRate()*100:.1f}%) vs Linear {energyL.hits}/{energyL.shots} " & &"({energyL.hitRate()*100:.1f}%)" check "L3: Tsetlin.vHits diverges from Linear.vHits on at least one range fixture", diverged check "L3: Tsetlin.vHits != Linear.vHits on energy-threshold-turner", energyT.hits != energyL.hits if failures > 0: echo "\n", failures, " check(s) FAILED" quit(1) echo "\nAll Tsetlin-gun checks passed." when isMainModule: main()