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

6.8 KiB
Raw Blame History

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

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

# 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.