Files
SirRoboGarage/common_libs/guns/bitbrain_net.README.md
T
SirStone 59c5af0499 j140 fix: a pre-rename .env must not admit the disabled BitBrain net gun
TR_RACK_BITBRAIN=both is what the owner's live .env carries, and with the new
ADE+SBC gun registered at id 17 under the SAME rack name that value was also
landing on id 17 - so a gun that was not enabled (TR_BITBRAIN_NET unset) was
admitted into the rack and its placeholder predictions were pushed into the
shared VirtualTracker ring, which shifts every other gun's learning order.

While the namespace is LEGACY, loadRackMembership now skips id 17's
TR_RACK_BITBRAIN entirely, so that value addresses ONLY the gun it always
addressed (LEADGAIN, id 16). ModularBot additionally gates admission on
BitbrainNetGun.gunAdmitted(), and test_bitbrain_net pins the truth table: over
6 (rack, switch) settings there is NO configuration that admits the gun while
leaving it disabled.

Guards: test_env_report 25, test_rack_membership 49 (was 48; the revert
one-liner now sets TR_BITBRAIN_NET=1 and one truth-table check was added),
test_tm_pattern_registration 20, test_bitbrain 56, test_gun_harness 39,
test_tfil_commit_env 30, test_lead_gain_registration 13,
test_lead_gain_legacy 24, test_bitbrain_net 44.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-26 16:54:43 +02:00

12 KiB
Raw Blame History

bitbrain_net — quick recap (inputs / outputs)

Recap card. Everything below is read off common_libs/guns/bitbrain_net.nim. Companion to the library's own README (common_libs/bitbrain/README.md) and to common_libs/guns/lead_gain.README.md (the other gun, the per-range-band lead-gain corrector at rack id 16).

What it is

The real ADE+SBC gun: an ADE layer (thresholded random projections with online threshold adaptation) feeding the SBC head from common_libs/bitbrain/, with the counted + decay mode available (the mode that delivers forgetting and true per-class probabilities — docs/bitbrain_counted_sbc.md).

