Files
SirRoboGarage/common_libs/bitbrain/README.md
T
SirStone 77e6dace01 BitBrain: generic clean-room ADE + SBC library with MNIST acceptance
Implement the BitBrain (Address Decoder Element + Sparse Binary Coincidence)
classifier as a generic, deterministic Nim library under common_libs/bitbrain/,
written from the published algorithm (Front. Neuroinform. 17:1125844), not from
the GPL-3.0 reference C.

- ade.nim: signed thresholded random projection (scale 64 / centre 127 defaults
  reproduce the reference), multi-width ADs, optional deterministic homeostatic
  threshold adaptation. Hebbian longevity and Metropolis-Hastings sampling are
  described but not implemented.
- sbc.nim: packed class-bit coincidence memory; idempotent learn, counting
  inference.
- bitbrain.nim: container over several ADs and SBCs, online learn/infer, argmax
  readout, memory accounting.
- tests: 32 unit checks (idempotence, planted rule + monotone online curve,
  shuffled-label chance control, unseen input, homeostasis, memory).
- tests/test_bitbrain_mnist.nim: loads the reference pretrained ADs/thresholds
  and MNIST from /tmp, reproduces the reference exactly - 97.210% corrected and
  96.540% bug-compatible - confirming the port.

No gun/wiring integration yet; inputs and outputs to be agreed separately.
2026-09-24 20:52:45 +02:00

