Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

This project connects a computational model of a fruit fly’s nervous system to a small quadrotor. The nervous system model is built directly from the wiring diagram of a real fly. The quadrotor is a Bitcraze Crazyflie 2.1 carrying a forward-facing camera and a downward-facing flow and range sensor. The camera feeds the model’s photoreceptors, the gyroscope feeds the neurons that a fly uses to sense body rotation, and the model’s wing motor neurons are read out and turned into flight commands. A learning procedure tunes the boundaries of the system, the way sensors are encoded and the way motor neurons are decoded, along with a small number of physiological constants, so that the whole thing can keep the drone in the air. The wiring itself is never changed.

The project is named Flatline, after the Dixie Flatline of Gibson’s Neuromancer: a recorded mind, run as a program, to do a job. The recorded wiring that the engine runs is called an imago, the entomological term for the adult insect and the Latin for image. A built imago is a file, and the engine animates it.

This section explains the ideas the design rests on, for readers who know software or robotics but not neuroscience, or the reverse.

Connectomes

A connectome is a map of every neuron in a piece of nervous tissue and every synapse between them, reconstructed from electron microscopy at a resolution fine enough to see individual synaptic contacts. The dataset used here is the Janelia FlyEM male central nervous system (CNS) release, version 1.0. It covers the brain, the optic lobes, and the ventral nerve cord (the fly’s equivalent of a spinal cord) of one adult male Drosophila melanogaster. It contains about 166,000 neurons with assigned cell types and about 25 million significant connections, where each connection records how many synapses link a presynaptic neuron to a postsynaptic one.

Three properties of this dataset make the project possible.

  • It is complete from sense organ to muscle. Photoreceptors, the mechanosensory neurons of the halteres (the fly’s gyroscopic organs), the descending neurons that carry commands from the brain to the body, and the motor neurons that drive the wing muscles are all present and labeled.
  • Each neuron has a predicted neurotransmitter. In the fly, acetylcholine is the main excitatory transmitter and GABA and glutamate are the main inhibitory ones. Knowing the transmitter tells us the sign of every connection.
  • The optic lobe neurons carry coordinates on the hexagonal lattice of the compound eye, which lets us place each photoreceptor in the visual field.

A connectome is a structure, not a program. It says which neurons talk to which and how strongly, but not what any of them are doing at a given moment. To get behavior out of it, the structure has to be animated with a model of how neurons work.

Leaky integrate-and-fire models

The simplest useful model of a neuron treats its membrane voltage as a leaky capacitor. Incoming spikes push the voltage up (excitatory) or down (inhibitory), the voltage relaxes back toward a resting level with a characteristic time constant, and when it crosses a threshold the neuron emits a spike of its own, resets, and stays quiet for a brief refractory period. This is the leaky integrate-and-fire (LIF) model.

In 2024, Shiu and colleagues built a LIF model of an entire fly brain from the FlyWire connectome, with one free parameter, the voltage jump per synapse, and with connection signs taken from neurotransmitter predictions. Despite its simplicity the model reproduced known sensorimotor circuits: stimulating sugar-sensing neurons activated the feeding motor program, and stimulating specific mechanosensory neurons activated grooming. That result is the license for this project. If a signed LIF network built from a connectome recapitulates real circuits, it is worth asking what its flight circuits do when connected to a flying machine.

This project uses the same neuron model and the same initial constants, applied to the male CNS connectome, which unlike FlyWire includes the ventral nerve cord and therefore the wing motor neurons. One caution is owed up front: Shiu et al. drove spiking taste and touch neurons. This project’s main input is the retina, whose photoreceptors release histamine, an inhibitory transmitter, onto lamina cells that in the real fly signal by graded potentials rather than spikes. The design spec, section 8.6, states how this design keeps that pathway alive and how it will be checked.

How a fly flies, briefly

A fly’s flight control loop has four stages that the design mirrors.

  • Vision. The compound eyes each have about 800 facets, and each facet samples one direction in the visual field. Downstream circuits in the optic lobes compute local motion, and wide-field neurons in the lobula plate integrate that motion into estimates of self-rotation and self-translation. This is optic flow processing, and it is the fly’s main source of information about drift. How the fly sees motion walks through that pipeline cell by cell.
  • Halteres. The hindwings of flies have evolved into small club-shaped organs that beat with the wings. When the body rotates, Coriolis forces deflect them, and strain-sensing neurons at their base report the rotation. Halteres are fast gyroscopes and are essential for stable flight.
  • Descending neurons. About 1,300 neurons carry commands from the brain down into the ventral nerve cord.
  • Wing motor neurons. Two large power muscles drive the wingbeat, and about a dozen small steering muscles per side adjust wing stroke to produce turns, climbs, and corrections. The male CNS contains 67 wing motor neurons and 16 haltere motor neurons.

A quadrotor has none of this anatomy, so the last stage requires a translation: the pattern of activity across the wing motor neurons is mapped to the four things a quadrotor can be told to do, move sideways, move forward, turn, and climb.

The Crazyflie platform

The Crazyflie 2.1 is a 27 gram open-source quadrotor with a well-documented firmware and a Python client library. Two expansion decks are used. The AI deck adds a monochrome 324 by 324 pixel camera with an 87 degree field of view, a small processor, and a WiFi radio that can stream frames to a laptop. The flow deck v2 adds a downward time-of-flight range sensor and an optical-flow sensor, which let the firmware hold height and estimate horizontal velocity.

The firmware’s own stabilizer stays in the loop. The fly brain does not drive motors directly; it issues setpoints that the firmware executes. Two levels of setpoint are used. In hover mode the brain commands velocities and the firmware nulls drift on its own, which is safe but masks the brain’s contribution. In attitude mode the brain commands roll and pitch angles and must correct drift itself from vision and haltere input, which is the real experiment.

Evolution strategies

The parameters that must be learned are few, roughly ten thousand, and the system being tuned is a spiking network that is not differentiable in any convenient way. Evolution strategies fit this case. The optimizer keeps a mean parameter vector, samples a population of perturbed copies, runs each through a simulated flight, scores it, and moves the mean toward the perturbations that scored well. It needs only episode returns, runs identically on any hardware, and parallelizes across a population. The price is sample inefficiency, which is paid for with a batched simulator that advances many brains and many drones at once.

What to expect

It is not known whether a frozen connectome with tuned boundaries can hold a quadrotor in a hover. The literature suggests the visual and haltere circuits are the right ones, and the model will be given every sensible advantage: correct signs, correct sensory placement, a stabilizing firmware underneath, and a training curriculum that starts easy. But the project is an experiment, and the documentation is built as a lab notebook so that negative results are recorded as carefully as positive ones.

How the fly sees motion

Why this matters here

The drone has to know when it is rotating, and one way to know is to watch the world slide past the camera. A fly does this with a chain of five stages in its optic lobes, from the photoreceptors to a handful of large cells that report “the whole world is turning”. The camera feeds the model’s photoreceptors, so the drone gets the same chain. Every notebook entry about the optic-flow probe asks one question: does a signal get through that chain, stage by stage, on the real wiring? This page names the cells the notebook names and says what each one is for.

The pipeline at a glance

Each row is one stage. Signals flow down the table. The visual field is tiled into about 800 columns per eye, one per facet, and every stage up to the motion detectors repeats once per column, so “L2” in the notebook means about 1,780 cells across the two eyes, roughly one per column, all doing the same job at different points in the image.

stagecell typesjobanalogy
EyeR1–R6 photoreceptorsturn light into an electrical signalpixels
LaminaL1, L2, L3; relays L4, L5; feedback cells C2, C3split each pixel’s signal into “getting brighter” (ON) and “getting darker” (OFF) channelstwo filters on the same pixel stream
MedullaON: Mi1, Tm3 (fast), Mi4, Mi9 (slower, inhibitory). OFF: Tm1, Tm2, Tm4 (fast), Tm9 (delayed)give each channel a fast copy and a delayed copy of the signal, per columna tap and a delay line
Motion detectorsT4a–d (ON edges), T5a–d (OFF edges); CT1, a large inhibitory cell feeding bothcompare fast and delayed copies from neighbouring columns to detect local motion in one of four directionsa direction-selective edge detector per patch
Lobula plateHS (horizontal system), VS (vertical system)pool the local detectors over large parts of the eye into a wide-field rotation signalsumming a motion field into one rotation estimate

The four subtypes of T4 and T5 are the four directions: a prefers front-to-back motion, b back-to-front, c upward, d downward. Each subtype sends its output to its own layer of the lobula plate, so the lobula plate holds four maps of local motion, one per direction. HS cells read the horizontal layers and respond to turning about the vertical axis (yaw); VS cells read the vertical layers and respond to rolling and pitching. HS and VS are the end of the line for this project’s probe: their signal is the one the drone needs.

T4 sits in the medulla and T5 in the lobula, a neighbouring neuropil, but they are best thought of as one stage: the same computation, done once for brightening edges (T4) and once for darkening edges (T5) (Borst, Haag & Mauss 2020).

How a motion detector works

A single pixel cannot tell motion from flicker. Two neighbouring pixels can, if one of them is listened to late. Suppose an edge moves from column A to column B. A sees it first, B a moment later. If A’s signal is delayed by about that moment and then compared with B’s undelayed signal, the two arrive together for motion from A to B and miss each other for motion from B to A.

   column A        column B
      |               |
   [delay]            |
      |               |
      +----> [ × ] <--+
               |
        responds to A -> B,
        not to B -> A

This is the Hassenstein–Reichardt detector, worked out from beetle behaviour in 1956, and in its simplest form the comparison is a multiplication. Barlow and Levick proposed the mirror image for the rabbit retina in 1965: a delayed inhibitory signal from one side that vetoes the response to motion in the wrong direction. The fly’s T4 and T5 appear to use both: an excitatory, delayed-comparison enhancement for the preferred direction and a delayed inhibition for the opposite one (Haag et al. 2016).

The medulla supplies the parts. Each T4 cell takes its fast ON inputs (Mi1, Tm3) from a central column and its slower inputs (Mi4, Mi9) from neighbouring columns, so the spatial offset is built into which neighbour each input comes from and the delay into how slowly those inputs respond (Takemura et al. 2017; Arenz et al. 2017). CT1 is one wide cell that contacts T4 across the field. T5 has the same plan with OFF inputs: Tm1, Tm2 and Tm4 fast, Tm9 slower, and CT1 again (Shinomiya et al. 2014, 2019). Which medulla cell is fast and which is slow, and by how much, has been measured differently by different labs (Behnia et al. 2014; Arenz et al. 2017); the outline is settled, the exact delays are not.

Two channels, and why the ON channel is subtle

In flies, light makes the photoreceptors release more histamine, and histamine is inhibitory. So the lamina cells under them do the opposite of what one might guess: L1, L2 and L3 all dim when light arrives and brighten when it goes away. At this point there is no split yet. All three lamina cells respond to light the same way. What makes one channel ON and the other OFF is what their targets do with the signal.

The OFF channel is the straightforward one. L2 releases acetylcholine, which is excitatory, onto Tm1, Tm2 and Tm4. When the scene gets darker, L2 brightens, releases more, and its targets are pushed up. Darkness in, excitation out.

The ON channel has to invert the sign once more, and it does it with a brake. L1 releases glutamate onto Mi1, and on Mi1 that glutamate opens an inhibitory channel (Molina-Obando et al. 2019). In the dark, L1 is active and holds the brake on: Mi1 is pressed down. When light arrives, L1 dims, lets off the brake, and Mi1 rises. Mi1 is not excited by light; it is released by it. The same arrangement holds for Tm3 and for the lamina relay L5, which also take most of their lamina input from L1.

The project’s sign table follows the dataset’s transmitter labels: acetylcholine excitatory, GABA, glutamate and histamine inhibitory. That makes L1 inhibitory everywhere, which is right for L1 onto Mi1. The lamina drive entry has the transmitter table and the weights.

Dimmers versus switches

Most cells in the first stages of this pipeline do not spike. The photoreceptors, the lamina cells, and most of the medulla cells signal with a voltage that varies smoothly with their input, called a graded potential, and they release transmitter continuously in proportion to it. They behave like dimmers. Further along, the picture is less clean: T4 and T5 are generally reported to be graded, with little or no spiking, and how far that holds is still argued over. HS and VS are graded with occasional spikes.

This project’s engine models every neuron the same way, as a leaky integrate-and-fire cell: a switch that is either quiet or emitting spikes, and that stays quiet until its input crosses a threshold (see the introduction). That is the model Shiu et al. used for the whole brain, and the reason it is used here.

A brake only does something to a dimmer. A dimmer held below its resting level by a brake rises when the brake is released. A spiking cell held below rest by a brake, once released, returns to rest, and rest is below threshold, so it stays off. Releasing the brake on a switch that is already off changes nothing anyone downstream can see. The lamina has the same problem one step earlier, because photoreceptor input onto it is inhibitory too; the design answers it with the lamina bias, a constant background push that keeps L1, L2 and L3 firing so light can modulate them in both directions. Mi1 and the other braked medulla cells have no such push yet.

There are two ways to proceed, and the choice is Kyle’s.

  • (a) Keep switches everywhere and give the braked cells a background push, the same kind of tonic bias the lamina already has, applied by a rule stated from the wiring (cells whose dominant input is inhibitory) rather than chosen cell by cell. This keeps one neuron model across the whole nervous system and the existing sign table. It asks the model to supply, as a fixed bias, an operating point that the real cells reach through their own steady transmitter release.
  • (b) Model the early visual stages as dimmers: a graded-rate model of the columnar optic lobe, as Lappalainen et al. 2024 did for the fly’s visual system, with the rest of the nervous system staying spiking. This is closer to how these cells actually work, and Lappalainen et al. showed that such a model, built on the connectome and trained on a task, predicts real responses. It means two neuron models in one engine and a boundary between them.

Where the model stands

  • Lamina drive onto the medulla: the lamina fires, the medulla does not. The ON path is braked by L1, as described above. The OFF path is wired correctly but too weak: L2 fires about half as fast as Tm1, Tm2 and Tm4 need.
  • Photoreceptor operating-point sweep: raising the camera’s baseline rate speeds up L2 and wakes the OFF medulla (Tm1, Tm2 and Tm4 fire a few hertz). T5 still produces no spikes, and neither do T4, HS or VS.
  • T5 drive: T5 is both braked and under-powered. CT1 fires flat out and holds every T5 well below threshold, and Tm9, its largest excitatory input, is silent.

Every result in the notebook carries a shuffled-connectome control, a copy of the imago with the wiring randomized, so that a signal can be credited to the real wiring and not to the engine.

Glossary

The stage overlay in the probe report covers the photoreceptors, L1–L5, C2, C3, Mi1, Tm3, Mi4, Mi9, Tm1, Tm2 and Tm4. The verdict sets are T4a–d, T5a–d, HS and VS. Transmitters are given where the sign matters to the model.

  • C2, C3: lamina feedback cells, sent back from the medulla to the lamina. GABA, inhibitory. Both take most of their lamina input from L1.
  • CT1: one very large cell per optic lobe, with a small branch in every column, inhibiting T4 in the medulla and T5 in the lobula. GABA, inhibitory. Thought to contribute to the null-direction veto; its exact role is still debated. In the fly each of its roughly 700 column branches works as an independent unit (Meier & Borst 2019); the engine treats it as one point cell per side, so a whole lobe’s input switches it fully on and it brakes every T5 at once (see the T5 drive entry).
  • HS: horizontal system cells, lobula plate. Pool front-to-back and back-to-front motion over much of the eye; signal turning about the vertical axis (yaw).
  • L1: lamina, ON channel. Glutamate, inhibitory under the project’s sign table. The brake on Mi1, Tm3 and L5.
  • L2: lamina, OFF channel. Acetylcholine, excitatory. Main input to Tm1, Tm2 and Tm4.
  • L3: lamina, feeds both channels. Acetylcholine, excitatory. Main lamina input to Mi9; in the fly also a main input to Tm9.
  • L4: lamina relay, OFF channel. Acetylcholine. Driven mostly by L2.
  • L5: lamina relay, ON channel. Acetylcholine. Driven by L1 (braked) and L2; the main lamina-side driver of Mi4.
  • Mi1: medulla, ON channel, fast. The main excitatory input to T4, at the centre of its receptive field. Braked by L1.
  • Mi4: medulla, ON channel, slower. GABA, inhibitory. Feeds T4 from a neighbouring column; little direct lamina input.
  • Mi9: medulla, ON-channel circuit, slower. Glutamate, inhibitory onto T4. Unusually for an ON-circuit cell, it responds to darkening (Strother et al. 2017), so light releases its inhibition of T4: one more sign inversion, on the null side of T4’s receptive field.
  • R1–R6: photoreceptors, the eye stage. Histamine, inhibitory onto the lamina. The outer photoreceptors that motion vision uses (R7 and R8 serve colour and are not part of this pipeline).
  • T4a–d: motion detectors for ON (brightening) edges, dendrites in the medulla. a front-to-back, b back-to-front, c up, d down. Acetylcholine in the fly, excitatory onto HS and VS.
  • T5a–d: motion detectors for OFF (darkening) edges, dendrites in the lobula. Same four directions and outputs as T4.
  • Tm1: medulla, OFF channel, fast. Input to T5. Driven by L2.
  • Tm2: medulla, OFF channel, fast. Input to T5. Driven by L2.
  • Tm3: medulla, ON channel, fast. Input to T4, alongside Mi1. Braked by L1.
  • Tm4: medulla, OFF channel, fast. Input to T5. Driven by L2.
  • Tm9: medulla, OFF channel, delayed. Input to T5 from a neighbouring column. Not yet in the stage overlay.
  • VS: vertical system cells, lobula plate. Pool upward and downward motion; signal rolling and pitching.

See the References chapter for full citations.

Background and related work

This chapter surveys the connectome dataset, the modeling approaches borrowed, and the flight hardware.

The male CNS connectome

The Janelia FlyEM male CNS v1.0 release is a full central nervous system reconstruction of one adult male Drosophila melanogaster: brain, optic lobes, and ventral nerve cord, imaged by electron microscopy and segmented into identified neurons. It contains roughly 166,700 typed neurons and about 25.6 million significant connections, recording how many synapses link a presynaptic neuron to a postsynaptic one. Every neuron carries a predicted neurotransmitter, giving a sign to each synapse, and optic lobe neurons carry column coordinates on the eye’s hexagonal lattice, letting photoreceptors be placed in a visual field.

A separate dataset, FlyWire, reconstructs a female whole brain but lacks a ventral nerve cord, and therefore wing motor neurons: it stops at the brain’s outputs, not muscle. That completeness is why this project uses the male CNS dataset.

Whole-brain spiking models

Shiu et al. 2024 built a leaky integrate-and-fire model of the entire FlyWire brain. Every synapse’s sign came from its presynaptic neuron’s predicted transmitter, and the model had one fitted free parameter, a voltage jump of 0.275 mV per synapse. Despite that simplicity, driving sugar-sensing neurons activated the feeding motor program, and driving mechanosensory neurons activated grooming. That result is this project’s license: a signed network built directly from a connectome can reproduce real circuit behavior without tuning individual connections.

A caution carries over rather than a guarantee. Shiu et al. drove excitatory taste and touch neurons, and their success says nothing about pathways that start differently. This project’s main input is the retina, and photoreceptors are histaminergic, an inhibitory transmitter; inhibition onto a resting spiking neuron does nothing, so vision needs its own answer first.

Connectome-constrained visual models

Lappalainen et al. 2024 took a different approach: a connectome-constrained rate model of the fly visual system, trained by task optimization rather than fit synapse by synapse, that predicted neural responses with good accuracy. The result matters here because fixed connectome structure plus a small set of learned parameters can produce function that matches a real nervous system. This project differs on three axes: spiking rather than rate-based, the whole nervous system rather than one pathway, and closed loop on a physical body rather than scored against recorded activity.

The optic lobe and the eye map

Nern et al. 2025 (Connectome-driven neural inventory of a complete visual system) is the source of the column coordinates used to place photoreceptors in the visual field. Zhao et al. 2025 (Eye structure shapes neuron function in Drosophila motion vision) reports measured facet directions for the compound eye, obtained directly rather than inferred from connectivity. The current design uses a parametric eye model built from the column coordinates; the measured directions are a natural input to a future, more accurate map.

Fly flight control

A fly’s flight loop runs through the stages this design mirrors. Vision starts at the compound eyes, where each facet samples one direction; motion detector neurons T4 and T5 compute local motion, and wide-field lobula plate neurons HS and VS integrate it into estimates of the fly’s own rotation and translation. This optic flow processing is the fly’s primary source of information about drift.

