fullseye

Language: 日本語 · English

Putting a Fly Visual Model on a Body and Measuring It

Walking

↑ The moving picture first. A physically simulated *Drosophila walks towards the brown target guided by nothing but the image on its compound eye (3× speed, external camera). The steering correction comes from an optic lobe model whose wiring is fixed by the measured connectome. The legs look like a tripod gait, but walking here is kinematic, not leg dynamics — the table below states exactly what is real and what was written by hand before any numbers appear.*

What the fly is asked to do

↑ The whole task in one picture. ① The input is the compound-eye image alone — no GPS, no inertial sensor, no map. ② Read your own rotation while walking, steer to the target with it, then return to the nest from the heading and distance you accumulated. ③ Scoring is the correlation against the imposed rotation, and 2 of the 12 places (C and D) are never used for selection or tuning. The null baseline is the textbook motion detector of 1956. Figure labels are in Japanese; every item is repeated in the text.

TL;DR

This is not an article about reproducing a fly brain. It is about where a pretrained model starts lying once you mount it in your own system — stated in numbers. The same traps appear when you put someone else’s pretrained model on your robot or your production line.

Experiments so far (this table grows)

One experiment = one question. This article is appended to every time an experiment finishes. The “current answer” column is the measurement as it stands, and it can be overturned — one entry already was, and the retraction is kept in §8.

Question Current answer Section
Does a test built by rotating in place predict performance while walking? No. Correlation 0.06–0.51 — a total failure §1
Does texture in the distance make rotation readable while walking? Yes. Correlation 0.98 (0.06 without the band) §2
Does a better sensor mean better behaviour? No change. Arrivals 7 → 6 of 12 §3
How many points of performance is the connectome wiring worth? 0.14–0.20. Preprocessing alone takes 0.6 → 0.8 §4
Does evolving all 734 free parameters generalise? 2 scenes overfit (−0.25); 10 scenes generalise (+0.92) §5
What if the search is compressed to 260 per-cell-type dims? Wins on little data (+0.71), ties on plenty (+0.91) §5
What if the readout is solved by regression instead? Same shape: 80 weights ≫ 5768 weights §5
Does better behaviour bring biological realism with it? The opposite. Direction selectivity 3/8 → 1/8; untrained is 0/8 §6
Can the visual estimate drive central-complex homing? Yes. The first numbers were retracted — the compass had not been calibrated (second entry in §8). Calibrated: the raw estimate homes at 2.1 %, amplitude correction makes it worse (12.4 %) §7 → appendix
Does deceleration interfere with the memory update? No. The two things that looked like interference were something else: a 10.8 % compass gain error, and an unstable equilibrium of a rule in which steering and speed both vanish with |H| appendix
Does enlarging the eye break it? No — and the first conclusion here was retracted §8
Does shuffling the wiring cost performance? Not measurable. Two shuffles differ from each other 3× more appendix
Does a staged curriculum produce direction selectivity? Yes (0/8 → 5/8) — and the final behavioural stage destroys it (→ 1/8) appendix
Can we freeze the optic-lobe front end and cache every cell type’s activity? Yes (45,669 nodes × 12 places × 350 frames) — but 41–73 % of the T4/T5 signal comes from the 90 border columns (a boundary artefact) appendix
Does the male whole-CNS connectome have left/right gaps? Not in cell counts (96 % of 11,229 types within ±1 cell). The only significant asymmetry is one type, Kenyon cell KCab-s — a subtype-boundary difference. Completion shrinks it by 20–30 % but does not remove it. A second implementation with the public tool coconatfly agrees (s connectivity cluster L 201 / R 352) appendix
Is the worm’s wiring left/right symmetric (within one animal)? Half of it. Overlap with the L/R-swapped wiring 0.473 (perfect symmetry 1.000, chance 0.080). The asymmetry is not concentrated in a few pairs Addendum
When a neuron’s tree is measured from an image, how far is it from the real tree? The viewing direction decides. 3-D Sholl does not move a single integer under rotation; the projection moves by up to 9–16 intersections, and 16 true branch points become 22–28 Addendum
How much wiring do 8 genetically identical worms share? 442 connections in all 8 (chance: 0) carry 57 % of the synapses. But two same-age adults overlap by only 0.53 Addendum
How much does a hand-traced wiring diagram from 40 years ago overlap today’s adults? 0.43–0.44, only 0.07 below two adults reconstructed the same way (0.51). What 1986 lacks are thin connections (mean 2.0 vs 5.7 synapses) Addendum
How much do a worm’s neurites grow from birth to adulthood? 4.3-fold (the paper says about 5). Summing the authors’ own file also gives 3.95, so the 5-fold itself does not come out. The longest skeleton path matches the authors’ values to 1.75e-9 Addendum
Scoring a neuron segmentation from electron microscopy as over-splitting and over-merging Injecting one error at a time raises only split or only merge, exactly by the closed form (error < 1e-12). A classic segmentation crosses over between the 65th and 70th percentiles. Leaving membrane pixels as background inflates merge 2.4-fold Addendum
Where segmentation errors break the wiring diagram The ratio is identically density × balance; the cut discount comes mostly from how few ends a neuron has (median 3, binomial 0.62). 4 in 10 cuts change the wiring by not a single bit. Wiring damage per pixel bit is 1.30 for merges and 0.58 for splits. Proofreading in pixel-score order works twice as well as random but falls 8 points short of ideal Addendum
Do synapses grow in proportion to neurites? System-wide, density rises ×1.32 during L1 and wobbles by 1.14 afterwards (as the paper says). Per cell, the rank correlation between neurite growth and synapse gain is only 0.23. Of 6,674 new synapses, 48 % thickened existing connections and 54 % made new ones Addendum
Run length forgives no small merge One cut’s ERL matches the closed form on 1,713 skeletons. Sweeping the merge size from 5 % to 50 %, VOI merge rises 0.14 → 0.66 bits, but the receiving skeleton’s ERL is 0 for every q. Uniform 3-point cuts of unbranched skeletons match the Dirichlet expectation, ratio 1.02 Addendum
What the slime mould taught: its tubes are continuous (work in progress) Maze 5/5; with continuous parts the ridge vanishes 24/24; greedy recovery returns 8/8 readable circuits that inherit 9/9. Under evolution the leak gives 8/8, but “evolution crossed the ridge” was retracted (the leak had erased it). Peeling the floors during the search: heritability 13/16 vs 4/16 at n=16 (p=0.0038). Three more schedules (early, late, two-step): heritability 8/16, 12/16, 11/16 regardless of tail length; crossing drops when the floors go early (6/16). One task — research remains Addendum
Slime-mould tubes solve the maze (two ops) Five lattices, a perfect maze and a terrain: the mould’s route equals Dijkstra / route_through_array everywhere (< 1e-9). 3/5 converged to the indicator; the two cut off have the smallest runner-up gaps (0.009, 0.032). The Lyapunov V never rose over 23 intervals Addendum
The worm brain’s core is there from birth (3 ops) Peeled by synapse count, the deepest shell stays at 6–10 cells through development (index 7 → 55) and the RIA pair never leaves it; the 0/1 k-core swells to 150 cells and cannot single out a core. 51 cells persistent in some core type = the published value Addendum
The slime mould solves optimal transport (two ops) With a supply vector the tube dynamics converge to L1 optimal transport. Ten truths (tree closed form, 1-D, Hungarian, LP, translation, Dijkstra) all match, and the Kantorovich–Rubinstein lower bound brackets the cost from below. The surviving tubes are the optimal assignment Addendum

Glossary (worth reading first)

What is real, and where the hand-written part begins

This table comes first. Without it, a reader will assume the fly’s brain did all of it.

Part What it is Origin
Body Full-body Drosophila model (mass 0.98 mg, cm/g/s units) in a physics simulator Public model (flybody)
Walking Leg stance patterns are kinematic — it is not walking by leg dynamics Hand-written
Eye Three body-mounted cameras resampled onto 721 hexagonal ommatidia (including the 1.118× diagonal distortion) Hand-written (geometry matched to the public model)
Optic lobe 65 cell types, wiring fixed, 734 free parameters, pretrained Public model (flyvis)
Readout One expression: “above the horizon, (preferred − opposite) ÷ sum” Hand-written, zero parameters
Behaviour Fixate on something darker than the sky, plus optomotor feedback and efference copy Hand-written
World Floor, sky, a target, distractors. The target stands in for dung: a knot implicit surface (knot_sdf — an organic shape whose silhouette changes from every viewing angle); the distractors are a gear (gear_sdf) and a gyroid sphere (gyroid_sphere_sdf). The sky texture is synthetic Hand-written

Signal path of the fly brain model

↑ The signal path and the boundary between real and hand-written. Only the blue box is wired by the measured connectome — the flyvis optic lobe, 65 cell types, in the order light travels: receptors R1–R6 → lamina L1/L2/L3 → medulla (ON: Mi1, Tm3 / OFF: Tm1, Tm2, Tm4, Tm9 / modulatory: Mi4, Mi9) → T4a–d (ON) → T5a–d (OFF). Every orange box was written here. Evolution moves only the continuous values inside the blue box; not one new connection is ever added. Figure labels are in Japanese; the same content is in the table above.

The columnar structure of the optic lobe in 3-D

↑ The same optic lobe, rotating in 3-D. The 721 columns keep their arrangement through all seven stages — that is the columnar organisation — with one colour per stage (receptors → lamina → medulla ON / OFF / modulatory → T4, the ON direction-selective cells → T5, the OFF ones). Column positions are the real hexagonal lattice, but the depth between stages is schematic: flyvis is a columns × cell-types network and carries no anatomical 3-D coordinates. When section 5 talks about “folding the degrees of freedom per cell type”, these colours are the unit it folds along.

So what is that measured wiring, concretely? Here it is, drawn.

The optic lobe connectome

↑ Left: the measured connectome (fib25-fib19; 65 cell types, 605 type-to-type connections). Nodes are cell types, laid out in the order light travels through them. Blue is excitatory, red inhibitory; line width is synapses per receiving cell. Right: a degree-preserving shuffle — every type keeps its in-degree, out-degree and sign, and only the targets are swapped. Same edge count, same degrees, but the direction profile changes: within-layer connections fall from 179 to 99, while backward ones (against the direction of light) rise from 148 to 208. Figure labels are in Japanese.

Those red lines criss-crossing the layers on the right are what the “shuffled-connectome control” later in this article actually destroyed. Same edges, same degrees — the layered structure is not preserved.

So only the optic lobe is genuinely connectome-derived; everything before and after it (eye geometry, readout, behaviour) was written by hand. Every number below describes “the optic lobe wired up this particular way”.

One more thing worth stating plainly: an insect’s central nervous system is not confined to its head. The thorax carries a ventral nerve cord (VNC) of roughly 23,000 neurons, and that is where walking is generated — the brain sends roughly 1,328 descending neurons to it. A decapitated fly can still walk (reported in 1997). When this article says “walking is kinematic”, what it means is that something the VNC would normally produce is hand-written here instead. Brain-plus-VNC connectomes (BANC, MaleCNS) were published in 2026, so this part can get closer to the real thing later.

0. Designing the eye itself — compound-eye optics as closed-form operators

The table above files “the eye” under hand-written. Here is what that hand-written part actually is. A compound eye is a design, and it has exactly three knobs:

Fix those three and what the eye can and cannot see follows from a formula. The flyvision family in Fullseye, my own vision library, implements that path as closed-form operators, so changing the design and re-measuring it takes a few lines.

import flyvision as fv

lat = fv.fly_hex_lattice(radius=15, dphi_deg=4.63, geometry="boxeye")  # 721 viewing axes
sky = fv.fly_sky_1f(width=1440, height=720, amp=0.45, seed=7)          # a synthetic 1/f sky
sig = fv.fly_hex_resample(img, lat, drho_deg=8.23, fov_deg=110)        # what the eye sees: 721 values