156 lines
6.8 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 (ADE + SBC) — generic Nim library
A clean-room Nim implementation of the classifier in
> *BitBrain and Sparse Binary Coincidence (SBC) memories*,
> Frontiers in Neuroinformatics 17:1125844, 2023.
**This is a library, not a gun.** There is no I/O design, no battle wiring and no
environment knobs here yet — input and output formats are to be agreed separately.
## Clean-room note
The implementation was written **from the published algorithm description only**.
No code was copied from the reference C program (`full_mnist_2048.c`) shipped with
the paper; that file is GPL-3.0-or-later, © 2022 The University of Manchester, and
copying it would impose that licence on this repository. The published defaults
(`scale = 64`, `centre = 127`) make the scoring rule numerically identical to the
reference, which the MNIST acceptance test confirms to the digit.
## Mechanism
1. **ADE (Address Decoder Element)** — a sparse, signed, thresholded random
projection. Each ADE has `width` synapses `(inputIndex, polarity)` and scores an
input as
```
raw = Σ_j polarity_j * (input[inputIndex_j] - center)
score = scale * raw
```
firing iff `score >= threshold`. Defaults `scale = 64`, `center = 127`
reproduce the reference exactly. Multi-width ADs (the paper's best setup uses
widths `{6, 8, 10, 12}`) detect features at different scales.
2. **Homeostatic threshold learning** (optional, unsupervised) — accumulate each
ADE's firing count over inputs, then nudge its threshold toward a target firing
rate (~1% in the paper). `accumulateFiring` + `adaptThresholds` implement the
paper's deterministic controller. The paper's additional Hebbian *longevity*
step (retire the weakest synapse, resample a new input index) and its
Metropolis–Hastings input-position sampling are **described in `ade.nim` but
not implemented** — `initRandomAddressDecoder` uses the paper's uniform random
initialisation.
3. **SBC memory** (supervised) — two ADs on the axes; a pair of simultaneously
firing ADEs `(i, j)` is a coincidence indexing one cell holding a class
bitmask. `learn` **sets** the class bit and is idempotent: setting it again is a
no-op. No clearing, no learning rate, no decay, no epochs. `infer` accesses the
same locations but **counts** set bits per class.
4. **BitBrain container** — several ADs (possibly different widths) plus several
SBCs built from pairs of them. Inference aggregates class counts across SBCs;
argmax wins (ties to the lowest class index).
## API
```nim
import bitbrain/bitbrain # re-exports ade + sbc
# --- construction ---
var bb = buildRandomBitBrain(
widths = @[6, 8, 10, 12], # one AD per width
nAde = 512, # ADEs per AD
inputWidth = 256, # input vector length
nClasses = 8,
seed = 1234'i64) # deterministic
# or build ADs yourself and assemble:
# var ad = initAddressDecoder(nAde = 2048, width = 6)
# ad.codes = ... # signed 1-based input codes (pretrained)
# ad.thresholds = ...
# var bb = initBitBrain(@[ad, ...], crossPairs(4) & withinPairs(4), nClasses = 10)
# --- online by construction (order-free, repeatable, interleaved) ---
bb.learn(input, class) # sets class bits, idempotent
let (label, counts) = bb.infer(input) # argmax + per-class counts
bb.resetLearning() # wipe SBCs (ADs unchanged)
# --- optional unsupervised homeostasis ---
for input in trainingStream:
ad.accumulateFiring(input)
ad.adaptThresholds(interval = 2000, targetRate = 0.01, step = 1)
# --- accounting ---
bb.memoryBytes # ADs + SBC tensors
bb.sbcMemoryBytes # SBC tensors only (the dominant term)
```
Generic over the input element type (`openArray[SomeInteger]`) and over the input
width, ADE count and class count — nothing is hardcoded to 784/10. Deterministic
given the seed. Dependencies: `std/` only.
Reference SBC wiring: `crossPairs(4)` gives the 6 cross-AD SBCs used by the
reference C. `withinPairs(n)` adds the paper's 4 within-AD ("half-size") SBCs;
this implementation stores them full-size (half-size packing is a separate memory
optimisation).
## Measured results
All figures measured on this machine with `-d:release`, single-threaded, on the
reference fixtures (pretrained ADs + MNIST). Nothing is committed: fixtures live
under `/tmp/bitbrain/BitBrain_C_code` (override with `$BITBRAIN_FIXTURES`).
### MNIST acceptance — exact reproduction of the reference
4 ADs × 2048 ADEs, widths `{6, 8, 10, 12}`, 6 cross-AD SBCs, 10 classes, one
online pass over 60,000 training samples, evaluated on all 10,000 test images:
| Reader | This library | Reference C |
|---|---:|---:|
| Corrected (clean-room, all row ADEs counted) | **97.210%** | 97.210% |
| Bug-compatible (`uint8_t bit_test`: only `i % 32 < 8`) | **96.540%** | 96.540% |
The bug-compatible mode reproduces the shipped reference's truncation bug exactly,
which proves the ADE scoring, the coincidence indexing and the idempotent SBC rule
are all correct. The default corrected reader is the one to use.
### Per-sample cost (reference MNIST configuration)
| Operation | Measured |
|---|---:|
| `learn` (4 ADs + 6 SBCs, dense active lists) | **0.384 ms/sample** |
| `infer` (corrected reader) | **0.630 ms/sample** |
| `infer` (bug-compatible reader) | 0.454 ms/sample |
All well inside this project's live budget of ~13.16 ms/tick. The reference C
measured ~0.2–0.56 ms/sample for the same geometry.
### Memory (measured via `sbcMemoryBytes` / `memoryBytes`)
The SBC tensor dominates: `nAde × nAde × nClasses` bits per SBC, rounded up to
`uint32` slots.
| Configuration | SBC tensors | ADs | Total |
|---|---:|---:|---:|
| Reference: 6 SBCs × 2048² × 10 | 31,457,280 B (30.0 MiB) | 360,448 B | 31,817,728 B (30.3 MiB) |
| Gun-sized: 6 SBCs × 512² × 8 | 1,572,864 B (1.5 MiB) | 90,112 B | 1,662,976 B (1.59 MiB) |
| 10 SBCs × 2048² × 10 (paper variant) | 52,428,800 B (50.0 MiB) | 360,448 B | 52,789,248 B (50.3 MiB) |
The reference configuration is L2/L3-hostile; a battle-sized configuration should
size `nAde` to the feature count, not copy 2048².
## Tests
```bash
# Unit / sanity suite (no fixtures required, ~3 s)
nim c -r --nimcache:/tmp/nc_j90 -d:release \
--path:common_libs common_libs/tests/test_bitbrain.nim
# MNIST acceptance harness (needs fixtures in /tmp; skips cleanly if absent)
nim c -r --nimcache:/tmp/nc_j90 -d:release --path:common_libs \
-o:/tmp/acceptance_bitbrain_mnist common_libs/tests/test_bitbrain_mnist.nim
```
The unit suite covers: hand-computed ADE scoring and the `>=` threshold, idempotent
SBC learning (learn one sample 1000× → bit-identical memory), a planted rule
learned near-perfectly with a monotone online accuracy curve, a shuffled-label
control that degrades to chance, unseen-input behaviour, homeostatic
threshold adaptation, and memory accounting.