Halteres, the fly’s reduced hindwings, deflect under Coriolis forces when the body rotates; sensory neurons at their base report the deflection, making halteres fast gyroscopes feeding the wing motor system. Descending neurons, about 1,300 of them, carry commands from the brain into the ventral nerve cord, reaching the wing motor neurons: 67 in this dataset, driving the power muscles and the smaller steering muscles per side that produce turns, climbs, and corrections.

The Crazyflie platform

The Crazyflie 2.1 is a 27 gram open-source quadrotor with documented firmware and a Python client library, cflib. Two expansion decks supply the sensors used here: the AI deck adds a monochrome camera with an 87 degree field of view, streaming frames over WiFi to a laptop, and the flow deck v2 adds a downward time-of-flight range sensor and an optical-flow sensor for height and horizontal velocity. The firmware’s own stabilizer stays in the loop; the brain issues setpoints for it to execute rather than driving motors directly, and cflib sends the setpoints and reads telemetry back.

Where this project sits

This project is an early attempt to put a whole-CNS connectome model on a physical body and close the loop in real flight. The wiring itself is never changed; only the boundaries, sensory encoding, motor decoding, and a few global constants, are learned, by evolution strategies rather than by hand. What that framing does and does not claim about the resulting system is the subject of the ethics chapter.

See the References chapter for full citations.

Ethics and philosophical implications

This project sits at the meeting point of three lines of work that each carry their own questions: whole-brain modeling, embodied artificial agents, and autonomous flight. The questions are recorded here so that the project’s claims, and its limits, are stated before any result exists to tempt overstatement.

What is being simulated

The model is built from the wiring of one individual male fly. It keeps the neurons, the synapse counts, and the transmitter signs, and it discards nearly everything else: dendritic computation, graded signaling, electrical synapses, neuromodulation, plasticity, glia, development, and the hormonal and metabolic state of a living animal. It is a connectome-constrained spiking network, a structural echo of a fly, not an emulation of one. The project will describe it that way. Phrases such as “a fly brain flies a drone” are convenient and will be avoided in favor of “a network built from a fly connectome”. The boundary parameters and global constants are tuned by an optimizer, so any behavior the system shows is a joint product of the wiring and the tuning, and the paper must separate the two.

Epistemic commitments

Two commitments follow. First, the wiring stays frozen and the learnable set stays small, so that whatever the optimizer finds is constrained by the connectome rather than replacing it. Second, every claim about the connectome’s contribution is tested against controls with the same learnable set: a connectome whose internal wiring is shuffled within synapse-count classes, preserving every neuron’s synapse totals, sign, and sensory interface, and an untrained network with default constants. If the shuffled control flies as well, the wiring did no work, and that is the result to report. Negative results are recorded in the notebook with the same care as positive ones.

Moral status

Whether insects have experiences that matter morally is an open scientific and philosophical question; recent statements by researchers in animal consciousness treat it as a realistic possibility rather than a settled negative. This project takes the position that the simulation is not the animal. It lacks the biophysics, the body, and the internal states that any current theory of experience relies on, and it is far coarser than the models that would raise the question seriously. That position is stated as an assumption, not a proof, and it is one the field will have to revisit as models become richer. No live animals are used in this project. The dataset derives from a single fly that was sacrificed for the connectome; using that data carefully and crediting it properly is the only way to honor that cost.

Re-embodiment

A fly’s nervous system evolved for a body with wings, halteres, legs, and eyes that span nearly the full sphere. Here it is given a quadrotor with a single 87 degree camera and a gyroscope, and a control interface that is at best an analogy to wing steering. Success would suggest that some of the circuits compute something general enough to survive that transplant, for example an estimate of self-rotation from optic flow that is useful to any flying body. Failure would be harder to interpret, because it could reflect the model, the interface, the sensors, or the optimizer. The design tries to make failure informative through ablations and through the hover-mode curriculum, which isolates the brain’s contribution stage by stage. Closed-loop embodiment is a stronger test than replaying recorded stimuli, because errors feed back into the input, and that is why the project puts the model on a physical machine rather than only in a simulator.

Whole-brain emulation

The fly is the first organism for which a complete central nervous system wiring diagram and a working functional model both exist. Giving such a model a body is the natural next step, and this project is among the early attempts to do so in hardware. That places it on a path that leads, in the long run, toward emulating larger nervous systems, with all the questions about identity, consent, and welfare that path raises. Nothing in this project approaches those questions directly, but a project on the path should say so, and should keep its methods and its language modest enough that others can build on them without inheriting exaggerated claims.

Dual use and physical safety

Autonomous flight controllers derived from neural networks have obvious uses beyond science, and a biologically derived controller is no exception in principle. In practice this controller is a 27 gram research platform that struggles to hover in a workshop, and its scientific value lies in what it reveals about a fly’s circuits, not in its performance as a controller. The repository is private during development and the results will be published as science. Physical safety is a direct obligation: the supervisor, geofence, kill switch, and prop guards exist to protect people in the workshop, and no flight takes place with a person inside the geofence.

Credit and provenance

The connectome is the product of years of work by the Janelia FlyEM project and its collaborators, and the eye-map and visual-system analyses come from the Reiser lab. Their data are used under their published terms and cited in every output. The imago built by this project records the source file hashes so that derived results can be traced to the exact release. No code from those groups is copied into this repository; their conventions are followed and referenced.

Design

This section documents how Flatline Imago is put together: how the raw connectome files are ingested into a single .imago construct, and how the brain engine animates that construct as a signed leaky integrate-and-fire network. A following chapter covers the eye and camera models, the photoreceptor and haltere encoders, the lamina tonic bias and optic-flow probe, and the motor decoder. The section also carries the external review that shaped the current design.

Connectome ingestion and brain engine

Connectome ingestion

Inputs

From ~/models/fly/model/flat-connectome/:

FileUsed for
body-annotations-male-cns-v1.0-minconf-0.5.featherNeuron identity: bodyId, superclass, class, subclass, type, instance, somaSide, rootSide, entryNerve, exitNerve, assignedOlHex1, assignedOlHex2, status
body-neurotransmitters-male-cns-v1.0.featherconsensus_nt per body
connectome-weights-male-cns-v1.0-minconf-0.5-significant-only.featherEdges: body_pre, body_post, weight (synapse count)
connectome-weights-male-cns-v1.0-minconf-0.5.featherFull edge list, used only to infer photoreceptor columns

The synapse-point and synapse-partner files are not used.

Neuron set

Neurons are the bodies with a non-null superclass, about 166,000. Each receives a dense index in a stable order (sorted by bodyId). Region masks (see Region masks) select subsets at load time without rebuilding.

Signs and effective weights

Each synapse’s sign comes from the presynaptic neuron’s consensus neurotransmitter, following Shiu et al.:

TransmitterSign
acetylcholine, dopamine, serotonin, octopamine+1
GABA, glutamate, histamine−1
unclear (the dataset’s own label, 2,999 neurons)+1, and the neuron is flagged
no row in the transmitter file (178 neurons)treated as unclear and flagged

The effective weight of an edge from neuron \(i\) to neuron \(j\) is

\[ w_{ij} = c_{ij} \cdot \mathrm{sign}(nt_i) \cdot s[nt_i] \cdot g[t_i] \cdot W_{\mathrm{syn}} \]

where \(c_{ij}\) is the synapse count, \(nt_i\) and \(t_i\) are the presynaptic neuron’s transmitter and gain group, \(s\) is a learnable per-transmitter scale (eight values in the fixed table order acetylcholine, gaba, glutamate, dopamine, serotonin, octopamine, histamine, unclear; initialized to 1 and constrained non-negative so learning can never flip a sign), \(g\) is a learnable per-gain-group factor (design spec, section 12.1), and \(W_{\mathrm{syn}}\) is 0.275 mV, the Shiu et al. value. Monoamines are excitatory here because Shiu et al. treated them so; that is a recorded decision (design spec, section 4), not a fact about the fly. \(W_{\mathrm{syn}}\) was fitted by Shiu et al. on FlyWire feeding circuits, and its transfer to this dataset is a risk carried in the design spec, section 15. The imago stores counts and signs; scales and gains are applied at load.

Edges whose postsynaptic neuron is an input neuron are dropped at build time and counted in the metadata: input neurons are driven by encoders, never by the network.

A build option produces a shuffled imago for the control condition of the design spec, section 12.5. Edges are grouped by synapse count and, within each group, their destinations are permuted with a seeded generator. Every neuron therefore keeps its exact outgoing edges and their counts, its exact incoming synapse total and incoming count distribution, and its transmitter sign, while its partners are randomized: the synapse totals are preserved exactly, but the number of distinct partners a neuron has may change, because two permuted edges occasionally land on the same pair and are merged. Edges whose presynaptic neuron is an input neuron are never shuffled, so the control shares the real sensory interface and randomizes only the brain’s internal wiring. The sidecar records the seed, the number of edges shuffled, and the number merged.

Imago format

A single safetensors file with the .imago extension plus a JSON sidecar, about 300 MB:

  • pre_indptr, pre_indices, pre_counts: edges grouped by presynaptic neuron (int64 pointers, int32 indices, int16 counts). Used by the event propagator.
  • post_indptr, post_indices, post_counts: the same edges grouped by postsynaptic neuron. Used by the batched propagator.
  • Per-neuron arrays: body_id (int64), nt (int8 index into the transmitter table), type_id (int32 index into the type table), superclass_id, side (int8: left, right, unknown).
  • Tables in the sidecar: transmitter names, type names, superclass names, build metadata (source file hashes, date, git describe).
  • Named neuron sets: photoreceptors with per-neuron eye and column, virtual_photoreceptors, haltere_afferents with side, wing_motor and haltere_motor with side and type, descending.

Photoreceptor placement

Photoreceptor cell bodies lie in the retina, outside the imaged volume, so they carry no hex coordinates and no soma side. Two inferences supply them:

  • Eye: rootSide (3,746 right, 2,345 left).
  • Column: the hex column receiving the largest summed synapse count from the photoreceptor, over hex-bearing targets, using the full edge list (only 4,038 photoreceptors have any significant-only outgoing edge). The column is chosen by summed weight rather than by the single strongest target because a lamina cartridge, not a cell, is what a photoreceptor innervates; on this dataset the two rules agree on nearly every cell (median fraction of weight on the best column is 1.0). The eye fallback and the recorded target side are the side of that winning column. Hex-bearing targets in v1.0 are exactly the 23,720 optic lobe intrinsic cells of fifteen types: L1, L2, L3, L5, C2, C3, T1, Mi1, Mi4, Mi9, Tm1, Tm2, Tm4, Tm9, Tm20. Placement succeeds for 5,895 of 6,091 photoreceptors, and the target side agrees with the root side in every case.

Coverage of the resulting map:

EyeColumnsWith any photoreceptorWith R1-R6With R7 or R8
Right892784526730
Left879682300638

The reconstructed retina is incomplete and imperfectly placed: a real column has six R1-R6 cells, but placed columns hold between one and eleven (v1.0 build, 826 columns with any R1-R6: 240 hold six, 547 hold fewer, 39 hold seven to eleven, the last being placement errors), and 2,054 of the 6,091 photoreceptors have no outgoing edge at all in the significant graph. The design spec, section 8.3, describes the two corrections that follow from this — a per-lamina-cell factor \(6m/S\), where \(S\) is the R1-R6 synapse count a lamina cell receives and \(m\) the median single-cell count onto its type, and a virtual photoreceptor for every column holding an undriven lamina cell — and the build report that records these counts.

Other named sets

SetCountSelection
Haltere afferents439 (220 right, 219 left)sensory superclass, entryNerve DMetaN, side from rootSide
Wing motor neurons67 (33 left, 34 right)subclass wm; side from somaSide
Haltere motor neurons16 (8 per side)subclass hm; side from somaSide
Descending neurons1,314superclass exactly descending_neuron

Side rule: sensory neurons carry rootSide and a null somaSide; motor and central neurons carry somaSide and a null rootSide. The imago records somaSide when present, else rootSide. The haltere afferent set keeps each cell’s subclass (haltere 205, campaniform sensilla 195, mechanosensory bristle 39) and type (22 types) as fields, so downstream encoders can group by type rather than by cell.

Brain engine

Neuron model

Each neuron has a membrane voltage \(v\) and a synaptic variable \(g\), following the Shiu et al. formulation:

\[ \tau_m \frac{dv}{dt} = (v_{\mathrm{rest}} - v) + g + b \qquad \text{(unless refractory)} \]

\[ \tau_s \frac{dg}{dt} = -g \qquad \text{(unless refractory)} \]

\[ g_j \leftarrow g_j + w_{ij} \qquad \text{on presynaptic spike } i \text{ after delay } d \text{, also while refractory} \]

\[ \text{if } v > v_{th}: \quad \text{spike}, \\; v = v_{\mathrm{reset}}, \\; g = 0, \\; \text{refractory for } t_{\mathrm{ref}} \]

These are the Shiu et al. rules as implemented in their released code: both differential equations are frozen while refractory, incoming synaptic weight still accumulates into g during that time, the threshold is strict, and the reset zeroes g. The term b is a per-neuron bias in mV, zero by default, used by the visual transmission policy of the design spec, section 8.6.

Initial constants (Shiu et al. 2024):

ConstantValue
τ_m (membrane time constant)20 ms
τ_s (synaptic time constant)5 ms
v_rest−52 mV
v_th−45 mV
v_reset−52 mV
t_ref2.2 ms
d (spike delay)1.8 ms, rounded to 2 steps at 1 ms
W_syn0.275 mV per synapse

Integration is exponential Euler with step \(dt\), default 1 ms, with 0.1 ms available for fidelity checks. For a non-refractory neuron:

\[ g \leftarrow g \\, e^{-dt/\tau_s} + (\text{delayed incoming weights}) \]

\[ v \leftarrow v_{\mathrm{rest}} + g + b + (v - v_{\mathrm{rest}} - g - b) \\, e^{-dt/\tau_m} \]

For a refractory neuron \(v\) stays at \(v_{\mathrm{reset}}\) and \(g\) keeps its value plus any delayed incoming weights (no decay). Step counts are round(t_ref/dt) for the refractory period (2 at 1 ms) and max(1, round(d/dt)) for the delay (2 at 1 ms).

Learnable global knobs: the eight transmitter scales (non-negative), \(\tau_m\), \(\tau_s\), an additive threshold offset, the per-group gains and the lamina bias of the design spec, sections 12.1 and 8.6. Gains are regularized toward 1 in the training objective. Optional Gaussian membrane noise is available as a knob but off by default.

Input neurons

Photoreceptors, virtual photoreceptors, and haltere afferents are input neurons. Their membrane dynamics are bypassed: v and g are held at rest and no edge targets them (see Signs and effective weights). Each step they spike with probability rate_hz × dt_ms / 1000 where the rate is supplied by an encoder. This is a deliberate simplification of Shiu et al., who added Poisson input to the membrane of otherwise normal neurons; forcing the spikes directly makes the encoder’s rate the exact firing rate and costs nothing in the pathway we care about, since photoreceptor output is the spike train itself.

Propagation interface

class Propagator(Protocol):
    def propagate(self, spikes: Tensor) -> Tensor:
        """spikes: bool [B, N] (B=1 in flight). Returns weight input [B, N]."""

Two implementations:

  • Event propagator. Finds spiking indices, gathers their outgoing segments from the presynaptic CSR, expands with repeat_interleave, and index_add_s effective weights into the input vector. Works on Metal, ROCm, and CPU. Used in flight (B = 1).
  • Batched propagator. Computes the input for all B brains as a sparse-by-dense product using the postsynaptic CSR on CPU and ROCm. On Metal, where sparse support is thin, the event propagator handles all B brains in one packed gather and scatter over the flattened [B × N] state, never a Python loop over brains.

The event path pays one device-to-host synchronization per step to size the gathered index (the spike count is data dependent); that is inherent to event-driven propagation and is accounted for in the compute budget of the design spec, section 19.

A native (Rust or C++) event kernel may be substituted behind the same interface later. Tests require the two implementations to agree exactly on weights that are exact in float32 (dyadic rationals) and to within a small tolerance on random graphs, because scatter-add order differs between devices and implementations.

Delays

A ring buffer of two spike vectors implements the 2 ms delay. The propagator consumes the vector from two steps ago.

Real-time clock

In flight the engine steps to keep brain time within a configured tolerance of wall time, default 20 ms. Falling behind by more than the tolerance raises a brain-lag fault to the supervisor. Before takeoff the supervisor requires a measured real-time ratio above 1.0 for the configured mask on the current device, taken from the benchmark tool.

Region masks

A mask is a TOML file listing superclasses, classes, or explicit types to include or exclude. Masks are a benchmarking and ablation tool, not a real-time strategy: the optic lobes alone are 89,403 of the 166,700 neurons and cannot be dropped without dropping vision, and excluding everything that is not plausibly involved in flight removes only about 0.2% of neurons. Presets:

  • full: every neuron in the imago (166,700 real plus 1,250 virtual photoreceptors, 167,950 in all).
  • no-optic-lobe: excludes superclasses ol_intrinsic, ol_sensory, visual_projection, visual_projection_tbc, and visual_centrifugal, and the virtual photoreceptors, leaving 61,433 neurons and 12,165,352 edges. A compute probe and a “blind” ablation; it is never a flight configuration.

Edges to or from excluded neurons are dropped at load. The benchmark tool reports steps per second for each preset on each device, and flight is gated on the measured real-time ratio of the configuration actually flown.

Recording

Named neuron sets can be recorded to a spike ring buffer and flushed to disk as compressed arrays. Flight runs record motor neurons and descending neurons by default; full-brain rasters are available for offline analysis.

Determinism

All randomness is seeded and drawn on the CPU so the random stream is identical across backends. Given the same device, backend, and seed, a run is reproducible up to floating-point scatter order, which is not bitwise stable on GPUs; comparisons across runs use tolerances, and exactness is asserted only on CPU.

Tests

  • Three-neuron chains and excitation-inhibition motifs on synthetic graphs with analytically known spike times.
  • Event and batched propagators agree exactly on dyadic weights and to tolerance on random graphs.
  • Input neurons hold v and g at rest under a strong incoming edge, and such edges are absent from the built imago.
  • Delay buffer ordering.
  • Refractory handling and reset.
  • Mask application drops the right edges.

The benchmark is a tool, not a test.

Implementation notes

Module layout

The engine is implemented across the following modules, per the sub-project plan’s File Structure table:

ModuleContents
src/flatline/cli.pyCLI entry point: the three subcommands build-imago, imago-info, and bench
src/flatline/util/devices.pyDevice selection (cuda/ROCm, mps, cpu)
src/flatline/connectome/sources.pyReading the feather source files
src/flatline/connectome/imago.pyThe .imago safetensors file and JSON sidecar format
src/flatline/connectome/ingest.pyNeuron set construction and edge ingestion
src/flatline/connectome/photoreceptors.pyPhotoreceptor eye and column placement
src/flatline/connectome/sets.pyNamed neuron sets (photoreceptors, haltere afferents, wing motor, haltere motor, descending)
src/flatline/connectome/shuffle.pyWithin-synapse-count-class partner shuffle for the control condition
src/flatline/brain/params.pyLIF parameters and learnable knobs
src/flatline/brain/weights.pyEffective edge weights, lamina-scoped retina scaling
src/flatline/brain/mask.pyRegion mask loading and application
src/flatline/brain/propagate.pyEvent and batched propagators
src/flatline/brain/engine.pyThe stepped LIF engine
src/flatline/brain/record.pySpike recording to disk
src/flatline/brain/clock.pyReal-time clock and lag detection
src/flatline/brain/bench.pyThe benchmark tool

Mask presets live at configs/masks/full.toml and configs/masks/no-optic-lobe.toml.

Build metadata, v1.0 release

Running flatline imago-info against the male CNS v1.0 imago built by build-imago on this release reports:

FieldValue
n_neurons167,950 (166,700 real + 1,250 virtual photoreceptors)
n_edges25,530,591 (after dropping edges onto input neurons)
n_virtual1,250
n_edges_dropped_onto_inputs32,441
nt_flagged3,177
n_gain_groups1,296
photoreceptors (named set)6,091
haltere_afferents (named set)439
wing_motor (named set)67
haltere_motor (named set)16
descending (named set)1,314

The same build writes a retina report into the sidecar:

FieldValue
photoreceptors_without_outgoing_edge2,054 of 6,091
columns_over_six_r1639
lamina_cells_zero_drive_before3,673 of 5,327
lamina_cells_zero_drive_after720
virtual_photoreceptors_added1,250
lamina_norm min / median / max0.476 / 6.0 / 252.0

The 720 lamina cells still without drive all lack a hex column (901 lamina cells do), so no virtual photoreceptor can reach them. A lamina_norm of 252 is a cell whose whole reconstructed input is a single small synapse count; the factor is uncapped by design, and whether it should be is an open question for the encoder work.

Build time on the Mac M3 Max is about 12 s.

Neuron model fidelity

