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>
11 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 BOTH
TR_RACK_BITBRAIN (rack table) and TR_BITBRAIN_NET=1 (the gun's own master
switch, the same switch that disowns the legacy TR_BITBRAIN_* aliases of
LEADGAIN). 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. 0 = off and the TR_BITBRAIN_* names are LEGACY aliases of LEADGAIN; 1 = on and the names below are the new gun's |
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
# 42 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, two different inputs give different class
outputs, a learn visibly raises SBC occupancy, a bitset learn is idempotent
while a counted learn is monotone, ADE threshold adaptation runs, and
construction + first predict leave the global RNG untouched.