Compound-eye optics

↑ Every panel is the operators actually run — there is no schematic anywhere in this figure. A the 721 viewing axes (inset: the central ±12°, orange = the acceptance width). Matched to the pixel grid of the published model, the diagonal spacing is 1.118× the axial one, and that distortion is reproduced. B how much neighbours overlap. At the fly’s ratio Δρ/Δφ = 1.78 the summed sensitivity ripples by 0.0052 % — no holes and no bumps across the field. C what a given acceptance angle throws away: the line is the closed form exp(−π²Δρ²/(4 ln2 λ²)), the dots are the operator, and they differ by at most 0.0017. D what happens with a grating finer than the sampling limit (λ = 2Δφ = 9.26°).

D is the part worth stopping at. The input is a 6.5° grating (D1). Narrow the acceptance to 2° and the row of ommatidia reports a coarse ripple that is not in the input at all (red, D2). That is aliasing, and its wavelength is predictable: λ = 1/ 1/6.5 − 1/4.63 = 16.1°. Measured: 16.2° (D3). At the fly’s own 8.23°, the same false grating is 223× smaller (0.357 → 0.0016).

So Δρ/Δφ ≈ 1.8 is not “blurry” — it is the acceptance angle tuned to the sampling limit so that false signal dies. Narrower looks sharper and invents patterns that were never there; wider kills those but dissolves the real ones too (the magenta curve in C).

This section comes first because before blaming the brain for what it could not read, you need to know what the eye already discarded — which is exactly what section 2 turns on. And none of this is fly-specific: change Δφ and Δρ and the same operators design and measure a bee’s eye, or an ant’s.

What follows is not a measurement but a hope. An event-based camera — a sensor whose pixels report brightness changes asynchronously — can be read as a silicon implementation of the very first thing the fly’s visual front end does: drop the DC and send only the change — the lamina’s L1/L2 band-pass. In the Fullseye PoC, skipping that one stage drops the rotation correlation from 0.785 to 0.504: the correlator starts emitting DC × high-pass jitter ten times larger than its mean motion response. If that is right, the part still missing sits one stage earlier, in the optics: a sensor whose inter-ommatidial angle and acceptance angle are design parameters, so that aliasing dies in the device rather than in post-processing. Today’s event cameras are square-gridded, with acceptance treated as an afterthought of the lens. A device that ships with a hexagonal lattice and Δρ/Δφ ≈ 1.8 built in would make the “inventing patterns that were never there” problem of panel D disappear with no downstream computation at all. The fly has shipped that design for half a billion years. If such a sensor appears, the way of measuring in this article moves to real hardware unchanged.

1. It passed every static test, then failed while walking

The first step was to calibrate the optic lobe output by rotating the fly in place. Three tests were built:

  1. Monotonicity —— does the output increase monotonically with rotation speed?
  2. Symmetry —— is it symmetric about the origin, left versus right?
  3. Latency —— is the rise time within a physiological range?

All three passed. The output was monotonic and origin-symmetric up to ±2 rad/s, with a 0.11 s latency (63 % rise). Across places the gain varied by only ±35 % after normalisation. At that point it was tempting to call the sensor usable.

Then the same quantity was read while walking. Correlation across every candidate readout: 0.06–0.51. A complete failure. The three tests that passed had predicted nothing about performance during locomotion.

The reason becomes clear in the next section, but the lesson is already fixed:

Put the test where the accident happens. A verdict obtained from static stimuli says nothing about performance under motion.

This is not specific to flies. A model with good benchmark accuracy that collapses once real motion enters your robot’s camera is the same failure, with the same shape.

2. Measure the world before suspecting the brain

The first suspects were the model (“maybe the pretrained weights do not fit this body”) and the readout expression. Both were wrong.

The cause was the statistics of the stimulus. Optic flow splits into two components:

The fly’s eyes sit 1.2 mm above the floor. Even at a walking speed of 2.8 cm/s, the floor directly below streams past at 330–1000°/s — far above the band T4/T5 can follow, burying the rotation signal in noise.

The fix was one line: read only above the horizon. Distant flow has a large denominator, so the translation term vanishes and only rotation survives. Walking self-rotation estimation then reached correlation 0.98.

Self-rotation estimated while walking

↑ Self-rotation during walking at place C, which was never used for any selection. Black is the imposed rotation, red is the estimate read out from T4/T5. Correlation 0.98, bias +0.022 rad/s — but the amplitude comes out at roughly 0.6× the truth (see section 7). Labels are in Japanese: the axes are rotation rate [rad/s] against time [s].

There is a second trap here. Reading above the horizon helps only if the distant background has texture at all. The first world had a smoothly graded sky, so everything above the horizon was nearly uniform and carried no cue whatsoever. Only after adding a cloud band at 20–60° elevation (1/f in azimuth, amplitude 0.12) did the 0.98 appear. Without the band: 0.06.

What the compound eye sees

↑ What the compound eye (721 ommatidia, 4.63° between optical axes) sees at that moment. Left: brightness per ommatidium. Right: the salience of “something dark above the horizon”. Whether the sky band exists changes the rotation estimate from 0.06 to 0.98. Figure labels are in Japanese; every number is repeated in the text.

A defect in the world looks exactly like a defect in the brain. Before measuring a model, measure whether the cue exists in the stimulus.

To be honest about it: adding texture to the sky is a convenient modification. The justification is that real outdoor scenes contain clouds, canopies and terrain silhouettes, and a perfectly uniform distance is the unusual case. Still, since the amount of texture was chosen here, 0.98 is a number that includes the world setting — it is not the model’s performance in isolation.

3. Being a good sensor and improving behaviour are different things

With rotation readable, the estimate was fed into steering. Three conditions, twelve runs each:

Closed-loop walking trajectories

↑ Twelve trajectories per condition. The coloured disc at the centre is the target; grey discs are distractors (gear, lattice sphere). Left to right: fixation only / + optomotor / + efference copy. Arrivals: 7 / 6 / 6.

(Of these three conditions, the one actually moving at the top of this article is “+ efference copy”. The approach to the brown target itself is the hand-written fixation behaviour; the optic lobe only contributes a steering correction — which is exactly the point of this section.)

The result: 7 / 6 / 6. With the disturbance tripled it became 7 / 4 / 6, meaning feedback without efference copy actively hurt (the agent’s own turns were mistaken for disturbance and cancelled).

So a sensor with correlation 0.98 changed behaviour not at all on this task. Fixation dominated; the optomotor path was redundant.

Measure and report sensor fidelity and behavioural success separately. One being good says nothing about the other.

This bears directly on objective design. Optimise for behaviour and the hand-written part wins, leaving the model almost stationary. Optimise for sensor accuracy and it overfits — which is the next section.

4. How much is the wiring actually worth?

The selling point of a connectome-constrained model is that the wiring is measured. So: how many points of performance is that wiring worth? Answering this requires comparing against a classical motion detector on the same stimulus with the same readout.

The comparison is the 1956 EMD (two neighbouring samples, delay one, multiply). It was implemented with operators from a self-built image-processing library, and its parameters (time constants for the delay and the high-pass) were chosen using training places only, leaving C and D unseen.

Motion detector A B C (held out) D (held out)
EMD on raw luminance (τ 0.05 s) 0.45 0.57 0.62 0.77
EMD + high-pass (τ 0.1 s, τ_hp 0.2 s) 0.79 0.88 0.84 0.78
flyvis (connectome-constrained, pretrained) 0.90 0.94 0.98 0.98

Two things follow.

  1. Preprocessing closes most of the gap. Adding the high-pass stage — the analogue of the lamina, the layer right after the photoreceptors — lifts the EMD from the low 0.6s to the low 0.8s. It is not “the connectome lets it see”; it is mostly removing the slowly varying part of brightness.
  2. The remaining 0.14–0.20 is the wiring’s share. ON/OFF pathway separation, normalisation and spatial integration earn that. Not zero, but not an order of magnitude either.

That number is directly usable: if 0.84 is good enough for your application, ten lines of EMD will do.

5. More degrees of freedom, less generalisation from little data

Now the main result. Keep the wiring fixed; evolve only the free parameters.

Setup: CMA-ES, 1500 evaluations, step size σ0 = 0.1, objective = correlation of walking self-rotation estimation on the training places. Held-out places C and D are never used for selection.

Round one of evolution

↑ Evolution with two training places. Left: training progress. Middle: held-out score before and after. Right: relative distance travelled from the pretrained weights. Starting point 007 improved on training while its held-out score went negative; starting points 000 and 001 never moved at all (distance 0). Labels are in Japanese; the numbers are all in the text below.

Two things happened.

Two levers were tried against this.

Lever A: more environments. Raising the training set from 2 places to 10 brought the same 734-dimensional evolution to C 0.887 / D 0.944 on held-out places. The overfitting disappears. A plain result.

Lever B: compress the degrees of freedom. This one is more interesting. The 734 free parameters live “per synapse”. A genome cannot hold per-synapse values — the most it can encode is something like rules per cell type (Zador’s genomic bottleneck, 2019).

So the search variables were folded into 260 dimensions:

Each synaptic strength becomes presynaptic gain × postsynaptic gain × pretrained value. Not a single new connection is created (attempting one is a hard failure).

Degrees of freedom versus generalisation

↑ Left: evolution (moving the brain’s parameters). Right: readout (brain frozen, solving linear weights). In both panels the vertical axis is the mean correlation on places C and D, never used for selection. Red is 2 training places, blue is 10. Figure labels are in Japanese; all values appear in the tables below.

In numbers:

Degrees of freedom 2 training places 10 training places
Free, 734 dims (per synapse) −0.25 +0.92
Bottleneck, 260 dims (per cell type) +0.71 +0.91

Two wins.

  1. It wins when data is scarce. Same two scenes, same starting point, same evaluation budget: the free search overfits into negative territory while the bottleneck generalises to +0.71. Cutting the degrees of freedom to a third reverses the ranking.
  2. It ties when data is plentiful. With 10 scenes: free 0.916, bottleneck 0.914. The same generalisation with one third of the freedom.

The same shape appeared without touching the brain at all, by solving the readout side (right panel) — a ridge regression from T4/T5 activity to self-rotation, with λ chosen by leave-one-out on the training places only:

Readout 2 training places 10 training places
Hand-written (0 parameters) +0.98 +0.98
Structured (cell type × elevation band = 80 weights) +0.96 +0.98
Free (cell type × column = 5768 weights) −0.03 +0.83

The 5768-weight fit memorised the scene outright on two places (held-out C 0.13 / D −0.19) and still had not caught up on ten (D 0.72). The 80-weight version, which only pools over elevation bands, reaches 0.96 with two places. Opening up its closed-form weights showed it had rediscovered the structure of the hand-written expression: T4a negative and T4b positive, concentrated in elevation bands 6–8 (exactly where the cloud band sits), essentially zero in the lower half.

An inductive bias wins when data is scarce and costs nothing when it is not — provided the axis you fold along is a real structural unit (cell type, elevation band).

Honest caveats. (a) The hand-written readout already sits at 0.98, so mathematical optimisation buys no performance on this task; what it bought was confirmation that the hand-written form is near-optimal, and an explanation of its structure. (b) The 260-dimensional cut is a design decision about granularity, not a deduction from biology. (c) The 2-place condition starts from 007, a starting point that is particularly bad for the free search; a different start changes the size of the gap.

6. Behaviour improves, biological realism degrades

One more quantity was measured at every step: direction selectivity (DSI) of T4/T5, probed with moving edges at six speeds.

Behavioural performance and biological realism dissociated cleanly. Reading self-rotation, as a single task, does not require T4/T5 to discriminate direction. Indeed, an individual whose T5 cells are almost non-selective still estimates self-rotation at 0.96 — T5 is redundant for this task.

