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

194 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.