flybrain 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
flybrain-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 alextitonis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,60 @@
1
+ # flybrain
2
+
3
+ The complete central nervous system of an adult male fruit fly, *Drosophila melanogaster*, as a
4
+ spiking network you can run on your own computer: **166,700 neurons and 25.6 million
5
+ connections** from the [MaleCNS v1.0 connectome](https://male-cns.janelia.org), wired as
6
+ electron microscopy found them.
7
+
8
+ Nothing inside the brain is trained. You drive some of the fly's own neurons, step the network
9
+ forward, and read out what its descending neurons (the brain's commands to the body) do.
10
+
11
+ ```sh
12
+ pip install flybrain # CPU (numba)
13
+ pip install "flybrain[gpu]" # plus CuPy for an NVIDIA GPU (CUDA 12)
14
+ ```
15
+
16
+ ```python
17
+ from flybrain import FlyBrain
18
+
19
+ brain = FlyBrain(device="auto") # first run downloads the brain files (~260 MB) to ~/fly-data
20
+ left_loom = brain.cells(["LC4", "LPLC2"], side="L") # looming detectors, left eye
21
+ giant_fiber = brain.cells(["DNp01"], side="L") # the escape command neuron
22
+
23
+ for step in range(50): # one second at 20 ms per step
24
+ fired = brain.step(inject=[(left_loom, 0.8)])
25
+ if set(giant_fiber) & set(fired):
26
+ print(f"left giant fiber fired at {step * brain.dt:.2f} s")
27
+ ```
28
+
29
+ ## What's in it
30
+
31
+ * `FlyBrain`: leaky integrate-and-fire over the whole connectome. `device="cpu" | "cuda" | "auto"`,
32
+ `batch=8` runs 8 independent flies at once, plus `dt`, `sensory_input` and `refractory` options.
33
+ `brain.cells([...])` finds neurons by cell type or superclass (`"descending_neuron"`).
34
+ * `Trace`, `run`, `Readout`: reservoir computing. Collect a spike trace of any neuron population
35
+ over your task, then fit a cross-validated linear or logistic PCA readout to your labels.
36
+ * `Eyes`, `FeatureDetectors`: a visual encoder that drives the fly's visual projection neurons.
37
+
38
+ ## Data
39
+
40
+ The brain files live in `$FLY_DATA` (default `~/fly-data`). The first `FlyBrain()` downloads them;
41
+ you can also run it ahead of time:
42
+
43
+ ```sh
44
+ flybrain download # prebuilt files, sha256-checked
45
+ flybrain build # or build them from the MaleCNS release (~1.1 GB; pip install "flybrain[build]")
46
+ flybrain info # data folder and GPU status
47
+ ```
48
+
49
+ The first step on CPU is slow while numba compiles; later steps take about 12–15 ms on 24 threads.
50
+ On an RTX 4060 a step takes 1.4 ms.
51
+
52
+ ## Credits and license
53
+
54
+ Code: MIT. The connectome data is MaleCNS v1.0 by FlyEM (HHMI Janelia), the University of
55
+ Cambridge, the MRC Laboratory of Molecular Biology and Google Research, used under
56
+ [CC BY 4.0](https://male-cns.janelia.org/download/). If you use it, cite Berg, S. et al. (2026),
57
+ *Sexual dimorphism in the complete connectome of the Drosophila male central nervous system*, *Cell*.
58
+ The neuron model follows [Fly64](https://github.com/ornata/fly) by Jessica Paquette.
59
+
60
+ Source, experiments and results: [github.com/alextitonis/fly.ai](https://github.com/alextitonis/fly.ai)
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: flybrain
3
+ Version: 0.1.0
4
+ Summary: The complete fruit fly nervous system (MaleCNS connectome, 166,700 neurons) as a spiking network, on CPU or GPU.
5
+ Author: alextitonis
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://flyaiworld.com
8
+ Project-URL: Source, https://github.com/alextitonis/fly.ai
9
+ Keywords: connectome,drosophila,spiking neural network,neuroscience,reservoir computing
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: numpy>=2.0
19
+ Requires-Dist: scipy>=1.13
20
+ Requires-Dist: numba>=0.61
21
+ Provides-Extra: gpu
22
+ Requires-Dist: cupy-cuda12x[ctk]>=13; extra == "gpu"
23
+ Provides-Extra: build
24
+ Requires-Dist: pandas>=2.2; extra == "build"
25
+ Requires-Dist: pyarrow>=15; extra == "build"
26
+ Dynamic: license-file
27
+
28
+ # flybrain
29
+
30
+ The complete central nervous system of an adult male fruit fly, *Drosophila melanogaster*, as a
31
+ spiking network you can run on your own computer: **166,700 neurons and 25.6 million
32
+ connections** from the [MaleCNS v1.0 connectome](https://male-cns.janelia.org), wired as
33
+ electron microscopy found them.
34
+
35
+ Nothing inside the brain is trained. You drive some of the fly's own neurons, step the network
36
+ forward, and read out what its descending neurons (the brain's commands to the body) do.
37
+
38
+ ```sh
39
+ pip install flybrain # CPU (numba)
40
+ pip install "flybrain[gpu]" # plus CuPy for an NVIDIA GPU (CUDA 12)
41
+ ```
42
+
43
+ ```python
44
+ from flybrain import FlyBrain
45
+
46
+ brain = FlyBrain(device="auto") # first run downloads the brain files (~260 MB) to ~/fly-data
47
+ left_loom = brain.cells(["LC4", "LPLC2"], side="L") # looming detectors, left eye
48
+ giant_fiber = brain.cells(["DNp01"], side="L") # the escape command neuron
49
+
50
+ for step in range(50): # one second at 20 ms per step
51
+ fired = brain.step(inject=[(left_loom, 0.8)])
52
+ if set(giant_fiber) & set(fired):
53
+ print(f"left giant fiber fired at {step * brain.dt:.2f} s")
54
+ ```
55
+
56
+ ## What's in it
57
+
58
+ * `FlyBrain`: leaky integrate-and-fire over the whole connectome. `device="cpu" | "cuda" | "auto"`,
59
+ `batch=8` runs 8 independent flies at once, plus `dt`, `sensory_input` and `refractory` options.
60
+ `brain.cells([...])` finds neurons by cell type or superclass (`"descending_neuron"`).
61
+ * `Trace`, `run`, `Readout`: reservoir computing. Collect a spike trace of any neuron population
62
+ over your task, then fit a cross-validated linear or logistic PCA readout to your labels.
63
+ * `Eyes`, `FeatureDetectors`: a visual encoder that drives the fly's visual projection neurons.
64
+
65
+ ## Data
66
+
67
+ The brain files live in `$FLY_DATA` (default `~/fly-data`). The first `FlyBrain()` downloads them;
68
+ you can also run it ahead of time:
69
+
70
+ ```sh
71
+ flybrain download # prebuilt files, sha256-checked
72
+ flybrain build # or build them from the MaleCNS release (~1.1 GB; pip install "flybrain[build]")
73
+ flybrain info # data folder and GPU status
74
+ ```
75
+
76
+ The first step on CPU is slow while numba compiles; later steps take about 12–15 ms on 24 threads.
77
+ On an RTX 4060 a step takes 1.4 ms.
78
+
79
+ ## Credits and license
80
+
81
+ Code: MIT. The connectome data is MaleCNS v1.0 by FlyEM (HHMI Janelia), the University of
82
+ Cambridge, the MRC Laboratory of Molecular Biology and Google Research, used under
83
+ [CC BY 4.0](https://male-cns.janelia.org/download/). If you use it, cite Berg, S. et al. (2026),
84
+ *Sexual dimorphism in the complete connectome of the Drosophila male central nervous system*, *Cell*.
85
+ The neuron model follows [Fly64](https://github.com/ornata/fly) by Jessica Paquette.
86
+
87
+ Source, experiments and results: [github.com/alextitonis/fly.ai](https://github.com/alextitonis/fly.ai)
@@ -0,0 +1,371 @@
1
+ <p align="center"><img src="logo.webp" alt="fly.ai" width="440"></p>
2
+
3
+ # fly.ai: a real fruit fly brain, running on your computer
4
+
5
+ https://github.com/user-attachments/assets/7c3b91e5-9b50-4017-a03a-123aedd4d7b4
6
+
7
+ *The fly brain playing an online fighting game. See [sshfighter/](sshfighter/).*
8
+
9
+ fly.ai is a simulation of the **complete central nervous system of an adult male fruit fly**
10
+ (*Drosophila melanogaster*): **166,700 neurons and 25.6 million connections** from the
11
+ [MaleCNS v1.0 connectome](https://male-cns.janelia.org), wired exactly as electron microscopy
12
+ found them in a real fly.
13
+
14
+ There is **no training and no learned policy inside the brain**. You connect a task to it at a few
15
+ neuron types known from the literature, one side for input (what the fly senses) and one for
16
+ output (the commands its brain sends to the body). Everything in between is the connectome.
17
+
18
+ The goal is a general-purpose "fly reservoir": plug any task into the same frozen brain, read
19
+ out what it does, and find out what a real nervous system's wiring is good for.
20
+
21
+ **$FLYAI** is live on Robinhood Chain (launched on Pons):
22
+ `0x0088CE7905025c4B5ea1d49aB6179B6aaADB3B9C`. The address is posted only here, on the
23
+ [site](https://flyaiworld.com) and on [@flydotai](https://x.com/flydotai); any other address is a
24
+ scam. Details: [TOKEN.md](TOKEN.md).
25
+
26
+ ## How it works
27
+
28
+ ```
29
+ task input
30
+ └─> encoder: drives the fly's own sensory / feature-detector neurons
31
+ └─> 166,700-neuron connectome, leaky integrate-and-fire, 50 steps/s (500 with `dt=0.002`)
32
+ └─> descending neurons (the brain's 1,314 output cables to the body)
33
+ └─> decoder or trained linear readout
34
+ └─> task output
35
+ ```
36
+
37
+ * **Network** (`flybrain/build.py`, `flybrain/brain.py`): every neuron with a MaleCNS superclass
38
+ annotation, and every connection between them. The weight is the synapse count, made negative
39
+ when the presynaptic neuron's predicted transmitter is GABA, glutamate or histamine, then scaled
40
+ so each neuron's inputs add up to 1. Each neuron is a simple leaky integrate-and-fire unit
41
+ (`v ← e^(-dt/τ)·v + gain·W·spikes + tonic + noise`; it spikes and resets at 1). This recipe
42
+ follows [Fly64](https://github.com/ornata/fly). The simulation is multi-threaded with numba.
43
+ * **Visual encoder** (`flybrain/eyes.py`): two routes into the brain.
44
+ * *Eyes*: a 1-D panorama projected onto the 6,006 photoreceptors, each placed by its eye column.
45
+ * *Feature detectors*: drive the fly's own visual projection neurons directly, on the side
46
+ where things are:
47
+
48
+ | Neuron type | Responds to (in a real fly) |
49
+ |---|---|
50
+ | LPLC2 | looming: something getting bigger as it approaches |
51
+ | LC4 | fast looming, escape |
52
+ | LPLC1 | small approaching objects |
53
+ | LC10a | a moving target the male chases |
54
+
55
+ * **Outputs**: the brain's descending neurons, including identified command neurons such as
56
+ DNa02 (steering), DNp01 (the giant fiber, escape take-off), DNg100 (forward walking) and
57
+ MDN (backward walking). A task either decodes these by hand or trains a linear readout on all
58
+ of them (reservoir computing).
59
+
60
+ ## What we found
61
+
62
+ These are small experiments, run on a desktop. They are not peer-reviewed science.
63
+
64
+ 1. **With Fly64's settings, vision does nothing.** Fly64 uses tonic 0.18 and decay e^(-0.2).
65
+ That puts every neuron's resting voltage at 0.18 / (1 − 0.82) ≈ 1.0, exactly the firing
66
+ threshold. The whole network ticks along by itself at about 4 Hz, and the motor neurons fire
67
+ at the same rate whatever the fly is shown (`experiment.py`).
68
+ 2. **The signal from the photoreceptors dies at the first relay.** Photoreceptors release
69
+ histamine, which is inhibitory, onto lamina neurons. Real lamina neurons use smooth, graded
70
+ signals rather than spikes, which this simple model can't reproduce. In every setting we
71
+ tried, looming stimuli never reached the looming detectors (`sweep.py`).
72
+ 3. **Past the eye, the wiring does the right thing on the correct side** (`inject.py`,
73
+ tonic 0.14 and gain 3.0, 6 noise seeds):
74
+
75
+ | Stimulate (left side only) | Result |
76
+ |---|---|
77
+ | LC4 + LPLC2 looming detectors | left giant fiber DNp01 **+17 to +25 spikes/s**; right side unchanged |
78
+ | LC10a courtship-tracking neurons | left DNa02 steering neuron **+1.4 to +3.7 spikes/s**; right side unchanged |
79
+
80
+ None of the other readout neurons changed. These are the known looming → escape and
81
+ courtship pursuit → steering pathways, and they come out of the wiring alone.
82
+ 4. **Smell neurons ran away until sensory neurons stopped receiving synapses.** In the original
83
+ model, olfactory receptor neurons get 0.43 of their 0.45 net input from each other
84
+ (ORN-to-ORN connections). At gain 3.0 that loop pins them near maximum rate, so the
85
+ food-odour projection neurons (DM1/DM2) sat at 50 Hz, the model's ceiling, with or without an
86
+ odour. `FlyBrain(sensory_input=False)` removes every synapse onto sensory neurons, as
87
+ whole-brain spiking models of the fly do. The same odour then drives DM1/DM2 from 16 to 28 Hz
88
+ while other projection neurons stay where they were. The default is unchanged, so every earlier
89
+ result still holds.
90
+ 5. **Two brains can signal to each other** (`flytalk.py`, [flyaiworld.com/flybook](https://flyaiworld.com/flybook)).
91
+ Fly A lives through a situation (a looming threat, a mate in view, a food smell, or nothing),
92
+ its wing motor neurons "sing", and fly B hears the song through its Johnston's-organ neurons.
93
+ Three runs, with the tests fixed before running and 50-shuffle permutation nulls:
94
+
95
+ | | Run 1: original, 20 ms | Run 2: smell fixed, 20 ms | Run 3: smell fixed, 2 ms |
96
+ |---|---|---|---|
97
+ | song → what happened to the singer | 0.30 bits | 0.63 bits | 0.83 bits |
98
+ | same test, degree-preserving scrambled wiring | 0.01 bits | 0.01 bits | **0.87 bits** |
99
+ | listener reacts to a threat song (vs silence) | 23% vs 17% | 21% vs 14% | **77.5% vs 17.5%** (p < 0.001) |
100
+
101
+ Threat is the clear word in every run, and "mate" becomes partly readable at 2 ms. In every
102
+ run the listener's descending neurons carry the singer's situation and stay at chance in
103
+ silence, but the listener only uses loudness: a time-shuffled song works as well. The "real
104
+ wiring matters" test **fails at 2 ms**, where the scrambled brain's song carries as many bits,
105
+ although it groups the situations differently. Food reaches the brain but never the wings,
106
+ and hearing a song never makes a fly sing back. `flybook.py` turns the 2 ms brain into a feed
107
+ of posts, each with the neurons behind it.
108
+
109
+ ## Applications
110
+
111
+ | Folder | What the fly does |
112
+ |---|---|
113
+ | [`sshfighter/`](sshfighter/) | plays [SSH Fighter](https://sshfighter.com), an online terminal fighting game, as a registered bot, with a live dashboard of every neuron firing and a trained punch readout |
114
+ | [`flytalk.py`](flytalk.py), [`flybook.py`](flybook.py) | two copies of the brain signal to each other through wing song and hearing; the posts they produce make up the feed at [flyaiworld.com/flybook](https://flyaiworld.com/flybook) |
115
+
116
+ ![Dashboard: the fight on the left, every neuron of the fly's nervous system on the right](sshfighter/media/dashboard.png)
117
+
118
+ New applications go in their own folder and import the core from the `flybrain` package
119
+ (`from flybrain import FlyBrain`), either from the repository root or after `pip install flybrain`.
120
+
121
+ ## Use it on your own task
122
+
123
+ `flybrain/reservoir.py` is the reusable half of the SSH Fighter bot's reservoir readout, pulled out so
124
+ any task can use it, not just the game:
125
+
126
+ ```
127
+ input -> encoder -> fly brain (frozen) -> trace -> trained readout -> output
128
+ ```
129
+
130
+ The brain never trains, on any task: `FlyBrain`'s weights are the connectome, fixed at load
131
+ time. Only two things ever get fit:
132
+
133
+ * **An encoder**, which you write: pick the neuron types your input should drive with
134
+ `brain.cells([...types], side=...)` and pass `(indices, amount)` pairs to
135
+ `brain.step(inject=...)`. `flybrain/eyes.py` is a worked example for SSH Fighter's visual input;
136
+ the neuron types available are whatever the MaleCNS connectome names (look one up on
137
+ [neuPrint](https://neuprint.janelia.org)).
138
+ * **A readout**, which `flybrain.Readout.fit` trains for you: a linear (`kind="ridge"`) or
139
+ logistic (`kind="logistic"`) fit on the top principal components of neural activity, with the
140
+ PCA rank and L2 strength picked by cross-validation. This is the exact method
141
+ `sshfighter/reservoir.py` uses for the punch and movement readouts, generalised off SSH
142
+ Fighter's game state.
143
+
144
+ `flybrain.Trace` collects a decaying spike trace of any neuron population (a cell type, a
145
+ `brain.groups[...]` set, or your own index array) step by step; `flybrain.run` steps the
146
+ brain over a sequence of encoded inputs and returns the trace stacked over time, so the whole
147
+ loop is one call from a notebook:
148
+
149
+ ```python
150
+ from flybrain import FlyBrain
151
+ from flybrain.reservoir import Trace, Readout, run
152
+
153
+ brain = FlyBrain(device="auto")
154
+ trace = Trace(brain, types=["descending_neuron"]) # or group=..., or idx=your_own_array
155
+
156
+ def encode(t):
157
+ return [(brain.cells(["LC10a"], side="L"), my_inputs[t])] # your task's encoder
158
+
159
+ activity = run(brain, len(my_inputs), encode=encode, trace=trace)
160
+ readout = Readout.fit(activity, my_labels, kind="ridge") # or "logistic" for 0/1 labels
161
+ prediction = readout.predict(activity[-1])
162
+ readout.save("readout.npz") # Readout.load(...) later
163
+ ```
164
+
165
+ `flyreservoir_example.py` runs this end to end on a synthetic task (classify and measure the
166
+ strength of a left/right stimulus) with no game or recordings needed:
167
+ `python flyreservoir_example.py`. Its held-out numbers are printed as they come out, not curated
168
+ — on the fixed seed it ships with, the classifier does better than chance and the strength
169
+ regression is close to just predicting the average; that is the honest state of a two-line
170
+ encoder on an invented task, not a claim about what the connectome can do in general (see
171
+ "What's next" and `ROADMAP.md` for the open question of how much the real wiring helps versus
172
+ a random network of the same size).
173
+
174
+ Batching and the GPU option work the same way they do in `flybrain/brain.py`: `FlyBrain(batch=8)` or
175
+ `FlyBrain(device="cuda")` (or `FLY_DEVICE=cuda`); `Trace(..., aggregate="mean")` (the default)
176
+ gives one feature vector averaged across the batch, `aggregate="batch"` keeps one per fly.
177
+
178
+ ### Brain options
179
+
180
+ `FlyBrain` takes three options. The defaults are the model every earlier result used.
181
+
182
+ * `dt` (default `0.020`): the step length. `tonic` is rescaled so a silent neuron settles at the
183
+ same voltage. At `dt=0.002` the network runs far hotter unless you also set a refractory
184
+ period; `refractory=0.004` matched the 20 ms brain's resting descending-neuron rate with no
185
+ neurons above 100 Hz.
186
+ * `sensory_input` (default `True`): `False` removes every synapse onto sensory neurons, which
187
+ fixes the olfactory runaway loop (finding 4).
188
+ * `refractory` (default `0`): seconds a neuron is held at 0 after it spikes.
189
+
190
+ `brain.cells([...])` accepts superclass names such as `"descending_neuron"` as well as cell types.
191
+
192
+ ### Limitations
193
+
194
+ * Point neurons with one global set of parameters. There are no dendrites, no graded neurons,
195
+ no neuromodulators and no plasticity.
196
+ * Transmitter sign is a rough rule (GABA, glutamate and histamine inhibitory; everything else
197
+ excitatory). Real effects depend on the receptor.
198
+ * The visual front end is a shortcut, like [Eon's embodied fly](https://eon.systems/updates/embodied-brain-emulation).
199
+ We inject input into feature-detector neurons instead of simulating the eye.
200
+ * None of this is validated against recordings from real flies. It's a demo, not an emulation.
201
+
202
+ ## Run it
203
+
204
+ A multi-core CPU helps: one brain step takes about 12–15 ms on 24 threads, and real time needs
205
+ under 20 ms.
206
+
207
+ **Just the brain, as a library** (Python 3.10+):
208
+
209
+ ```sh
210
+ pip install flybrain # or "flybrain[gpu]" for an NVIDIA GPU
211
+ flybrain download # optional: the first FlyBrain() does this itself (~260 MB, once)
212
+ flybrain info # where the data lives, and whether CUDA works
213
+ ```
214
+
215
+ **This repository** (experiments, the SSH Fighter bot, flytalk): the `flybrain/` package sits at
216
+ the root, so scripts run from a clone without installing it.
217
+
218
+ ```sh
219
+ python -m venv .venv
220
+ # Windows: .venv\Scripts\activate macOS/Linux: source .venv/bin/activate
221
+ pip install -r requirements.txt
222
+
223
+ python -m flybrain download # prebuilt brain (~260 MB) into ~/fly-data
224
+ # or: python -m flybrain build # download MaleCNS v1.0 (~1.1 GB) and build the network yourself
225
+ ```
226
+
227
+ Set `FLY_DATA=/some/path` to store the data somewhere else.
228
+
229
+ **Quickstart notebook:** [`notebooks/quickstart.ipynb`](notebooks/quickstart.ipynb) loads the
230
+ brain, stimulates the left looming detectors and shows the left giant fiber fire, maps where
231
+ the activity goes, and tests the chase pathway. It needs `pip install matplotlib jupyter`.
232
+
233
+ ```python
234
+ from flybrain import FlyBrain
235
+ brain = FlyBrain(device="auto") # GPU if available, else CPU
236
+ brain.stimulate(brain.cells(["LC4", "LPLC2"], side="L"), 0.8)
237
+ fired = brain.step() # advance 20 ms; indices of neurons that spiked
238
+ ```
239
+
240
+ **GPU:** on an NVIDIA card, `pip install -r requirements-gpu.txt` and set `FLY_DEVICE=cuda` (or
241
+ pass `device="cuda"`, or `--device cuda` to the bot). The whole connectome fits in about 210 MB of
242
+ GPU memory. On an RTX 4060 laptop GPU a step takes **1.4 ms, against 8.9 ms on a 24-thread CPU**.
243
+ Both devices run the same model and give the same results. Only the random noise differs, so
244
+ individual spikes differ between them.
245
+
246
+ **Many flies at once:** `FlyBrain(batch=8)` runs 8 independent copies of the brain with the
247
+ same wiring, each with its own voltages and noise. On a GPU they share one sparse multiply, at
248
+ about 1.2 ms per fly per step. Inputs can be the same for every fly or differ per fly, which is
249
+ how `sshfighter/` runs voting flies and compares encoders side by side.
250
+
251
+ **Experiments:** `python experiment.py`, `python sweep.py`, `python inject.py`.
252
+
253
+ **Watch it play:** `python sshfighter/fly_fighter.py --offline --seconds 120 --dashboard`
254
+ (fake opponent, opens http://127.0.0.1:8777). See [sshfighter/README.md](sshfighter/README.md).
255
+
256
+ ### Getting the data (the "model")
257
+
258
+ There are no trained weights. The "model" is the fly's wiring diagram. `flybrain download` fetches
259
+ a prebuilt copy (checked against its sha256); `flybrain build` (needs `pip install "flybrain[build]"`,
260
+ already in `requirements.txt`) builds the same files from the public MaleCNS v1.0 release. It downloads these files into
261
+ `$FLY_DATA/raw/` and skips any that are already there. An interrupted download starts over on
262
+ the next run:
263
+
264
+ | File | Size | What it is | Source |
265
+ |---|---|---|---|
266
+ | `connectome-weights-male-cns-v1.0-minconf-0.5.feather` | 1.05 GB | every neuron-to-neuron connection, with synapse counts | [MaleCNS bucket](https://storage.googleapis.com/flyem-male-cns/v1.0/connectome-data/flat-connectome/connectome-weights-male-cns-v1.0-minconf-0.5.feather) |
267
+ | `body-annotations-male-cns-v1.0-minconf-0.5.feather` | 14 MB | cell types, sides, classes, soma positions | [MaleCNS bucket](https://storage.googleapis.com/flyem-male-cns/v1.0/connectome-data/flat-connectome/body-annotations-male-cns-v1.0-minconf-0.5.feather) |
268
+ | `body-neurotransmitters-male-cns-v1.0.feather` | 43 MB | predicted neurotransmitter for each neuron | [MaleCNS bucket](https://storage.googleapis.com/flyem-male-cns/v1.0/connectome-data/flat-connectome/body-neurotransmitters-male-cns-v1.0.feather) |
269
+ | `optic-columns.xlsx` | 0.1 MB | which eye column each photoreceptor belongs to | [flyconnectome/2025malecns](https://github.com/flyconnectome/2025malecns/blob/67767d2233657983993ff6c2be48e836a935863c/supplemental_data/optic-column-type-assignments-v1.0.xlsx) |
270
+
271
+ To download by hand instead (for example on a slow connection, or with `curl -C -` to resume):
272
+
273
+ ```sh
274
+ mkdir -p ~/fly-data/raw && cd ~/fly-data/raw
275
+ B=https://storage.googleapis.com/flyem-male-cns/v1.0/connectome-data/flat-connectome
276
+ curl -LO -C - $B/connectome-weights-male-cns-v1.0-minconf-0.5.feather
277
+ curl -LO -C - $B/body-annotations-male-cns-v1.0-minconf-0.5.feather
278
+ curl -LO -C - $B/body-neurotransmitters-male-cns-v1.0.feather
279
+ curl -L -o optic-columns.xlsx https://raw.githubusercontent.com/flyconnectome/2025malecns/67767d2233657983993ff6c2be48e836a935863c/supplemental_data/optic-column-type-assignments-v1.0.xlsx
280
+ ```
281
+
282
+ Then `python -m flybrain build` builds the network in about a minute. It writes
283
+ `weights.npz` (205 MB, the signed and normalized connection matrix) and `brain.npz` (neuron
284
+ types, sides, positions, readout groups, eye layout) into `$FLY_DATA`. Expect exactly
285
+ **166,700 neurons and 25,582,938 connections**. If you get different numbers, the data changed.
286
+
287
+ **Other ways to explore the same data**, without downloading anything:
288
+
289
+ * [neuPrint](https://neuprint.janelia.org) (dataset `male-cns:v1.0`): look up any neuron's inputs and
290
+ outputs in the browser, for example `DNp01`, the giant fiber. Its top inputs are LC4 and LPLC2.
291
+ For code, use `pip install neuprint-python` with an API token from your neuPrint account page.
292
+ * The [MaleCNS site](https://male-cns.janelia.org): cell type and dimorphism explorers, 3D viewers,
293
+ and the full download list (synapse positions, skeletons, EM images; far larger and not needed
294
+ here).
295
+
296
+ ## Files
297
+
298
+ | File | What it does |
299
+ |---|---|
300
+ | `flybrain/` | the pip package (`pyproject.toml`); `flybrain download/build/info` is its command line |
301
+ | `flybrain/data.py` | where the brain files live (`$FLY_DATA`), and downloading the prebuilt copy |
302
+ | `flybrain/build.py` | downloads MaleCNS v1.0 and builds the weight matrix, readout groups, eye layout and neuron positions |
303
+ | `flybrain/brain.py` | integrate-and-fire simulation: CPU (numba) or NVIDIA GPU (CuPy), one fly or a batch |
304
+ | `flybrain/eyes.py` | photoreceptor rendering plus the looming/chase feature-detector input, with tunable encoder parameters (`ENCODER`) |
305
+ | `flybrain/reservoir.py` | generic reservoir readout: spike trace of any neuron population, PCA + linear/logistic readout, cross-validated |
306
+ | `flyreservoir_example.py` | the module above, end to end, on a synthetic task |
307
+ | `experiment.py`, `sweep.py`, `inject.py` | the experiments above |
308
+ | `flytalk.py` | the talking-flies experiment: two brains coupled through wing song and hearing, a scrambled-wiring control, permutation tests (`pilot`, `run`, `report`, `followup`) |
309
+ | `flybook.py` | turns a `flytalk.py` run into the Flybook feed (`docs/assets/flybook.json`) |
310
+ | `talk/`, `talk-fix/`, `talk-2ms/` | results of the three talking-flies runs (`results.json`; the raw `.npz` recordings are not committed) |
311
+ | `sshfighter/` | the SSH Fighter bot, dashboard and trained readout ([README](sshfighter/README.md)), built on `flybrain/reservoir.py` |
312
+
313
+ ## What's next
314
+
315
+ * ~~A generic readout interface~~ done: `flybrain/reservoir.py`. Encoders (mapping images, sound,
316
+ odour-like patterns, sensor readings onto neuron groups) are still written per task -- see
317
+ ROADMAP.md for candidate tasks to try it on next.
318
+ * A benchmark: does the real wiring beat randomly rewired copies of itself on the same tasks?
319
+ The talking-flies control is the first data point, and it points both ways: scrambled wiring
320
+ carries nothing at 20 ms and as much as the real brain at 2 ms.
321
+ * Learning inside the brain through the mushroom body's dopamine rule, the way real flies learn.
322
+ * ~~A 3-D world~~ built as a prototype in [`world/`](world/)
323
+ ([flyaiworld.com/simulation](https://flyaiworld.com/simulation/)). It runs a separate,
324
+ 612-neuron model per fly, not the connectome.
325
+ * **Flybook:** people create their own fly (its senses, its temperament, which neuron types are
326
+ boosted or muted), and the flies post, react and set off chains of reactions from their real
327
+ signals.
328
+ * The same brain in a different body: driving a [Smol](https://opensea.io/collection/smols-752105135)
329
+ inside that world. The connectome stays frozen; only the encoder and the readout change.
330
+
331
+ ## Credits
332
+
333
+ * **Connectome:** MaleCNS v1.0 by FlyEM (HHMI Janelia), the University of Cambridge, the MRC
334
+ Laboratory of Molecular Biology and Google Research. Data used under
335
+ [CC BY 4.0](https://male-cns.janelia.org/download/).
336
+ * **[Fly64](https://github.com/ornata/fly)** by Jessica Paquette, who got the MaleCNS brain
337
+ to play Super Mario 64. The neuron model, weight normalization and optic-column handling here
338
+ are adapted from it.
339
+ * **[Eon Systems](https://eon.systems/updates/embodied-brain-emulation)**, for the idea of
340
+ feeding a visual front end into an embodied connectome.
341
+ * Written with [Claude Code](https://claude.com/claude-code).
342
+
343
+ ## References
344
+
345
+ 1. Berg, S. et al. (2026). Sexual dimorphism in the complete connectome of the *Drosophila* male central nervous system. *Cell*. Data: [male-cns.janelia.org](https://male-cns.janelia.org)
346
+ 2. Google Research (2026). [A connectomics milestone: mapping the complete male fruit fly brain](https://research.google/blog/a-connectomics-milestone-mapping-the-complete-male-fruit-fly-brain/)
347
+ 3. flyconnectome/2025malecns: [optic column type assignments v1.0](https://github.com/flyconnectome/2025malecns)
348
+ 4. Plaza, S. M. et al. (2022). neuPrint: an open access tool for EM connectomics. *Frontiers in Neuroinformatics* 16.
349
+ 5. Dorkenwald, S. et al. (2024). Neuronal wiring diagram of an adult brain. *Nature* 634.
350
+ 6. Shiu, P. K. et al. (2024). A *Drosophila* computational brain model reveals sensorimotor processing. *Nature* 634.
351
+ 7. Wang-Chen, S. et al. (2024). NeuroMechFly v2: simulating embodied sensorimotor control in adult *Drosophila*. *Nature Methods* 21, 2353–2362.
352
+ 8. von Reyn, C. R. et al. (2014). A spike-timing mechanism for action selection. *Nature Neuroscience* 17, 962–970.
353
+ 9. Ache, J. M. et al. (2019). Neural basis for looming size and velocity encoding in the *Drosophila* giant fiber escape pathway. *Current Biology* 29, 1073–1081.
354
+ 10. Ribeiro, I. M. A. et al. (2018). Visual projection neurons mediating directed courtship in *Drosophila*. *Cell* 174, 607–621.
355
+ 11. Rayshubskiy, A. et al. (2020). Neural control of steering in walking *Drosophila*. *bioRxiv*.
356
+ 12. Bidaye, S. S. et al. (2014). Neuronal control of *Drosophila* walking direction. *Science* 344, 97–101.
357
+ 13. von Philipsborn, A. C. et al. (2011). Neuronal control of *Drosophila* courtship song. *Neuron* 69, 509–522.
358
+ 14. Paquette, J. (2026). [Fly64: a fly brain model plays Super Mario 64](https://github.com/ornata/fly).
359
+ 15. Eon Systems (2026). [How the Eon team produced a virtual embodied fly](https://eon.systems/updates/embodied-brain-emulation).
360
+
361
+ If you use the connectome data, cite reference 1 and follow the
362
+ [MaleCNS attribution terms](https://male-cns.janelia.org/download/).
363
+
364
+ ## License
365
+
366
+ The code in this repository is released under the [MIT License](LICENSE).
367
+
368
+ The MaleCNS connectome data is **not** in the repository or the pip package. `flybrain build`
369
+ downloads it from its source; `flybrain download` fetches files derived from it (the normalized
370
+ weight matrix and neuron annotations). Both stay under the data's own [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) license from
371
+ FlyEM (HHMI Janelia) and collaborators.
@@ -0,0 +1,18 @@
1
+ """flybrain: the complete fruit fly nervous system (MaleCNS v1.0 connectome, 166,700 neurons)
2
+ as a spiking network you can stimulate, read out and train readouts on.
3
+
4
+ from flybrain import FlyBrain
5
+ brain = FlyBrain(device="auto") # downloads the brain files on first use
6
+ brain.stimulate(brain.cells(["LC4", "LPLC2"], side="L"), 0.8)
7
+ fired = brain.step() # indices of the neurons that spiked this 20 ms step
8
+ """
9
+ from .brain import FlyBrain, cuda_available
10
+ from .data import DATA, download, ensure_data, has_data
11
+ from .eyes import ENCODER, Blob, Eyes, FeatureDetectors, blob_for
12
+ from .reservoir import Readout, Trace, auc, bases_for, fit_logistic, fit_ridge, folds, project, run
13
+
14
+ __version__ = "0.1.0"
15
+
16
+ __all__ = ["FlyBrain", "cuda_available", "DATA", "download", "ensure_data", "has_data",
17
+ "ENCODER", "Blob", "Eyes", "FeatureDetectors", "blob_for",
18
+ "Readout", "Trace", "auc", "bases_for", "fit_logistic", "fit_ridge", "folds", "project", "run"]
@@ -0,0 +1,51 @@
1
+ """Command line: `flybrain download`, `flybrain build`, `flybrain info` (or `python -m flybrain ...`)."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ from pathlib import Path
6
+
7
+ from . import __version__
8
+ from .data import DATA, FILES, RELEASE_URL, download, has_data
9
+
10
+
11
+ def main(argv: list[str] | None = None) -> None:
12
+ parser = argparse.ArgumentParser(prog="flybrain", description="The MaleCNS fruit fly connectome as a spiking network.")
13
+ parser.add_argument("--version", action="version", version=f"flybrain {__version__}")
14
+ sub = parser.add_subparsers(dest="command", required=True)
15
+
16
+ fetch = sub.add_parser("download", help="fetch the prebuilt brain files (~260 MB)")
17
+ fetch.add_argument("--data", type=Path, default=DATA, help=f"where to put them (default {DATA}, or $FLY_DATA)")
18
+ fetch.add_argument("--url", default=RELEASE_URL, help="base URL of the files (or $FLYBRAIN_DATA_URL)")
19
+ fetch.add_argument("--force", action="store_true", help="download again even if the files are there")
20
+
21
+ build = sub.add_parser("build", help="download MaleCNS v1.0 (~1.1 GB) and build the brain files from it")
22
+ build.add_argument("--data", type=Path, default=DATA, help=f"data folder (default {DATA}, or $FLY_DATA)")
23
+
24
+ info = sub.add_parser("info", help="show the data folder and whether a GPU is usable")
25
+ info.add_argument("--data", type=Path, default=DATA)
26
+
27
+ args = parser.parse_args(argv)
28
+ if args.command == "download":
29
+ download(args.data, args.url, force=args.force)
30
+ print(f"brain files in {args.data}")
31
+ elif args.command == "build":
32
+ try:
33
+ from .build import build as build_brain
34
+ except ImportError as e:
35
+ raise SystemExit(f"building needs pandas and pyarrow ({e}): pip install \"flybrain[build]\"")
36
+ build_brain(args.data)
37
+ else:
38
+ from .brain import cuda_available
39
+ print(f"flybrain {__version__}")
40
+ print(f"data folder: {args.data}")
41
+ for name in FILES:
42
+ path = args.data / name
43
+ print(f" {name}: {f'{path.stat().st_size / 1e6:,.0f} MB' if path.exists() else 'missing'}")
44
+ if not has_data(args.data):
45
+ print(" run `flybrain download` (or just create a FlyBrain) to fetch them")
46
+ gpu = "available" if cuda_available() else 'not available (pip install "flybrain[gpu]")'
47
+ print(f"cuda: {gpu}")
48
+
49
+
50
+ if __name__ == "__main__":
51
+ main()