Experiments · E184

Could the brain's slowest calculation run on the Mac's graphics chip, and how much faster?

Yes. One walker move fell from 1.88 s to 0.04 s on the graphics chip, 47 times faster; learning stayed on the main processor.

In the log: the GPU port of the propagation (2026-09-18 10:13; operator: "yes do the GPU port")

recordedDate 2026-09-18 10:13, as written in the logunclassified0 predictions · 0 result paragraphsEXPERIMENTS.md lines 11710–11750
exp E184 diagram
What E184 did and how it came out, drawn from this record and the files it names (book/assets/diagrams/exp/E184.svg).

Results

No result paragraph for this entry was found in the log.

The full record

EXPERIMENTS.md · lines 11710–11750

E184 — the GPU port of the propagation (2026-09-18 10:13; operator: "yes do the GPU port")

What was ported. The one loop the search spends its time in — h = tanh(W h + drive) with Kenyon rectification over 25.1 M synapses — existed three times (score_batch, head_scores, PopulationReadout._states). It is now one function, forager/brain/propagate.py:propagate, with the backend chosen by FORAGER_DEVICE: unset/cpu = scipy float64 (the reference, unchanged); torch = torch sparse CSR on CPU, float64; mps/cuda = torch sparse COO on the accelerator, float32. The built operator sits in model._operator, the slot every weight update already clears, so the eight invalidation sites are inherited. Learning is untouched: forward (single sample, the pre cache the three-factor rules read) stays numpy float64 — lessons are exact; only batch scoring moves. MPS has no sparse-CSR kernel (NotImplementedError); COO works.

Measured, real head (whole brain, broadcast, steps 4, 24 walkers = one walker move):

device      per score_batch(24)   max|h − scipy|   max|score − scipy|
cpu/scipy       1.88 s                 —                 —
torch (f64)     1.35 s              1.7e-16            1.6e-19      bit-identical
mps  (f32)      0.04 s              7.6e-07            2.7e-10      47× faster

Raw sparse product: scipy 3.60 s / torch-cpu 1.91 s / mps 0.37 s per four products. Tests: tests/test_propagate_device.py (5) — cpu path equals the pre-port loop exactly, torch-cpu within 1e-12, float32 device within 1e-4 (measured 8e-7), call sites proven to use the helper, weight updates invalidate the device operator. 59 brain tests pass.

What it does and does not change. A 200 × 4 × 2 search spent ~2 h in scoring; on MPS that is ~3 min, and the run's remaining time is the verifier's icet screen and the learning forward passes (CPU). The float32 path is a different number by 3e-10 in score — far below any threshold the search applies (p ≥ 0.832 on a −40 meV drive) but not zero, so device is recorded per run and a scipy-vs-mps replication is the first thing to run on it: E184b = E171b seed 0 on FORAGER_BRAIN_DEVICE=mps, same seed, same protocol. Prediction: AUC_Q within 1.0 of the scipy seed-0 value (≈ 33) and finds within ±2 of 81; if either differs by more, a float32 tie-flip in the walker's greedy comparison is compounding, and the search's device must then stay fixed per experiment family. Queued behind E173 (one search at a time; the CPU parts of an MPS search still count).

JAX (operator asked). Not for this: JAX has no usable Metal backend for sparse ops (jax-metal is experimental and jax.experimental.sparse is CUDA/TPU), so on the Mac it would give nothing; torch covers M4 today and CUDA on Colab with the same code. JAX earns its place only if the substrate moves to the chaobrain LIF reproduction of Shiu 2024 (JAX) or the runs go to Colab TPU — both are open decisions, not this port.

Related entries

Built with PRISMWebsite and visualizations made using Claude