For completeness, the untrained network (wiring only, parameters from the initial distribution) was also measured: direction selectivity 0/8, self-rotation estimation ≈ 0, insensitive to the random seed. The wiring does not supply the function; it supplies the scaffold that makes learning it fast.

Optimise for a single task and every property that task does not need will be removed. To keep a property, include a task that requires it.

7. Closing the loop: see → orient → move

Finally, the visual estimate was connected to navigation. The insect central complex contains a ring attractor representing heading (a bump travelling around a circle) and a path integrator accumulating the home vector. Both were implemented with closed-form checks (bound on phase error, closed-loop residual, monotonic approach on the homeward leg), and the flyvis angular velocity estimate was fed into the compass.

Correction (2026-09-15): the numbers in this section (7.4 / 23.5 / 19.8 %), the “error decomposition” and the “circling” explanation are retracted. The ring attractor had been used without calibration (second entry in §8). The calibrated numbers and the deceleration experiment are in the appendix, “Homing with deceleration”. The original text and figure are left as they were.

Homing from the visual pathway through the central complex (first version, uncalibrated)

↑ The first-version figure (uncalibrated compass). Left: a ground-truth compass (zero visual error). Middle: the raw visual estimate. Right: the visual estimate with its amplitude corrected by a walking calibration. Grey is the outbound path, colour is the homeward path, the star is the nest. Labels are in Japanese; the error percentages are in the text.

The visual estimate has only 0.63× the true amplitude, so naively multiplying by 1.54 ought to fix it. It does not — calibration recovers only as far as 19.8 %. Decomposing the error:

The residual dominates by 3.4×. Path integration accumulates error, so even an instantaneous correlation of 0.977 matters after a few seconds. In other words, the next effective lever is not calibration but raising the readout correlation itself — which ties this section back to the previous ones: environment diversity, compressed degrees of freedom and staged task curricula all feed straight into homing accuracy.

The 7.4 % that remains even with a perfect compass comes from holding forward speed constant, which makes the agent circle over the nest. Insects decelerate on approach; adding deceleration interferes with the memory update, so that needs its own experiment.

8. A retracted result

One conclusion here was wrong and has been withdrawn.

In an experiment that enlarged the eye (721 → 1951 ommatidia, same pretrained weights), the first write-up said: “if you add ommatidia without narrowing the acceptance angle, performance collapses — the optics must co-adapt.” A plausible story, and the numbers did collapse (correlations −0.11 to 0.45).

The cause was not optics. It was a tooling defect in which the second eye constructed inside the same process rendered a broken image — the giveaway was near-zero variance across columns and a comparison EMD returning nan. Rebuilt in a separate process, the same configuration behaved normally (0.96–0.99).

What actually holds: enlarging the eye does not break anything even without retraining; it improves slightly and saturates (0.98 → 0.98–0.99). Shrinking (331 ommatidia) does hurt. Acceptance-angle co-adaptation does not matter over this range.

When a result surprises you, suspect tool ordering before you reach for physics. The second object built in one process, the second call to a function, the second run that hit a cache — reproduce it in isolation before interpreting it.

Second retraction (2026-09-15). The homing numbers in §7 (ground truth 7.4 % / raw visual 23.5 % / amplitude-corrected 19.8 %) and what was drawn from them — “the error splits into 3.8 points of amplitude and 12.4 points of residual” and “the 7.4 % is the agent circling at constant speed” — are withdrawn. The ring attractor had been used without calibration, and its bump travelled 10.8 % faster than the angular velocity. Calibrated, the numbers are 1.7 % / 2.1 % / 12.4 %: the raw visual estimate gets home on its own, and the amplitude correction makes things worse (appendix, “Homing with deceleration”). The direction of the conclusion — the residual is what limits homing — survived, but every number that supported it has been replaced. It was found by the deceleration experiment: the agent stopped 9 cm from the nest, I almost called that “interference”, and the cause turned out to be calibration.

Check that a component was calibrated on the caller’s side, not the component’s. A calibration function existing and being called on this path are two different things. §7 used a calibratable part without calibrating it.

What to take away

Folded into forms that survive without the author, there are six:

  1. Put the test where the accident happens. A verdict from static stimuli does not predict performance under motion.
  2. Measure the world before suspecting the brain. If the cue is absent from the stimulus, no model can read it — and any performance number includes the world’s settings.
  3. Report sensor fidelity and behavioural success separately. One says nothing about the other.
  4. Cut degrees of freedom along structural units. Variables folded per cell type or per elevation band win when data is scarce and cost nothing when it is not — provided the fold matches real structure.
  5. Single-task optimisation deletes every property the task does not need. Include a task that needs what you want to keep.
  6. Surprising results: suspect tool ordering before physics.

None of these are specific to flies. They appear whenever someone else’s pretrained model is mounted in your own system.

What was not measured

Public models and papers this rests on

What this PoC uses from Fullseye, and what it gives back

The visual front end is built from the flyvision family (8 operators) in Fullseye, my own vision library. Every one of them is closed-form and ships with a ground-truth test:

op role where it appears here
fly_hex_lattice hexagonal viewing axes (n = 3R(R+1)+1) §0 panel A — the eye’s geometry
fly_hex_resample Gaussian acceptance onto the ommatidia §0 panels C and D (MTF, aliasing)
fly_sky_1f synthetic sky with a 1/f band §2, “texture in the distance”
fly_emd_response Hassenstein–Reichardt correlator §4, the zero point
fly_hs_readout wide-field opponent sum (HS-like) the shape of the readout
fly_dsi direction selectivity index §6, the 8-type test
fly_lgmd_eta / fly_tau_from_expansion looming (η and time-to-contact τ) unused here — measurable from the same eye

What goes back into Fullseye is two things. (1) A measurement bench: swap the motion detector and the stimulus, readout and scoring stay identical — which is the only reason section 4’s question (“how many points does a connectome-constrained model add over the textbook formula?” → 0.14–0.20) can be answered at all. Existing implementations can run a model; they are not built to line it up against a different detector on the same stimulus. (2) Counter-examples: the aliasing in §0 and the floor flow in §2 come back as a concrete demand — lift the operators’ tests from “a static synthetic image” to “measured while walking”.

Conversely, the body, the physics and the connectome model stay external dependencies (flybody / flyvis), and there is no plan to reimplement them. The value is in the bench you mount them on.


Appendix: later experiments

Where flies and vertebrates do the same job (2026-09-14)

“Which part of a human brain is this?” is a natural question. But the last common ancestor of insects and vertebrates lived 550 million years ago, and their visual circuits share no cell lineage. So every answer below is convergence, not homology — two lineages arriving at the same solution separately. Only correspondences that could be checked against primary literature are drawn.

Fly and vertebrate correspondences

↑ Fly on the left, vertebrate on the right. A dotted line means “a correspondence that is not homology.” The red row (photoreceptors) shares a role but not a lineage — and the response polarity is opposite as well: fly photoreceptors depolarise to light, vertebrate rods and cones hyperpolarise. The safest correspondence is L1 (ON) / L2 (OFF) with ON / OFF bipolar cells; the strongest computational match is the central-complex ring with the head-direction system, where a ring attractor has been measured on both sides. The lower right lists what was researched and then deliberately left out — an insect version of grid cells, or homology between the central complex and the basal ganglia, would be speculation. Figure labels are in Japanese.

This reframes the first half of the article slightly. Splitting ON from OFF in the lamina, multiplying two inputs of different dynamics offset in space, then pooling the result in wide-field cells to stabilise posture — none of that is “how flies do it”. It is a design adopted twice, independently, across 550 million years. The 0.14–0.20 measured in §4 is then the premium that having that design in measured wiring buys over writing the textbook equation yourself.

Shuffling the wiring changed nothing — and “nothing” is not what I get to write (2026-09-14)

§4 measured the wiring’s share by swapping the wiring underneath pretrained weights: 0.14–0.20. This is the harder version of that question. From the same untrained initialisation, evolve the real wiring and two degree-preserving shuffles of it on the same task with the same budget (734 dimensions, 1500 evaluations, about three hours per condition). Starting untrained removes the confound of the pretrained point.

Condition Train Held-out mean C D
Real wiring +0.196 +0.171 −0.292 +0.633
Shuffle 0 +0.205 +0.198 −0.138 +0.535
Shuffle 1 +0.145 +0.039 −0.215 +0.294

Real minus the shuffle mean is +0.052 on held-out scenes. But the two shuffles differ from each other by 0.159 — three times that. This experiment does not have the sensitivity to measure what it was built to measure.

Worth recording is where this nearly went wrong. With only the first shuffle finished, the numbers said the shuffle was slightly better (−0.028) — exactly the shape that invites writing “the wiring is worth nothing”. The second shuffle flips the sign. At n = 1 you cannot trust the magnitude of a difference, and you cannot trust its sign either.

Three reasons the sensitivity is missing. (1) The search had effectively collapsed to biases alone. Normalising each tensor by its standard deviation drove the scale to 3.75e-09 for a near-constant initialisation (all time constants start at 0.05), and the guard only tested std > 0, so it sailed through. In all three conditions the time constants moved by 1e-8 — not at all. An effective floor now fixes this, but the three runs above are from the old code, so the caveat stays. (2) One seed per condition: no error bars. (3) As §6 shows, direction selectivity is 0/8 before training — wiring alone produces no function, so any difference due to wiring may be swamped by differences in learning before it can appear.

The next step is not to write a conclusion; it is to raise the sensitivity — floored scales, at least three seeds per condition, and more shuffles.

A staged curriculum can build direction selectivity — and the last stage destroys it (2026-09-14)

§6 reported that optimising for behaviour alone breaks direction selectivity — the property that makes T4/T5 discriminate motion direction. The obvious countermeasure is to demand discrimination explicitly, in stages: brightness → ON/OFF → direction → rotation, 400 evaluations each, every stage starting from the previous stage’s solution. Two starting points were run.

Stage From untrained From the trained start (007)
S1 brightness 0.353 1.000
S2 ON/OFF 0.796 0.964
S3 direction (DSI) 0.002 0.377 (5 of 8 types pass)
S4 rotation (M4) 0.061 0.799 (held-out C 0.823 / D 0.811, 1/8 pass)

Two things follow.

(1) The curriculum can build direction selectivity — but only from a trained starting point. From 007 the direction stage takes it from 0/8 to 5/8. From the untrained start, four stages of climbing leave it at 0/8, and the direction stage itself scores 0.002 — no climb at all. Wiring is the scaffold, but you cannot climb the scaffold alone. That is the same statement as the shuffle appendix above and as §6’s “0/8 before training”, arrived at from a third direction.

(2) What the curriculum builds, the final behavioural stage destroys. The 5/8 earned in the direction stage falls to 1/8 once the rotation stage is learned — while rotation estimation itself climbs to 0.82 on scenes never used. The performance-versus-realism trade-off is running inside a single search, over time. Earlier stages decay too (ON/OFF 0.874 → 0.536).

The remedy is known — make stage k’s objective the mean over stages 1…k. It has now been run; the result is in “Forgetting disappeared when the objective was written differently” below — point (2) of this section is one I go on to overturn myself.

One more thing: this is the first experiment to run after the scale collapse described above was fixed. Its report file records “the time-constant scale was 3.75e-09, so the floor of 0.001 was used” — the fix is visible in the output, not just in the diff.

Looking at the fly’s own world through other sensors (2026-09-14)

§0 designed the optics. Here is what that eye actually sees: the walk replayed, rendered from the eye camera that the published Drosophila model already carries (140° field of view).

Fly's-eye perception

↑ From the left: ① the fly in its world (the small orange dot at centre), ② the view from its compound eye (the thick bars in front are its own legs, the black combs above are the antennae, the mass past the horizon is the target), ③ depth (blue-green near → amber far; the sky is excluded from the colour range), ④ synthetic events (pixels that brightened in blue-green, pixels that darkened in red). One viewpoint, three sensors, at once.