The neuron model follows the Shiu et al. released code, not just the published equations: both differential equations are frozen while refractory, incoming synaptic weight still accumulates into g during the refractory period, the threshold is strict, and reset zeroes g. Input neurons (photoreceptors, virtual photoreceptors, and haltere afferents) are held at rest with no incoming edges; they are driven entirely by their encoder’s spike probability, never by the network.

Propagator agreement

The event propagator and the batched propagator are required to agree exactly on weights that are exact in float32 (dyadic rationals), and to within a small tolerance on random graphs, since scatter-add order differs across devices and implementations.

Senses and motor decoder

The engine of the previous chapter takes a rate per input neuron and returns a spike per step. This chapter is the boundary around that engine: how a camera frame and a gyroscope reading become those rates, how a tonic bias keeps the inhibitory retina from silencing the lamina, how a drifting grating decides whether the frozen wiring computes motion at all, and how the 83 wing and haltere motor neurons become four command channels. Wiring stays frozen. The learnable set here is six photoreceptor-encoder parameters, four haltere-encoder parameters, three lamina biases, and about 340 decoder weights.

The first real-imago probe, recorded in the notebook entry Optic-flow probe, first run, failed in every condition. The chapter cites that outcome rather than a hypothetical pass.

Eye model

Photoreceptors in the imago carry an eye and a pair of hex coordinates (hex1, hex2) on the Reiser lab’s medulla column grid, inferred at build time from the cartridge each cell innervates. The eye model assigns every unique (eye, hex1, hex2) a viewing direction.

Hex coordinates are shifted to the conventional eye centre (18, 19) and laid on the plane with the second axis at 120 degrees from the first:

\[ x = p - q/2, \qquad y = q \cdot \sqrt{3}/2. \]

The sign of the shear is a measurement, not a convention. Under \(x = p + q/2\) the placed columns of the v1.0 imago span 48 units of \(x\) (240 degrees at \(\Delta\phi = 5\)) as a sheared parallelogram. Under \(x = p - q/2\) they form a round disk spanning 143 by 165 degrees, which is the shape of an eye. That layout is therefore the one the code uses.

Directions then follow a three-parameter model. Defaults, from EyeMapParams: \(\Delta\phi = 5\) degrees (the interommatidial angle), \(\mathrm{az}_0 = 60\) degrees, \(\mathrm{el}_0 = 0\) degrees.

\[ \mathrm{azimuth} = \mathrm{az}_0 + \Delta\phi \cdot x \quad\text{(negated on the left eye)}, \qquad \mathrm{elevation} = \mathrm{el}_0 + \Delta\phi \cdot y. \]

Left-eye mirroring reflects the right-eye map through the midline, so body azimuth is positive toward the animal’s right and the eye centre looks \(\mathrm{az}_0\) off the front. The probe stimulus, which should look the same to both eyes relative to each eye’s own front, uses the absolute value local_azimuth_deg instead. The orientation of \(x\) along the horizon and the sign of elevation remain unchecked conventions: a consistent inversion of every direction-selective type on one axis is reported as a suspected axis flip, not as a probe failure.

Literature extents (Zhao et al. 2025: azimuth about \(-10\) to \(+155\) degrees per eye, elevation about \(-70\) to \(+90\)) are a sanity check, not an assertion. The first v1.0 map reports 877 columns on the left and 891 on the right, local azimuth \([0.0, 135.0]\) degrees on both eyes, elevation \([-77.94, 86.60]\) degrees, and a warning that the elevation floor sits below the literature bound. Those numbers live on the eye map data page.

A measured table with header eye,hex1,hex2,azimuth_deg,elevation_deg (eye L or R) can replace the parametric model. Every column of the imago must be present; missing rows raise. The Reiser lab’s eyemap_T4 data is the intended source. The first probe used the parametric model.

Camera model

The AI-deck camera is a 324-pixel-wide monochrome sensor with an 87 degree field. The stock streamer sends a 324 by 244 window of that sensor. Focal length is computed from the sensor, not the window:

\[ f = \frac{w_{\mathrm{sensor}}/2}{\tan(\mathrm{FOV}/2)}, \]

so cropping the height does not pretend the optics changed. CameraIntrinsics defaults are width 324, height 244, sensor_width_px 324, fov_deg 87. The principal point is \(((W-1)/2, (H-1)/2)\). An explicit focal_px overrides the formula.

Body frame: \(x\) forward along the optical axis, \(y\) left, \(z\) up. Image \(u\) grows to the right, \(v\) down. A direction \((\mathrm{az}, \mathrm{el})\) projects as

\[ \begin{aligned} x &= \cos \mathrm{el}\,\cos \mathrm{az}, & y &= \cos \mathrm{el}\,\sin \mathrm{az}, & z &= \sin \mathrm{el}, \\ u &= c_x - f\, y/x, & v &= c_y - f\, z/x. \end{aligned} \]

and is in the field only if \(x > 0\) and \((u, v)\) lands inside the window.

Each column inside the field is sampled with a Gaussian acceptance cone of 5 degrees full width at half maximum, the default VisionParams.acceptance_fwhm_deg. Angular sigma is FWHM over \(2\sqrt{2\ln 2}\); pixel sigma is \(f \tan(\sigma_{\mathrm{ang}})\). The kernel covers two pixel-sigma and is renormalized per column. Columns outside the field, or whose kernel has no valid pixel, read constant mid-gray luminance 0.5 and therefore adapt to silence.

Photoreceptor encoder

Per column, luminance \(\ell \in [0, 1]\) is log-compressed, high-pass filtered, mixed with a DC fraction of the deviation from mid-gray, and read out as a rate:

\[ L = \log(\ell + \varepsilon),\qquad \dot{L}_{lp} = \frac{L - L_{lp}}{\tau_{\mathrm{adapt}}},\qquad c = (L - L_{lp}) + d\,(L - L_{\mathrm{mid}}),\qquad r = \mathrm{clip}(r_0 + g\,c,\,0,\,r_{\max}). \]

The code is forward Euler on the low-pass at every brain step, with the frame value held between pushes (zero-order hold). Mid-gray is sent through the same ops as a real frame so a mid-gray column reads \(r_0\) bit-exactly rather than \(r_0\) plus a float32 ulp.

VisionParams defaults, in as_vector order:

ParameterDefault
gain_hz (\(g\))100 Hz per log unit
r_base_hz (\(r_0\))10 Hz
tau_adapt_ms200 ms
dc_fraction (\(d\))0.1
acceptance_fwhm_deg5.0
r78_gain1.0

\(\varepsilon = 0.01\), \(L_{\mathrm{mid}} = \log(0.5 + \varepsilon)\), \(r_{\max} = 300\) Hz. R1–R6 of a placed column receive \(r\); R7 and R8 receive \(r\) times r78_gain. Unplaced photoreceptors (hex column \(-1\)) receive \(r_0\) and so sit at the dark baseline.

The adaptation time constant is longer than a frame interval on the stock streamer. A 10 Hz stream held for 100 ms through a 200 ms high-pass produces a contrast transient at every frame boundary, which is flicker, not optic flow. That is why the probe records both a 1 ms ideal stream and a 100 ms hold.

Input layout

InputLayout is the map from column rates and haltere rates onto the brain’s input-rate vector of length \(K\) (the count of imago.is_input). Photoreceptors, virtual photoreceptors, and haltere afferents occupy disjoint slots in that vector; a member that is not an input neuron is an error at construction.

assemble(column_rates, haltere_rates, params) writes, for batch \(B\), a tensor \([B, K]\):

  • placed photoreceptors: the matching column rate times 1 or r78_gain;
  • unplaced photoreceptors: r_base_hz;
  • virtual photoreceptors: the column rate with no extra gain (they stand in for missing R1–R6);
  • haltere afferents: the encoder’s rates, or left at zero if none are supplied.

The probe drives vision only and passes haltere_rates=None.

Haltere encoder

Halteres are the fly’s gyroscopes. Version one does not attempt the measured directional fields of campaniform sensilla. It groups the 439 afferents by cell type: the distinct type labels present (24 in v1.0, including the untyped label <unknown>) are sorted by name and assigned round-robin to six groups, positive and negative for roll, pitch, and yaw, in that order. Every cell of a type, on either side, shares a group, so the type’s downstream wiring stays coherent.

The grouping is written to the imago sidecar as haltere_groups at build time. An encoder reading an older imago, whose sidecar predates that entry, recomputes the same function from the type table. Per-cell round-robin, counted separately per side, exists only as an ablation.

Given body-frame angular rates \(\omega = (\omega_{\mathrm{roll}}, \omega_{\mathrm{pitch}}, \omega_{\mathrm{yaw}})\) in rad/s, a cell in group \(a\) with sign \(\pm\) fires at

\[ r = \mathrm{clip}(r_0 + k_a \max(0, \pm\omega_a),\, 0,\, r_{\max}). \]

HaltereParams defaults: k_roll, k_pitch, k_yaw each 50 Hz per rad/s, r_base_hz 10. The clip ceiling is the same 300 Hz as the photoreceptor encoder. The four values are the learnable haltere vector.

Lamina bias

Photoreceptors are histaminergic and therefore inhibitory. In the fly, L1, L2, and L3 are graded neurons held at an operating point by tonic transmitter release, so more light hyperpolarizes them and less light depolarizes them. In a spiking network with no tonic drive, inhibition onto a cell at rest does nothing, the lamina stays silent, and every motion circuit behind it stays silent with it.

The policy is a per-type tonic bias \(b\) (the per-neuron term of the LIF equation) on L1, L2, and L3, initialized so that under the encoder’s dark baseline the lamina fires at a mid-range target and photoreceptor rate changes modulate it in both directions. Transmitter scales stay non-negative, so the optimizer cannot get the same effect by flipping the histamine sign.

The initialization is mean-field. A photoreceptor spike adds \(w\) mV to the synaptic variable \(g\), which decays with \(\tau_s\), so under a Poisson barrage of rate \(r_0\) the mean of \(g\) is \(r_0 \tau_s W_{\mathrm{in}}\) with \(\tau_s\) in seconds (the code writes r_base_hz * (tau_s_ms / 1000) * W_in). A noiseless LIF with steady input \(v_\infty\) fires at rate \(f\) when

\[ v_\infty(f) = \frac{E\, v_{th} - v_{\mathrm{reset}}}{E - 1},\quad E = \exp\!\left(\frac{1/f - t_{\mathrm{ref}}}{\tau_m}\right), \]

which the code evaluates in milliseconds as \(E = \exp((1000/f - t_{\mathrm{ref}})/\tau_m)\). The per-type bias is

\[ b = v_\infty(f^*) - v_{\mathrm{rest}} - r_0\, \tau_s\, \tilde W_{\mathrm{in}}, \]

where \(f^*\) defaults to 50 Hz and \(\tilde W_{\mathrm{in}}\) is the median incoming photoreceptor weight over the type’s cells that have any such input. Histamine weights are negative, so the last term raises the bias above \(v_\infty - v_{\mathrm{rest}}\).

Only lamina cells that receive photoreceptor input are biased. A cell nothing modulates would add tonic noise, so the 720 column-less lamina cells of v1.0 stay silent. bias_undriven=True applies the type’s bias to every cell of the type, which is the literal reading of the original spec sentence, kept as an option.

On the v1.0 imago at \(r_0 = 10\) Hz and target 50 Hz the first probe recorded L1 15.6276 mV, L2 15.3426 mV, L3 12.8401 mV. Those values are identical on the shuffled control, because they are computed from the imago’s weights, which the shuffle preserves, not from the rewired targets.

Optic-flow probe

Before any training and before any flight, a synthetic drifting grating is shown to the encoder in the four cardinal directions (progressive, regressive, up, down) and the direction-selective sets are read out. The grating lives in eye-local coordinates so both eyes see the same motion relative to their own front:

\[ \ell = \tfrac12 + \tfrac12\, C \sin\bigl(2\pi(\pm \theta / \lambda - f_t t)\bigr), \]

with default period \(\lambda = 30\) degrees, temporal frequency \(f_t = 1\) Hz, contrast \(C = 0.8\). Horizontal directions use local azimuth; vertical directions use elevation. Each direction runs 0.5 s of warm-up plus 2 s of counted spikes. The frame interval is a parameter: 1 ms is the ideal stream, 100 ms mimics the stock streamer, and both are recorded.

Sets are taken from the type table. T4a–d and T5a–d match those names exactly, so T4_unclear and T5a_unclear are excluded. HS matches any type beginning HS (eight cells in v1.0: HSN, HSE, HSS, HST), VS any type beginning VS (34 cells). Empty sets are omitted.

The verdict is on selectivity, not on literature direction. Each subtype is compared across the two directions of its axis (a and b horizontal, c and d vertical; HS horizontal, VS vertical). Selectivity is the larger mean rate over the smaller, each floored at 0.5 Hz so that silence does not pass:

\[ s = \frac{\max(r_a, r_b) + 0.5}{\min(r_a, r_b) + 0.5}. \]

The probe passes when every present subtype of T4, T5, HS, and VS reaches the configured ratio (default 1.5). evaluate_probe scopes that check to the required groups; the CLI default keeps all four. Which direction wins is reported against the literature (T4a/T5a front-to-back, b back-to-front, c up, d down, HS front-to-back, VS down). If every passing T4/T5 subtype on one axis prefers the opposite of the literature, that axis is listed in axis_flip_suspected. A flip is a calibration fact about the eye map, not evidence against the wiring, and it does not fail the probe.

The first run on male-cns-v1.0.imago failed in all three conditions. On the real imago, both the 1 ms stream and the 100 ms hold produced exactly zero spikes in every probed set in every direction for the full two-second window: selectivity 1.0 is the floor formula at 0/0, not a signal. The shuffled control fired (T4/T5 about 0.43–0.90 Hz, HS about 55.6–57.4 Hz, VS about 30.0–30.9 Hz) but was flat (selectivity 1.00–1.01). No axis flip was suspected, because none of the sets passed.

The report also carries a diagnostic stages block that is not part of the verdict: mean rates for the photoreceptor input pool (the photoreceptors and virtual_photoreceptors named sets), L1–L5, C2, C3, and the medulla types Mi1, Tm3, Mi4, Mi9, Tm1, Tm2, Tm4. A follow-up run found the lamina firing (L1 ~38 Hz, L2/L3 ~21 Hz) and the medulla silent on the real wiring, while the shuffled control carried signal through the same medulla types. The break is lamina → medulla, not a silent lamina; see the stage-localization notebook.

flatline stage-drive accounts for that break without simulating: per target type it sums the signed effective weight from each source type, the cells reached, and the mean-field input at measured source rates against the threshold gap. On v1.0 it finds the ON pathway held below rest by glutamatergic (inhibitory) L1 and the OFF pathway under threshold because L2 fires at 20 Hz where 40–53 Hz is needed; see the lamina drive notebook. Raising --r-base wakes Tm1, Tm2 and Tm4, and T5 stays silent on the real imago; see the operating-point sweep.

Motor decoder

The 67 wing motor neurons followed by the 16 haltere motor neurons form an 83-element vector. Spikes pass through an exponential rate filter with learnable time constant \(\tau_{\mathrm{dec}}\) (default 30 ms) and then a linear map onto four channels, in order right, forward, yaw_right, up:

\[ r \leftarrow r\, e^{-\Delta t / \tau_{\mathrm{dec}}} + s\, \tfrac{1000}{\tau_{\mathrm{dec}}}, \qquad u = \tanh(W r + b). \]

\(W\) is 4 by 83, \(b\) is 4, 336 parameters, “about 340” in the spec. Outputs are in \((-1, 1)\). Type names of the form b1 MN are stripped to a muscle label by dropping the MN suffix.

Structured initialization gives the optimizer a sensible start. Each channel’s nonzero weights are then divided by (the count of nonzeros times 100 Hz) so that a cell firing at 100 Hz contributes on the order of one unit before the tanh.

ChannelCellsSign
rightsteering: b1, b2, b3, i1, i2, iii1, iii3, hg1–hg4, tp1, tp2, tpn, ps1, ps2\(+1\) right, \(-1\) left
forwardstroke amplitude: b1, i1\(+1\) both sides; bias \(-0.5\)
yaw_rightyaw subset: b2, iii1, iii3, hg1–hg4\(+1\) right, \(-1\) left
uppower muscles: type names starting DLMn or DVMn\(+1\)

Haltere motor neurons and unmatched types start at zero weight. Shadow mode — running the decoder on a live flight without acting on it — is the flight sub-project; nothing here touches the radio.

External review, 2026-09-15

An external review dated 2026-09-15 examined the spec and the first implementation plan against the dataset, the Shiu et al. code, and the cflib source. Its findings and their dispositions follow. The section numbers in the disposition column refer to the design specification, docs/superpowers/specs/2026-09-15-flatline-imago-design.md, not to chapters of this book.

FindingDisposition
Signed LIF with histaminergic photoreceptors silences the laminaAccepted; section 8.6 policy and probe
LIF differs from Shiu et al. (g reset, refractory freeze, strict threshold)Accepted after checking the released code; section 7.1
Hover curriculum solvable by a zero decoderAccepted; section 12.3
No optic-flow diagnosticAccepted; section 8.6 probe
10 Hz camera far below motion-vision bandwidthAccepted as risk and measurement; section 8.5
Haltere round-robin discards type structureAccepted; grouping by type; section 8.4
flight-core mask removes 0.2% of neuronsAccepted; replaced by no-optic-lobe, masks are not a real-time strategy; section 7.6
Training compute unbudgetedAccepted; section 19
Safety envelope inadequate for an open workshopAccepted; section 10.5
Missing success criteria and open questionsAccepted; section 17
Connectome contract errors (unclear label, eight scales, 1,314 descending, side rule, hex target types)Accepted; sections 6.3, 6.5, 6.6
Decision log lacks alternatives and reasonsAccepted; section 4
Spec and plan file names divergeAccepted; section 13.1
Command-axis signs half-specifiedAccepted after checking cflib; section 10.4
Clock, threads, determinism underspecifiedAccepted; sections 7.8 and 10.1
Shuffle does not preserve in-degreeAccepted; the shuffle permutes partners within synapse-count classes, section 6.3
Retina normalization scaled every photoreceptor edgeAccepted; section 8.3
Photoreceptor column should be the strongest single targetRejected; strongest column by summed weight retained with the reason in section 4
Input neurons not bypassed and edges onto them keptAccepted; sections 6.3 and 7.2
Full edge table loaded whole for placementAccepted; plan filters by photoreceptor before materializing
Event propagator loops per brain on MetalAccepted; packed batch; section 7.3
Config and checkpoint schemas missingDeferred to the sub-project plans that create them

The full review is filed at docs/reviews/2026-09-15-grok-review.md in the repository.

Execution rulings, sub-project 1

Sub-project 1 (connectome ingestion, brain engine, and this book) was implemented on 2026-09-15 by a controller session dispatching one fresh implementer per plan task with a review after each, followed by a whole-branch review, two independent code reviews, and one consolidated fix wave. Decisions the controller took on the owner’s behalf are listed here in the order they were made, with what each costs if it turns out wrong. The ledger they were copied from lived in the gitignored SDD workspace and was deleted after the merge; this chapter is the record.

Process rulings

  1. Work proceeded on branch engine in the main checkout rather than a worktree: the repository was new with nothing on master to protect. Cost if wrong: a branch switch.
  2. .superpowers/ was added to .gitignore before the first task so the execution workspace could never be committed. Cost: a one-line merge.
  3. Docstring-only __init__.py stubs need no from __future__ import annotations; the style rule exists for annotated code. Cost: one import per stub.
  4. A commit-message-only amend was verified by the controller instead of a scoped re-review, since the file diff was empty. Cost: none.
  5. The commit trailer named in the plan binds every subagent regardless of the model that actually ran it. Cost: none.
  6. Two small changes to finished modules (the gain_group field and the pre_ids filter) were folded into a later task rather than reopening finished ones. Cost: none.
  7. At the owner’s request the book task ran in parallel with the engine tasks in a separate worktree on branch book, with the benchmark notebook entry deferred until the benchmark existed. Cost: a merge, which was clean.
  8. Two defects in the plan’s own test code (a zip(..., strict=True) over a shifted slice, and a mutable default argument) were fixed by ruling rather than by changing the engine. Cost: none.
  9. After the re-review of the fix wave, the controller edited two documentation lines and one configuration comment directly, gated by make all. Cost: none.

