Files
SirRoboGarage/common_libs/guns/bitbrain_net.README.md
T
SirStone e9302bc9f6 j140 rebuild a real BitBrain gun: ADE+SBC at rack id 17, default off, and measure its scaling
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>
2026-09-26 16:50:21 +02:00

186 lines
11 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 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:
```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
# 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.