Panel ④ connects back to §0. As the fly advances, the floor texture streams outward from below and shows up as events — the same flow that §2 measured at 330–1000°/s, past the passband of the motion detectors. An event camera transmits only change, so that flow is the sensor’s output.

And the interference pattern on the floor in ① and ② is not a rendering defect — it is the aliasing measured in §0. The floor texture is finer than the pixel spacing, so the renderer’s sampling invents a coarse pattern that is not there. Exactly what D3 predicted and measured by wavelength, happening in an ordinary render: any eye that tries to see detail finer than it samples will manufacture detail that does not exist.

Honest notes: the body’s trajectory is the real one the closed loop produced, but the leg motion is a cosmetic tripod gait — it is not walking by leg dynamics (as the first table in this article says). Depth and events are synthesised from the render, not the output of a physical sensor.

Which layer, when silenced, breaks self-rotation estimation (developmental ladder, 2026-09-15)

Silencing the visual pathway stage by stage, from the bottom up, and lining up where self-rotation estimation (M4, §5) breaks. 34 conditions — a control, photoreceptors R1–R6, lamina L1–L3, nine medulla Mi/Tm types, T4/T5 in four directions each, and six bundled stages. Each condition pins the output of the named cell types to zero and measures 12 scenes on the same stimulus. Eight threads, 3 h 23 min, 34/34 completed.

Stage silenced Worst condition Correlation Drop from control
(control) none 0.9487 —
Photoreceptors R1–R6 R6 0.5742 0.375
Lamina L1–L3 all, bundled 0.4853 0.463
Medulla Mi/Tm all, bundled -0.0057 0.954
T4/T5 T4, bundled 0.5182 0.431

Self-rotation estimation is concentrated in the medulla. Drop that layer entirely and the correlation falls to -0.0057 — essentially no correlation. Dropping photoreceptors, lamina or T4/T5 costs only 0.37–0.46 by comparison. Among single cell types, Mi1 (0.031) and Mi4 (0.064) matter most — both ON-pathway relays. T4/T5 are the textbook carriers of direction selectivity, yet for rotation estimation they are not individually decisive. 24 of the 34 conditions fell at least 0.01 below the control.

But only half of this experiment measured anything. Direction selectivity (DSI) was not measurable in all 34 conditions — about 4 million of 10.09 million response elements were non-finite. And since even the control, with nothing silenced, went non-finite, the cause is not the silencing but the response generation itself. The fix made last time — record a condition as “not measurable” and carry on — only made the failure report correctly; it did not fix the non-finite values. A fix landing is not the same as an experiment working.

One lesson. Ride two metrics on a single run and one metric’s failure invalidates every condition. M4 came back 34/34; DSI was lost 34/34. Metrics should run independently, or at minimum the control condition should establish that the metric itself is sound before the sweep starts.

Addendum (same day, afternoon) — what the non-finite values were. The smallest reproduction (model 000, nothing silenced, no hook registered, just two speeds, 19 and 25) shows the speed-25 responses going non-finite from frame 465 on, with zero non-finite values inside the frames where the stimulus itself is finite. The primary source is flyvis’s moving_bar.py, _resample: stimuli of different lengths per speed are padded to the slowest one’s length with NaN (the code says so in a comment), and the network carries the NaN forward with the layer delays. At six speeds that is 62,616 elements per column, structurally — matching the control’s 4,057,080 of 10,090,080 in the full run, and explaining why silenced conditions had exactly 62,616 fewer. flyvis’s own DSI reads only its time window and is unaffected; what was broken was the ladder’s “every element finite” gate. A smoke test at speed 19 alone has no padding, so it could never have found this. The gate now checks only frames where the stimulus is finite; the selftest matches the stored references for all eight cell types with ΔDSI 0.0000. The 34-condition ladder goes back into the queue, DSI included. Lesson: a “must be finite” gate raises false alarms unless it knows where the tool uses NaN on purpose.

Forgetting disappeared when the objective was written differently (2026-09-15)

The previous section reported that a staged curriculum can build direction selectivity but that the final behavioural stage destroys it (5/8 → 1/8). The proposed remedy — the cumulative objective, where stage k is scored as the mean over stages 1…k — has now been run from the same starting point, with the same budget and the same tasks.

Stage Contrast ON/OFF Direction Selectivity passing
S1 1.000 0.372 0.123 1/8
S1+S2 1.000 0.997 0.056 1/8
S1+S2+S3 0.987 0.979 0.365 4/8
S1+S2+S3+S4 0.999 0.961 0.392 6/8

Rotation estimation at the final stage, on scenes never used: 0.875 / 0.814.

The forgetting is gone — and behaviour did not pay for it. Staging alone (non-cumulative) ended with selectivity at 5/8 → 1/8 and ON/OFF at 0.874 → 0.536. With the cumulative objective, selectivity goes 4/8 → 6/8 — it increases — and ON/OFF holds at 0.979 → 0.961. Rotation estimation matches or beats the earlier run (0.823 / 0.811).

So the performance-versus-realism trade-off was not a law; it was a way of writing the objective. The previous section’s observation was right as a phenomenon, but I had not read the cause correctly — what broke it was not keeping the property I wanted in the final stage’s objective.

The cost is explicit. Each evaluation measures every stage, so the final stage takes 239.8 s per generation — about 7× the non-cumulative run, 140 minutes in total against roughly 40. Evaluation gets heavier in proportion to how many properties you insist on keeping, which makes “what do we keep” a design decision to be made out loud.

Homing with deceleration — both things that looked like “interference” were something else (2026-09-15)

§7 ended with “adding deceleration interferes with the memory update, so that needs its own experiment”. Running that experiment showed not interference but that the numbers in §7 were wrong.

First: the compass had not been calibrated. With deceleration (forward speed = v0 · H / r_slow, where H is the length of the home vector the memory decodes — nothing the brain does not have), even the ground-truth compass stopped 31–41 % of the outbound distance from the nest (worse than the constant-speed 7.4 %), while the memory said “home” and the true position was 9 cm away. Tracing it back: the ring attractor in §7 had been used with its default gain (g = 1), and its bump travelled 10.8 % faster than the angular velocity (1.000 after calibration; the pure time lag is 0.002 rad). The first homeward turn of ≈ π rad puts it 0.34 rad off. Re-measuring §7 with the compass calibrated:
Condition §7 as first published (uncalibrated) Calibrated
Ground-truth compass 7.4 % 1.7 % (home)
Raw visual estimate 23.5 % (not home) 2.1 % (home)
Amplitude corrected by gain 1.54 19.8 % 12.4 % (worse than raw)

Homing after calibration

↑ §7 after calibration. The raw visual estimate gets home (middle). The amplitude correction throws the homeward leg off (right).

§7’s “3.8 points amplitude + 12.4 points residual”, “the lever is correlation, not calibration” and “the 7.4 % is circling” were all products of that missing calibration and are retracted (second entry in §8). The calibrated reading: the 0.63× amplitude only matters through the net rotation (−0.26 rad on this outbound path, against 4.6 rad of total turning), whereas the least-squares gain of 1.54 also amplifies the part of the noise that accumulates into heading: the instantaneous residual (std) falls from 0.226 to 0.119 rad/s, but the integrated heading error ∫(ŵ − ω)dt rises from 0.124 to 0.210 rad rms, and the memory error at the turning point from 2.8 cm to 5.3 cm. Path integration only cares about the latter. What decides homing is the residual that accumulates into heading, not the amplitude — the conclusion points the same way, but the numbers behind it are different ones.

Second: an unstable equilibrium in the rule. With the compass calibrated, deceleration ran away at exactly r_slow = 2 cm, stopping 6.9 cm out. Not a measurement hole but a hole in the rule: steering w = k H sin ε (ε the angle to the nest from the heading) and speed both scale with H , so the path curvature w / v does not depend on H at all — **the moment the agent comes to rest facing away from the nest (sin ε ≈ 0) it does not turn, walks straight off at the speed its residual memory error dictates, and the further it walks the larger H gets and the faster it goes** (exponential, time constant r_slow / v0). Which r_slow triggers it depends only on the heading at the moment of stopping; 2 cm was luck. Adding “walk only while the nest is ahead” (multiply v by max(0, cos ε) — the fly’s body saccade: no walking mid-turn) stops it at every r_slow: 0.03 cm for the ground-truth compass, arrival time monotonic in r_slow (10.8 → 25.1 s), and for the visual conditions the stopping error, 0.59 cm (raw) / 3.81 cm (gain-corrected), coincides exactly with the error the memory holds at the moment of stopping.

Homing with deceleration

↑ Left: sweep of the deceleration radius r_slow. Dotted (no gate) runs away at 2 cm; solid (walk only while the nest is ahead) stops everywhere. Dashed lines: distance after 30 s at constant speed. Middle: stopping error against the memory’s own error at the stop (on the identity line). Right: the homeward leg at r_slow = 2 cm (markers are the stopping points). Labels are in Japanese.

Deceleration does not interfere with the memory update. Deceleration hides nothing: it writes the error accumulated in memory straight into the stopping position — and since the constant-speed “closest approach” was an optimistic number (the track happened to pass near the nest), deceleration is the more honest metric. The experiment ran four times and three of its pre-registered predictions failed; the record is kept in the script’s docstring.

Freezing the front end and caching every cell type — half the signal was the border (2026-09-19)

As the first step towards replacing the hand-written readout (ratio_relu_up in §2) with an evolving head, I froze the optic-lobe model and cached it: the walking-while-turning stimuli of 12 places × 350 frames go through the trained model 000 once, and every node’s activity is stored — 45,669 nodes (63 types × 721 columns plus Lawf1 / Lawf2 × 123 columns; “65 types × 721 columns”, as I had been writing, was an approximation that the gate caught on the first run), 688 MB, 94 s. From here on, evolving or distilling the head is pure array arithmetic. The hand-written readout computed from the cache matches the original evaluation (a gate); held-out correlations are C 0.976 / D 0.977.

activity raster of 65 cell types

↑ Activity of the 65 optic-lobe cell types while walking and turning at place C (rectified mean per type, z-scored); the top trace is the true rotation. The photoreceptors R1–8 move together with brightness, and T4a / T4b light up alternately with the sign of rotation. Labels in the figure are Japanese.

column activity while walking

↑ The 721 columns (hexagonal coordinates) over the same interval: input R1 → the convergence point Mi1 → the opposing motion detectors T4a / T4b. Brighter = stronger (a still frame is f1_hex_walk_C_static.png).

Drawing the figure revealed something the numbers had hidden. T4 / T5 activity looked confined to the rim of the hexagon, so I counted: the 90 border columns (12 % of 721) carry 41–73 % of the rectified sum (up to 81 % above the horizon), with mean activity 8–20× that of the interior (Mi1: 13 %, i.e. normal). Border columns lack lateral input — a boundary artefact. Dropping the border, the hand-written readout’s correlation goes A 0.900→0.880 / B 0.941→0.944 / C 0.976→0.970 / D 0.977→0.952, so the function survives, but more than half of what §2 called “the sum of motion detectors over the whole field” was the boundary. Not a retraction — a caveat that stays — and the head’s input and the distillation teacher will use the 631 interior columns.

hex maps of T4/T5

↑ T4a–d / T5a–d at the frame of maximum rotation. With the colour scale set by the interior 99th percentile the interior stripes become visible; the 90 border columns saturate (yellow).

The zero points were set on the same cache. A linear regression on the 4 hand-written ingredients reaches held-out C 0.980 / D 0.990; 122 per-type features give D 0.961; 366 type × side features give D 0.948 — more features, worse hold-out (§4’s “fold the degrees of freedom by structural unit”, again). The evolving head must beat 0.98–0.99, or else compete on what it preserves.

