# 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: ```bash 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 ```bash # 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.