Design rulings

  1. A photoreceptor’s column is the hex column receiving the largest summed synapse count, not its single strongest target, because a lamina cartridge is what a photoreceptor innervates; the side comes from the winning column. On this dataset the two rules agree on nearly every cell. Cost if wrong: a re-placement pass.
  2. The external review’s findings were accepted and folded into the spec and plan, except the single-strongest-target rule above.
  3. Config and checkpoint schemas are deferred to the sub-project plans that create them.
  4. The book keeps a paraphrased lead-in and drops a cross-reference in the data chapter, because the referenced section is not in the book. A reviewer’s objections to the background chapter’s sourcing were overruled: the neuron and edge counts, the FlyWire brain being female, and the characterizations of the cited work are correct. Cost: a sentence edit.
  5. MathJax stays enabled and genuine equations are rendered as math, at the owner’s instruction.
  6. The retina correction was redesigned after the whole-branch review measured that the per-photoreceptor rule left the median lamina cell with one sixth of the intended drive and 27% of lamina cells with none. It is now per lamina cell (six cell-equivalents from whatever reaches the cell) with virtual photoreceptors wherever a lamina cell has no drive. Cost: a rebuild, already done; the first benchmark entry records both builds.
  7. The shuffled control was redesigned after the code review measured that the synapse-stub model quadrupled the edge count and flattened every weight. It now permutes destinations within synapse-count classes, preserving each neuron’s synapse totals, the edge count, and the weight distribution, and it never touches edges from input neurons, so the control shares the real sensory interface. Cost: the control’s definition changed before any training used it.
  8. The event propagator’s second host synchronization per step, the pandas typing convention, and the duplication between the spec and the book chapters are deferred follow-ups, recorded in the notebook and below.

Parked and deferred

  • The shuffle’s int16 merge clamp is latent: the largest merged count in the v1.0 control is 2,601 against a ceiling of 32,767, and no counter reports a clamp. Real, not load-bearing.
  • Per-task minor findings, all triaged “can wait” by the whole-branch review: broader ruff selection than the standard; pick_device("") auto-selects; shared tables and meta between derived imagos; the fixture’s repeated photoreceptor loops; mislabeled type-ignore codes and an unused import in one test; pandas cast density; path comments and a rebound local in ingestion; an untyped test helper; no CLI handling of missing paths (must be fixed before the flight supervisor ships); a shared helper for the two edge-orientation functions; no direct test of class-based mask inclusion or of the Metal batched propagator selection; per-step temporaries in the engine step and an untested noise branch; recorder docstrings; two type-ignores in the benchmark test.

Data

This section documents the data artifacts Flatline Imago builds and consumes: the .imago file that packages the connectome into a form the brain engine can load directly, its named neuron sets, and how the photoreceptor and other sensory placements were derived from the source dataset.

The imago

Format

A single safetensors file with the .imago extension plus a JSON sidecar, about 300 MB:

  • pre_indptr, pre_indices, pre_counts: edges grouped by presynaptic neuron (int64 pointers, int32 indices, int16 counts). Used by the event propagator.
  • post_indptr, post_indices, post_counts: the same edges grouped by postsynaptic neuron. Used by the batched propagator.
  • Per-neuron arrays: body_id (int64), nt (int8 index into the transmitter table), type_id (int32 index into the type table), superclass_id, side (int8: left, right, unknown).
  • Tables in the sidecar: transmitter names, type names, superclass names, build metadata (source file hashes, date, git describe).
  • Named neuron sets: photoreceptors with per-neuron eye and column, virtual_photoreceptors, haltere_afferents with side, wing_motor and haltere_motor with side and type, descending.

Transmitter signs

Each synapse’s sign comes from the presynaptic neuron’s consensus neurotransmitter, following Shiu et al.:

TransmitterSign
acetylcholine, dopamine, serotonin, octopamine+1
GABA, glutamate, histamine−1
unclear (the dataset’s own label, 2,999 neurons)+1, and the neuron is flagged
no row in the transmitter file (178 neurons)treated as unclear and flagged

Photoreceptor coverage

Coverage of the reconstructed retina map, produced by inferring each photoreceptor’s eye from rootSide and its column from the hex column receiving the largest summed synapse count over hex-bearing targets:

EyeColumnsWith any photoreceptorWith R1-R6With R7 or R8
Right892784526730
Left879682300638

The reconstructed retina is incomplete and imperfectly placed: a real column has six R1-R6 cells, but placed columns hold between one and eleven (v1.0 build, 826 columns with any R1-R6: 240 hold six, 547 hold fewer, 39 hold seven to eleven, the last being placement errors), and 2,054 of the 6,091 photoreceptors have no outgoing edge at all in the significant graph.

Side rule

Sensory neurons carry rootSide and a null somaSide; motor and central neurons carry somaSide and a null rootSide. The imago records somaSide when present, else rootSide. The haltere afferent set keeps each cell’s subclass (haltere 205, campaniform sensilla 195, mechanosensory bristle 39) and type (22 types) as fields, so downstream encoders can group by type rather than by cell.

The eye map

Viewing directions of every hex column in the v1.0 imago, under the parametric model of the senses chapter: \(\Delta\phi = 5\) degrees, \(\mathrm{az}_0 = 60\) degrees, \(\mathrm{el}_0 = 0\) degrees, planar layout \(x = p - q/2\), \(y = q \cdot \sqrt{3}/2\), left eye mirrored in azimuth. Counts and extents are the output of

uv run flatline eyemap --imago data/imagos/male-cns-v1.0.imago

written to data/runs/probe/eyemap.json (gitignored). Azimuth here is eye-local: the absolute value of body azimuth, so 0 degrees is the front of each eye and 135 degrees is the rear limit of the placed columns.

EyeColumnsLocal azimuth (deg)Elevation (deg)
Left877[0.0, 135.0][−77.9422836303711, 86.6025390625]
Right891[0.0, 135.0][−77.9422836303711, 86.6025390625]

Warnings from the same report: left and right eye elevation (-77.9422836303711, 86.6025390625) sit outside the literature range (-70.0, 90.0) (Zhao et al. 2025). The floor is the parametric lattice reaching below the published field, not a missing-column error. The figure plots signed body azimuth, so the left eye appears on the negative side.

These column counts are unique (eye, hex1, hex2) over placed photoreceptors and virtual photoreceptors with a known eye and hex1 >= 0. They are not the reconstructed-retina column counts on the imago page, which count hex columns in the placement map.

The 5 degree parametric model is version one. A real fly eye has varying interommatidial angles and a spherical embedding, not a constant \(\Delta\phi\) on a plane. EyeMap.from_table replaces the parametric directions with a measured CSV of eye,hex1,hex2,azimuth_deg,elevation_deg (eye L or R); every column of the imago must be present. The Reiser lab’s eyemap_T4 table is the intended source. The first optic-flow probe used the parametric model; a later run can swap the table without rebuilding the imago.

Lab notebook

Dated entries, newest last. Every entry has the same shape: goal, setup, what happened, results, conclusions, next steps. Setup always records the git describe string, the machine, the device, the imago build metadata, and the exact command.

First benchmark of the whole-CNS imago

Goal

Measure steps per second at batch 1 (the flight gate) for the full imago and for the no-optic-lobe probe on the Mac, on cpu and mps, at a baseline input rate and a high-contrast input rate, to learn whether real time is within reach before any optimization. Masks are a compute probe here, not a strategy: the no-optic-lobe run removes vision and can never fly.

Setup

  • git describe: 996072e, then the two mps batch-1 rows re-measured on the fix-wave build (see the note under Results)
  • machine: Mac M3 Max, 128 GB
  • imago: data/imagos/male-cns-v1.0.imago, meta: n_neurons 167,950 (166,700 real + 1,250 virtual photoreceptors), n_edges 25,530,591, n_virtual 1,250, n_edges_dropped_onto_inputs 32,441, n_gain_groups 1,296, nt_flagged 3,177
  • the edge count is arithmetic: 25,560,079 significant edges with both ends in the neuron set, minus 32,441 edges onto input neurons, plus 2,953 edges from the 1,250 virtual photoreceptors, is 25,530,591
  • command: uv run flatline bench --imago data/imagos/male-cns-v1.0.imago --mask configs/masks/<mask>.toml --device <device> --seconds 2 --rate <rate> (batch 1); the batch run used --mask configs/masks/full.toml --device cpu --batch 16 --seconds 0.5 --rate 50.

What happened

All nine runs completed with no crashes, timeouts, or memory problems. Each 2 s run took well under a minute including imago load and a 50-step warm-up.

Results

maskdevicerate Hzneuronsedgessteps/sreal-time ratiomean spikes/step
fullcpu5016764225529886129.10.1291926.9
fullcpu20016764225529886116.70.1172070.6
fullmps5016795025530591526.90.5271966.8
fullmps20016795025530591510.70.5112095.6
no-optic-lobecpu506143512165384152.90.1531907.0
no-optic-lobecpu2006143512165384145.20.1451994.6
no-optic-lobemps506143512165384588.40.5881907.0
no-optic-lobemps2006143512165384590.30.5901994.6

Training-shaped run (batch 16, cpu, full, 0.5 s, rate 50): 9.8 steps/s, about 157 brain-steps per second, mean spikes/step 1731.0.

The two mps full-imago rows were re-measured after the fix wave that redesigned the retina correction (the imago now carries 1,250 virtual photoreceptors rather than 942, and 705 more edges). At 50 Hz throughput rose from 494.0 to 526.9 steps per second, 6.7%; at 200 Hz it moved from 507.7 to 510.7, under 1%. The other six rows are the original measurements on the pre-fix build, whose counts were 167,642 neurons and 25,529,886 edges.

Conclusions

The flight gate, a real-time ratio above 1.0 at batch 1, is not met on either device. Metal reaches about half real time on the full imago, and the CPU reaches about an eighth.

Throughput looks bound by per-step overhead rather than edge count. Dropping the optic lobes removes 63% of neurons and most edges, yet Metal throughput only rises from about 500 to about 590 steps per second. Candidates for the bottleneck are the per-step host synchronization in the event propagator, the number of small kernel launches per step in the engine (several where operations with fresh temporaries), and the Bernoulli draw that is currently made on the CPU each step.

Mean activity sits between about 1,900 and 2,100 spikes per step, around 1.2% of neurons per millisecond (3.1% of the 61,435 neurons in the no-optic-lobe runs), and barely changes between 50 Hz and 200 Hz input. This suggests the network’s activity is dominated by its own recurrent dynamics under this drive rather than by the input rate. This needs a look before any encoder work: spike rasters by superclass, and a check on whether activity is stable or growing over a longer run.

Two structural facts from the final review bear on this. Of the 167,950 neurons, 3,270 have no outgoing edge at all and 2,337 are fully isolated, so about 2% of the graph can never contribute. And the optic lobe is nearly silent under inhibitory photoreceptor drive: that is why removing it barely changes spikes per step, and it is exactly the phenomenon the visual transmission policy of the design spec, section 8.6, is meant to address.

The CPU batched path gives no per-brain speedup at batch 16, about 157 brain-steps per second total, roughly the same as batch 1. CPU training is not viable at this scale. The training gate depends on straylight’s ROCm path or a faster kernel.

Next steps

Profile one step on Metal to attribute time between propagation, the engine’s elementwise updates, and the input draw. The event propagator makes two device-to-host synchronizations per step, one to list the spiking neurons and one to size the gather, at roughly 0.2 ms each on mps; a fixed-capacity padded gather that removes both is the lever to pull first. Fuse the engine’s elementwise updates and keep the Bernoulli draw on the device. Measure a longer run (30 s) to check activity stability and record rasters by superclass. Then measure batch 64 on straylight.

Optic-flow probe, first run

Goal

Learn whether the frozen wiring computes direction-selective motion under the tonic-bias policy, before any training: the go/no-go of spec section 8.6. Three conditions: frames every step (the ideal stream), frames held 100 ms (the stock streamer), and the shuffled control with continuous frames.

Setup

  • git describe: d19f4fe
  • machine: Mac M3 Max, device mps
  • imago: data/imagos/male-cns-v1.0.imago (n_neurons 167,950, n_edges 25,530,591, built_at 2026-09-16T02:29:04+00:00, imago git_describe 4428500-dirty, shuffle_seed null); control data/imagos/male-cns-v1.0-shuffled-1.imago (n_edges 25,440,553, n_edges_shuffled 25,414,963, shuffle_seed 1). Both sidecars predate the haltere_groups entry, which is expected and irrelevant to this probe.
  • eye map: parametric, Δφ 5°, az0 60°, el0 0°, planar layout x = p − q/2; extents left: 877 columns, azimuth [0.0, 135.0]°, elevation [−77.94, 86.60]°; right: 891 columns, azimuth [0.0, 135.0]°, elevation [−77.94, 86.60]°; warnings: left eye elevation (-77.9422836303711, 86.6025390625) outside literature (-70.0, 90.0), right eye elevation (-77.9422836303711, 86.6025390625) outside literature (-70.0, 90.0)
  • lamina bias (mV) at r_base 10 Hz, target 50 Hz: L1 15.6276, L2 15.3426, L3 12.8401 (identical across all three runs; computed from the imago’s weights, not from the shuffle)
  • grating: period 30°, 1 Hz, contrast 0.8; 0.5 s warm-up, 2 s per direction; ratio 1.5
  • commands:
    uv run flatline eyemap --imago data/imagos/male-cns-v1.0.imago
    uv run flatline probe --imago data/imagos/male-cns-v1.0.imago --device mps --frame-interval-ms 1 --out data/runs/probe/2026-09-16-continuous.json
    uv run flatline probe --imago data/imagos/male-cns-v1.0.imago --device mps --frame-interval-ms 100 --out data/runs/probe/2026-09-16-hold100.json
    uv run flatline probe --imago data/imagos/male-cns-v1.0-shuffled-1.imago --device mps --frame-interval-ms 1 --out data/runs/probe/2026-09-16-shuffled-continuous.json
    

What happened

All four commands (the eye map and the three probes) completed with no exceptions. The eye map took under a second. Each probe run took about 23-26 s wall time (continuous 25.7 s, hold-100 23.3 s, shuffled 24.0 s), well under the 1-3 minute estimate; the four-directions × 2.5 s-of-brain-time workload at roughly half real time on mps plus imago load accounts for it, but it ran faster than expected, possibly because most of the network stayed silent (see below). All three probes returned exit code 2 (verdict failed) — a result, not an error. On the real imago, both the continuous and the 100 ms-hold conditions produced exactly zero spikes in every probed set (T4a-d, T5a-d, HS, VS) in all four grating directions, for the full 2 s read-out window after the 0.5 s warm-up. The shuffled control produced small but nonzero rates (roughly 0.4-0.9 Hz for T4/T5, 30-57 Hz for the tiny HS/VS sets) that vary little across grating direction.

Results

conditionpassedT4T5HSVSaxis flip suspected
continuousfalsefalsefalsefalsefalsenone
hold 100 msfalsefalsefalsefalsefalsenone
shuffled, continuousfalsefalsefalsefalsefalsenone

Per-subtype rates (Hz) and selectivity for the continuous condition:

setcellsprogressiveregressiveupdownwinnerselectivityliterature
T4a16840.00000.00000.00000.0000progressive1.0000progressive (match)
T4b16900.00000.00000.00000.0000progressive1.0000regressive (mismatch)
T4c17780.00000.00000.00000.0000up1.0000up (match)
T4d17090.00000.00000.00000.0000up1.0000down (mismatch)
T5a16640.00000.00000.00000.0000progressive1.0000progressive (match)
T5b17150.00000.00000.00000.0000progressive1.0000regressive (mismatch)
T5c17200.00000.00000.00000.0000up1.0000up (match)
T5d16200.00000.00000.00000.0000up1.0000down (mismatch)
HS80.00000.00000.00000.0000progressive1.0000progressive (match)
VS340.00000.00000.00000.0000up1.0000down (mismatch)

Every rate in this condition is exactly zero. selectivity is defined as (max + 0.5) / (min + 0.5), so at 0/0 it evaluates to 1.0 and the winner tie-break (a >= b) always picks progressive or up. The “match”/“mismatch” column above is therefore an artifact of that tie-break, not evidence of literature agreement — it is the same pattern every direction-agnostic axis produces at zero rate. The 100 ms-hold condition’s per-subtype table is identical in every field (all rates 0.0000, same winners, same matches) and is omitted here for space; it is in data/runs/probe/2026-09-16-hold100.json.

Conclusions

All three verdicts fail, but they fail for different reasons that together are informative. On the real imago, T4, T5, HS, and VS are completely silent under this grating in both the continuous and the 100 ms-hold condition — not merely non-selective, but producing zero spikes over the full 2 s read-out window in every direction. The selectivity and match/mismatch figures reported for that condition are a numerical artifact of the zero-rate tie-break in evaluate_probe, not a real (even if failing) signal. The tonic lamina bias is doing its job at the source (L1/L2/L3 are biased 12.8-15.6 mV above rest, matching the target 50 Hz operating point at the 10 Hz dark baseline), but whatever drive that produces does not reach T4, T5, HS, or VS several synapses downstream in 2.5 s of brain time on this imago. This is consistent with, and sharpens, the “optic lobe nearly silent” finding from the first benchmark entry (2026-09-15): the silence documented there under a flat input rate is not confined to a crude bulk measure, it extends specifically to the direction-selective sets that the probe exists to test.

Since the real imago produced no signal to select on, the frame-hold condition cannot be distinguished from the continuous condition here — both are silent, so this run cannot yet say whether a 100 ms hold specifically kills selectivity, only that neither condition has any selectivity to kill.

The shuffled control is informative on its own terms: it is not silent (T4/T5 show small nonzero rates around 0.4-0.9 Hz, HS/VS around 30-57 Hz), but it is flat — selectivity sits at 1.00-1.01 across every subtype, far below the 1.5 ratio, exactly what a random rewiring should produce. A shuffled connectome that also passed would have meant the probe measures something other than wiring; instead it provides no selectivity, so if the real wiring is later made to pass, a pass would be meaningful. No axis flip is suspected in any condition (the axis_flip_suspected list is empty in all three reports), because axis-flip detection requires directionally-selective passing sets to compare against literature, and none of the sets passed here.

The proximate puzzle is not “is the wiring direction-selective” but “why is downstream activity zero at all” on the real connectome, when the shuffled connectome (with weight magnitudes preserved, only targets permuted) is weakly active. That the shuffle is active while the real wiring is silent suggests the real connectome’s specific routing — not its weight statistics — is suppressing activity between the lamina and the optic-lobe output stages probed here, worth investigating before touching the tonic-bias policy itself.

Next steps

Per spec 8.6, the fallback if the tonic-bias policy cannot produce motion-selective responses is a graded-rate lamina stage as in Lappalainen et al. 2024, replacing the spiking L1/L2/L3 with graded neurons that propagate both directions of contrast change. Before reaching for that fallback, the knobs worth sweeping first are target_hz (raise the lamina operating point well above 50 Hz to test whether downstream silence is a threshold effect), gain (encoder gain, Hz per log unit), --bias-undriven (bias lamina cells with no photoreceptor input too, in case those are damming the pool), and a longer --seconds to rule out a slow build-up that 2 s does not reach. It is also worth tracing spike counts stage by stage (lamina → medulla → T4/T5) on one direction to find exactly where activity drops to zero between the biased lamina and the silent probe sets, since the shuffled control shows that some path through the same weights can carry signal.

Optic-flow probe, stage localization

Goal

The first optic-flow probe (2026-09-16) found T4, T5, HS, and VS silent on the real v1.0 imago under the tonic-bias policy. That run could not say where activity died. This entry records mean spike rates at the photoreceptors, lamina (L1–L3), and medulla (Mi1, Tm3, Mi4, Mi9, Tm1, Tm2, Tm4) on the same three conditions, without changing the T4/T5/HS/VS go/no-go.

Setup

  • git describe: 107ddba-dirty
  • machine: Mac M3 Max, device mps
  • imago: data/imagos/male-cns-v1.0.imago; control data/imagos/male-cns-v1.0-shuffled-1.imago
  • diagnostic overlay: stages in the probe report; verdict still T4/T5/HS/VS only. Photoreceptor rate is the pooled spike rate of the photoreceptors and virtual_photoreceptors named sets. Lamina and medulla sets are exact type names.
  • lamina bias (mV) at r_base 10 Hz, target 50 Hz: L1 15.6276, L2 15.3426, L3 12.8401 (identical across runs)
  • grating: period 30°, 1 Hz, contrast 0.8; 0.5 s warm-up, 2 s per direction; ratio 1.5
  • commands:
    uv run flatline probe --imago data/imagos/male-cns-v1.0.imago --device mps --frame-interval-ms 1 --out data/runs/probe/2026-09-16-stages-continuous.json
    uv run flatline probe --imago data/imagos/male-cns-v1.0.imago --device mps --frame-interval-ms 100 --out data/runs/probe/2026-09-16-stages-hold100.json
    uv run flatline probe --imago data/imagos/male-cns-v1.0-shuffled-1.imago --device mps --frame-interval-ms 1 --out data/runs/probe/2026-09-16-stages-shuffled.json
    

What happened