Counting “left/right gaps” in the male whole-CNS connectome — only a mushroom-body subtype (2026-09-19)

The optic-lobe model in this PoC is one right eye. Before stacking left and right as mirror images, I asked how symmetric a real brain is and whether anything is missing, using the freshly released MaleCNS v1.0 (Janelia FlyEM × Google Research, male, brain plus ventral nerve cord, over 166,000 neurons, CC-BY 4.0). The annotation table (211,577 bodies) and the connectivity table (150 M rows) were downloaded and aggregated locally (the data is not in the repository).

Quantity Value
Neuron-like bodies 166,283
Soma side L / R / M 73,999 / 74,223 / 392
Of 11,229 types with somata on either side, |L−R| ≤ 1 10,789 (96 %)
Significant asymmetry (binomial test, Bonferroni) exactly one type, KCab-s (L 234 / R 423)

left/right counts of KCab

↑ Left: published labels. Right: labels re-drawn jointly from both sides using connectivity fingerprints. The gap shrinks but does not vanish. Data: MaleCNS v1.0 (CC-BY 4.0). Labels in the figure are Japanese.

There are no left/right gaps at the level of cell counts; the one significant asymmetry sits at the boundary between the Kenyon-cell subtypes KCab-s / m / c (KCab totals 885 / 925, all KC types 2,018 / 2,045 — symmetric). Four completion methods: (1) averaging the counts — fills the numbers but says nothing about which cells; (2) a classifier trained on one side applied to the other — 0.78 within-side accuracy, 0.48–0.61 agreement across; (3) side-agnostic consensus labels trained on both — |L−R| shrinks s 189→151 / m 84→64 / c 66→46 but remains; (4) nearest neighbours across sides by cosine similarity of connectivity profiles (the idea behind the public tool coconatfly) — 0.55 agreement. Reading: the connectivity fingerprints themselves lean towards s on the right; whether that is a real difference in this individual or a confound from partner-type labels cannot be decided from this data alone. Recorded as a subtype asymmetry, not a gap.

Same-day addendum (second implementation): the same 1,810 KCab cells were re-clustered with the public tool coconatfly (R, natverse: the cosine matrix from cf_cosine_plot, then ward.D into 4 clusters). Agreement with the published labels is L 0.88 / R 0.84 (higher than the home-grown classifier’s 0.72 / 0.76); agreement with the home-grown consensus labels is 0.72, and the disagreements sit at the c / m boundary. The s connectivity cluster is L 201 / R 352 — about 150 more on the right, independent of the implementation. One step further in the reading: the left/right difference of s in connectivity space is real, while the c / m boundary moves with the method (part of the subtype asymmetry is where the line is drawn). Two implementations agreeing may still mean the same hole seen twice, so the third fingerprint will be morphology, not connectivity.

The whole brain (added 2026-10-18): here is the brain that was counted above. From the soma coordinates in the same annotation table, the 126,104 brain cell bodies (VNC and ascending neurons excluded) are drawn one point per cell and coloured by superclass: blue = optic-lobe intrinsic (81,055), orange = central-brain intrinsic (31,997), purple = visual projection from the optic lobe to the central brain (9,162), green = descending neurons from the brain to the VNC (1,308). The black holes in the central brain are the neuropils (bundles of wiring) where no cell bodies sit.

The male fly brain, 126,104 cell bodies

↑ Front, side and top views at one common scale (labels in the figure are in Japanese). Data = MaleCNS v1.0 (Janelia FlyEM / Google Research, CC BY 4.0); rendered with Fullseye’s points_render.

The brain turning

↑ One turn about the dorso-ventral axis (36 frames, each a single call of points_render).

The picture is drawn with a Fullseye op. Until now Fullseye had no op that turns a point cloud into a colour picture, and the same brain printed on the back of the ViEW 2026 business card was placed point by point in hand-written numpy. The new points_render splats sub-pixel footprints in depth order or adds them conserving the total light. A pretty picture does not show that it is right, so it is scored against values the renderer never computes: 99.5 % of the cell bodies labelled L land in the right half of the front view (seen from the front, the fly’s left is on the right), and 4,050 Kenyon cells picked by type name sit dorsal and posterior of 437 antennal-lobe neurons. The column sums of the front and top views (two different cameras) agree, mirrored, to 1.8e-15. The PoC is examples/poc_flybrain_pointcloud.py.

Is the worm’s wiring left/right symmetric? — Testing the ruler on a brain whose wiring is complete (2026-09-27)

Before measuring “left vs right” or “individual differences” in the fly brain, I tested the ruler itself on the smallest brain whose wiring is completely known. The C. elegans hermaphrodite has 302 neurons and its chemical-synapse wiring is fully public (Cook et al. 2019, wormwiring.org; the data is not in the repository).

The question is simple: if every left/right pair (98 pairs such as AVAL and AVAR) is swapped, does the wiring stay the same? If it does, it is perfectly symmetric. I measured the overlap (Jaccard) between the swapped wiring and the original.

Quantity Value
Neurons / directed chemical connections / L-R pairs 300 / 3,669 / 98
Overlap with the L/R-swapped wiring (Jaccard) 0.473
If perfectly symmetric 1.000
Chance: rewired keeping every in/out degree (20 samples) 0.080 ± 0.002 (z 159)

Closed form vs the op for the L/R swap

↑ The ground truth is a counting closed form. Take a perfectly mirrored synthetic wiring (m edges per side) and move k right-hand edges: the overlap is exactly (m−k)/(m+k). The op matched it at all 16 steps, down to the integer numerator and denominator. (Figure text is in Japanese.)

The worm sits exactly between perfect symmetry and chance: half of the wiring is mirrored and half is not. I expected the asymmetry to be concentrated in a few pairs; it was not. The 10 most asymmetric pairs hold 20 % of the asymmetric edges, against 10 % if spread evenly — only about twice uniform. The leading pairs are HSN 60 %, PVN 57 %, RMG 53 % and URX 48 %.

Asymmetry per pair

The ops used are Fullseye’s graph_swap_symmetry (Jaccard against the pair-swapped wiring) and graph_degree_preserving_null (chance that keeps every degree).

When a neuron’s tree is measured from an image, how far is it from the real tree? (2026-09-27)

A connectome is built by cutting neuron “trees” out of electron-microscope images. So I went the other way: take the real tree as ground truth and measure what image-based measurement breaks. The material is three mouse neocortex reconstructions from NeuroMorpho.Org (SWC format, CC BY 4.0, RRID:SCR_002145). SWC carries structural promises — one root, a parent’s id is smaller than its child’s, nodes = edges + 1 — and those promises are themselves a gate (a line that breaks them fails on load).

The same tree was viewed from 12 directions and put through what image measurement usually does: project, then thin.

Quantity 3-D (the real tree) Projected image
Sholl analysis (intersections with concentric spheres / circles) not a single integer moves over 12 rotations moves by up to 9–16 with direction
Branch points true 16 / 11 / 37 22–28 / 15–24 / 41–49
Skeleton length / real cable length 1 0.72–0.82

Projected vs 3-D Sholl

Branch points vs viewing direction

↑ The extra branch points are branches in front and behind that cross on the image and turn into “branches” when thinned.

The accuracy of tree measurement was set by the viewing direction, not by the tree. When dendritic branches are counted from a single microscope image, “where it was seen from” is mixed straight into the count.

How much of the brain’s wiring do 8 genetically identical worms share? (2026-09-27)

After left vs right comes animal vs animal. Witvliet et al. 2021 (Nature 596:257) reconstructed the brain (nerve ring) of 8 genetically identical worms by electron microscopy, from just after birth to adulthood (data at nemanode.org; it carries no explicit licence, only a request to cite, so it is not in the repository). I stacked the 8 wirings on the 183 cells present in all 8 and counted how many worms carry each connection.

The 8 wirings in developmental order

↑ The 8 wirings shown in order from just after birth to adulthood. Each dot is one connection (row = sender, column = receiver). The colour is how many of the 8 worms carry it: yellow = all 8, purple = only one. The newborn’s wiring is almost all yellow; purple and blue dots (connections only some worms have) accumulate as the animal grows.

Quantity Value
Connections per worm (newborn → adult) 768 → 1,792
Connections in any of the 8 (union) 2,977
Connections present in all 8 442
The same by chance (each worm rewired keeping its degrees, 20 samples) mean 0.0, max 0
Synapses (summed over the 8) on connections present in all 8 57 % (15 % of the connections)
Overlap (Jaccard) of the two same-age adults 0.53
Overlap of neighbouring stages / newborn vs adult 0.51 / 0.33–0.34
Connections that appear and stay to the end / that disappear 701 / 55

How far above chance is the shared core

The core is few connections but most synapses

There are two readings. The core shared by everyone cannot be explained by chance (chance gave 0 in all 20 tries), and although it is only 15 % of the connections it carries 57 % of the synapses — the thicker a connection, the more it is conserved across animals. At the same time individual differences are large: two adults of the same age overlap by 0.53, no more than a 5-hour-old and an 8-hour-old larva.

Age difference vs overlap

Against the published value (not fitted): the paper calls a connection stable when it is present in at least 7 datasets and reports about 43 % of adult connections as stable. The naive count here gives 34 %. The whole 9-point gap could be split by matching against the per-connection classification table the authors published.

