flybrain · week 1 · validation report · 2026-10-24

A fruit fly brain that tastes words, in plain NumPy

We reimplemented the Shiu et al. 2024 whole-brain leaky integrate-and-fire model (127,400 neurons, 14.7M synapses, FlyWire v630) as ~100 lines of NumPy and scipy.sparse, validated it against the authors' Brian2 reference, and wrapped it in a word demo: type a taste, watch the brain decide whether to feed.

What one trial does

Every neuron is the same two-variable LIF unit; all biology lives in the wiring. A "word" is a set of annotated sensory neurons driven with 150 Hz Poisson spikes for one simulated second. Activity propagates through the connectome, and we read the verdict off MN9, the motor neuron that extends the proboscis (the fly's "start eating" command).

stimulate 21 sugar GRNs ──▶ 127,400-neuron LIF, dt = 0.1 ms ──▶ read MN9 rate
      word in                   spikes propagate                 behavior out

The sugar → MN9 circuit our simulation runs through

This is not a cartoon: nodes and arrows below are the actual strongest synaptic paths in the FlyWire v630 connectivity table we simulate. Sugar sensory neurons excite a band of second-order neurons; those converge onto a handful of premotor neurons; three of them excite MN9 while three inhibit it. The feeding decision is literally this tug-of-war.

Try a word

Pick a word to drive its sensory neurons and watch the signal travel to MN9. Numbers are from the Week 1 runs in the table below.

excitatory (width ∝ synapse count) inhibitory weak (<20 synapses)

Node labels: FlyWire cell-type names where the Shiu annotations provide one, otherwise the last four digits of the FlyWire root ID. Rates in parentheses: our NumPy simulation, sugar at 150 Hz. Edge widths scale with summed synapse count; only edges with ≥5 synapses are drawn. Bitter GRNs connect to this sugar pathway almost entirely through neurons outside this subgraph, which is why their suppression of MN9 (105.7 → 2.7 Hz) works without strong direct edges here.

Validation against the Brian2 reference

Stimulus (150 Hz)NumPy (ours)Brian2 (Shiu code)chetaslua's postVerdict
sugar → MN9105.7 Hz93.3 ± 3.2 Hz78 Hzextend (feed) ✓, rate +13% — open item
sugar + bitter → MN92.7 Hzsuppressed3 Hzsuppression reproduced ✓
water → MN913.0 Hz––mild extend, plausible
runtime, 3 trials15 sminutes–~20× faster on one CPU

The bitter result is the strong check: feeding suppression emerges from connectome structure alone, and we land within rounding of the viral demo. The sugar rate runs 13% hot, which is systematic, not noise (Brian2's trial-to-trial std is 3.2 Hz). Prime suspects, in order: Brian2's within-step event ordering (integrate → threshold → synapses → reset, where ours detects spikes in the same step inputs arrive), its exact coupled-ODE integrator versus our exponential Euler, and refractory-freeze semantics. The fix and a per-neuron scatter (all ~450 active neurons, NumPy vs Brian2, slope ≈ 1) is the one item left open from Week 1.

What shipped

Code — github.com/CharlieZhang1999/flybrain

  • sim/lif_numpy.py — the simulator: sparse CSC weight matrix, 1.8 ms delay ring buffer, Poisson drive, 5 s per trial
  • stimuli/words.py — word → GRN mappings (sugar, bitter, water, salt) from the paper's notebooks
  • scripts/word_demo.py — the demo CLI, plus a /fly-word Claude skill

Try it

python scripts/word_demo.py sugar+bitter
# 409 neurons spike, MN9 at 2.7 Hz
# -> no feeding response

Early fix worth remembering: the paper's neuron IDs are FlyWire v630, not v783 — the demo loads the v630 tables to match the reference exactly.

Week 2: more words, and the male brain