77e6dace01
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.
156 lines
6.8 KiB
Markdown
156 lines
6.8 KiB
Markdown
# 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.
|