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>
12 KiB
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² × nClassescells. 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 areuint32-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 vsnAde: 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^inputWidthsynapses the ADE codes may draw from; the codes are stillnAde × wper AD. The only width-dependent term is the ADE scoring cost and the tinyAD Bcolumn (unchanged here because it is dominated bynAde × 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 %). vsnAde: quadratic-ish (0.30 / 2.72 / 9.76 ms), because the ADE pass isO(nAde × w)and the SBC read isO(|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.