e9302bc9f6
The BITBRAIN name was sitting on a gun with no network in it. This is the gun
that actually runs the algorithm: an ADE layer (thresholded random projections
with ONLINE threshold adaptation) feeding the SBC head from
common_libs/bitbrain/, with the counted+decay mode available.
common_libs/guns/bitbrain_net.nim the gun
rack name BITBRAIN, rack id 17 (rack 17 -> 18 guns), both new guns default OFF
admitted by TR_RACK_BITBRAIN=both AND TR_BITBRAIN_NET=1 (the switch that also
disowns LEADGAIN's legacy TR_BITBRAIN_* aliases)
OUTPUT: a fine-grained aim CORRECTION on top of Pattern - the probability-
weighted mean of the nClasses class centres under inferProb - not a direct aim
point from the argmax. That is the shape docs/bitbrain_gate.md measured, and
Pattern is already a strong predictor, so the net's job is the signed residual.
Below TR_BITBRAIN_MINOBS the shift is exactly 0 and Pattern is returned
unchanged.
INPUT: a CONFIGURED set of FEATURE BLOCKS (TR_BITBRAIN_FEATURES=name:W), each
block's width == its resolution, laid out as a thermometer code over 0/255 slots
(so an ADE synapse 'matches' when its polarity agrees with the slot and a random
ADE fires iff its w synapses all match, rate 2^-w). Default is 52 slots over 9
blocks. NO long temporal window, per docs/state_window_gate.md: the only history
is a 12-tick ring feeding three rate/turn quantities.
Every knob env-configurable: _INPUT (width), _NCLASSES, _NADES, _WIDTHS
(clause widths), _FEATURES, _SPAN, _MODE, _DECAY_EVERY, _DECAY_SHIFT,
_MINOBS, _ADAPT_EVERY, _TARGET, _NETSEED, _NETLOG, _NET_RESET_ON_TARGET.
MEASURED SCALING (measure_bitbrain_scaling.nim, 3 recorded runs, 37412 ticks,
-d:release, one predict per power bin per tick, timed region = predicts only):
RAM 1.59 MB default (98.6% SBC tensors); linear in nClasses, QUADRATIC in
nAde, FLAT in input width; counted/bitset = 7.30x on RAM, ~1x on time.
ms/tick 2.70 default = 21% of the 13.16 ms budget; 64 classes busts it (149%),
nAde 512 uses 74%, nAde 64 uses 2%.
CAPACITY vs ACCURACY: over a 100x RAM range the offline mean |err| moves
17.254 -> 17.115 deg around Pattern's 16.964, and the sign flips along the
nClasses axis, so it is noise, not a trend. The corrector is consistently
slightly WORSE than Pattern. The ceiling is the STATE, not the classifier.
VETO-CAPABLE OFFLINE CHECK ONLY (docs/offline_harness_trust.md), never
presented as a live win.
ENGAGEMENT is proven, not assumed: test_bitbrain_net.nim (42 checks) shows 0
bytes before first use, different inputs -> different class outputs, a learn
raises SBC occupancy, bitset learn idempotent while counted learn is monotone,
threshold adaptation runs, and the global RNG is untouched.
Parity: shipped rack still onlyPattern, shipped movement still strafe. Guards:
test_env_report 25, test_rack_membership 48, test_tm_pattern_registration 20,
test_lead_gain_registration 13, test_lead_gain_legacy 24, test_bitbrain 56,
test_gun_harness 39, test_tfil_commit_env 30, test_bitbrain_net 42.
Clean archive build: [SuccessX].
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
162 lines
6.8 KiB
Nim
162 lines
6.8 KiB
Nim
## Guard test for the boot-time env report's Part 2 (the warnings).
|
|
##
|
|
## NO battle, NO Java, NO server. Run with:
|
|
## nim c -r common_libs/tests/test_env_report.nim
|
|
##
|
|
## It pins the two tables that must not drift:
|
|
## 1. the KNOWN TR_*/GUN_* name set covers every env read in the tree, so a
|
|
## new knob cannot silently start producing a false "unknown variable"
|
|
## warning (and, conversely, the set cannot rot);
|
|
## 2. the compile-time define map covers every `{.intdefine.}`/`{.strdefine.}`
|
|
## /`{.booldefine.}` symbol in the tree, and classifies the ONE real env
|
|
## twin (`TMH_NSTATES` -> `TR_TMHORIZON_NSTATES`) correctly.
|
|
|
|
import std/[os, strutils, sets, sequtils]
|
|
import gun_harness/selector
|
|
import "../../ModularBot_garage/src/env_report"
|
|
|
|
var failures = 0
|
|
proc check(name: string, ok: bool) =
|
|
if ok: echo "PASS: ", name
|
|
else: echo "FAIL: ", name; inc failures
|
|
|
|
const repoRoot = currentSourcePath().parentDir.parentDir.parentDir
|
|
|
|
# ── the source surface the scan walks (the bot's real modules) ───────────────
|
|
|
|
proc scannedFiles(): seq[string] =
|
|
## .nim files under common_libs (excluding common_libs/tests, which are
|
|
## probes/measurement scripts, not bot code) and ModularBot_garage/src.
|
|
## env_report.nim is excluded: it DEFINES the known set, so scanning it is
|
|
## circular (it also names "suggested but nonexistent" env vars on purpose).
|
|
for dir in [repoRoot / "common_libs", repoRoot / "ModularBot_garage" / "src"]:
|
|
for path in walkDirRec(dir):
|
|
if not path.endsWith(".nim"): continue
|
|
if "/tests/" in path: continue
|
|
if path.endsWith("/env_report.nim"): continue
|
|
result.add path
|
|
|
|
proc literalsOn(line: string): seq[string] =
|
|
## Double-quoted string literals on one line (env names have no escapes).
|
|
var i = 0
|
|
while i < line.len:
|
|
if line[i] == '"':
|
|
var j = i + 1
|
|
while j < line.len and line[j] != '"': inc j
|
|
if j >= line.len: break
|
|
result.add line[i + 1 ..< j]
|
|
i = j + 1
|
|
else:
|
|
inc i
|
|
|
|
proc isKnownOrPrefix(name: string, known: HashSet[string]): bool =
|
|
## A literal counts as covered when it is a known name OR a prefix of one
|
|
## (`TR_RACK_` is the rack prefix; `TR_`/`GUN_` are the filter prefixes).
|
|
if name in known: return true
|
|
for k in known:
|
|
if k.startsWith(name): return true
|
|
|
|
proc defineSymbolsOn(line: string): seq[string] =
|
|
## ` TMH_NCLAUSES* {.intdefine.} = 40` -> "TMH_NCLAUSES"
|
|
for pragma in ["{.intdefine.}", "{.strdefine.}", "{.booldefine.}"]:
|
|
if pragma in line:
|
|
var sym = line.split('{')[0].strip()
|
|
if sym.endsWith("*"): sym.setLen(sym.len - 1)
|
|
let parts = sym.splitWhitespace()
|
|
if parts.len > 0: result.add parts[^1]
|
|
|
|
# ── 1. the known-name set ────────────────────────────────────────────────────
|
|
|
|
proc testKnownNames() =
|
|
let known = knownEnvNames()
|
|
let s = known.toHashSet()
|
|
check "known set has no duplicates", known.len == s.len
|
|
check "known set only holds TR_*/GUN_* names",
|
|
known.allIt(it.startsWith("TR_") or it.startsWith("GUN_"))
|
|
check "known set covers all 18 rack names",
|
|
RackGunNames.allIt((RackEnvPrefix & it) in s)
|
|
for name in ["TR_MOVEMENT", "TR_POWER_ENERGY_MIN", "TR_TMHORIZON_NSTATES",
|
|
"GUN_VBULLET_METRIC", "TR_ENV_REPORT", "GUN_SELECTOR_SEED",
|
|
"TR_RACK_TMHORIZON", "TR_TMHORIZON_NSTATES"]:
|
|
check "known set contains " & name, name in s
|
|
|
|
# ── 2. the pure warning helpers ──────────────────────────────────────────────
|
|
|
|
proc testUnknownEnvNames() =
|
|
let unknown = unknownEnvNames(@[
|
|
("TR_POWER_LOG", "1"), ("GUN_STATS_PATH", "/tmp/x"),
|
|
("TR_TMHORIZON_NSTATE", "5"), ("PATH", "/bin"),
|
|
("TMH_NSTATES", "2"), ("JAVA_HOME", "/jdk")])
|
|
check "unknownEnvNames finds the misspelled TR_* only",
|
|
unknown == @["TR_TMHORIZON_NSTATE"]
|
|
check "unknownEnvNames ignores non TR_*/GUN_* names",
|
|
unknownEnvNames(@[("PATH", "/bin")]) == @[]
|
|
|
|
proc testDefineMap() =
|
|
check "isCompileTimeDefine(TMH_NSTATES)", isCompileTimeDefine("TMH_NSTATES")
|
|
check "isCompileTimeDefine(TM_S_DEF)", isCompileTimeDefine("TM_S_DEF")
|
|
check "isCompileTimeDefine(TR_TMHORIZON_NSTATES) is false",
|
|
not isCompileTimeDefine("TR_TMHORIZON_NSTATES")
|
|
check "defineWarningMessage is empty for a non-define",
|
|
defineWarningMessage("TR_MOVEMENT", "x") == ""
|
|
|
|
let m1 = defineWarningMessage("TMH_NSTATES", "2")
|
|
check "TMH_NSTATES warns it is compile-time and names TR_TMHORIZON_NSTATES",
|
|
"COMPILE-TIME define (-d:TMH_NSTATES=2)" in m1 and
|
|
"TR_TMHORIZON_NSTATES=2" in m1
|
|
let m2 = defineWarningMessage("TMH_NCLAUSES", "40")
|
|
check "TMH_NCLAUSES says TR_TMHORIZON_NCLAUSES does not exist",
|
|
"TR_TMHORIZON_NCLAUSES does not exist" in m2
|
|
let m3 = defineWarningMessage("TM_S_DEF", "1.5")
|
|
check "TM_S_DEF says this knob is compile-time only",
|
|
"compile-time only" in m3
|
|
|
|
let defs = setCompileTimeDefines(@[
|
|
("TMH_NSTATES", "2"), ("TR_MOVEMENT", "tfil"), ("TM_S_DEF", "1.5")])
|
|
check "setCompileTimeDefines finds both -d symbols",
|
|
defs == @["TMH_NSTATES", "TM_S_DEF"]
|
|
|
|
# ── 3. the tree scans (the anti-drift guard) ─────────────────────────────────
|
|
|
|
proc testTreeScan() =
|
|
let known = knownEnvNames().toHashSet()
|
|
var literals = 0
|
|
var missing: seq[string]
|
|
for path in scannedFiles():
|
|
for line in lines(path):
|
|
for lit in literalsOn(line):
|
|
if lit.startsWith("TR_") or lit.startsWith("GUN_"):
|
|
inc literals
|
|
if not isKnownOrPrefix(lit, known):
|
|
missing.add path.extractFilename & ":" & lit
|
|
check "tree scan is alive (>= 50 TR_*/GUN_* literals)", literals >= 50
|
|
check "every TR_*/GUN_* literal in the tree is in the known set: " &
|
|
$missing, missing.len == 0
|
|
|
|
proc testDefineScan() =
|
|
let mapped = CompileTimeDefineMap.mapIt(it.defineName).toHashSet()
|
|
var found: seq[string]
|
|
for path in scannedFiles():
|
|
for line in lines(path):
|
|
for sym in defineSymbolsOn(line):
|
|
found.add sym
|
|
var missing: seq[string]
|
|
for sym in found:
|
|
if sym notin mapped: missing.add sym
|
|
check "tree define scan is alive (>= 20 symbols)", found.len >= 20
|
|
check "every intdefine/strdefine/booldefine is in the map: " &
|
|
$missing, missing.len == 0
|
|
|
|
# ── driver ───────────────────────────────────────────────────────────────────
|
|
|
|
testKnownNames()
|
|
testUnknownEnvNames()
|
|
testDefineMap()
|
|
testTreeScan()
|
|
testDefineScan()
|
|
|
|
if failures > 0:
|
|
echo "\n", failures, " check(s) FAILED"
|
|
quit(1)
|
|
echo "\nAll env-report checks passed."
|