It took the BITBRAIN rack name (id 17) and the TR_BITBRAIN_* knob prefix that the renamed corrector gave up. Default OFF. It needs TR_RACK_BITBRAIN=both in the rack table and TR_BITBRAIN_NET=1 — the one switch that decides whether the TR_BITBRAIN_* namespace is legacy (LEADGAIN's) or the new gun's, and the new gun's master on/off at the same time. The two are gated by the SAME switch, so no env configuration can admit the gun while leaving it disabled (test_bitbrain_net.nim pins that over a 6-setting truth table) — which matters, because a pre-rename .env still carries TR_RACK_BITBRAIN=both and a disabled gun's placeholder predictions in the shared VirtualTracker ring would shift every other gun's learning order. The network is built lazily, so an unset environment never allocates a byte.

OUTPUT SHAPE — a fine-grained correction ON TOP of Pattern

Not a direct aim point from the argmax class. The class-resolved angular correction added to Pattern's bearing is the shape docs/bitbrain_gate.md actually measured, and Pattern is already a strong predictor, so the net's job is the small signed residual, not the whole aim.

Output Formula / meaning
Correction shift = Σ_k P(k)·centre_k / Σ_k P(k) — the probability-weighted mean of the nClasses class centres under inferProb, in degrees, over ±TR_BITBRAIN_SPAN
Aim point tmhApplyShift(self, Pattern prediction, shift). Below TR_BITBRAIN_MINOBS resolved samples, or when the posterior has no mass, shift == 0.0 and Pattern's prediction is returned unchanged
[bbn] log (only TR_BITBRAIN_NETLOG=1, change-gated) t, shift (deg), class (argmax), in, ncl, nAde, mode, trained, adapts, pend, dropped
Does NOT output an aim point of its own; it never discards Pattern

INPUT — a CONFIGURED SET OF FEATURE BLOCKS

The input vector is a concatenation of feature blocks, each independently selectable (TR_BITBRAIN_FEATURES) and with a settable width. A block is a list of scalar quantities; a block of width W lays each quantity out as a W-slot thermometer code, so width == resolution. Slots are 0/255 uint8, which the ADE scorer centres at 127 (DefaultCenter): a synapse matches when its polarity agrees with the slot, so a random ADE fires iff its w synapses all match — a thresholded random projection with firing rate 2^-w, which online homeostasis then drives toward TR_BITBRAIN_TARGET.

The default block set is 52 slots:

block quantities width slots quantity
epos 2 4 8 enemy offset from us, x and y, over the arena span
evel 2 3 6 enemy speed; enemy heading minus the bearing to us
eturn 2 2 4 turn direction this tick; turn consistency over the ring
eself 2 2 4 our speed; our heading minus the bearing to the enemy
dist 2 5 10 range; signed range rate over the last 10 ticks
bear 1 4 4 relative bearing (enemy bearing minus our heading)
walls 4 2 8 distance to each of the four arena walls
bull 2 2 4 live-bullet count; nearest bullet's signed lateral offset
hzn 1 4 4 bullet flight time to the current range

docs/state_window_gate.md measured that a long temporal window of states destroys recurrence, so there is deliberately no window block: the only history-derived inputs are the three rate/turn quantities above (a 12-tick ring), i.e. the same causal information Pattern itself uses.

KNOB TABLE

Env Default Meaning
TR_RACK_BITBRAIN off rack admission for id 17 (the current name of the rack key)
TR_BITBRAIN_NET 0 master switch + namespace disambiguator. 0 = the gun is off AND the TR_BITBRAIN_* names are LEGACY aliases of LEADGAIN (and TR_RACK_BITBRAIN selects id 16); 1 = the gun is on and the names below are this gun's (and TR_RACK_BITBRAIN selects id 17)
TR_BITBRAIN_INPUT 52 total input slots; pads or truncates the block layout so the ADE codes can never index out of range
TR_BITBRAIN_FEATURES all blocks at their shipped width name:W list, comma separated. name:0 switches a block OFF; an unlisted block keeps its shipped width; an unknown name warns and is ignored
TR_BITBRAIN_NCLASSES 8 output resolution
TR_BITBRAIN_NADES 256 ADEs per address decoder (RAM is quadratic in this)
TR_BITBRAIN_WIDTHS 4,5,6 ADE clause widths; one AD per width, one cross-AD SBC per pair (3 widths → 3 SBCs)
TR_BITBRAIN_SPAN 40.0 class half-range, degrees
TR_BITBRAIN_MODE counted bitset | counted (saturating counters + decay)
TR_BITBRAIN_DECAY_EVERY 64 counted mode: learns between global decay passes
TR_BITBRAIN_DECAY_SHIFT 3 counted mode: c -= c shr shift per pass (0 disables)
TR_BITBRAIN_MINOBS 32 resolved samples before the correction is applied at all
TR_BITBRAIN_ADAPT_EVERY 200 inputs between ADE threshold-adaptation passes
TR_BITBRAIN_TARGET 0.01 the paper's target ADE firing rate
TR_BITBRAIN_NETSEED 20240921 network seed (a private RNG, never the global one)
TR_BITBRAIN_NETLOG 0 1 = emit the [bbn] line
TR_BITBRAIN_CALIB_EVERY — compat alias for TR_BITBRAIN_ADAPT_EVERY
TR_BITBRAIN_NET_RESET_ON_TARGET 1 wipe the SBC counters when the enemy id changes

HOW TO TURN IT ON

TR_BITBRAIN_NET=1            # the master switch — the legacy aliases go quiet
TR_RACK_BITBRAIN=both
TR_RACK_PATTERN=off
TR_BITBRAIN_NETLOG=1

MEASURED SCALING (RAM / ms-per-tick / quality)

common_libs/tests/measure_bitbrain_scaling.nim, replaying 3 recorded live runs (37 412 recorded ticks, 149 650 tick × power-bin samples), -d:release, single-threaded, one predict per power bin per tick — the same call pattern as the live loop. The timed region contains only the gun's predict calls: the interception solve is done once, up front, so the ruler's own cost cannot contaminate the timing.

arm in nCl nAde mode RAM B SBC B AD B ms/tick % of 13.16 mean|err|° hit% Pattern|err|°
input-small 26 8 256 counted 1 594 368 1 572 864 21 504 2.60 20 % 17.329 8.00 16.964
input-medium 52 8 256 counted 1 594 368 1 572 864 21 504 2.70 21 % 17.270 8.04 16.964
input-large 84 8 256 counted 1 594 368 1 572 864 21 504 2.54 19 % 17.259 8.06 16.964
classes-small 52 2 256 counted 414 720 393 216 21 504 1.07 8 % 17.201 8.12 16.964
classes-medium 52 8 256 counted 1 594 368 1 572 864 21 504 2.70 21 % 17.270 8.04 16.964
classes-large 52 64 256 counted 12 604 416 12 582 912 21 504 19.56 149 % 17.115 8.27 16.964
nAde-small 52 8 64 counted 103 680 98 304 5 376 0.30 2 % 17.254 8.08 16.964
nAde-medium 52 8 256 counted 1 594 368 1 572 864 21 504 2.72 21 % 17.270 8.04 16.964
nAde-large 52 8 512 counted 6 334 464 6 291 456 43 008 9.76 74 % 17.264 8.03 16.964
mode-bitset 52 8 256 bitset 218 112 196 608 21 504 2.72 21 % 16.975 8.53 16.964
mode-counted 52 8 256 counted 1 594 368 1 572 864 21 504 2.71 21 % 17.270 8.04 16.964

Reproduce:

nim c -r -d:release --path:common_libs common_libs/tests/measure_bitbrain_scaling.nim --limit 3

The scaling laws, as MEASURED

  • RAM is dominated by the SBC tensors (98.6 % at the default). One SBC is nAde² × nClasses cells. Bitset: 1 bit/cell. Counted: 1 byte/cell. The measured counted/bitset factor is 7.30× on the default geometry (1 594 368 / 218 112), which is the bitset tensor's 1/8-of-a-uint32-slot overhead — the tensors are uint32-slot-packed, so 8 classes share slots.
  • RAM vs nClasses: exactly linear. 2 → 8 → 64 classes: 0.41 → 1.59 → 12.60 MB (4× and 32× for 4× and 8× the classes). RAM vs nAde: quadratic — 64 → 256 → 512 gives 0.10 → 1.59 → 6.33 MB (4× then 16× for 4× then 2×).
  • RAM vs input width: FLAT. 26 → 52 → 84 slots: 1 594 368 B in all three cases. The input width only sets how many of the 2^inputWidth synapses the ADE codes may draw from; the codes are still nAde × w per AD. The only width-dependent term is the ADE scoring cost and the tiny AD B column (unchanged here because it is dominated by nAde × w, not by input width).
  • ms/tick vs input width: flat (2.60 / 2.70 / 2.54 ms). vs nClasses: linear (1.07 / 2.70 / 19.56 ms — the 64-class arm busts the 13.16 ms budget at 149 %). vs nAde: quadratic-ish (0.30 / 2.72 / 9.76 ms), because the ADE pass is O(nAde × w) and the SBC read is O(|row| × |col|).
  • counted vs bitset: same time, 7.3× the RAM (2.71 vs 2.72 ms/tick).

Capacity vs accuracy — the direct answer

More capacity buys essentially nothing here; it costs RAM and, past a point, the tick budget. Across a 100× range of RAM (0.10 MB → 12.60 MB) the offline mean |angular error| moves from 17.254° to 17.115° — a 0.14° spread around Pattern's own 16.964°, and the sign of the effect flips across the nClasses axis (17.201° at 2 classes, 17.115° at 64), so it is not a trend, it is noise. The only arm that helps is mode-bitset (16.975° vs 17.270° counted), and even that costs nothing in accuracy terms until you switch to counted for forgetting — at which point you pay 7.3× the RAM and lose the 0.3°.

The honest reading is that the information ceiling here is the state, not the classifier: docs/bitbrain_gate.md measured ~1 bit of information in a 53-bit input, and this sweep reproduces that at 26, 52 and 84 slots alike. The corrector is consistently slightly WORSE than Pattern offline (17.2° vs 17.0°), which is the same verdict the campaign already reached for every additive-shift design. Capacity is not the binding constraint and buying more of it is not the fix.

VETO-CAPABLE CHECK ONLY. Per docs/offline_harness_trust.md this is the single trustworthy use of the offline harness — per-gun single-tick prediction quality on a fixed trajectory. It is not a live result: nothing here says anything about damage, survival or round wins, and it is never presented as one.

Tests

# 44 checks: the gun really engages the network (not just that it links)
nim c -r -d:release --path:common_libs common_libs/tests/test_bitbrain_net.nim
# the library itself (56 checks, unchanged)
nim c -r -d:release --path:common_libs common_libs/tests/test_bitbrain.nim

test_bitbrain_net.nim proves engagement by observation, not by linkage: the network is 0 bytes before first use, a trained head returns different classes for different states and separates two taught populations, a learn visibly raises SBC occupancy, a bitset learn is idempotent while a counted learn's evidence is monotone, ADE threshold adaptation runs, no env setting can admit a disabled gun, and construction + first predict leave the global RNG untouched.