Counting rule (adult #7, 1,746 connections on the 183 cells present in all 8) Share stable
① a cell-level connection present in at least 7 worms (this article’s naive count) 34.5 %
② if the left/right-pooled connection is present in at least 7 worms, label every cell-level connection of that pair stable 48.9 %
③ the paper’s table (variable and “developmentally changing” connections removed first) 45.4 %

The paper labels the way ② does. Of the 792 connections the paper calls stable, 789 (99.6 %) are reproduced by ② alone. A connection present in only 6 or fewer worms at the cell level still counts as stable once its left/right partner connection is added. The 3.5 points from ② down to ③ are the priority rule that assigns some of those connections to variable or dynamic first. Counting on all cells with only the post-embryonic cells removed, instead of the 183, gives 43.2 % — matching the published ~43 %.

I first nearly wrote “37 % after pooling pairs”, but that was a different quantity with the denominator also switched to pooled connections. The paper puts a pair-level label on cell-level connections. The two differ only in what counts as one connection; neither is wrong.

Against the published value, with the breakdown

(Figure text is in Japanese, as elsewhere in this article: rows ①–③ are the three counting rules above.)

The ground truth is a closed form on a synthetic series: with a core of C connections shared by everyone and u unique, non-overlapping connections per worm, “present in all” is exactly C and every pairwise overlap is C/(C+2u). The op matches this down to the integer before touching real data. This round added graph_edge_consensus to Fullseye (stack K individuals’ wirings and return per-connection occupancy, pairwise overlap, synapse share, the developmental split and a per-individual null). The same op compares “wiring present in every unit / only in some” across many units built from one design.

How much does a hand-traced wiring diagram from 40 years ago overlap today’s adults? (2026-09-27)

The origin of worm wiring diagrams is White et al. 1986 — the first complete wiring traced by hand from electron micrographs; one adult (N2U) is on nemanode.org. I stacked it with the two 2021 adults of the previous section (datasets 7 and 8) on the same cell names (215 cells in common). The question: how large is the difference of era and method, compared with the difference between individuals?

Pair Overlap (Jaccard)
The two 2021 adults (same method) 0.508
1986 N2U vs the 2021 adults 0.431 / 0.442
Connections present in all 3 1,015 (degree-preserving null: 28 on average = 36×)

Across era and method, the overlap is only 0.07 lower than between two animals done the same way — small next to the individual difference of 1 − 0.508 = 0.49. What does the 1986 diagram lack? The 451 connections present in both 2021 adults but absent in 1986 are thin — 2.0 synapses on average (connections present in all three average 5.7).

What 1986 missed is thin

↑ x-axis = the summed synapse count of the two 2021 adults (figure text in Japanese). Connections missing in 1986 (blue) cluster around a sum of 2. My first version rounded the two animals’ mean and produced an odd/even zigzag — the trace of numpy’s round-half-to-even (2.5 → 2), not biology. Counting the integer sum removed it.

Honest breakdown: the N2U on nemanode was supplemented with muscle information by the Zhen lab in 2020, so “the era difference” also contains re-annotation. White 1986’s other animal (JSH) is an L4 larva and is left out of the adult comparison. The only op used is graph_edge_consensus, added in this round.

How much do a worm’s neurites grow from birth to adulthood? (2026-09-27)

After the wiring (who synapses onto whom) comes the shape. For the 8 worms of Witvliet 2021 the skeleton of every neuron’s neurites (a branching line) is public too, and the paper says the total neurite length grew 5-fold from birth to adulthood. I measured it again with the three neuron-tree ops added to Fullseye this round: tree_from_swc (reads SWC and refuses a tree that breaks its promises — one root, a parent numbered below its child, nodes = edges + 1), tree_morphometry (branches, tips, cable length) and tree_sholl (Sholl analysis).

Stage Skeletons Total neurite length
L1 at birth 196 2,806 µm
L1 16 h 205 4,544 µm
L3 27 h 210 6,396 µm
Adult #8 218 12,038 µm

Total neurite length

(Figure text is in Japanese.)

Adult over newborn is 4.29-fold (3.86-fold on the 195 cells present in all 8) — the same order as the paper’s 5, but not 5. Summing the length the authors wrote into the skeleton files gives 3.95-fold, also not 5. I read this as: the paper’s 5-fold is not a plain sum of this file (the cells or the region counted differ). The L3 total is close to L2 because of specimen shrinkage; the authors’ code scales L3 lengths by 1.1.

Two layers of checking. First, all 1,727 trees (the 61 skeletons that break into fragments are measured per fragment and summed) passed the structural promises and the Sholl closed form (area under the curve = sum d_child − d_parent ). Second, as a second implementation, on the 1,586 single-fragment skeletons the op’s “longest distance from the root along the branches” matched the per-node distances the authors wrote in the file to a relative 1.75e-9.

Getting there, I rebuilt the gate three times:

  1. I nearly used the authors’ “length” field as ground truth — and 1,360 of 1,586 disagreed. That field is not the sum of segment lengths (median 0.89 of it); it was computed upstream with a definition this repository cannot confirm, so it is not a gate.
  2. The per-node distance field also lists nodes without coordinates (54 of 196 skeletons in the first animal). Comparing only nodes with coordinates, they match.
  3. Writing coordinates to SWC with 3 decimals shifted the longest path by 1.9e-6; with 6 decimals it drops to 1.75e-9.

Sholl curves of AVAL

↑ Sholl curves of the neuron named AVAL at four stages. The x-axis is the distance from the skeleton’s start point (not necessarily the cell body). In the adult the process reaches 25 µm out. Being 3-D Sholl, the curve is the same from any viewing direction — unlike the section above on measuring trees from images, where the projection moved by 9–16 crossings with the view.

Scoring a neuron segmentation from electron microscopy as over-splitting and over-merging (2026-09-27)

Every wiring diagram so far was built by cutting neurons, one by one, out of electron-microscope sections. That cutting is now automatic, and it can go wrong in only two ways: a split cuts one neuron into two, a merge glues two different neurons together. A split can be repaired later by joining the pieces; a merge puts synapses into the diagram that do not exist. So the challenges (CREMI / SNEMI) do not score “how wrong” as one number — they report the two separately.

This round adds three scoring ops to Fullseye: seg_contingency (the contingency table of truth against candidate), seg_variation_of_information (VOI returned in bits as split = over-splitting and merge = over-merging) and seg_rand (the Rand index, the adjusted Rand index and CREMI’s adapted Rand error). The data is CREMI sample A (electron microscopy of a fruit-fly brain, z = 40, 512 × 512).

The check is a closed form. Using the existing seg_inject_split / seg_inject_merge, I inject one error at a time into the ground truth. Cutting a region of m pixels into m1 and m2 raises split by exactly (m/N)·H2(m1/m) bits and leaves merge at 0 (H2 is the binary entropy); a merge does the opposite. All 4 splits and 4 merges agree with the formula to below 1e-12. The formula holds exactly for any division of any real data, so the gate stands on the real ground truth, not on synthetic data.

Next I scored a classic segmentation (membrane response → threshold → connected components → membrane pixels assigned to the nearest cell).

split and merge by threshold

Threshold (share of pixels taken as cell) split (over-splitting) merge (over-merging)
60 % 1.14 0.18
65 % 0.91 0.35
85 % 0.05 5.02

Raising the threshold fattens the cells until they start to join their neighbours through gaps in the membrane: split falls, merge rises, and they cross between the 65th and 70th percentiles. VOI (split + merge) is lowest at 65 %, just before the crossing, at 1.263 bits. One number stops at “65 % is best”; two numbers also say “even the best point is still on the over-split side”.

EM section, ground truth, classic segmentation

↑ From the left: the EM section, the ground-truth labels, and the classic segmentation at 65 %. Holes and small fragments left inside the cells are what split counts.

The conclusion flipped once on the way. In the first version every threshold looked over-merged. The cause was a preprocessing step in the scoring: membrane pixels were left as background (label 0). The whole background then counts as one huge “region” that glues dozens of true cells together. At the 70th percentile merge was 2.07; with membrane pixels assigned to the nearest cell it is 0.85 — a 2.4-fold inflation. The segmentation is the same; one scoring convention turns over-splitting into apparent over-merging.

The second implementation had a defect of its own. Checking against scikit-image’s adapted_rand_error, the error agreed but precision and recall came out swapped: scikit-image’s code divides its precision by the truth pairs, the opposite of its docstring. The F-score is symmetric, so the error is right; only separate precision and recall disagree. Fullseye’s seg_rand states the mapping in its docstring and the tests pin it explicitly.

Where segmentation errors break the wiring diagram (2026-09-27)

The scores in the previous section count pixels. But a connectome wants a wiring diagram, not pixels, and the diagram is read by dropping synapse annotations (a pre point and a post point) onto the segmented neurons. So segmentation errors turn into wiring errors — but not all of them. Cutting a neuron where it has no synapses leaves the diagram unchanged.

This round adds two wiring ops to Fullseye. seg_synapse_partners reads which neuron each end of a synapse falls in, returning the wiring diagram the segmentation implies. seg_wiring_variation retakes the pixel VOI at the 2n synapse ends only. CREMI sample A comes with 216 annotated synapses; I used the 115 (107 connections) whose two ends both fall in a block of 125 slices × 625 × 625.

Taking the VOI at the synapses only is not new: Plaza, Scheffer and Chklovskii defined it as the synapse VI in 2014 (Focused proofreading, arXiv:1409.1199), split into the same two terms, and implemented it in NeuroProof (C++). What this round adds is a numpy-only implementation, the connection level, the experiment that injects one error at a time and compares pixel and wiring damage in one unit, and the proofreading-order comparison. NRI (Reilly 2018) is an F-score over pairs of ends and ERL (Januszewski 2018) a run length along skeletons; neither is information-theoretic.

The experiment: into each of the 54 neurons carrying synapses, inject one error at a time — cut it in half at its median x, or glue it to the neighbour it touches most — and compare pixel VOI with ends VOI. The check is a closed form: when a neuron’s s ends divide s1 / s2, split rises by exactly (s/2n)·H2(s1/s) bits. All 108 cases matched it (largest error 1.4e-17).

pixel errors against wiring errors

  54 splits 54 merges
Change the wiring by not a single bit 23 5
Wiring damage per pixel bit (median) 0.58 1.30

As a proxy for wiring damage, the pixel score overweights splits and underweights merges. Four in ten cuts leave the wiring intact, because one side of the cut holds no synapse end at all. A merge gathers the synapses of different neurons onto one object and creates connections that do not exist. Proofreaders say “a split is fixed by joining the pieces later; a merge creates false synapses” — in one unit (bits), this is what that looks like.

a neuron whose cut leaves the wiring intact, and one whose small cut breaks it

↑ Left: a large error in pixels (0.027 bits), but all 16 synapse ends lie on one side, so the wiring damage is 0. Right: a twelfth of that in pixels (0.002 bits), yet its 8 ends split 4 to 4, and per pixel bit it breaks the wiring more than any of the 54 cuts (0.035 bits).

So should proofreading go in order of pixel VOI?

order of fixing against wiring damage removed

Fixing the top 20 removes 41 % of the wiring damage in pixel order, 49 % in wiring order (ideal) and 19 % at random. The pixel score is not a bad proxy — twice as good as random — but it falls 8 points short of ideal. The gap is not large, so I cannot claim that “pixel scores cannot order proofreading”. What I can say is that the bias has a fixed direction: the pixel score sends proofreading effort to cuts that do not matter for the wiring. Errors were injected one at a time, so interactions between errors are not measured.

The same errors were also scored with the other convention, NRI (Reilly 2018: the F-score of the pairs of synapse ends that lie in one object in both the truth and the candidate). The new op seg_synapse_nri checks inside that it equals exactly 1 − adapted Rand error over the ends. An error that costs 0 ends-VOI bits also costs 0 NRI (no pair is separated), and that is a gate. Measured, the rank correlation between NRI loss and ends VOI is 1.00 — both count pairs of ends, so they order the same errors almost identically — and 0.59 against pixel VOI, the same story as the 0.60 above.

What is inside 1.30 / 0.58 (added 2026-09-28). Dividing the two closed forms, the ratio wiring bits / pixel bits of one error is identically density × balance: the neuron’s share of synapse ends over its share of voxels, times the H2 of the two-way split of its ends over the H2 of the two-way split of its voxels. That identity is a gate on all 108 errors. For merges the ends follow the voxels, so the median balance is 1.00 (quartiles 0.90–1.27) and the 1.30 is essentially density (median 1.16: neurons that carry synapses are denser than average). A cut halves the voxels, so the denominator H2 is 1 and the balance is H2(s1/s) itself. What matters there is how few ends a neuron has — a median of 3 — so that even ends spread evenly over the neuron give an expected H2 of only 0.62, the binomial closed form (re-spreading the ends over the neuron’s voxels and cutting again also gives 0.62, a gate). The measured mean is 0.53, split between the 23 zeros and “nearly balanced”. The 0.09 difference is the spatial clustering of the ends.

end count against the balance factor

The new op seg_wiring_exposure returns this count-only prediction per neuron: the bound s/2n (the bounds sum to exactly 1 bit, a gate), the largest reachable cost, the binomial expectation, and the relative density. Over the 54 cuts the wiring damage sums to 0.677 bits measured, 0.788 predicted from counts alone, 1.000 as the bound. A proofreader has only the candidate segmentation and the synapse annotations, so a table that says how much each object is staking before any geometry is looked at is what this is for (Plaza 2014 prioritises by the synapse count itself; this turns that count into an information bound and an expectation).

The gates caught two things here too.

  1. A comparison that only renamed the labels gave a VOI of 8.9e-16 instead of 0. The conditional entropy was computed as H(a,b) − H(a), and subtracting two large terms leaves rounding dust. The direct sum −Σ p log2(n_ij / n_i) makes it exactly 0 (the pixel VOI now uses the same formula). The regression test uses an input on which the old code actually produced the dust.
  2. At first only the VOI over synapses grouped by connection was returned. But a connection with a single synapse has nothing to scatter into when it is cut, so it stays at 0 — and most of the 107 connections are like that. The ends level became the primary measure; the connection level stays as connection_split / connection_merge.

The classic 3-D segmentation I tried first (membrane response per slice → threshold → 3-D connected components) joined along z into one giant object at every threshold and could not serve as a candidate. In EM where the z spacing (40 nm) is ten times the xy spacing (4 nm), cutting per slice and then linking along z needs a separate stage.

Do synapses grow in proportion to neurites? (2026-09-28)

“How much do neurites grow” and “how much wiring do 8 worms share” above measured shape and wiring separately. They are the same 8 animals, so the two can be set side by side. Witvliet 2021 writes that “except for L1, synapse number increased in proportion to neurite length, maintaining synapse density”, and that hub neurons (many partners at birth) “disproportionately” add inputs but not outputs. This round adds graph_strength_growth to Fullseye — between two wiring matrices, it counts where synapses were added, split into inputs and outputs per cell — and checks both claims with it and the existing tree ops.

density by stage

Stage Total neurite length Chemical synapses Density (/µm)
L1 at birth 2,806 µm 1,296 0.462
L1 16 h 4,544 µm 2,777 0.611
L3 27 h 6,396 µm 4,456 0.697
Adult #2 12,038 µm 7,970 0.662

System-wide, the paper holds: density rises ×1.32 during L1, and afterwards its wobble (max / min) is 1.14. The gate is “the L1 rise exceeds the later wobble” — no number is hard-coded.

Per cell, the story changes. Over the 178 cells that have both a skeleton and synapses in animals 1 and 8, the rank correlation between neurite growth (median ×3.7) and synapse gain (median ×6.0) is 0.23. It clears the 97.5th percentile of a null that shuffles cells (0.17), so it is not zero, but it is too weak to say that the cells whose neurites grew most gained the most synapses. Density rose in 82 % of cells — in nearly every cell, synapses grow faster than shape.

per-cell growth

The cells where shape and wiring diverge most: SIADL grows ×3.8 in neurite but ×22 in synapses, SIBDR ×4.1 against ×21, RIR ×1.9 against ×9. In the other direction, a body-wall muscle (BWM-VR04) grows ×15 in neurite for ×7 in synapses. Which cells stand out is not the paper’s subject, so only the names and numbers are recorded here.

Where did the 6,674 new synapses go? Comparing the matrices of animals 1 and 8 with graph_strength_growth: 3,232 (48 %) thickened connections present at birth, 3,627 (54 %) made new connections, −159 were on connections that disappeared and −26 on connections that thinned (the four sum to 6,674 exactly). This does not match the paper’s “~4,500 new synapses strengthened most connections present at birth”. The 3,232 here is the weight gained on connections present in both animals; how the paper matches connections and counts “strengthened” cannot be checked from this repository, so the number is recorded without a reason for the gap.

gain against partners at birth

As for hubs: the rank correlation between the number of partners at birth and the synapses gained is 0.58 for inputs and 0.51 for outputs — both positive. The top-decile 19 hubs’ share goes from 34 % of inputs at birth to 28 % of the input gain, and from 24 % of outputs to 20 % of the output gain — down, not up. If “hubs disproportionately add inputs” is read as “their share of the gain exceeds their share at birth”, it does not appear under this definition. The paper’s quantity (strengthening per connection, hubs against non-hubs) is different, so this is not a contradiction; the definition and the numbers are recorded here.

Run length forgives no small merge (2026-09-28)

The two sections above scored with VOI. Connectomics has a second convention: ERL (expected run length, Januszewski 2018) — how far along the ground-truth skeleton one can travel while staying inside one object. Runs of a merged object count as zero, however small the other party. This round adds tree_run_length to Fullseye: it counts the runs of a skeleton painted with candidate labels, and zero_labels zeroes the merged objects.

The ground truth is Witvliet 2021’s 1,713 skeletons (12+ nodes); the candidate labellings were injected one at a time (they are not a segmenter’s output). The check is a closed form: cutting at edge e gives exactly (A² + (L − A − e )²)/L, where A is the cable of the cut-off subtree, counted by a separate traversal. All 1,713 matched (relative 4e-15). The VOI split of the same cut matches (m/N)·H2(m1/m) too (3.5e-16).

cut position and ERL

Where a cut lands decides how much it hurts. On 100 unbranched skeletons, a cut in the middle leaves ERL at 0.48 L; a cut near an end (one tenth in), 0.80 L. VOI is also largest in the middle, so for cuts the two scores agree in direction.

They disagree on merges. I glued the far q of skeleton a onto skeleton b’s object and swept q from 5 % to 50 % (856 pairs).

merge size against the two scores

q VOI merge ERL loss (a, the glued one) ERL loss (b, the receiver)
5 % 0.142 bits 0.094 1.000
20 % 0.377 0.356 1.000
50 % 0.662 0.752 1.000

VOI merge goes to 0 as q → 0. Under ERL the receiving skeleton b loses its entire run however small the fragment glued onto it; a’s own loss follows the cut formula 1 − (1 − q)². This b column is what “ERL is harsh on merges” means: a neuron that contains a merged object cannot be trusted anywhere, by definition.

One probabilistic gate to finish. Cutting 65 unbranched skeletons at 3 points uniform along the cable, the run-length fractions follow Dirichlet(1, …, 1), so E[Σ l_i² / L’²] = 2/(m + 2). The measured ratio is 1.023.

The gates caught two things. The first version claimed that a’s loss was independent of q — wrong. ERL zeroes the runs of the merged object; the remaining 1 − q of a survives as an intact run. The constant loss falls on b, which that object covers entirely. And sampling the cut positions uniformly over node indices biased the ratio to 0.945: skeleton edges are uneven in length, so the positions must be uniform along the cable.

ERL implementations exist in funkelab’s funlib.evaluate (needs graph_tool) and the Allen Institute’s segmentation-skeleton-metrics (numpy + networkx); this one is numpy only, with the closed-form gates attached.

What the slime mould taught was not its dynamics but that its tubes are continuous — preparing to replace the hand-written readout with an evolving head (work in progress, 2026-09-28)

The fly in this article carries a fixed wiring on a body. Its readout (the one line that turns the four T4/T5 directions into a self-rotation estimate) is hand-written, and the sections above found that the readout’s correlation is the bottleneck. The next bridge is to put an evolving head (from a separate project, Afterman: a Transformer that decodes structure from a DNA) where that hand-written line sits. The slime-mould experiments run between 2026-08-30 and 09-15 were the preparation, and they are recorded here. This is work in progress with research left in it. Three parts: what held, what was retracted, what is open.

Origin. When evolution searches for structure, a task that needs three things at once (L layers × depth × FFN, a logical-AND ridge) was crossed by discrete evolution in only 2 of 8 seeds. The slime mould Physarum polycephalum finds the shortest path of a maze by nothing more than thickening and thinning its tubes (conductances) with the flow through them (Tero et al. Science 327:439, 2010; convergence proved by Bonifaci, Mehlhorn and Varma 2012). The question was whether importing that dynamics would cross the ridge.

What held (numbers are the pre-registered predictions against the measurements).

Act Question Result
Maze Does the Physarum equation reach the shortest path of a 15×15 maze (random edge lengths, unique shortest path)? Exact version (Kirchhoff solve): 4 of 5 seeds match within 600 iterations, the fifth at 5,000. The control that reinforces by gross traffic ignoring direction: 0/5. A (1+4)-ES on the same graph: 1/5 after 50,000 evaluations — the mould won on reachability, not cost
Ridge Does the ridge vanish once the parts are continuous conductances? Gradient, Physarum rule, and a task-blind activity flow, 3 arms × 8 seeds = 24/24 solved (validation MSE 0.006–0.035, floor 0.216). What mattered was not the selection dynamics but continuity itself
Recovery Continuous solutions are spread out and unreadable. Can discretisation pressure recover a readable circuit? Greedy zeroing with re-adaptation after each cut: 8/8 recovered to 4–5-part minimal circuits. The three recovered circuit types solved the task from fresh weights 9/9 — they are heritable
Into evolution Do continuous conductances work under mutation and selection rather than gradients? Evolution with a leak in the decoder (lending a thin conductance to absent parts): 8/8 (baseline 2/8, Fisher p=0.007). But see the retractions below
Recovery inside evolution If the lent conductance is peeled away during the search, are the solutions heritable? Zeroing each part’s floor at generations 20/40/60/80 until the genome is bare (progressive_all): n=16, crossed 13/16 ∧ heritable 13/16; the control that keeps the floor, 14/16 ∧ 4/16, Fisher p=0.0038. All five pre-registered predictions held

What was retracted (this is the substance).

Schedule sensitivity (added 2026-09-28, pre-registered). The peeling generations were moved early (10/20/30/40), late (60/70/80/90) and to two steps (33/67), 16 seeds each. Heritability (seeds whose bare-with-motor retraining works) was 8/16, 12/16 and 11/16 and did not depend on the length of the bare tail (60, 10, 33 generations) — the pre-registered prediction “a shorter tail lowers heritability” missed, and the fallback reading “the peeling event itself recovers it” holds. The effect of progressive removal (control without peeling: 4/16) held at the late schedule 12/16 (p = 0.012) and the two-step 11/16 (p = 0.032), so it is not an overfit to one schedule. Crossing the ridge, however, is schedule-sensitive: the earlier the floors vanish, the fewer cross (6/16, 12/16, 8/16; control 13/16) — the floors are the scaffold for crossing, and removing them early prevents it. The mechanism statement “a seed that acquires LOCAL before the floors vanish always crosses” is retracted: 5 counter-examples among 31. Five seeds were cut off by a NaN in the fitness; four of them had already crossed at the cut, but under the pre-registered definition they still count as not crossed (the definition is not changed).

What is open (the research left). One task only (exp_position). Still to do: a Baldwin-type comparison (bare retraining mixed into the fitness), replication on another task, a dose–response over how long the floors last, the source of the NaN, whether cosine’s 5/8 is chance, and Afterman’s own zero point that the quaternion does not beat a real-valued version of equal parameter count. Until these are filled, the design guideline for Afterman — search = leak, recovery = progressive removal, readout = scaffold — stays a candidate.

Why it sits in the fly article. On the fly side the front end (flyvis) is frozen and the activity of 65 types × 721 columns is cached (688 MB, 94 s), so evolving a head is an hour of compute, the same scale as PHD6. What we measure next: does an evolved head beat the hand-written readout’s correlation, and when it does, is the circuit readable (does the recovery stage work)? The slime-mould story has no figures because the results are jsonl and logs and none were drawn; they will be drawn together with the fly run.

Slime-mould tubes solve the maze — the dynamics as Fullseye ops, bracketed by a theorem and Dijkstra (2026-09-28)

The maze dynamics from the “origin” of the section above became two Fullseye ops. graph_physarum_path runs on a weighted graph, physarum_route on a cost image (neighbouring pixels joined by a tube of length (c_u + c_v)/2, minimising the same quantity as route_through_array). The dynamics are only this: solve the Kirchhoff pressures for a unit flow, read the flow Q in each tube, and move each conductance toward it, dD/dt = Q − D. When the shortest path is unique the conductances are proved to converge to its indicator (1 on it, 0 elsewhere; Bonifaci, Mehlhorn and Varma 2012). The truth is Dijkstra (scipy) and route_through_array (skimage).

tubes thickening

On a 21×21 perfect maze (one route) it converged in 79 iterations; the route differs from route_through_array by no pixel and crosses no wall. The Lyapunov function V = Σ L·D (Bonifaci’s “infrastructure cost”, non-increasing in continuous time) fell from 227,280 to 133.60 (shortest path 133.59) and never rose over the 23 intervals between 24 snapshots.

lattice tube thickness

On five 15×15 lattices (edge lengths U(0.5, 1.5)) every route equals Dijkstra’s (difference < 1e-9), and by the Afterman PoC’s criterion — the route already right at 600 iterations — it is 5/5 (Afterman had 4/5). But only 3/5 reached the indicator by 3,000 iterations. The two cut off are those whose gap to the runner-up (the exact second-shortest route: Dijkstra rerun with each edge of the shortest path removed in turn, since any other route must miss one of them) is smallest, 0.009 and 0.032, against 0.041–0.113 for the three that converged. The lower row of the figure is such a seed: two routes still compete at 3,000 iterations. The gap sets the speed; the route is read by following the flow, so it matches the shortest path even when the gap is small. Where many routes tie (uniform cost) the tubes never settle on one, and the op refuses (fail-closed).

terrain route

A 32×32 smooth terrain matched too (cut off at 2,500 iterations, with neighbouring tubes still faintly present). The inequality flow length Σ Q L ≥ shortest path held exactly in every case (equality at convergence).

Prior art: several reimplementations of Tero 2010 exist on GitHub, but none I found gates against Dijkstra or uses route_through_array as the truth on a cost image. Jones 2010’s “Physarum machine” (chemotactic particles) is a different model. These ops surface physarum_search.py, written on 2026-08-26 during a GPU study and never registered, now with the Dijkstra, inequality and Lyapunov gates attached.

The worm brain’s core is there from birth — counting the cells that stay in the deepest shell across 8 developmental stages (2026-09-28)

“How much wiring do 8 worms share” counted connections by how many animals carry them. This time we count how deep in the wiring each cell sits. The ruler is the k-core (Seidman 1983): the maximal subgraph in which every member has at least k connections; peel the shells from the outside and what remains is the “deepest shell”. Its weighted twin, the s-core (Eidsaa & Almaas 2013), peels by “every member has at least s synapses”. Three new ops — graph_kcore (in / out / total / undirected × binary / weighted), graph_rich_club_curve (the rich-club coefficient for every k at once, divided by the degree-preserving null) and graph_core_persistence (the cells that stay in the deepest shell of all K animals) — are gated by theorems: a complete graph is one shell of index n − 1, a tree 1, a cycle 2; the s-core of a 0/1 matrix equals the k-core exactly; scaling the weights by c scales the indices by c; the undirected k-core agrees with networkx on every node; φ(k) equals the single-k graph_rich_club for every k.

8 wirings on the neuron positions

from 0 h after birth to adulthood

The 8 wirings are drawn on the neuron positions (the roots of the authors’ published skeletons, viewed along the body axis), aligned to the 7th animal (an adult) by Procrustes. Orange is that animal’s deepest shell (in-degree s-core); red are the cells in the deepest shell of all 8. Results:

core type deepest index (0 h → adult) size of the deepest shell in all 8
in, s-core (peel by synapse count) 7 → 55 6–10 cells RIAL RIAR RMDDR RMDVL (4)
out, s-core 6 → 48 2–33 RIAL RIAR (2)
in, k-core (peel by 0/1) 3 → 5 15 → 150 12 (RIA, RMD, SMD)
out, k-core 3 → 5 65 → 110 42

Peeling by synapse count, the deepest shell stays small — 6 to 10 cells — throughout development while its index deepens from 7 to 55, and the interneuron pair RIA never leaves it from 0 h to adulthood (the shell holds 10 motor and 2 interneuron classes; no muscle or glia enters). Peeling by 0/1 the k-core is coarse even in the adult (index 3–5) and its deepest shell swells to 150 cells, which cannot single out a core — the way of counting decides the claim.

depth of the deepest shell

Published value: Yadav & Singh (bioRxiv, 2026-06) measured the same quantities on the same 8 animals and report 51 cells persistent in at least one of the 4 core types (in / out × k / s). Here it is 51 as well. They also write that among head neurons only AIBR, RIBL and RIAR persist; under my definition (persistent in all 4 types) it is RIAL and RIAR. I could not tell from the text which definition gives their 3, so the mismatch is recorded as is (nothing fitted).

rich club regime

The rich club (density among high in-degree cells over the null) exceeds 1 in some band at every stage, with peak ratios 1.4–3.3 and the band widening to k = 1–28 in the adult. One L3 animal dips below 1 at high k, so the 8 animals do not form a monotone series.

Prior art: the k-core and rich-club quantities are those of Yadav & Singh 2026 (they came first). Our part is the numpy ops gated by theorems, agreement with a second implementation (networkx), the s-core vs k-core contrast, and the moving figure on the cell positions. Data from nemanode.org and the authors’ skeleton repository (not bundled).

The slime mould solves optimal transport — make the source and sink mass distributions and the same tube dynamics converge to the Earth Mover’s Distance (2026-09-28)

The maze ops above had one source and one sink. Give the same dynamics a supply vector b (sum 0; positive where mass enters, negative where it leaves) and it is proved to converge to a minimiser of Σ L_e q_e under flow conservation — L1 optimal transport on the graph, the Beckmann problem, i.e. the 1-Wasserstein distance between b⁺ and b⁻ (Bonifaci 2017; Facca, Karrenbauer, Kolev and Mehlhorn 2020; Facca, Cardin and Putti 2018 for the continuum). Two new ops, graph_physarum_transport (weighted graph + supply) and physarum_transport_image (two mass images, pixels joined by unit tubes = the Earth Mover’s Distance in the Manhattan metric), return the cost Σ L Q (an upper bound) together with the Kantorovich–Rubinstein lower bound — b·φ for the McShane envelope φ of the pressure, made 1-Lipschitz. The true distance always lies between the two, so the gap certifies how far the current flow is from optimal, and the run stops when it closes (no external solver needed).

tubes forming

The tubes carrying the mass of a disk (red) to two disks (blue) form over 183 iterations (24×24 pixels, 4-neighbourhood, 36 frames). Tubes start uniform; only the used routes thicken and remain. The EMD is 12.0001, and the run stopped when the gap to the lower bound 12.0000 closed.

12-vs-12 assignment

A complete bipartite graph of 12 vs 12 points (Euclidean lengths, 144 tubes). After 459 iterations the surviving tubes are exactly the Hungarian optimal assignment (≥ 1.00 on it, ≤ 0.001 elsewhere), and the cost 0.163244 matches the minimum assignment 0.163242 to 1e-5.

upper and lower bounds close

Ten truths were checked: on five random trees the closed form Σ L_e subtree supply to 1.7e-15 (the flow on a tree is unique; 37–38 iterations); on an uneven 1-D grid the existing op wasserstein_1d at 0.316477; on a 6×6 grid of random mass the LP (HiGHS) at 0.591849; a disk shifted by (5, 8) pixels gives EMD 13.000086 = dr + dc as the theorem says; with one source and one sink the lower bound equals Dijkstra’s distance 9.367260049 exactly (the envelope of the pressure is the shortest-path potential). The metric axioms (symmetry, linearity in length and mass, the triangle inequality) hold. 9.1 s in all.

Honestly: the mould is not faster than the Hungarian algorithm or the network simplex. Its three merits are that a local rule alone reaches the optimum, that it batches in parallel, and that it certifies its own answer. Prior art: that Physarum dynamics solve optimal transport is proved in the papers above — we came later. Our part is a numpy op that returns a dual certificate, gated by the truths of trees, 1-D, assignment, LP and translation; Facca et al.’s DMK solver is a finite-element continuum version, a different thing. Next, the op goes onto the worm wiring: is the flow backbone from sensory sources to motor sinks the same in all 8 animals?

A fly’s eye closing in on stripes — do zebra stripes really spoil a compound eye’s sense of distance, and would a Fibonacci eye do better? (2026-10-13)

Tabanid flies cannot slow down in a controlled way before zebra stripes (Caro et al. 2019) and fly faster, keep away and do not land near stripes or checkerboards (How et al. 2020, 3-D trajectories). We counted how much of the folk claim that stripes spoil the sense of distance can be explained by the compound-eye optics of §0 and the closed forms of a Hassenstein–Reichardt correlator. The striped surface is never an image: it is integrated analytically under the hexagonal lattice and a Gaussian acceptance function from the coordinates (fly_view_plane_texture, 1e-9 against the closed form). Nothing is learned; one gain is calibrated (the first checkerboard window).

the reversal ring

Left: what a tabanid eye of 1.1° inter-ommatidial angle (1027 ommatidia) sees of 7 cm stripes from 1.95 down to 1.45 m. Right: the correlator between azimuthal neighbours (orange and blue are opposite directions; the black lines are the closed-form reversal boundary). A period P aliases on a hexagonal lattice beyond d > P cos²θ/(√3 Δφ) (2.105 m head-on, 0.526 m at 60° for the tabanid), and every ommatidium whose phase difference ψ = 2πΔφD/(P cos²az) exceeds π reports the opposite motion. Far away the whole eye is reversed; as the eye closes in the correct report grows from the centre and the reversal retreats to the periphery. The measured reversal agrees with the closed form in more than 97 % of ommatidia, and the azimuthal pairs switch from “receding” to “approaching” at 1.71 m against a predicted 1.65–1.77 m. A Drosophila eye (4.63° / 8.23°) is protected by its blur: the contrast left at Nyquist is 0.024.

tau over stripes

The time-to-contact readout. Approaching a frontal plane at 0.5 m/s, vertical stripes kill the vertical flow (the aperture effect) and both the radial fit over the whole eye and the divergence return exactly 2τ (closed form 2, measured 1.992); a checkerboard restores τ. The argument of How et al. 2020 — the flies avoided checkerboards too, so the aperture effect is ruled out — reproduces in these numbers. And the temporal frequency during the approach, v tanθ/P, does not depend on distance: over stripes the correlator loses the range.

three eyes

Would a Fibonacci arrangement make a better eye? With the same 721 ommatidia and the same mean spacing of 1.1° we compared the hexagon, the Vogel spiral (r ∝ √n, θ = n·137.508°) and a jittered hexagon. The moiré sharpness of a texture past Nyquist (peak over background median) is 2521 for the hexagon, 4.3 for the spiral and 5.2 for the jitter: the spiral wins on diffusing the moiré. Resolution just below Nyquist (reproduction and residual) is best for the hexagon. But the false flow — the share of correlators reporting the wrong direction — is set by pair spacing, not by lattice regularity: every azimuthal pair of the hexagon is reversed, every diagonal pair (projected spacing Δφ/2) is correct, and pairs of the same spacing reverse on the spiral too. A spiral with a denser centre (density power 0.5 → 0.8) sharpens the true-frequency peak from 5.2 to 11.0. The conclusion: an eye that hides moiré and an eye that does not misread motion are different things.

Honestly: the tabanid optics (1.1° from the acute zone, a 1° acceptance angle that is unpublished) are weak constants, so Δφ and Δρ/Δφ are swept. The stage that scores against the Dryad 3-D trajectories of How et al. 2020 (CC0, 1.21 GB) is implemented — place the data and the count gate and the τ̇ comparison run — but this round it was not placed and the run says so and skips it. Dettlaff (2026, arXiv:2605.17049), who argues for a moiré account, is a preprint, single author, code not public, and is named only as the hypothesis compared against. What Fullseye keeps is sampling_alias_distance: read the pixel’s instantaneous field of view as Δφ and it is the working-distance rule of an inspection camera looking at displays, meshes and weaves. The PoC is examples/poc_zebra_stripes_fly_eye.py (exhibit No.2026.227).

What we measure next

When a result lands, it gains a row in “Experiments so far” and loses its line here.

About the author

The questions and the direction are mine; implementation, sweeps, added controls and the checks that disproved my predictions were delegated to Claude Code. Section 8 records one of those disproofs.


These experiments were run together with Claude Code. I set the questions and the direction; Claude Code did the implementation, the sweeps, the management of long overnight jobs and the adversarial checks. If you want to try it, this invitation link gives you a one-week free trial: claude.ai/referral/0sqPw8E_lw

“I mounted a pretrained model in my own system, it passed the static tests, and it fell over in production” — that is the story I most want to hear from you. A like or a stock helps decide which experiment gets written up next.