All three probes completed with no exceptions and returned exit code 2 (verdict failed), as before. Wall time was 28.9 s (continuous), 26.1 s (hold 100 ms), and 26.7 s (shuffled). T4/T5/HS/VS on the real imago remain exactly 0.0 Hz in every direction; the shuffled T4a rate is still about 0.87–0.90 Hz and flat. The new information is upstream.

Results

Stage mean rates (Hz), continuous frames, real imago:

setcellsprogressiveregressiveupdown
photoreceptors734130.923630.873030.947730.8438
L1177637.791937.768937.742437.7993
L2177920.427220.483120.516620.4269
L3177221.808121.987921.708021.6718
Mi117730.00080.00060.00110.0011
Tm320540.00000.00000.00000.0000
Mi417720.00000.00000.00000.0000
Mi917750.00000.00000.00000.0000
Tm117770.35880.36550.35170.3472
Tm217660.16620.15570.15200.1622
Tm416700.01020.00990.00720.0075

The 100 ms-hold table is the same shape: photoreceptors ~30.9 Hz, L1 ~37.5 Hz, L2 ~20.3 Hz, L3 ~21.7 Hz, Mi1 ~0.001 Hz, Tm3/Mi4/Mi9 exactly 0, Tm1 ~0.35 Hz, Tm2 ~0.17 Hz, Tm4 ~0.01 Hz. It is in data/runs/probe/2026-09-16-stages-hold100.json.

Shuffled control, continuous frames:

setcellsprogressiveregressiveupdown
photoreceptors734130.923630.873030.947730.8438
L1177637.103037.114937.102537.1357
L2177936.080736.135236.122036.2018
L3177233.433433.437433.380933.4763
Mi117736.75806.71606.74486.7735
Tm320541.87851.86441.90681.8941
Mi417720.80160.79630.80530.7963
Mi917754.23634.19724.26064.2617
Tm1177711.173611.195011.285611.2648
Tm2176611.374311.364111.471111.4754
Tm416702.01952.00302.04882.0302

Conclusions

The motion pathway is not silent from the first synapse. On the real imago the encoder is driving photoreceptors at about 31 Hz under this grating, and the lamina is firing: L1 near 38 Hz, L2 and L3 near 21 Hz, flat across direction as expected for a first-order stage. That is below the 50 Hz mean-field target (the bias was computed at the 10 Hz dark baseline, and the grating raises photoreceptor rate, which is inhibitory), but it is not rest. The first-run inference that “the bias is doing its job at the source” is now a measurement, not a claim from bias_mv alone.

Activity drops between the lamina and the medulla. The ON-path medulla cells Mi1, Tm3, Mi4, and Mi9 are effectively silent (Mi1 at ~0.001 Hz, the others at exactly 0). The OFF-path Tm1/Tm2/Tm4 leak at a fraction of a hertz. T4/T5/HS/VS remain 0. The 100 ms hold does not change this picture.

The shuffled control, with the same weight magnitudes and permuted targets, carries the signal through the medulla (Mi1 ~6.7 Hz, Tm1/Tm2 ~11 Hz, Tm3 ~1.9 Hz) and produces the small flat T4/T5 rates of the first run. So the silence is the real wiring’s routing, not a global inability of these cells to spike under these weights.

The spec 8.6 fallback — a graded lamina stage — is the wrong next move. The lamina already spikes. The break is lamina → medulla.

Next steps

Trace L1/L2/L3 outgoing effective weight onto Mi1, Tm3, Mi4, Mi9, Tm1, Tm2, and Tm4 on the real imago (median \(W_{\mathrm{in}}\) per target type, sign, and how many of those cells have any lamina drive at all). A medulla cell with no lamina input, or with a net input that never crosses threshold given these lamina rates, would explain the drop without touching the tonic-bias policy. Only if that accounting says the medulla should fire, and does not, would a graded or resampled lamina stage become the fallback again.

Open work after stage localization

Status, 2026-10-30. The weight accounting in option 1 is done: lamina drive onto the medulla found the ON path held below rest by inhibitory L1 and the OFF path under threshold because L2 fires too slowly. The operating-point sweep then raised --r-base: the OFF medulla (Tm1, Tm2, Tm4) wakes at higher r_base, and T5 stays silent on the real imago. What reaches T5 found T5 both braked (CT1, modelled as one point cell per side, fires flat out) and under-powered (Tm9 silent). Open: a graded-rate early visual model (option (b)) and a per-column CT1, and why the lamina bias rule misses L1 and L3. The rest of this note is the 2026-09-16 record, annotated where it is out of date.

Goal

Record the next experiments and the machine layout after the stage-localization probe, so a later session does not have to reconstruct the discussion.

Setup

  • git: master at 5457ed1 when this note was written (stage-overlay code and notebook already pushed)
  • machines in play: Talos (no GPU), straylight (AMD Radeon 8060S, ROCm, gfx1151), titan (GPU), Mac M3 Max (mps)
  • layout: most machines share the same source paths (~/src/flatline, data/imagos/, connectome feathers under the same --source-dir)
  • imagos are runtime data and are never committed

What happened

After the stage-localization run showed the lamina firing and the medulla silent, the next science step and the CPU-vs-GPU machine split were discussed, then parked here instead of being implemented in the same session.

Options

Science (priority order)

  1. Lamina → medulla weight accounting (recommended next). On the real v1.0 imago, for each of Mi1, Tm3, Mi4, Mi9, Tm1, Tm2, Tm4: median effective incoming weight from L1/L2/L3, the sign of that input, and the fraction of cells with any lamina drive at all. Compare the shuffled control. A medulla cell with no lamina input, or with a net input that never crosses threshold at the measured lamina rates (L1 ~38 Hz, L2/L3 ~21 Hz), would explain the drop without touching the tonic-bias policy.
  2. Only if (1) says the medulla should fire and does not: revisit the spec 8.6 fallback (graded or resampled lamina). The stage-localization run already ruled this out as the first move: the lamina spikes.
  3. Not next: raising target_hz, encoder gain, --bias-undriven, or a longer --seconds to “wake” a silent lamina. The lamina is not silent.

Keep the probe’s T4/T5/HS/VS verdict unchanged. Stage rates stay a diagnostic overlay (report["stages"]). Every result still needs the shuffled-connectome control. Tests still never read the real dataset; real-imago commands are CLI tools.

Machines and imagos

Facts, not proposals:

  • build-imago is CPU ingest (pandas/feather). It does not use a GPU.
  • Imago.save writes CPU tensors through safetensors plus a JSON sidecar. Imago.load returns CPU tensors. Imago.to(device) is what places a run on cuda, mps, or cpu. An imago built on any host runs on any other.
  • Running the imago (probe, bench) is the GPU-shaped part. Full-imago bench on the Mac was about 0.13× realtime on cpu and 0.53× on mps. On Talos, pick_device("auto") already falls through to cpu. A four-direction probe there is minutes, not impossible.
  • make sync-straylight excludes data/, so a built imago does not travel with the code tree.

Deferred at this checkpoint (status as of 2026-10-30):

  • Built since: make push-imagos / make pull-imagos (IMAGO_HOST, default straylight) and make sync-titan now exist; see the Remote target section of AGENTS.md.
  • Remote checkout path is src/flatline on straylight; titan and Talos are expected to match.
  • Correction: titan is the M3 Max Mac (--device mps), not a CUDA host, and straylight runs --device cpu until a ROCm torch is set up; the --device cuda advice below is wrong for both.
  • Since 2026-10-30, wntrmute replaces Talos. Read “wntrmute” wherever this note says Talos; its checkout is src/flatline-imago, and the Remote target section of AGENTS.md has the current layout.

Recommended workflow until those targets exist: build on straylight or titan if the feathers live there, scp the imago pair onto Talos, run with --device cpu (or omit the flag). On straylight/titan run the same file with --device cuda. Superseded (2026-10-30): use make push-imagos / make pull-imagos instead of scp.

Conclusions

The next experiment is the lamina→medulla \(W_{\mathrm{in}}\) account, not a graded lamina and not more tonic-bias knobs. Imagos are already portable; Talos does not need a GPU to load one. GPU hosts are for running the engine, and for holding the large source and product files. Same paths on most machines. Do not commit data/.

Next steps

  1. Weight accounting in option 1, with a notebook entry, on whatever host is convenient (--device cpu on Talos, --device cuda on straylight or titan). Done 2026-10-30: lamina drive onto the medulla.
  2. Add the deferred rsync targets when copying imagos by hand gets old. Done: make push-imagos, make pull-imagos and make sync-titan.
  3. Only then consider the spec 8.6 graded-lamina fallback, and only if the accounting says the medulla should already be firing. Answered no (2026-10-30): at the measured lamina rates no medulla cell has a net lamina input above the threshold gap, so the condition is not met.

Lamina drive onto the medulla

Goal

The stage-localization run found the lamina firing (L1 ~38 Hz, L2/L3 ~21 Hz) and the medulla silent on the real v1.0 imago, and left one question: why does activity stop between L1–L3 and Mi1, Tm3, Mi4, Mi9, Tm1, Tm2, Tm4? This entry answers it by accounting, not simulation. For each medulla type it records the summed signed effective weight arriving from each lamina type, its sign, how many cells it reaches, and whether the mean-field input at the measured lamina rates crosses threshold. The shuffled-connectome control gets the same accounting.

Setup

  • git describe: 4a8e1b4; the shuffled lamina-relay run (the fourth stage-drive command below) was added after the branch review, at ca33f29, on the same machine and imagos. The accounting and the figure script did not change between the two; ca33f29 adds only a docstring and the CLI’s exit 2 on an unreadable --rates-from file.
  • machine: straylight (AMD Ryzen AI Max host, Radeon 8060S, Linux), device cpu. The accounting is CPU-only by design: it reads the imago’s effective weights and does arithmetic on them; nothing is simulated.
  • imago: data/imagos/male-cns-v1.0.imago, built_at 2026-09-16T02:29:04+00:00, git_describe 4428500-dirty; control data/imagos/male-cns-v1.0-shuffled-1.imago, built_at 2026-09-16T02:30:12+00:00, git_describe 4428500-dirty, shuffle_seed 1
  • constants (LIFParams() defaults): \(\tau_s = 5\) ms, \(\tau_m = 20\) ms, \(W_{\mathrm{syn}} = 0.275\) mV, threshold gap \(v_{\mathrm{th}} - v_{\mathrm{rest}} = 7.0\) mV, single-spike PSP peak factor 0.1575
  • per target cell, \(W_{\mathrm{in}}\) is the summed signed effective weight (mV per presynaptic spike) from one source type; the mean-field input from that source is \(g = r \cdot \tau_s \cdot W_{\mathrm{in}}\); net lamina \(g\) sums L1, L2 and L3 per cell; the rate at which one source alone puts the median cell at threshold is \(7 / (\tau_s \cdot W_{\mathrm{in}})\), defined only for positive \(W_{\mathrm{in}}\). Medians and quartiles are over the cells the source reaches; net \(g\) is over all cells of the type.
  • source rates: mean of the four directions in the continuous-frame stage reports, produced on 2026-09-16 on the Mac (mps) and copied to straylight from titan. Real (data/runs/probe/2026-09-16-stages-continuous.json): L1 37.7756, L2 20.4635, L3 21.7939 Hz. Shuffled (data/runs/probe/2026-09-16-stages-shuffled.json): L1 37.1140, L2 36.1349, L3 33.4320 Hz.
  • commands:
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0.imago \
      --rates-from data/runs/probe/2026-09-16-stages-continuous.json \
      --out data/runs/stage-drive/2026-10-30-real.json
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0.imago \
      --rates-from data/runs/probe/2026-09-16-stages-continuous.json \
      --to L4 L5 C2 C3 \
      --out data/runs/stage-drive/2026-10-30-lamina-relays.json
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0-shuffled-1.imago \
      --rates-from data/runs/probe/2026-09-16-stages-shuffled.json \
      --out data/runs/stage-drive/2026-10-30-shuffled.json
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0-shuffled-1.imago \
      --rates-from data/runs/probe/2026-09-16-stages-shuffled.json \
      --to L4 L5 C2 C3 \
      --out data/runs/stage-drive/2026-10-30-lamina-relays-shuffled.json
    uv run python scripts/figures/stage_drive.py data/runs/stage-drive/2026-10-30-real.json book/src/figures/stage-drive-real.svg
    uv run python scripts/figures/stage_drive.py data/runs/stage-drive/2026-10-30-lamina-relays.json book/src/figures/stage-drive-lamina-relays.svg
    uv run python scripts/figures/stage_drive.py data/runs/stage-drive/2026-10-30-shuffled.json book/src/figures/stage-drive-shuffled.svg
    uv run python scripts/figures/stage_drive.py data/runs/stage-drive/2026-10-30-lamina-relays-shuffled.json book/src/figures/stage-drive-lamina-relays-shuffled.svg
    

What happened

All four accounting runs completed with no exceptions and exit 0. Wall time, including uv run start-up and loading the 25.5 M-edge imago, was 2.3 s (real), 1.8 s (lamina relays), 2.2 s (shuffled), and 2.2 s (shuffled lamina relays, run later). The four figures took 0.3–0.4 s each.

Results

Source transmitters, as labelled in the imago. Every cell of each type carries the same label; the shuffled imago has the same labels.

typecellstransmittersignuniform
L11776glutamate−1yes
L21779acetylcholine+1yes
L31772acetylcholine+1yes

Real imago, per medulla type, the dominant lamina source (largest mean-field \(g\) in magnitude). \(W_{\mathrm{in}}\) and \(g\) in mV; “net g > 7” is the fraction of all cells whose net lamina \(g\) exceeds the threshold gap; “rate to cross” is the source rate in Hz.

targetnsourcereachedsynapses (median)\(W_{\mathrm{in}}\) median [q1, q3]\(g\) from sourcenet lamina \(g\) mediannet g > 7rate to cross
Mi11773L199.9%83−22.83 [−25.30, −19.52]−4.31−3.630.0%none
Tm32054L1100.0%73−20.07 [−23.38, −16.77]−3.79−3.740.0%none
Mi41772L283.5%2+0.55 [+0.28, +1.10]+0.06+0.060.0%2545.5
Mi91775L399.9%54+14.85 [+12.38, +17.33]+1.62+1.650.0%94.3
Tm11777L299.8%120+33.00 [+27.23, +37.67]+3.38+3.380.0%42.4
Tm21766L299.9%131+36.03 [+29.98, +40.42]+3.69+3.710.0%38.9
Tm41670L2100.0%96+26.40 [+20.90, +32.72]+2.70+2.700.0%53.0

The one other substantial lamina input is L3 onto Mi1: +5.50 mV per spike (99.7% reached, 20 synapses), \(g = +0.60\) mV. Every other non-dominant lamina source is a median of one or two synapses (±0.28 to ±0.55 mV) reaching 1.6–56% of cells. The single-spike PSP peak of the median L2 input is 5.20 mV on Tm1, 5.67 mV on Tm2, and 4.16 mV on Tm4, each under the 7 mV gap.

All inputs onto the same cells (every presynaptic type, mV per spike per cell; medians of the excitatory sum, inhibitory sum and net, then the four largest presynaptic types by summed signed weight):

targetexcitatoryinhibitorynetlargest presynaptic types
Mi1+33.3−77.3−43.5L1 −22.1, L5 +11.9, Pm2b −10.0, Mi13 −6.0
Tm3+49.2−51.2−1.9L1 −20.3, Mi1 +16.9, L5 +13.7, Tm3 +7.6
Mi4+42.1−42.6+0.8L5 +11.1, TmY16 −6.7, Dm4 −6.1, Mi9 −5.6
Mi9+39.6−36.8+2.8L3 +14.8, Mi4 −13.0, Tm2 +5.6, Dm12 −4.3
Tm1+39.1−67.4−27.8L2 +31.8, Pm2b −10.2, Pm2a −8.7, Mi13 −6.1
Tm2+49.2−56.7−7.2L2 +34.4, Dm13 −6.9, L4 +6.7, Mi13 −6.0
Tm4+55.4−63.3−7.7L2 +26.8, C3 −10.7, Tm1 +7.8, Mi13 −6.7

Lamina relays on the real imago, same rates. Each cell is \(W_{\mathrm{in}}\) median (reached, median synapses).

targetntransmitterfrom L1from L2from L3net lamina \(g\) mediannet g > 7
L41770acetylcholine−0.28 (12.6%, 1 syn)+4.68 (82.4%, 17 syn)+0.28 (54.1%, 1 syn)+0.390.0%
L51787acetylcholine−21.45 (100.0%, 78 syn)+14.03 (99.6%, 51 syn)+0.28 (13.4%, 1 syn)−2.580.0%
C21745gaba−8.53 (100.0%, 31 syn)+0.28 (18.7%, 1 syn)+0.28 (22.5%, 1 syn)−1.580.0%
C31779gaba−14.85 (99.9%, 54 syn)+1.10 (96.9%, 4 syn)+2.20 (99.3%, 8 syn)−2.460.0%

Shuffled control, per medulla type, at the shuffled lamina rates. “Reached” is the fraction of cells with any input from L1 / L2 / L3, then from any of the three. All-input medians are mV per spike per cell. The last column is the largest presynaptic type by summed signed weight, over all inputs.

targetreached L1 / L2 / L3any laminanet lamina \(g\) median [q1, q3]net g > 7all inputs: exc / inh / netlargest presynaptic type
Mi110% / 20% / 30%49.4%0.00 [0.00, +0.15]0.0%+64.6 / −40.7 / +23.4Mi1 +2.05
Tm37% / 21% / 32%49.3%0.00 [0.00, +0.18]0.0%+62.7 / −36.3 / +26.1Mi1 +1.89
Mi47% / 17% / 30%45.8%0.00 [0.00, +0.14]0.0%+50.9 / −33.0 / +16.8Mi1 +1.41
Mi96% / 14% / 24%37.7%0.00 [0.00, +0.05]0.0%+45.7 / −27.8 / +16.2Mi1 +1.43
Tm15% / 23% / 28%47.0%0.00 [0.00, +0.23]3.2%+63.8 / −32.5 / +35.5L2 +3.23
Tm25% / 24% / 27%47.6%0.00 [0.00, +0.20]3.5%+62.7 / −30.7 / +35.8L2 +3.15
Tm410% / 26% / 45%63.8%+0.05 [0.00, +0.23]0.0%+73.7 / −43.7 / +28.9Mi1 +2.05

Lamina relays on the shuffled control, at the shuffled lamina rates, same columns as the real relay table.

targetntransmitterfrom L1from L2from L3net lamina \(g\) mediannet g > 7
L41770acetylcholine−0.28 (1.2%, 1 syn)+0.55 (3.4%, 2 syn)+0.55 (7.7%, 2 syn)0.000.0%
L51787acetylcholine−15.68 (8.8%, 57 syn)+3.58 (11.0%, 13 syn)+0.83 (14.5%, 3 syn)0.000.1%
C21745gaba−1.38 (2.0%, 5 syn)+1.38 (6.4%, 5 syn)+0.83 (9.0%, 3 syn)0.000.0%
C31779gaba−2.89 (3.0%, 10.5 syn)+1.38 (9.1%, 5 syn)+0.55 (16.6%, 2 syn)0.000.0%

Any lamina source reaches 11.9% of L4, 30.8% of L5, 16.4% of C2 and 26.8% of C3 in the control, against 92.1% of L4 and 100% of L5, C2 and C3 on the real wiring. The 0.1% of L5 above the gap is one cell. All-input net medians are +16.5 (L5), +7.7 (C2) and +14.3 (C3) mV per spike, against −12.4, +4.4 and +9.6 on the real wiring; the largest presynaptic type onto each is Mi1 (+1.95, +0.67, +0.86), where on the real wiring it is L1 (−20.96, −8.43, −14.61).

Per-source \(W_{\mathrm{in}}\) medians in the control, over the cells reached, are 0.55–3.58 mV in magnitude. Tm1 and Tm2 have a long tail from L2 (q3 +33.48 and +33.96 mV); that tail is the 3.2% and 3.5% of cells above the gap. Tm4’s +0.05 mV median is one L3 synapse (\(0.275 \times 33.43 \times 0.005 = 0.046\) mV): Tm4 is the one type the shuffled lamina reaches in more than half its cells.

In the two shuffled figures each coloured segment is the median \(g\) over the cells that source reaches (5–45% of a medulla type, 1–17% of a lamina relay), while the black tick is the net median over all cells, so the segments do not sum to the tick and overstate the drive a typical cell gets: L1’s segment on L5 is −2.91 mV over the 8.8% of cells it reaches, and the net tick is 0.00.

Conclusions

The medulla is silent for two different reasons, one per pathway. Neither is a missing connection: on the real wiring every medulla type except Mi4 gets its dominant lamina input in 99.8–100% of its cells, from 54 to 131 synapses.

