Balance
Brunel's (2000) network has four excitatory neurons for every inhibitory one. Each neuron receives 10% of each population's neurons, plus Poisson input from outside, and every inhibitory synapse is g times stronger than an excitatory one. With g above 4 inhibition wins on average, and the neurons sit below threshold, pushed over it now and then by fluctuations: they fire irregularly, and out of step with each other. That is the asynchronous irregular state cortex seems to work in.
Change the balance and the network changes state. With weak inhibition it fires in lockstep, regularly. With strong inhibition and strong drive it oscillates fast, and with strong inhibition and weak drive, slowly, while each neuron fires irregularly within the rhythm.
2,500 LIF neurons, 250 inputs each, 0.1 ms steps · excitatory rate …
Each dot is a spike: 160 excitatory neurons above, 40 inhibitory below, over the last 400 ms; the trace is the excitatory population's rate. The network runs in a worker in your browser with sparx's step: membranes solved exactly over 0.1 ms, delta synapses arriving 1.5 ms after the spike and landing before the threshold test, and a reset held for 2 ms. Brunel's figures use 12,500 neurons, each receiving 1,250 inputs; at 2,500 each receives 250, and the regimes are less distinct than his. The 5,000-neuron network gives each 500, and runs slower.
The code
import jax
from sparx.graph import PopulationRate, SpikeRaster, simulatefrom sparx.graph.models import brunel
network = brunel(250, g=5.0, eta=2.0) # 1,250 LIF neuronsresult = simulate(network, network.init(jax.random.key(0)), duration=400.0, key=jax.random.key(1), monitors={"spikes": SpikeRaster("e"), "rate": PopulationRate("e")})spikes = result.records["spikes"] # [4000, 1000]: a row of booleans per 0.1 ms stepprint(float(result.records["rate"][1000:].mean()), "Hz")How sparx steps a network
A sparx.graph.Network is a Flax module whose variables split by role: connectome holds the edges and fixed weights, params any trainable weights, and state the membranes, synapses and spikes in flight. Every step runs in NEST's order: delta synapses deliver what is due, membranes integrate and fire, spikes enter each population's ring buffer, other synapses receive their arrivals, plasticity updates, and monitors record. A spike sent in step m over a delay of D steps lands at the end of step m + D.
Spikes are delivered as events: each step takes the neurons that fired and adds their out-edges, so a quiet network costs little. The same projection can be stored as a dense matrix or an edge list instead, and every format gives the same input up to the order of summation.simulate compiles a chunk of steps, carries the state between chunks, and can run trials and neurons across devices.
A whole brain
The same machinery runs Shiu et al.'s (2024) model of the whole fly brain, built from the FlyWire connectome: 127,400 neurons and 14.7 million connections. Activating 21 sugar-sensing neurons at 100 Hz, sparx's rates over ten trials correlate with their published Brian2 runs at 0.9989, and the motor neuron MN9 fires at 67.1 Hz against their 67.0 ± 6.6. That takes about 30 s per simulated second on 4 CPU cores, too much for a browser.
Checked
site/test/neurons.test.ts runs the browser's network on a Brunel network sparx built, 250 neurons and 6,250 synapses read back with Network.connections, with the same external input replayed. Over 2,000 steps both fire the same 4,587 spikes.
sparx's Brunel network falls within NEST's spread over eight seeds in rate, irregularity and Fano factor in all four regimes, at 2,500 neurons. A chaotic network cannot match spike for spike between simulators, so this is the check. On a 60-neuron network with the same edges, sparx and NEST do fire spike for spike (fidelity ledger).