Getting started
This repository is three things: the library (src/DotFly.*, NuGet-style projects), the
CLI (src/DotFly.Cli) and the demos (samples/). The connectome data are not in the
repository (CC BY 4.0, downloaded once into the git-ignored .data/ folder).
1. Prerequisites
- .NET 11 SDK, RC1 or later — the exact version is pinned in
global.json(download). - Godot 4.7, .NET edition — only for the two Godot demos (download).
Until .NET 11 GA, Godot needs
DOTNET_ROLL_FORWARD_TO_PRERELEASE=1in its environment. - Disk: ~300 MB for the FlyWire v630 files, ~3 GB for MaleCNS v1.0 plus its checkpoints.
2. Build and test
git clone https://github.com/kkokosa/dotFly
cd dotFly
dotnet build -c Release
dotnet test -c Release # 94 unit + integration tests; no data needed
dotnet run --project src/DotFly.Cli -c Release -- info
3. Get a connectome
Option A — FlyWire v630 (90 MB): enough for the library, the CLI and the sugar experiment. The files are in the Shiu et al. model repository:
git clone --depth 1 https://github.com/philshiu/Drosophila_brain_model .data/shiu2024
dotnet run --project src/DotFly.Cli -c Release -- build flywire .data/shiu2024/2023_03_23_completeness_630_final.csv .data/shiu2024/2023_03_23_connectivity_630_final.parquet -m 630
dotnet run --project samples/DotFly.Sample.SugarExperiment -c Release # 30 trials of sugar → MN9 in ~2 s
Option B — MaleCNS v1.0 (1.1 GB): needed by the room demo and the 2D demo.
From https://male-cns.janelia.org/download/ ("flat files"), put these three files into
.data/malecns-v1.0/:
body-annotations-male-cns-v1.0-minconf-0.5.feather (~15 MB)
body-neurotransmitters-male-cns-v1.0.feather (~43 MB)
connectome-weights-male-cns-v1.0-minconf-0.5.feather (~1.05 GB; or the 508 MB -traced-only file)
then build the checkpoint (a minute; writes .data/malecns-v1.0/malecns-v1.0-superclass.dfb):
dotnet run --project src/DotFly.Cli -c Release -- build malecns .data/malecns-v1.0 --filter superclass
dotnet run --project src/DotFly.Cli -c Release -- inspect .data/malecns-v1.0/malecns-v1.0-superclass.dfb
--filter superclass keeps bodies with a superclass annotation (166,700 neurons; the graph the
demos use); --filter traced keeps status == Traced (165,122; the official traced-only export;
needs the -traced-only weights file). Edges whose presynaptic transmitter is unclear or histamine
are dropped as no-ops; the structural counts stay in the checkpoint's provenance.
4. Run the room demo
$env:DOTNET_ROLL_FORWARD_TO_PRERELEASE = "1"
& "<path to>\Godot_v4.7.1-stable_mono_win64.exe" --path samples\DotFly.Sample.Godot3D
or open samples/DotFly.Sample.Godot3D in the Godot editor and press Play. Keys: C camera,
R recurrent transmission, E external input, V vision, O smell/taste, S silence DNp04+HS,
Space pause. -- --flies 4 puts four flies in the room. The
room demo page explains
everything on screen. Note: on Windows run the real Godot executable, not the winget shim
(godot.exe in a Links folder) — the .NET module fails silently through the shim.
5. Try the CLI
dotnet run --project src/DotFly.Cli -c Release -- explore .data/malecns-v1.0/malecns-v1.0-superclass.dfb --stimulate type:T4a
dotnet run --project src/DotFly.Cli -c Release -- run .data/malecns-v1.0/malecns-v1.0-superclass.dfb --stimulate "type:LC4@L" --hz 100 --seconds 1 --silence "class:Kenyon_Cell;type:lLN*"
dotnet run --project src/DotFly.Cli -c Release -- bench .data/malecns-v1.0/malecns-v1.0-superclass.dfb --scenario type:T4a --threads 1,4,8
tools/brian2_golden.py regenerates the Brian2 reference fixtures (needs a Python environment with
brian2, pandas, pyarrow; it is not part of the build).
Use the engine
using DotFly;
using DotFly.Core.Graph;
using Brain brain = Brain.Open(".data/shiu2024/flywire-v630.dfb");
using Simulation sim = brain.CreateSimulation(new SimulationOptions { Seed = 1 });
NeuronSet sugar = brain.ByBodyIds("sugar_GRN_R", sugarIds); // exact 64-bit IDs (the 21 sugar GRNs of Shiu et al.)
sim.Input(sugar, InputKind.PoissonToV).Fill(150f); // Poisson drive, 150 Hz each
OutputPort mn9 = sim.Output(brain.ByBodyId(720575940660219265), OutputKind.Rate(50.Ms()));
long spikes = 0;
sim.OnSpikes += (long step, ReadOnlySpan<int> ids) => spikes += ids.Length; // every spike, no allocation
sim.Run(1.Seconds()); // neural time
Console.WriteLine($"{brain.Provenance.Name}: {brain.NeuronCount:N0} neurons, {brain.EdgeCount:N0} edges");
Console.WriteLine($"MN9 {mn9.Snapshot.Mean():F1} Hz in the last 50 ms; {spikes:N0} spikes in {sim.NeuralTime.TotalSeconds:F1} s neural time, " +
$"{sim.Clock.WallTime.TotalMilliseconds:F0} ms wall ({sim.Clock.RealTimeFactor:F1}× real time)");
Output (8-core laptop, default thread count; the spike count is the same on every run and at any thread count — only the wall time changes):
flywire-v630: 127,400 neurons, 14,687,178 edges
MN9 66.7 Hz in the last 50 ms; 13,776 spikes in 1.0 s neural time, 170 ms wall (5.9× real time)
For a game loop, new RealtimeDriver(sim).Start() runs the network on its own thread; the game
thread writes InputPorts and reads OutputPort.Snapshot frames (lock-free), and a readout
adapter (LinearAdapter, ThresholdAdapter, or an ONNX/ML.NET model) turns a frame's values into
actions — labelled fixed, calibrated or trained, because the graph never learns.
sim.Record("run.dfs") captures spikes, input writes and output frames; Recording.Read(...) .Replay(sim) reproduces the run. samples/DotFly.Sample.SugarExperiment is the worked example.
Samples
| sample | what it shows |
|---|---|
samples/DotFly.Sample.SugarExperiment |
the Shiu et al. sugar → MN9 experiment (30 trials + silencing) against the published rates |
samples/DotFly.Sample.TrainReadout |
recording output frames and training a readout three ways (C# logistic regression, ML.NET, ONNX) |
samples/DotFly.Sample.Godot |
the 2D closed loop: retina → LC4 → DNp04/GF → a fixed decoder steers a fly away from a looming target |
samples/DotFly.Sample.Godot3D |
the room: eyes, antennae, wind, sugar, feeding, panels, cameras, several flies |