The ON pathway is silent because of a sign. L1 is glutamatergic in the dataset’s labels, so under the Shiu et al. sign table (spec section 6.3) its output is inhibitory. L1 is the dominant lamina input to Mi1 (−22.83 mV per spike, 83 synapses), Tm3 (−20.07), L5 (−21.45), C2 (−8.53) and C3 (−14.85). With L1 biased to fire at 38 Hz, the lamina holds the first ON-pathway medulla stage 3.6–3.7 mV below rest (net \(g\) −3.63 on Mi1, −3.74 on Tm3). There is no L1 rate that brings them to threshold. In the fly this synapse is in fact sign-inverting (L1 → Mi1 through glutamate-gated chloride channels). The ON pathway works there because Mi1 is a graded cell that is disinhibited when L1’s tonic glutamate release falls in the light. A spiking Mi1 at rest has no inhibition to lose. This is the spec 8.6 problem one synapse further on. A graded lamina alone, the 8.6 fallback, would not fix it: a graded L1 that hyperpolarizes to light still only reduces inhibition on a Mi1 that has nothing to reduce from.

The OFF pathway is silent because of an operating point. L2 is cholinergic and excites Tm1, Tm2 and Tm4 with +26.40 to +36.03 mV per spike from 96–131 synapses per cell, reaching 99.8–100% of cells. At the 20.46 Hz L2 rate measured under the grating, that is a mean-field input of 2.70–3.69 mV, under the 7 mV gap, and no cell of the three types crosses it. L2 would need roughly 40–53 Hz: 38.9 Hz for Tm2, 42.4 Hz for Tm1, 53.0 Hz for Tm4. The lamina bias was set for a 50 Hz rate at the 10 Hz dark photoreceptor baseline. Under the probe grating the photoreceptors average 31 Hz because the encoder rectifies, \(r = \mathrm{clamp}(10 + 100 \cdot \mathrm{contrast}, 0, 300)\): the negative half of the contrast cycle is clipped at zero, so the mean rises and histaminergic inhibition onto the lamina roughly triples. The working hypothesis here was that this excess is what holds L2 at 20 Hz instead of its 50 Hz target: an encoder operating-point effect, testable with existing flags. This entry measured only the L2 → medulla weights; the photoreceptor → L2 step was not tested. Tested in the operating-point sweep: raising --r-base wakes Tm1, Tm2 and Tm4 and T5 stays silent, but at r_base 100, where the photoreceptors average 98.64 Hz and the bias rule’s assumption holds, it still misses L1 (65.17 Hz) and L3 (33.37 Hz) against the 50 Hz target. The encoder mean also does not explain the L1/L2 split at r_base 10 (37.78 against 20.49 Hz), and the shuffled imago, whose lamina gets the same bias, runs L2 15.7–17.9 Hz hotter at every r_base in the sweep’s cpu runs. Part of the L2 shortfall is per-type, from the wiring or the LIF nonlinearity, and is not measured. The median single L2 spike is a 4.16–5.67 mV PSP, also under the gap, which is why Tm1/Tm2/Tm4 leak at 0.007–0.37 Hz in the stage-localization run (two L2 spikes close together) instead of being exactly silent.

Mi4 and Mi9 are second-order on the lamina. Mi4 has almost no direct lamina input (+0.55 mV per spike from two L2 synapses, net \(g\) +0.06 mV); from that 0.55 mV median input, L2 would need 2545.5 Hz to put the median cell at threshold, which is effectively unreachable. Its lamina driver is L5 (+11.1 mV per spike), and L5 is itself held below rest by L1 (net lamina \(g\) −2.58 mV). Mi9 is driven by L3 (+14.85 mV per spike, 54 synapses), but at 21.79 Hz that is +1.62 mV; L3 would need 94.3 Hz. Mi9 is also inhibited by Mi4 (−13.0 mV per spike), which is silent, so that term is currently zero.

The shuffled control’s medulla activity is recurrent, not lamina-driven. In the control the lamina reaches only 38–64% of each medulla type through any source, with per-source medians of a few millivolts, and the net lamina \(g\) median is 0.00 mV for six of the seven types and +0.05 mV (one synapse) for Tm4. The largest presynaptic type onto any of them is under 3.3 mV per spike. What the control does have is positive all-input net medians, +16.2 to +35.8 mV per spike, against negative or near-zero ones on the real wiring (−43.5 to +2.8). The medulla rates the control shows (Mi1 6.7 Hz, Tm1/Tm2 11 Hz) come from recurrent excitation in the randomized brain. They say these cells can spike under these weight magnitudes. They do not say the lamina drives them there. The shuffled lamina relays show the same: L1 reaches 2.0–8.8% of L5, C2 and C3 instead of 99.9–100%, and their net lamina \(g\) median is 0.00 mV against −1.58 to −2.58 mV on the real wiring, so the L1 hold below rest is a property of the real L1 wiring, not of the sign table or the lamina rates.

The question was whether the accounting says the medulla should fire. It does not: on the real wiring, at these lamina rates, no medulla cell has a net lamina input above the gap. The silence is predicted by the weights and the operating point, not an engine failure.

Next steps

A decision for Kyle, among three options:

  • (a) Extend the tonic operating-point policy from the lamina to the graded types whose dominant drive is inhibitory (Mi1, Tm3, L5, C2, C3, and by the same rule Mi4 and Mi9), with the rule stated mechanically from the wiring rather than chosen per type.
  • (b) A graded-rate model of the columnar optic lobe, as in Lappalainen et al. 2024.
  • (c) First test the OFF pathway and T5 alone, which needs only the photoreceptor operating-point fix: raise --r-base so the bias rule sees the stimulus-mean photoreceptor rate and L2 lands near its target.

The recommendation at the time was (c) first, because it is a parameter run with existing flags, then (a) as the smallest code change that can wake the ON pathway. (c) ran next as the operating-point sweep: --r-base at 10, 50 and 100 Hz on the real imago and 50 and 100 Hz on the shuffled control.

Photoreceptor operating-point sweep

Goal

The lamina-drive accounting found the OFF-path medulla silent because of an operating point, not a sign. L2 excites Tm1, Tm2 and Tm4 through 96–131 synapses per cell. It would put the median cell at threshold at 42.4, 38.9 and 53.0 Hz. Under the probe grating it runs at 20.46 Hz. The lamina bias rule, \(b = v_\infty(\mathrm{target}) - v_{\mathrm{rest}} - r_{\mathrm{base}} \cdot \tau_s \cdot W_{\mathrm{in}}\), assumes the photoreceptors sit at r_base (10 Hz). The encoder rectifies the dark half of the grating, so they average about 31 Hz, and the extra histaminergic inhibition drags L2 from its 50 Hz target. This entry tests that by sweeping the existing --r-base flag at 10, 50 and 100 Hz on the real imago and at 50 and 100 Hz on the shuffled control. --r-base moves the encoder baseline and the bias rule together, so a higher value brings the rate the bias assumes closer to the rate the grating produces. No code changes.

Prediction, written before the runs. The encoder alone, stepped over the grating outside the network (360 phases, 2 s after a 0.5 s warm-up), gives a photoreceptor mean of 31.5 Hz at r_base 10 (measured 30.9 Hz on 2026-09-16), 58.1 Hz at 50, and 98.7 Hz at 100. The rectification does not vanish at 100. The log compression takes the dark half of the grating to a contrast near −1.4, so 41%, 27% and 10% of samples clip at zero. The excess of the stimulus mean over r_base, which is the term the bias rule misses, is +21.5, +8.1 and −1.4 Hz. The bias rule then gives L1 30.63, L2 29.20, L3 16.69 mV at r_base 50 and L1 49.38, L2 46.53, L3 21.50 mV at 100. At r_base 50 L2 should land nearer its 50 Hz target, somewhere in 35–50 Hz. At 100 it should be at or above target. The range is wide because the static mean field does not reproduce the 2026-09-16 lamina split: it gives L1 and L2 the same rate, and the measurement has L1 at 38 Hz and L2 at 20 Hz. Tm1, Tm2 and Tm4 should cross into the hertz range on the real imago. Tm2 should go first and Tm4 last, in the order of their crossing rates; at r_base 50 Tm4 may still be under 1 Hz. Mi1 and Tm3 should stay silent at every r_base: their dominant lamina input is L1, which is inhibitory, and L1 rises with the rest. Mi9 needs L3 at 94 Hz and should stay silent; Mi4 has no direct lamina drive and should stay silent. T4 should stay at zero, since its inputs are the ON cells. T5 is then the first verdict set that gets a fair test. Its inputs include Tm9, which is not in the stage overlay, so the entry can show whether T5 fires, but not every input it needs. No prediction on T5 direction selectivity. On the shuffled control the lamina and medulla rates should rise with r_base, and T4/T5/HS/VS should stay flat across direction, with selectivity near 1.

Setup

  • how the prediction was computed: the photoreceptor means, the clipping fractions and the excess over r_base came from a scratch computation, not a committed tool. It steps the repo’s PhotoreceptorEncoder alone, with no network, over 360 grating phases at 1 ms for 0.5 s warm-up plus 2 s, using the probe’s luminance \(0.5 + 0.4 \sin(\phi - 2\pi t)\) and default VisionParams apart from r_base_hz. The lamina biases in the prediction are the bias rule evaluated with the median \(W_{\mathrm{in}}\) implied by the 2026-09-16 bias_mv values. The script, saved as encoder_prediction.py and re-run after the probes from the repo root with uv run python encoder_prediction.py:

    import math
    
    import torch
    
    from flatline.senses.vision import PhotoreceptorEncoder, VisionParams
    
    n = 360  # grating phases, one per simulated column
    phase = torch.linspace(0.0, 2.0 * math.pi, n + 1)[:-1]
    for r_base in (10.0, 50.0, 100.0):
        vp = VisionParams(r_base_hz=r_base)
        enc = PhotoreceptorEncoder(n, vp, 1, 1.0)
        rates, contrast = [], []
        for t in range(2500):  # 1 ms steps: 0.5 s warm-up, then 2 s
            enc.push_luminance(0.5 + 0.4 * torch.sin(phase - 2.0 * math.pi * t / 1000.0))
            r = enc.step()[0]
            if t >= 500:  # the encoder's own contrast, before the clamp
                rates.append(r)
                c = (enc.held - enc.lowpass) + vp.dc_fraction * (enc.held - enc.l_mid)
                contrast.append(c[0])
        rate, c = torch.stack(rates), torch.stack(contrast)
        print(
            f"r_base {r_base:5.1f}: mean {rate.mean():6.2f} Hz, excess {rate.mean() - r_base:+6.2f} Hz,"
            f" clipped {(rate == 0).float().mean():.1%}, contrast {c.min():+.2f} to {c.max():+.2f}"
        )
    

    Output:

    r_base  10.0: mean  31.51 Hz, excess +21.51 Hz, clipped 40.9%, contrast -1.15 to +0.73
    r_base  50.0: mean  58.05 Hz, excess  +8.05 Hz, clipped 26.8%, contrast -1.15 to +0.73
    r_base 100.0: mean  98.65 Hz, excess  -1.35 Hz, clipped 10.2%, contrast -1.15 to +0.73
    

    The prediction’s 31.5, 58.1 and 98.7 Hz, 41%, 27% and 10%, and +21.5, +8.1 and −1.4 Hz are these values rounded. The prediction’s “contrast near −1.4” is not from this script. It was a hand estimate of the log compression, and it is wrong. The encoder’s contrast minimum is −1.15, which is still below −1, so the dark half still clips at r_base 100.

  • prediction integrity: before the runs, the prediction paragraph hashed to sha256 aff464c27e79363063e7298da546a7e27b7786ba12609628de789b752476c62e with awk '/^Prediction, written before the runs/,/^## Setup/' book/src/notebook/2026-10-30-operating-point-sweep.md | sha256sum. The span runs from the paragraph’s first line through the ## Setup heading. The hash cannot show that the paragraph came before the runs: the prediction and the results landed in the same commit, 083fad7, so it shows only that the paragraph is unchanged since then.

  • git describe: 3ad9b97. The shuffled r_base 10 run was added later, at 5364baf, on the same machine and imagos. Between the two only stage_drive.py and the CLI’s stage-drive handling changed under src/; the probe, encoder and engine did not.

  • machine: straylight (AMD Ryzen AI Max+ 395, Radeon 8060S, Linux), device cpu. The venv’s torch is 2.14.0+cu130 with no ROCm, so torch.cuda.is_available() is False here. The 2026-09-16 baseline reports were run on the Mac with mps, so comparing the cpu r_base 10 runs with them is cpu against mps, and differences at the noise level are expected.

  • imago: data/imagos/male-cns-v1.0.imago, built_at 2026-09-16T02:29:04+00:00, git_describe 4428500-dirty; control data/imagos/male-cns-v1.0-shuffled-1.imago, built_at 2026-09-16T02:30:12+00:00, git_describe 4428500-dirty, shuffle_seed 1

  • probe flags, all defaults except --r-base and --device: grating period 30°, 1 Hz, contrast 0.8; 0.5 s warm-up, 2 s per direction; frame interval 1 ms (continuous); ratio 1.5; lamina target 50 Hz; encoder gain 100 Hz per log unit; seed 0; undriven lamina cells unbiased

  • stage overlay: photoreceptors, L1–L5, C2, C3, Mi1, Tm3, Mi4, Mi9, Tm1, Tm2, Tm4. L4, L5, C2 and C3 were added in 3ad9b97, so the 2026-09-16 baseline reports do not carry them.

  • lamina bias (mV) from bias_mv, target 50 Hz. It changes with r_base and is the same on the real and shuffled imagos at each value:

    r_base (Hz)L1L2L3
    1015.627615.342612.8401
    5030.627629.202616.6901
    10049.377646.527621.5026
  • commands:

    for R in 10 50 100; do
      uv run flatline probe --imago data/imagos/male-cns-v1.0.imago --device cpu --r-base $R \
        --out data/runs/probe/2026-10-30-rbase$R.json
    done
    for R in 50 100; do
      uv run flatline probe --imago data/imagos/male-cns-v1.0-shuffled-1.imago --device cpu --r-base $R \
        --out data/runs/probe/2026-10-30-rbase$R-shuffled.json
    done
    uv run python scripts/figures/stages.py data/runs/probe/2026-10-30-rbase10.json book/src/figures/probe-stages-rbase10.svg
    uv run python scripts/figures/stages.py data/runs/probe/2026-10-30-rbase50.json book/src/figures/probe-stages-rbase50.svg
    uv run python scripts/figures/stages.py data/runs/probe/2026-10-30-rbase100.json book/src/figures/probe-stages-rbase100.svg
    uv run python scripts/figures/stages.py data/runs/probe/2026-10-30-rbase50-shuffled.json book/src/figures/probe-stages-rbase50-shuffled.svg
    uv run python scripts/figures/stages.py data/runs/probe/2026-10-30-rbase100-shuffled.json book/src/figures/probe-stages-rbase100-shuffled.svg
    

    Added later, at 5364baf:

    uv run flatline probe --imago data/imagos/male-cns-v1.0-shuffled-1.imago --device cpu --r-base 10 \
      --out data/runs/probe/2026-10-30-rbase10-shuffled.json
    uv run python scripts/figures/stages.py data/runs/probe/2026-10-30-rbase10-shuffled.json book/src/figures/probe-stages-rbase10-shuffled.svg
    

    The shuffled r_base 10 baseline was at first the existing data/runs/probe/2026-09-16-stages-shuffled.json (mps, without L4, L5, C2, C3). The cpu run replaces it, so that all six runs share a device and the full stage overlay.

What happened

All five probes completed with no exceptions and returned exit code 2 (verdict failed), as every probe so far has. Wall time, including uv run start-up and loading the imago, was 37 s (r_base 10), 37 s (50), 38 s (100), 26 s (shuffled, 50) and 28 s (shuffled, 100). The five figures took 0.3–0.5 s each. The shuffled r_base 10 probe, run later, also exited 2, in 27 s; its figure took 0.4 s.

The r_base 10 run on cpu reproduces the 2026-09-16 mps run. In the progressive, regressive and up directions every stage rate and every verdict rate is identical, except the progressive photoreceptor rate, which is 6.8 × 10⁻⁵ Hz lower: one photoreceptor spike. The down direction differs by up to 0.098 Hz (L2 20.5253 against 20.4269 Hz), and by 0.029 Hz or less on every other set. That is the noise level the device change was expected to bring.

The shuffled r_base 10 run on cpu matches the 2026-09-16 mps shuffled run the same way, except that here the progressive direction carries the whole difference. Regressive, up and down are identical in every stage and verdict rate. In the progressive direction the photoreceptor rate is again 6.8 × 10⁻⁵ Hz lower (one spike), the other stage sets differ by up to 0.049 Hz (Tm2 11.4236 against 11.3743 Hz), T4a–d and T5a–d by up to 0.010 Hz, and HS and VS by 0.56 and 0.34 Hz (9 and 23 spikes over 8 and 34 cells in 2 s). Four-direction means move by at most 0.012 Hz (Tm2). The verdict sets are flat, so this is enough to flip T4a’s winning direction (regressive to progressive) and to move selectivities by up to 0.010 (HS 1.000 to 1.010); all ten stay within 1.000–1.012.

Results

Summary, mean of the four directions (Hz); the T4a–d and T5a–d rows are the range over subtypes and directions. Shuffled at r_base 10 is the later cpu run, which replaces the 2026-09-16 mps report.

setreal 10real 50real 100shuffled 10shuffled 50shuffled 100
photoreceptors30.9057.7898.6430.9057.7898.64
L137.7857.7065.1737.1258.9267.04
L220.4939.8649.2636.1457.7665.83
L321.8027.0233.3733.4341.0546.81
Mi10.0008006.767.677.80
Tm30001.892.502.85
Mi900.00410.254.245.055.33
Tm10.365.218.9211.2413.3414.05
Tm20.162.927.7511.4313.4214.16
Tm40.00873.087.472.032.622.96
T4a–d0000.55–0.900.79–1.180.97–1.37
T5a–d0000.44–0.780.63–1.070.77–1.24

Per run, every set in every direction. Stage rows first, then the verdict sets with the winning direction of the literature axis and the selectivity (preferred + floor over null + floor; pass is 1.5).

Real imago, r_base 10:

setcellsprogressiveregressiveupdownwinner, selectivity
photoreceptors734130.923530.873030.947730.8438—
L1177637.791937.768937.742437.7998—
L2177920.427220.483120.516620.5253—
L3177221.808121.987921.708021.7003—
L417700.00000.00000.00000.0000—
L517870.00000.00000.00000.0000—
C217450.00000.00000.00000.0000—
C317790.07340.07590.07030.0675—
Mi117730.00080.00060.00110.0006—
Tm320540.00000.00000.00000.0000—
Mi417720.00000.00000.00000.0000—
Mi917750.00000.00000.00000.0000—
Tm117770.35880.36550.35170.3455—
Tm217660.16620.15570.15200.1673—
Tm416700.01020.00990.00720.0075—
T4a16840.00000.00000.00000.0000progressive, 1.000
T4b16900.00000.00000.00000.0000progressive, 1.000
T4c17780.00000.00000.00000.0000up, 1.000
T4d17090.00000.00000.00000.0000up, 1.000
T5a16640.00000.00000.00000.0000progressive, 1.000
T5b17150.00000.00000.00000.0000progressive, 1.000
T5c17200.00000.00000.00000.0000up, 1.000
T5d16200.00000.00000.00000.0000up, 1.000
HS80.00000.00000.00000.0000progressive, 1.000
VS340.00000.00000.00000.0000up, 1.000

Real imago, r_base 50:

setcellsprogressiveregressiveupdownwinner, selectivity
photoreceptors734157.846157.644957.850857.7778—
L1177657.718857.844657.577757.6543—
L2177939.967440.022539.716739.7352—
L3177226.889727.262726.874727.0440—
L417700.00000.00000.00000.0000—
L517870.00620.00670.00780.0056—
C217450.00000.00000.00000.0000—
C317790.03340.04080.03630.0377—
Mi117730.00000.00000.00000.0000—
Tm320540.00000.00000.00000.0000—
Mi417720.00000.00000.00000.0000—
Mi917750.00450.00420.00310.0045—
Tm117775.22485.22575.19475.2043—
Tm217662.95362.92332.90372.9060—
Tm416703.14613.13113.03802.9997—
T4a16840.00000.00000.00000.0000progressive, 1.000
T4b16900.00000.00000.00000.0000progressive, 1.000
T4c17780.00000.00000.00000.0000up, 1.000
T4d17090.00000.00000.00000.0000up, 1.000
T5a16640.00000.00000.00000.0000progressive, 1.000
T5b17150.00000.00000.00000.0000progressive, 1.000
T5c17200.00000.00000.00000.0000up, 1.000
T5d16200.00000.00000.00000.0000up, 1.000
HS80.00000.00000.00000.0000progressive, 1.000
VS340.00000.00000.00000.0000up, 1.000

