j140 rebuild a real BitBrain gun: ADE+SBC at rack id 17, default off, and measure its scaling
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>
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
|
||||
import std/[os, strformat, strutils, times, math]
|
||||
import gun_harness/[gun_interface, virtual_bullets, prediction_quality]
|
||||
import guns/[head_on, pattern_matcher, tm_horizon, lead_gain]
|
||||
import guns/[head_on, pattern_matcher, tm_horizon, lead_gain, bitbrain_net]
|
||||
|
||||
# arm indices (fixed order = fixed output)
|
||||
const
|
||||
@@ -43,10 +43,16 @@ const
|
||||
# is the rule the corrector must match; it needs no learning (range is known at fire
|
||||
# time). The table was selected in-sample from this corpus.
|
||||
A_BAND* = 13
|
||||
## The REAL ADE+SBC gun (rack id 17, `guns/bitbrain_net.nim`): Pattern base plus
|
||||
## a fine-grained, class-resolved angular correction from the network. The gun
|
||||
## is default OFF (`TR_BITBRAIN_NET=0`), so the arm turns its own switch on
|
||||
## here — the ruler is the one place that must exercise it.
|
||||
A_BBN* = 14
|
||||
BandGainTable* = [1.0, 1.0, 1.0, 0.0, 0.0]
|
||||
ArmNames* = ["Oracle", "OracleQuant", "HeadOn", "Pattern", "PatternGain1.5",
|
||||
"PatternGain2.0", "PatternGain3.0", "NaiveLinear", "TMHorizon", "LeadGain",
|
||||
"PatternGain0.25", "PatternGain0.50", "PatternGain0.75", "PatternBandGain"]
|
||||
"PatternGain0.25", "PatternGain0.50", "PatternGain0.75", "PatternBandGain",
|
||||
"BitBrainNet"]
|
||||
|
||||
const
|
||||
## The five sub-unity gain arms, in increasing order, resolved to arm indices.
|
||||
@@ -96,6 +102,7 @@ type Ctx = object
|
||||
naive: NaiveLinearGun
|
||||
tmh: TmHorizonGun
|
||||
lg: LeadGainGun
|
||||
bbn: BitbrainNetGun
|
||||
headon: HeadOnGun
|
||||
st: WorldState
|
||||
enemy: seq[EnemyInfo]
|
||||
@@ -173,6 +180,10 @@ proc runRound(ctx: var Ctx, arms: var seq[ArmAcc], r: int) =
|
||||
let bp = predict(ctx.lg, ctx.st, speed)
|
||||
let bl = wrap180(bearingDeg(ox, oy, bp.x, bp.y) - los)
|
||||
arms[A_LG].record(rng, wrap180(bl - targetLead), bl, targetLead)
|
||||
# BITBRAIN (ADE+SBC): Pattern base + the network's fine-grained correction
|
||||
let np2 = predict(ctx.bbn, ctx.st, speed)
|
||||
let nl2 = wrap180(bearingDeg(ox, oy, np2.x, np2.y) - los)
|
||||
arms[A_BBN].record(rng, wrap180(nl2 - targetLead), nl2, targetLead)
|
||||
|
||||
proc runOne(runPath: string, arms: var seq[ArmAcc], shotsCont, shotsQuant: var ShotStat,
|
||||
doShots: bool, timing: bool, cont: bool): int =
|
||||
@@ -186,6 +197,7 @@ proc runOne(runPath: string, arms: var seq[ArmAcc], shotsCont, shotsQuant: var S
|
||||
naive: NaiveLinearGun(lastTick: -1),
|
||||
tmh: initTmHorizonGun(),
|
||||
lg: initLeadGainGun(),
|
||||
bbn: initBitbrainNetGun(),
|
||||
headon: HeadOnGun(),
|
||||
st: WorldState(arenaWidth: c.arenaW, arenaHeight: c.arenaH),
|
||||
enemy: newSeq[EnemyInfo](1))
|
||||
@@ -213,6 +225,11 @@ proc fmt3(x: float): string =
|
||||
if x.classify in {fcNan, fcInf, fcNegInf}: "-" else: fmt"{x:.3f}"
|
||||
|
||||
proc main() =
|
||||
# The ADE+SBC gun is default OFF (two switches). This arm is the one place
|
||||
# that must exercise it, so the ruler turns its master switch on for the whole
|
||||
# process — every other arm is unaffected (they never read TR_BITBRAIN_*).
|
||||
putEnv(BBN_NET_ENV, "1")
|
||||
putEnv(BBN_MINOBS_ENV, "1")
|
||||
var corpusRoot = "/tmp/tfil_ab2/out"
|
||||
var limit = 0
|
||||
var doShots = true
|
||||
@@ -296,9 +313,10 @@ proc main() =
|
||||
let mPat = overallMean(arms, A_PATTERN)
|
||||
let mTmh = overallMean(arms, A_TMH)
|
||||
let mLg = overallMean(arms, A_LG)
|
||||
let mBbn = overallMean(arms, A_BBN)
|
||||
let mLin = overallMean(arms, A_NAIVE)
|
||||
let ordOk = mHead > mPat and mHead > mTmh and mHead > mLg
|
||||
echo fmt"3. HeadOn (static LOS) mean|err| = {mHead:.3f} deg vs Pattern {mPat:.3f} / TMHorizon {mTmh:.3f} / LeadGain {mLg:.3f}"
|
||||
let ordOk = mHead > mPat and mHead > mTmh and mHead > mLg and mHead > mBbn
|
||||
echo fmt"3. HeadOn (static LOS) mean|err| = {mHead:.3f} deg vs Pattern {mPat:.3f} / TMHorizon {mTmh:.3f} / LeadGain {mLg:.3f} / BitBrainNet {mBbn:.3f}"
|
||||
let ordMsg = if ordOk: "OK (static gun worst among real guns)" else: "UNEXPECTED: a predictive gun is worse than static LOS"
|
||||
echo fmt" -> {ordMsg}"
|
||||
echo fmt" NaiveLinear mean|err| = {mLin:.3f} deg (over-leads; see the lead-gain sweep for why a larger"
|
||||
@@ -317,7 +335,7 @@ proc main() =
|
||||
echo "=".repeat(120)
|
||||
echo "HEADROOM -- the direct answer: how far each arm is from the oracle ceiling, per band"
|
||||
echo "=" .repeat(120)
|
||||
let hdr = "band Pattern n Pattern|err| Pattern hpx Oracle hpx headroom pp naive hpx TMHoriz hpx LeadGain hpx"
|
||||
let hdr = "band Pattern n Pattern|err| Pattern hpx Oracle hpx headroom pp naive hpx TMHoriz hpx LeadGain hpx BitBrainNet hpx"
|
||||
echo hdr
|
||||
echo "-".repeat(hdr.len)
|
||||
for b in 0 ..< NBands:
|
||||
@@ -325,8 +343,10 @@ proc main() =
|
||||
let orc = arms[A_ORACLE].bands[b]
|
||||
let hp = pat.hitProxy
|
||||
let ohp = orc.hitProxy
|
||||
echo fmt"{BandLabels[b]:<9} {pat.n:>8} {fmt3(meanAbs(pat)):>12} {fmt4(hp):>12} {fmt4(ohp):>12} {ohp - hp:>13.4f} {fmt4(arms[A_NAIVE].bands[b].hitProxy):>11} {fmt4(arms[A_TMH].bands[b].hitProxy):>12} {fmt4(arms[A_LG].bands[b].hitProxy):>13}"
|
||||
echo fmt"{BandLabels[b]:<9} {pat.n:>8} {fmt3(meanAbs(pat)):>12} {fmt4(hp):>12} {fmt4(ohp):>12} {ohp - hp:>13.4f} {fmt4(arms[A_NAIVE].bands[b].hitProxy):>11} {fmt4(arms[A_TMH].bands[b].hitProxy):>12} {fmt4(arms[A_LG].bands[b].hitProxy):>13} {fmt4(arms[A_BBN].bands[b].hitProxy):>15}"
|
||||
echo ""
|
||||
echo "BitBrainNet hpx = the ADE+SBC gun (rack id 17), Pattern base + a class-resolved"
|
||||
echo "angular correction. VETO-CAPABLE OFFLINE CHECK ONLY, never a live claim."
|
||||
echo "hitProxy = fraction of tick-bins aimed within atan(18/range) of the true interception point."
|
||||
echo "headroom pp = oracle hitProxy - Pattern hitProxy = the absolute hit-probability points available"
|
||||
echo "to a perfect predictor (the campaign is playing for a slice of this)."
|
||||
|
||||
Reference in New Issue
Block a user