e9302bc9f6
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>
186 lines
11 KiB
Markdown
186 lines
11 KiB
Markdown
# 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.
|