Real imago, r_base 100:

setcellsprogressiveregressiveupdownwinner, selectivity
photoreceptors734198.721198.547998.621898.6629—
L1177665.088165.251165.210365.1244—
L2177949.319049.467749.203549.0669—
L3177233.502333.416833.246033.3273—
L417700.00250.00280.00110.0020—
L517870.01820.02320.02660.0238—
C217450.00000.00000.00000.0000—
C317790.06860.05930.05650.0582—
Mi117730.00000.00000.00000.0000—
Tm320540.00000.00000.00000.0000—
Mi417720.00000.00000.00000.0000—
Mi917750.28030.26870.22390.2285—
Tm117778.93428.94328.90778.8981—
Tm217667.79597.74807.72377.7290—
Tm416707.62197.65187.33687.2883—
T4a16840.00000.00000.00000.0000progressive, 1.000
T4b16900.00000.00000.00000.0000progressive, 1.000
T4c17780.00000.00000.00000.0000up, 1.000
T4d17090.00000.00000.00000.0000up, 1.000
T5a16640.00000.00000.00000.0000progressive, 1.000
T5b17150.00000.00000.00000.0000progressive, 1.000
T5c17200.00000.00000.00000.0000up, 1.000
T5d16200.00000.00000.00000.0000up, 1.000
HS80.00000.00000.00000.0000progressive, 1.000
VS340.00000.00000.00000.0000up, 1.000

Shuffled control, r_base 10:

setcellsprogressiveregressiveupdownwinner, selectivity
photoreceptors734130.923530.873030.947730.8438—
L1177637.124737.114937.102537.1357—
L2177936.106236.135236.122036.2018—
L3177233.440533.437433.380933.4763—
L417700.00000.00000.00000.0000—
L517877.35427.27537.40107.3744—
C217450.43500.42780.43670.4315—
C317792.64592.60932.64902.6498—
Mi117736.79136.71606.74486.7735—
Tm320541.89631.86441.90681.8941—
Mi417720.81290.79630.80530.7963—
Mi917754.25074.19724.26064.2617—
Tm1177711.213611.195011.285611.2648—
Tm2176611.423611.364111.471111.4754—
Tm416702.03502.00302.04882.0302—
T4a16840.88210.87740.87980.8955progressive, 1.003
T4b16900.55770.55800.54590.5541regressive, 1.000
T4c17780.82310.82450.83660.8211up, 1.012
T4d17090.64070.64510.64540.6337up, 1.010
T5a16640.52400.51440.52400.5261progressive, 1.009
T5b17150.75710.74780.76620.7755progressive, 1.007
T5c17200.43550.43920.44160.4352up, 1.007
T5d16200.50620.50770.50930.5049up, 1.004
HS857.937557.375055.562556.1875progressive, 1.010
VS3431.250030.014730.941230.6324up, 1.010

Shuffled control, r_base 50:

setcellsprogressiveregressiveupdownwinner, selectivity
photoreceptors734157.846157.644957.850857.7778—
L1177658.927959.062558.796558.8812—
L2177957.730557.830257.713357.7825—
L3177241.037041.127341.014141.0375—
L417700.00000.00000.00000.0000—
L517878.59408.56668.54818.6489—
C217450.56160.56480.56100.5633—
C317793.23413.24823.16473.2442—
Mi117737.67517.72027.56017.7250—
Tm320542.48522.51682.47692.5331—
Mi417721.02091.05811.00621.0305—
Mi917755.04085.08035.00595.0913—
Tm1177713.349213.371113.261113.3942—
Tm2176613.376813.351413.438013.5102—
Tm416702.61442.63802.58682.6365—
T4a16841.16571.17551.14251.1621regressive, 1.006
T4b16900.81510.82070.79440.8263regressive, 1.004
T4c17781.15161.16231.12681.1561down, 1.018
T4d17090.88390.89790.85110.8947down, 1.032
T5a16640.73440.74880.73830.7335regressive, 1.012
T5b17151.05921.06941.02541.0676regressive, 1.007
T5c17200.64680.65610.62760.6535down, 1.023
T5d16200.73270.73860.71640.7364down, 1.016
HS866.375067.625063.562562.1250regressive, 1.019
VS3435.264735.941235.985335.6029up, 1.011

Shuffled control, r_base 100:

setcellsprogressiveregressiveupdownwinner, selectivity
photoreceptors734198.721198.547998.621898.6629—
L1177666.920967.102267.121967.0025—
L2177965.660565.935165.899765.8162—
L3177246.805946.843146.823646.7621—
L417700.00000.00000.00000.0000—
L517879.04929.05999.27179.2043—
C217450.62920.62320.63810.6404—
C317793.51323.50733.54023.5129—
Mi117737.71697.75307.87547.8370—
Tm320542.80742.79942.90122.8856—
Mi417721.02141.02771.07511.0545—
Mi917755.26005.26625.39495.3862—
Tm1177713.970213.999414.108614.1207—
Tm2176614.003714.041914.306114.3001—
Tm416702.92282.93353.00363.0000—
T4a16841.34061.36071.36881.3596regressive, 1.011
T4b16900.98700.97430.98760.9899progressive, 1.009
T4c17781.30261.30761.36841.3459up, 1.012
T4d17091.01871.01991.06701.0421up, 1.016
T5a16640.86690.86060.89780.8753progressive, 1.005
T5b17151.18131.20231.24291.2370regressive, 1.012
T5c17200.77560.77300.79300.8049down, 1.009
T5d16200.83210.84170.86940.8651up, 1.003
HS868.937569.937573.187569.1250regressive, 1.014
VS3437.676537.397137.867637.5441up, 1.009

Conclusions

The operating point was the OFF-path blocker at the medulla, and moving it wakes Tm1, Tm2 and Tm4. The photoreceptor means landed where the encoder calculation put them: 57.78 Hz at r_base 50 (predicted 58.1) and 98.64 Hz at 100 (predicted 98.7). L2 rose from 20.49 to 39.86 to 49.26 Hz. That is inside the predicted 35–50 Hz at 50, and 0.7 Hz under the 50 Hz target at 100, so “at or above target” held only as “at”. L2 is the only lamina type that lands on target: at r_base 100, where the photoreceptors average 98.64 Hz and the bias rule’s assumption holds, L1 runs at 65.17 Hz and L3 at 33.37 Hz, 15.17 Hz over and 16.63 Hz under the 50 Hz target.

With L2 at 39.86 Hz (r_base 50), Tm1 fires 5.21 Hz, Tm2 2.92 Hz and Tm4 3.08 Hz; with L2 at 49.26 Hz (r_base 100), they fire 8.92, 7.75 and 7.47 Hz. The lamina-drive entry put the median cell at threshold, from L2 alone, at 38.9 Hz for Tm2, 42.4 Hz for Tm1 and 53.0 Hz for Tm4. The sweep took L2 past the first at r_base 50 and past the first two at 100, never to 53.0 Hz, and all three types are in the hertz range at both settings, Tm4 included.

The predicted order was wrong. The prediction had Tm2 first and Tm4 last, and Tm4 possibly under 1 Hz at r_base 50. Tm1 led at both settings. Tm4 was above Tm2 at 50 (3.08 against 2.92 Hz), even though the median Tm4 cell needs L2 14 Hz higher than the median Tm2 cell. The static mean-field crossing rate does not rank these cells. The grating modulates L2 at 1 Hz, so L2 spends part of each cycle well above its mean; the per-cell spread of \(W_{\mathrm{in}}\) (Tm4’s interquartile range is the widest of the three) and the other inputs onto each type also enter. None of these was measured here.

The ON path stayed silent, as predicted. Mi1 went from 0.0008 Hz at r_base 10 to exactly 0 at 50 and 100; Tm3 and Mi4 are exactly 0 at all three. L1 rose with the rest (37.78, 57.70, 65.17 Hz), and L1 is the inhibitory dominant input onto Mi1, Tm3, L5, C2 and C3. L4, L5 and C2 are at or under 0.03 Hz on every real run; C3 is 0.03–0.08 Hz in every direction. Mi9 leaked slightly at 100 (0.25 Hz) with L3 at 33.37 Hz, against the 94.3 Hz the accounting gives for L3 alone. The prediction said silent; 0.25 Hz is near silent, but not zero.

Nothing reached T5. T4a–d, T5a–d, HS and VS are exactly 0.0 Hz in every direction at every r_base on the real imago. T4 was predicted to stay at zero. T5 was the set the sweep was meant to give a fair test, and with Tm1, Tm2 and Tm4 at 7.5–8.9 Hz it produced no spike in 2 s across 6719 cells and four directions. This is the surprising result. The probe cannot say why: Tm9, the fourth major T5 input, is not in the stage overlay, and nothing here accounts for the weight from Tm1/Tm2/Tm4 onto T5 or for inhibition onto T5.

The shuffled control fires T5, flat. At the same settings the control’s lamina runs hotter than the real one (L2 57.76 and 65.83 Hz), its medulla rises with r_base (Tm1 11.24 → 13.34 → 14.05 Hz, Mi1 6.76 → 7.67 → 7.80 Hz), and T4a–d and T5a–d fire 0.63–1.37 Hz in every direction. Selectivity is 1.003–1.032 on every verdict set. HS runs 62–73 Hz and VS 35–38 Hz, both flat. This is the same picture as at r_base 10 (T4a–d and T5a–d 0.44–0.90 Hz, selectivity at most 1.012), scaled up.

That the control’s medulla activity comes from recurrent excitation is carried over from the stage-drive accounting in the lamina-drive entry, not measured here: at the control’s r_base 10 lamina rates its net lamina \(g\) median is 0.00 mV for six of the seven medulla types and +0.05 mV for Tm4, so the lamina is not what drives them. The accounting was not repeated at r_base 50 or 100. On that reading the control’s T5 rates say a randomized brain can push T5 over threshold; they do not say the real lamina-to-T5 route is broken. Type by type at r_base 100, the real Tm1 and Tm2 (8.92 and 7.75 Hz) run at 0.63 and 0.55 times the control’s (14.05 and 14.16 Hz), and the real Tm4 (7.47 Hz) at 2.5 times the control’s 2.96 Hz. The real T5 is still at zero; the control’s T5a–d fire 0.77–1.24 Hz.

Next steps

Left to Kyle. Options, not ranked:

  • Account for the step from the OFF medulla to T5 with the existing stage-drive command: --from Tm1 Tm2 Tm4 Tm9 --to T5a T5b T5c T5d, with --rates-from data/runs/probe/2026-10-30-rbase100.json on the real and shuffled imagos. The report has no Tm9 rate, so Tm9 needs --rate Tm9=HZ or has to be dropped from --from. This is the same accounting that explained the lamina-to-medulla drop.
  • Add Tm9 (and any other large T5 input the accounting names) to the probe’s stage overlay, then repeat the r_base 100 run.
  • Keep r_base near the stimulus-mean photoreceptor rate as the probe default, or make the lamina bias rule use the measured stimulus mean instead of r_base. That removes the encoder term only. At r_base 100, where that term is gone, L2 sits on its target but L1 (65.17 Hz) and L3 (33.37 Hz) do not, so the rule also misses per type for a reason not measured here; find that before relying on it.
  • Option (a) from the lamina-drive entry, the tonic operating-point policy for the inhibition-dominated graded types, for the ON path, which this sweep does not touch.

What reaches T5

Goal

T5 is the fly’s detector for moving dark edges (OFF edges). There are four subtypes, T5a–d, one per direction of motion, and about 1,700 cells of each, one per column of the eye. Each T5 cell compares a fast and a delayed copy of the darkening signal from neighbouring columns. It gets them from four medulla cell types, all part of the OFF channel: Tm1, Tm2 and Tm4 (fast relays of L2’s darkening signal) and Tm9 (a slower relay, driven by L3). It also gets inhibition from CT1, one very large inhibitory cell per optic lobe with a branch in every column. The motion pipeline chapter walks through the circuit; its glossary names every type used here.

The operating-point sweep raised --r-base to 100 Hz. That woke Tm1, Tm2 and Tm4 (7.5–8.9 Hz), but T5 stayed at exactly 0 Hz in every direction on the real imago. The shuffled control fired T5 at about 1 Hz with no direction selectivity. This entry asks why T5 is silent. There are two candidate answers. T5 could be braked: inhibition dominates its input, as L1 brakes Mi1 on the ON path. Or it could be under-powered: its inputs are excitatory but too weak at these rates to reach threshold. The answer comes from the same accounting as the lamina-drive entry, after adding Tm9 and CT1 to the probe’s stage overlay so their rates are measured rather than guessed.

A scratch stage-drive run before this entry named the T5 inputs: Tm9, Tm2, Tm1, Tm4 excitatory, CT1 inhibitory, plus T5 recurrence, at an all-input net of +16 to +21 mV per spike on the real imago and no structure on the shuffled one. That was treated as a hypothesis to reproduce, and the “all inputs” table below reproduces it.

Setup

  • code: the stage overlay gained Tm9 and CT1 in ad973b6 (“Probe: add Tm9 and CT1 to the stage overlay”). The probe reports and the first two stage-drive reports record git describe a9c0298-dirty; the later stage-drive reports record 8405dfe. Another agent was committing book pages on the same branch in parallel: a9c0298 and 8405dfe are its book commits, and -dirty is its uncommitted book edits. git diff ad973b6 8405dfe -- src scripts tests is empty, so every run used the code of ad973b6.
  • machine: straylight (AMD Ryzen AI Max+ 395, Radeon 8060S, Linux), device cpu. torch 2.14.0+cu130; torch.cuda.is_available() is False on this venv.
  • imago: data/imagos/male-cns-v1.0.imago, built_at 2026-09-16T02:29:04+00:00, git_describe 4428500-dirty; control data/imagos/male-cns-v1.0-shuffled-1.imago, built_at 2026-09-16T02:30:12+00:00, git_describe 4428500-dirty, shuffle_seed 1. In both, the type table has Tm9 (1771 cells) and CT1 (2 cells, one per optic lobe) under exactly those names.
  • probe flags, all defaults except --r-base 100 and --device cpu: grating period 30°, 1 Hz, contrast 0.8; 0.5 s warm-up, 2 s per direction; frame interval 1 ms; ratio 1.5; lamina target 50 Hz; gain 100 Hz per log unit; seed 0. Lamina bias (bias_mv): L1 49.3776, L2 46.5276, L3 21.5026 mV, the same as the sweep’s r_base 100 runs.
  • constants (LIFParams() defaults): \(\tau_s = 5\) ms, \(\tau_m = 20\) ms, \(W_{\mathrm{syn}} = 0.275\) mV, threshold gap \(v_{\mathrm{th}} - v_{\mathrm{rest}} = 7.0\) mV, single-spike PSP peak factor 0.1575.
  • accounting, as in the lamina-drive entry: per target cell, \(W_{\mathrm{in}}\) is the summed signed effective weight (mV per presynaptic spike) from one source type; its mean-field input is \(g = r \cdot \tau_s \cdot W_{\mathrm{in}}\) at the source’s measured rate \(r\); net \(g\) sums the listed sources per cell; the rate at which one source alone puts the median cell at threshold is \(7 / (\tau_s \cdot W_{\mathrm{in}})\), only for positive \(W_{\mathrm{in}}\). Medians and quartiles of \(W_{\mathrm{in}}\) are over the cells a source reaches; net \(g\) is over all cells of the type. Source rates are the four-direction means of the probe report’s stage rates. T5 rates are not stage rates; they are in the report’s verdict rates, so the recurrence runs pass their four-direction means with --rate.
  • commands:
    uv run flatline probe --imago data/imagos/male-cns-v1.0.imago --device cpu --r-base 100 \
      --out data/runs/probe/2026-10-30-rbase100-t5.json
    uv run flatline probe --imago data/imagos/male-cns-v1.0-shuffled-1.imago --device cpu --r-base 100 \
      --out data/runs/probe/2026-10-30-rbase100-t5-shuffled.json
    
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0.imago \
      --rates-from data/runs/probe/2026-10-30-rbase100-t5.json \
      --from Tm1 Tm2 Tm4 Tm9 CT1 --to T5a T5b T5c T5d \
      --out data/runs/stage-drive/2026-10-30-t5-real.json
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0-shuffled-1.imago \
      --rates-from data/runs/probe/2026-10-30-rbase100-t5-shuffled.json \
      --from Tm1 Tm2 Tm4 Tm9 CT1 --to T5a T5b T5c T5d \
      --out data/runs/stage-drive/2026-10-30-t5-shuffled.json
    
    # with T5 recurrence; T5 rates from the reports' verdict rates
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0.imago \
      --rates-from data/runs/probe/2026-10-30-rbase100-t5.json \
      --from Tm1 Tm2 Tm4 Tm9 CT1 T5a T5b T5c T5d --to T5a T5b T5c T5d \
      --rate T5a=0.0 --rate T5b=0.0 --rate T5c=0.0 --rate T5d=0.0 \
      --out data/runs/stage-drive/2026-10-30-t5-recurrent-real.json
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0-shuffled-1.imago \
      --rates-from data/runs/probe/2026-10-30-rbase100-t5-shuffled.json \
      --from Tm1 Tm2 Tm4 Tm9 CT1 T5a T5b T5c T5d --to T5a T5b T5c T5d \
      --rate T5a=0.8751502403846154 --rate T5b=1.2158892128279883 \
      --rate T5c=0.7866279069767441 --rate T5d=0.8520833333333333 \
      --out data/runs/stage-drive/2026-10-30-t5-recurrent-shuffled.json
    
    # excitatory sources only: T5 with the CT1 term removed
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0.imago \
      --rates-from data/runs/probe/2026-10-30-rbase100-t5.json \
      --from Tm1 Tm2 Tm4 Tm9 --to T5a T5b T5c T5d \
      --out data/runs/stage-drive/2026-10-30-t5-excitatory-real.json
    
    # what drives Tm9 and CT1
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0.imago \
      --rates-from data/runs/probe/2026-10-30-rbase100-t5.json \
      --from L1 L2 L3 Tm1 Tm2 Tm4 CT1 --to Tm9 CT1 --top 10 \
      --out data/runs/stage-drive/2026-10-30-t5-inputs-real.json
    uv run flatline stage-drive --imago data/imagos/male-cns-v1.0-shuffled-1.imago \
      --rates-from data/runs/probe/2026-10-30-rbase100-t5-shuffled.json \
      --from L1 L2 L3 Tm1 Tm2 Tm4 CT1 --to Tm9 CT1 --top 10 \
      --out data/runs/stage-drive/2026-10-30-t5-inputs-shuffled.json
    
    uv run python scripts/figures/stage_drive.py data/runs/stage-drive/2026-10-30-t5-real.json book/src/figures/stage-drive-t5-real.svg
    uv run python scripts/figures/stage_drive.py data/runs/stage-drive/2026-10-30-t5-shuffled.json book/src/figures/stage-drive-t5-shuffled.svg
    uv run python scripts/figures/stages.py data/runs/probe/2026-10-30-rbase100-t5.json book/src/figures/probe-stages-rbase100-t5.svg
    uv run python scripts/figures/stages.py data/runs/probe/2026-10-30-rbase100-t5-shuffled.json book/src/figures/probe-stages-rbase100-t5-shuffled.svg
    

What happened

Both probes completed with no exceptions and exited 2 (verdict failed), as every probe so far has. Wall time, including uv run start-up and loading the imago, was 39.9 s (real) and 28.3 s (shuffled). Each probe was run three times because the first two timing wrappers failed (no bc on straylight, then a zsh arithmetic slip). The stage rates, verdict rates and verdict entries of the first and last runs are identical.

The new reports reproduce the sweep’s r_base 100 reports (2026-10-30-rbase100.json and -shuffled.json) exactly: on every shared stage set and every verdict set, in every direction, the largest difference is 0.0 Hz, and the configs are identical. Adding two types to the overlay changes only what is recorded.

The seven stage-drive runs exited 0 in 1.8–2.5 s each. The two “what drives Tm9 and CT1” runs were first made without CT1 as a source, then rerun with it to measure CT1’s inhibition of Tm9; the reports above are the reruns. The four figures took 0.3–0.5 s each.

Results

Source transmitters, as labelled in the imago. Every source is transmitter-uniform on both imagos, so “reached”, which counts a cell by its net signed weight from a source, has no cancellation caveat here.

typecellstransmittersignuniformuniform (shuffled)
Tm11777acetylcholine+1yesyes
Tm21766acetylcholine+1yesyes
Tm41670acetylcholine+1yesyes
Tm91771acetylcholine+1yesyes
CT12gaba−1yesyes

The T5 subtypes are acetylcholine on both imagos.

Rates

Mean of the four directions (Hz), r_base 100.

