flymsg

source

ADR: electrical coupling with refractory voltage clamps

Status: accepted, 2026-10-13. Accepted by the user before implementation. Scope: the optional electrical path of B2; supplements the accepted architecture and runtime decisions.

Context

At 9a52fe2, gap_arrays exponentiates the full connected component assuming all membranes are free. _advance zeroes the refractory cell's synaptic forcing and discards its updated voltage, but the exponential still includes its virtual voltage excursion inside the step. This does not integrate the stated condition that a refractory cell stays at rest throughout that step. Kernel/reference parity shares this assumption and cannot establish correctness of the clamp.

Independent two-cell diagnostic: coupling/leak = 5, default parameters, cell 0 initially 5 mV above rest, cell 1 refractory at rest, no chemical weights or input. The free cell must satisfy v(dt) = v_rest + 5 exp(-(1 + 5) dt / tau_m). Current runtime gives −47.14625549316406 mV; the analytic value is −47.14777233225746 mV. The error is 0.001516839093397948 mV in one step, larger than float32 rounding at this voltage. This demonstrates the numerical defect, not its effect size in a connectome run.

Concurrent update: #121 (7ccedb2) keeps this algorithm and explicitly documents a different convention: refractory voltages may move virtually within a step and are reset at its boundaries. The diagnostic therefore now distinguishes two model semantics, rather than disproving that newly documented boundary-only convention. The proposal below selects the continuous clamp; accepting it is a numerical-model decision, not a claim that #121 fails to implement its own new docstring. Do not present the two conventions as mathematically equivalent or silently change historical runs.

Decision

Keep the existing model contract: refractory membranes are clamped at rest, their chemical conductance stays frozen, their threshold adaptation continues, and they accept electrical current only after leaving refractory. They remain an electrical sink for their free partners. Silencing suppresses spike output; it does not remove electrical contacts or clamp the membrane. A graded cell never enters refractory through a spike.

For a fixed refractory mask over a neural step, integrate only the free cells. Write x = v - v_rest, let F denote free cells, and define A = (K - diag(1 + row_sum(K))) / tau_m. Use A_FF, retaining the diagonal contribution of contacts to clamped cells. Integrate dx_F/dt = A_FF x_F + g_F/tau_m together with dg_F/dt = -g_F/tau_syn. Clamped x is zero throughout the step. Obtain the step map from a matrix exponential of this reduced block system. Do not remove clamped contacts from the diagonal. Refractory deadlines are integer step boundaries, so the mask is constant during each step under the existing spike timing contract.

Use the existing all-free map when the component has no refractory cells. Precompute or cache reduced maps only on demand, with a bounded memory policy. Before choosing cache capacity or a more elaborate solver, measure component sizes, distinct masks, hit rate, setup time, per-step time and memory on the existing KC topology. Failure to meet the resource budget must be explicit; do not silently substitute Euler integration or move the clamp to the end of the step. If exact dense maps are impractical, bring a separate solver decision for review with those measurements.

Version the optional electrical semantics and bind them into neural-checkpoint identity; reject restoration of old electrical checkpoints under the new algorithm. Preserve compatibility for uncoupled checkpoints when their execution semantics have not changed. The native path continues to refuse electrical fixtures until explicitly qualified.

Consequences

Solver decision for large components (#155)

Status: accepted by the user, 2026-10-14. Locks may name flymsg.gap/continuous-clamp-taylor-v1.

Measurement: on the KC v6/v7 couplings one connected component holds 688 cells (726 with lLN1_bc). Each new refractory mask needs a dense exponential of about 2,100 x 2,100, 1.1-1.6 s, with no cache hit, because masks change almost every step once PNs fire; one simulation takes hours (v6 was stopped for this reason, v7 used the boundary reset).

Proposal: flymsg.gap/continuous-clamp-taylor-v1. For the same reduced block B of this decision (free cells only, sink diagonals retained, clamped x = 0, frozen chemical drive excluded, spikelets of every member as constant input), compute exp(B dt) u as the sum of the first K terms of its Taylor series, each a sparse product with the component's generator, and add the result as the dense maps do. K is the fewest terms with theta^(K+1) / (K+1)! e^theta <= 2^-53, theta the 1-norm of the full component's B dt; every mask's block is a principal submatrix with no larger norm, so the bound holds for all masks. Above theta = 0.5 the solver refuses and the dense maps remain. It differs from the dense maps only by rounding, not by model: the clamp, the sink and the spikelets are unchanged.

Evidence: the analytic one-free/one-clamped test holds bit for bit; against the independent NumPy reference (dense exponential per mask) a spiking network with spikelets, a chunk boundary and a checkpoint gives the same spikes and potentials within 1e-4 mV. On the KC couplings (theta = 0.025, K = 7), arm E of both eLN sets, 3-octanol, 50 ms, spikelets 0 and 1 ms: identical spike counts for every neuron and identical first spikes (9,798 / 9,634 / 5,299 / 5,255 spikes), 3.6-4.9 s against 1,412-1,655 s; a full 1,000 ms odor takes 14.7 s (docs/evidence/gap-taylor-qualification-2026-10-14.json, scripts/qualify_taylor.py). The checkpoint identity names the solver, so dense and Taylor checkpoints do not restore into each other. Native and GPU paths refuse it, like every continuous clamp.

Alternatives