setcellsrealshuffled
L1177665.168567.0369
L2177949.264365.8279
L3177233.373146.8087
Mi917750.25045.3268
Tm117778.920814.0497
Tm217667.749214.1629
Tm416707.47472.9650
Tm917710.00010.5398
CT12163.8750103.8125
T5a16640.00000.8752
T5b17150.00001.2159
T5c17200.00000.7866
T5d16200.00000.8521

The two new stage sets by direction:

setimagoprogressiveregressiveupdown
Tm9real0.00030.00000.00000.0000
Tm9shuffled0.53250.53840.54320.5452
CT1real165.5000163.7500162.7500163.5000
CT1shuffled104.0000104.7500103.0000103.5000

On the real imago Tm9 fired 0.0003 Hz in the progressive direction and nothing in the other three. CT1 fired at about 164 Hz in every direction. T4a–d, T5a–d, HS and VS are 0 Hz in every direction, as in the sweep. In the control, T5a–d fire 0.77–1.24 Hz per direction with selectivity 1.003–1.012.

All inputs onto T5

Every presynaptic type, mV per spike per cell. The medians are of the excitatory sum, the inhibitory sum and the net. The last column gives the six largest presynaptic types by summed signed weight (cell mean).

targetexcitatoryinhibitorynetlargest presynaptic types
T5a+38.2−22.8+15.7Tm2 +9.55, Tm9 +9.17, Tm1 +6.90, CT1 −6.51, Tm4 +5.46, T5a +4.62
T5b+40.7−22.3+18.7Tm9 +10.31, Tm2 +9.85, CT1 −6.52, Tm1 +6.32, T5b +5.71, Tm4 +5.03
T5c+42.4−21.4+20.9Tm9 +11.84, Tm2 +9.90, Tm1 +7.19, Tm4 +6.02, CT1 −5.42, T5c +4.20
T5d+39.6−19.2+20.3Tm9 +10.80, Tm2 +8.91, Tm1 +7.11, Tm4 +5.52, CT1 −5.33, T5d +4.36

This is the scratch run’s list. Tm9, Tm2, Tm1 and Tm4 are the four largest excitatory inputs and CT1 is the largest inhibitory one. Each subtype’s next input is its own subtype (recurrence). By weight alone, T5 is excitation-dominated (net +15.7 to +20.9 mV per spike). The weights alone do not make it a braked cell in the way Mi1 is (all-input net −43.5).

Per source, real imago

\(W_{\mathrm{in}}\), PSP peak and \(g\) in mV; rate to cross in Hz.

targetsourcereachedsynapses (median)\(W_{\mathrm{in}}\) median [q1, q3]PSP peak\(g\)rate to cross
T5aTm199.9%25+6.88 [+5.50, +7.98]+1.08+0.31203.6
T5aTm299.9%34+9.35 [+7.98, +11.27]+1.47+0.36149.7
T5aTm499.9%20+5.50 [+4.12, +6.60]+0.87+0.21254.5
T5aTm999.9%32+8.80 [+6.33, +11.55]+1.39+0.00159.1
T5aCT199.9%24−6.60 [−7.98, −5.22]−1.04−5.41none
T5bTm1100.0%23+6.32 [+5.22, +7.43]+1.00+0.28221.3
T5bTm2100.0%36+9.90 [+8.25, +11.55]+1.56+0.38141.4
T5bTm4100.0%18+4.95 [+3.85, +6.05]+0.78+0.18282.8
T5bTm9100.0%36+9.90 [+7.43, +12.93]+1.56+0.00141.4
T5bCT1100.0%24−6.60 [−7.70, −5.22]−1.04−5.41none
T5cTm1100.0%26+7.15 [+5.78, +8.52]+1.13+0.32195.8
T5cTm2100.0%36+9.90 [+7.98, +11.55]+1.56+0.38141.4
T5cTm4100.0%22+6.05 [+4.68, +7.43]+0.95+0.23231.4
T5cTm9100.0%42+11.55 [+8.52, +15.12]+1.82+0.00121.2
T5cCT199.8%20−5.50 [−6.60, −4.12]−0.87−4.51none
T5dTm1100.0%25+6.88 [+5.50, +8.53]+1.08+0.31203.6
T5dTm2100.0%32+8.80 [+7.42, +10.45]+1.39+0.34159.1
T5dTm4100.0%19.5+5.36 [+4.13, +6.60]+0.84+0.20261.1
T5dTm9100.0%38+10.45 [+7.70, +13.48]+1.65+0.00134.0
T5dCT199.9%20−5.50 [−6.60, −4.12]−0.87−4.51none

Net \(g\) over the five sources, against the 7 mV gap. “7 − net g” is how far the median cell’s mean-field input sits below threshold. “Excitatory only” is the run without CT1. “With T5 recurrence” adds T5a–d as sources at their measured rates.

targetnnet \(g\) median [q1, q3]net g > 0net g > 77 − net gexcitatory only: net \(g\) median [q1, q3]with T5 recurrence: net \(g\) median
T5a1664−4.59 [−5.53, −3.48]0.3%0.0%11.59+0.88 [+0.77, +1.01]−4.59
T5b1715−4.62 [−5.50, −3.59]0.5%0.0%11.62+0.85 [+0.74, +0.98]−4.62
T5c1720−3.53 [−4.48, −2.61]0.9%0.0%10.53+0.93 [+0.79, +1.06]−3.53
T5d1620−3.56 [−4.47, −2.56]0.7%0.0%10.56+0.86 [+0.74, +0.99]−3.56

T5 recurrence is real wiring but contributes nothing here. Each subtype gets +4.12 to +5.50 mV per spike from its own subtype (15–20 synapses, 99.8–100% of cells) and +0.28 to +0.55 mV from each of the other three. With every T5 rate at 0 Hz, that term is 0 mV and net \(g\) is unchanged.

What drives Tm9 and CT1, real imago

targetsourcereachedsynapses (median)\(W_{\mathrm{in}}\) median [q1, q3]PSP peak\(g\)rate to cross
Tm9L11.2%1−0.28 [−0.28, −0.28]−0.04−0.09none
Tm9L27.7%1+0.28 [+0.28, +0.28]+0.04+0.075090.9
Tm9L399.9%28+7.70 [+6.33, +9.07]+1.21+1.28181.8
Tm9Tm182.4%2+0.55 [+0.28, +1.10]+0.09+0.022545.5
Tm9Tm257.9%1+0.28 [+0.28, +0.55]+0.04+0.015090.9
Tm9Tm411.2%1+0.28 [+0.28, +0.28]+0.04+0.015090.9
Tm9CT196.0%10−2.75 [−3.30, −2.20]−0.43−2.25none
CT1Tm1100.0%5366+1475.65 [+1462.31, +1488.99]+232.40+65.820.95
CT1Tm2100.0%465+127.88 [+125.81, +129.94]+20.14+4.9510.9
CT1Tm4100.0%136.5+37.54 [+31.42, +43.66]+5.91+1.4037.3

L1, L2, L3 and CT1 itself reach neither CT1 cell on the real imago. Tm9’s net \(g\) over these seven sources is −0.89 mV [−1.41, −0.40], positive in 12.0% of cells and above the gap in none. Its other large inputs are Mi4 (−5.04 mV per spike, cell mean; Mi4 is silent) and Tm16 (+2.28, rate not measured). CT1’s net \(g\) is +72.18 mV [+71.89, +72.46] on both cells. Each CT1 cell’s all-input sums are +20155.0 mV excitatory and −1106.3 mV inhibitory per spike. Its largest presynaptic type is Tm9 (+7169.11 mV per spike), which is silent, then T4b, Tm1, T4a, T4c and T5a–d (+817.99 to +1653.85), of which only Tm1 fires.

Shuffled control

targetsourcereachedsynapses (median)\(W_{\mathrm{in}}\) median [q1, q3]PSP peak\(g\)rate to cross
T5aTm139.7%5+1.38 [+0.55, +3.03]+0.22+0.101018.2
T5aTm241.2%4+1.10 [+0.55, +2.48]+0.17+0.081272.7
T5aTm447.5%4+1.10 [+0.55, +1.93]+0.17+0.021272.7
T5aTm917.3%2+0.55 [+0.28, +1.93]+0.09+0.002545.5
T5aCT14.5%13−3.58 [−5.22, −2.48]−0.56−1.86none
T5bTm139.8%5+1.38 [+0.55, +3.30]+0.22+0.101018.2
T5bTm245.3%4+1.10 [+0.55, +2.20]+0.17+0.081272.7
T5bTm451.3%4+1.10 [+0.55, +1.93]+0.17+0.021272.7
T5bTm916.9%3+0.83 [+0.28, +1.38]+0.13+0.001697.0
T5bCT13.6%12−3.30 [−5.78, −0.83]−0.52−1.71none
T5cTm143.8%5+1.38 [+0.55, +3.30]+0.22+0.101018.2
T5cTm240.6%4+1.10 [+0.55, +2.20]+0.17+0.081272.7
T5cTm450.3%3+0.83 [+0.55, +1.93]+0.13+0.011697.0
T5cTm919.5%2+0.55 [+0.28, +1.65]+0.09+0.002545.5
T5cCT14.0%12−3.30 [−4.68, −2.20]−0.52−1.71none
T5dTm138.9%4+1.10 [+0.55, +3.03]+0.17+0.081272.7
T5dTm242.2%4+1.10 [+0.55, +2.48]+0.17+0.081272.7
T5dTm447.2%4+1.10 [+0.55, +2.20]+0.17+0.021272.7
T5dTm917.3%2+0.55 [+0.28, +1.38]+0.09+0.002545.5
T5dCT13.6%10.5−2.89 [−4.95, −1.38]−0.45−1.50none
targetnnet \(g\) median [q1, q3]net g > 0net g > 77 − net gwith T5 recurrence: net \(g\) median
T5a1664+0.06 [+0.01, +0.17]82.3%0.0%6.94+0.06
T5b1715+0.06 [+0.01, +0.18]83.3%0.0%6.94+0.06
T5c1720+0.06 [+0.01, +0.18]83.5%0.0%6.94+0.07
T5d1620+0.05 [+0.01, +0.16]82.1%0.0%6.95+0.06

The five sources together reach 85.4–87.3% of each shuffled T5 subtype, and 95.0–96.4% with T5 recurrence added. Recurrence adds at most 0.01 mV to the median: each T5 subtype is a median +0.55 to +0.83 mV per spike onto 20.6–29.8% of cells, at 0.79–1.22 Hz. All-input medians on the shuffled T5 are +37.1 to +39.9 mV excitatory, −21.4 to −23.4 mV inhibitory and +15.1 to +16.2 mV net. The largest presynaptic type is Mi1 or Tm3, at +1.02 to +1.19 mV per spike.

In the control, CT1 also fires (103.81 Hz). Its net \(g\) over the seven sources is +80.86 mV [+80.15, +81.58]: L2, L3, Tm1 and Tm2 each put +14.46 to +42.41 mV into it, and every source reaches both cells. But CT1 reaches only 3.6–4.5% of each T5 subtype and 2.1% of Tm9. Shuffled Tm9’s net \(g\) is +0.02 mV [0.00, +0.13], and its largest presynaptic type is Mi1 at +0.71 mV per spike.

In the shuffled figure each coloured segment is the median \(g\) over the cells that source reaches (3.6–51.3% of a T5 subtype). The black tick is the net median over all cells, so CT1’s −1.50 to −1.86 mV segments overstate what a typical shuffled T5 cell gets: its net is +0.05 to +0.06 mV.

Conclusions

T5 is both braked and under-powered. The brake is the larger term, and taking it away is not enough. At the measured rates the median T5 cell’s mean-field input from Tm1, Tm2, Tm4, Tm9 and CT1 is −3.53 to −4.62 mV, below rest. That puts it 10.53–11.62 mV below threshold, and no cell of any subtype has a net input above the gap (0.3–0.9% are even positive). CT1 alone contributes −4.51 to −5.41 mV; Tm1, Tm2 and Tm4 contribute +0.18 to +0.38 mV each, and Tm9 contributes 0.00 mV. With CT1 removed, the net is +0.85 to +0.93 mV: still 6.07–6.15 mV short, with excitation about an eighth of what threshold needs. Each excitatory source alone would have to fire at 121–283 Hz to put the median cell at threshold; Tm1, Tm2 and Tm4 run at 7.5–8.9 Hz. No single input spike comes close: the median PSP peaks are +0.78 to +1.82 mV against the 7 mV gap.

The brake is not like L1 on Mi1. By weight, T5 is excitation-dominated: all-input net +15.7 to +20.9 mV per spike, with CT1’s −5.33 to −6.52 mV the only large inhibitory term against four excitatory ones of +5.03 to +11.84 (cell means). Per spike, the excitation wins. What makes CT1 dominate is its rate. CT1 fires at 164 Hz, 18–22 times Tm1, Tm2 and Tm4, because the engine models it as two point neurons, each summing Tm1 output from across its whole optic lobe through a median of 5366 synapses. Tm1 alone puts a mean field of +65.82 mV into each CT1 cell, and 0.95 Hz of Tm1 would be enough to hold CT1 above threshold. So any OFF activity anywhere turns CT1 fully on, and CT1 inhibits every T5 cell (99.8–100% reached) and 96.0% of Tm9. In the fly, CT1 has a small branch in every column (see the glossary), and the literature describes those branches as acting largely independently of each other, column by column (Meier and Borst 2019, in the References chapter). A single spiking point cell cannot do that.

The under-power has two parts. First, the fast inputs are too slow: Tm1, Tm2 and Tm4 at 7.5–8.9 Hz give +0.18 to +0.38 mV each. Second, Tm9, the largest excitatory input to T5b, T5c and T5d and the second largest to T5a, is silent (0.0001 Hz). Tm9 is under-powered and braked in the same way as T5. Its driver, L3, gives +7.70 mV per spike from 28 synapses, which at 33.37 Hz is +1.28 mV; L3 would need 181.8 Hz. CT1 takes −2.25 mV away, so Tm9’s net is −0.89 mV.

The control says the CT1 brake on T5 comes from the real wiring, and the shuffled T5 activity does not come from these inputs. The shuffled CT1 also fires near saturation (103.81 Hz, net \(g\) +80.86 mV), which suggests that a two-cell type with tens of thousands of input synapses ends up switched on under any wiring. But the shuffle leaves CT1 contacting only 3.6–4.5% of T5 cells. On the real wiring CT1 contacts 99.8–100% of them, through 20–24 synapses each. Every shuffled source reaches T5 through a median of 2–13 synapses, and the five-source net is +0.05 to +0.06 mV, 6.94–6.95 mV short of threshold. The control’s 0.79–1.24 Hz T5 rates come from the rest of its input: the all-input net is +15.1 to +16.2 mV, spread over many types of about 1 mV each. The same recurrent excitation explained the control’s medulla in the lamina-drive entry. As there, it shows the engine can push T5 over threshold. It does not show the real OFF route works.

What this implies for (a) versus (b). Option (a) as written in the lamina-drive entry is a tonic bias for graded cells whose dominant input is inhibitory. None of the three cells on this path qualifies: T5’s dominant inputs are Tm9 and Tm2 (excitatory), Tm9’s is L3 (excitatory), and CT1 is over-driven, not braked. So (a) as specified would leave T5 silent. Waking T5 under (a) would take two further fixes of a different kind: something that stops CT1 from acting as one whole-eye switch, and more drive into Tm9 and into T5 itself (about eight times the present excitation). Both are new per-type operating points on top of the ON-path list. The under-powered half of this result points toward (b). In a graded-rate model, the gap between a 0.275 mV synapse at a few hertz and a 7 mV threshold stops being a hand-set bias and becomes a per-type parameter fitted with the rest of the model, the approach Lappalainen et al. 2024 took. The braked half does not choose between them: CT1 is a problem under either model as long as it is two whole-eye units. A graded CT1 summing the whole eye would still inhibit every T5 at once. Either way, CT1 needs to be split by column or otherwise kept local. These numbers do not settle the choice. That is Kyle’s.

Next steps

Options, not ranked:

  • Test the brake directly: a probe run with CT1 silenced (or its output gain set to zero). The probe has no flag for this yet. The accounting predicts T5 still does not fire (+0.85 to +0.93 mV against 7), so a T5 spike there would mean something the mean field misses, such as the grating’s modulation or the per-cell spread.
  • Split CT1 per column in build-imago: assign each CT1 synapse to the column of its partner and make one unit per column per lobe. This needs synapse-level column data at build time, which the current imago does not carry; check what the dataset provides first.
  • Account for the step into Tm9 further: Tm16 (+2.28 mV per spike) and Mi4 (−5.04) are large Tm9 inputs not in the overlay. Mi4 is already measured and silent. Tm16 is not measured.
  • Option (a), extended by a stated rule to CT1 and Tm9, or option (b), the graded-rate model of the columnar optic lobe. This entry adds to the evidence for that choice but does not make it.

References

  • Shiu, P. K. et al. A Drosophila computational brain model reveals sensorimotor processing. Nature 634, 210–219 (2024). https://www.nature.com/articles/s41586-024-07763-9
  • Nern, A. et al. Connectome-driven neural inventory of a complete visual system. Nature (2025). https://doi.org/10.1038/s41586-025-08746-0 Code: https://github.com/reiserlab/male-drosophila-visual-system-connectome-code
  • Zhao, A. et al. Eye structure shapes neuron function in Drosophila motion vision. Nature (2025). https://www.nature.com/articles/s41586-025-09276-5 Code and eye map: https://github.com/reiserlab/eyemap_T4
  • Lappalainen, J. K. et al. Connectome-constrained networks predict neural activity across the fly visual system. Nature 634, 1132–1140 (2024).
  • Hassenstein, B. & Reichardt, W. Systemtheoretische Analyse der Zeit-, Reihenfolgen- und Vorzeichenauswertung bei der Bewegungsperzeption des Rüsselkäfers Chlorophanus. Z. Naturforsch. B 11, 513–524 (1956). https://doi.org/10.1515/znb-1956-9-1004
  • Barlow, H. B. & Levick, W. R. The mechanism of directionally selective units in rabbit’s retina. J. Physiol. 178, 477–504 (1965). https://doi.org/10.1113/jphysiol.1965.sp007638
  • Borst, A., Haag, J. & Mauss, A. S. How fly neurons compute the direction of visual motion. J. Comp. Physiol. A 206, 109–124 (2020). https://doi.org/10.1007/s00359-019-01375-9
  • Haag, J., Arenz, A., Serbe, E., Gabbiani, F. & Borst, A. Complementary mechanisms create direction selectivity in the fly. eLife 5, e17421 (2016). https://doi.org/10.7554/eLife.17421
  • Strother, J. A. et al. The emergence of directional selectivity in the visual motion pathway of Drosophila. Neuron 94, 168–182 (2017). https://www.cell.com/neuron/fulltext/S0896-6273(17)30193-9
  • Takemura, S. et al. The comprehensive connectome of a neural substrate for ‘ON’ motion detection in Drosophila. eLife 6, e24394 (2017). https://doi.org/10.7554/eLife.24394
  • Shinomiya, K. et al. Candidate neural substrates for off-edge motion detection in Drosophila. Curr. Biol. 24, 1062–1070 (2014). https://doi.org/10.1016/j.cub.2014.03.051
  • Shinomiya, K. et al. Comparisons between the ON- and OFF-edge motion pathways in the Drosophila brain. eLife 8, e40025 (2019). https://doi.org/10.7554/eLife.40025
  • Behnia, R., Clark, D. A., Carter, A. G., Clandinin, T. R. & Desplan, C. Processing properties of ON and OFF pathways for Drosophila motion detection. Nature 512, 427–430 (2014). https://doi.org/10.1038/nature13427
  • Arenz, A., Drews, M. S., Richter, F. G., Ammer, G. & Borst, A. The temporal tuning of the Drosophila motion detectors is determined by the dynamics of their input elements. Curr. Biol. 27, 929–944 (2017). https://doi.org/10.1016/j.cub.2017.01.051
  • Meier, M. & Borst, A. Extreme compartmentalization in a Drosophila amacrine cell. Curr. Biol. 29, 1545–1550 (2019). https://doi.org/10.1016/j.cub.2019.03.070
  • Molina-Obando, S. et al. ON selectivity in the Drosophila visual system is a multisynaptic process involving both glutamatergic and GABAergic inhibition. eLife 8, e49373 (2019). https://doi.org/10.7554/eLife.49373
  • Janelia FlyEM male CNS connectome v1.0 (dataset used here).
  • Bitcraze AB. Crazyflie 2.1, AI deck 1.1, and Flow deck v2 documentation (2025). https://www.bitcraze.io/documentation/
  • Bitcraze AB. cflib (crazyflie-lib-python) 0.1.30 (2024), commander API: send_hover_setpoint, send_zdistance_setpoint, send_stop_setpoint. https://github.com/bitcraze/crazyflie-lib-python

BibTeX entries for these references are in book/references.bib.