exact-event-adjoint
Grid-free exact continuous-time adjoint gradients for Time-To-First-Spike (TTFS)
spiking neural networks. No surrogate gradients. No dense time grid. Every gradient
is the analytic derivative of the threshold-crossing event times, and every claim in
this file is backed by a numerical test in tests/.
phi_k(t) = sum_m W[o,m] * K(t, t_in[m]) - (theta - u_reset) * sum_{j<k} R(t, t_j) - theta = 0
K(t, s) = (tau_s*tau_m)/(tau_m-tau_s) * (exp(-(t-s)/tau_m) - exp(-(t-s)/tau_s))double-exponential postsynaptic kernel.R(t, s) = exp(-(t-s)/tau_m)decaying post-spike reset kernel (subtractive reset).
Installed features (each one was verified empirically in tests/)
| Feature | Claim | Verified |
|---|---|---|
| Continuous-time IFT | dt*/dx = - (d phi/dx) / u_dot(t*) for the first firing time, closed form, no surrogate |
✓ weight & input Jacobians match central finite differences to 1e-9 relative (test_core_engine.py) |
| Reset saltation coupling | multi-spike backward solves the coupled system dt_k/dx = -(dphi_k/dx + sum_{j<k} b_{k,j} dt_j/dx) / u_dot(t_k), with b_{k,j} = -(theta-u_reset)/tau_m * R(t_k, t_j) |
✓ backward matches finite differences to 1e-9 relative, every spike satisfies phi(t_k)=0 to 8e-10 (test_multispike.py) |
| Event-ordered, grid-free | crossings are found from input arrivals, kernel-peak lags and previous spikes — no uniform grid; probe count scales with # events, not horizon | ✓ static guard: no linspace anywhere in the gradient path; first-crossing equals a brute-force time-domain sim to 2e-6; multi-spike equals brute-force re-crossing (test_gridfree.py, test_solver.py) |
| O(pending-events) engine | EventDrivenO1Engine is a priority-queue event simulator of the same kernel+reset dynamics; membrane is advanced analytically between events |
✓ reproduces the full solver spike chain, identical spike counts, max delta 2e-5 (test_engine.py) |
| Existence regularization | squared peak-margin penalty for silent neurons so their gradient does not collapse | ✓ silent-neuron weight gradients are nonzero and negative (descent raises weights) (test_attention_existence.py) |
| Full layer suite | Linear, Conv2D (patchwise), Multi-Spike (reset-aware trains), Recurrent (w_in + w_rec), Spiking Attention (single + multi-head), Feed-Forward | ✓ every layer constructs, runs forward and backwards finite-differenced where meaningful (test_layers.py) |
| Event engine == core | ExactEventFunction and ExactAdjointFunction return bit-identical outputs and gradients |
✓ test_core_engine.py::test_engine_identical_to_core |
Precise wording (we do not overclaim)
These are the exact, defensible statements you may make in a paper:
-
Continuous-Time IFT & Saltation resetting — TRUE. The first firing time is an implicit function of the latent variables and its gradient is the exact IFT derivative — no surrogate approximation is used. For multi-spike trains the implicit equations for all spike times are coupled through the reset kernels; the backward pass solves that coupled system exactly. The coupling terms
b_{k,j}are precisely the saltation-type reset multipliers-(theta-u_reset)/tau_m * R(t_k, t_j)of the threshold-reset discontinuity: the classic instantaneous linearization1 + (theta-u_reset)/(tau_m u_dot)is the degenerate short-gap limit, ours is the exact finite-gap generalization. -
Priority-queue, grid-free — TRUE, with one precision note. The package evaluates all membrane quantities on event-ordered sets (presynaptic times, kernel-peak lags, previous spikes,
t_max) and never builds a uniform time grid. Probe counts scale with the number of events, not with the horizon length or any step size. The label "O(1) memory" that circulated in earlier demos is not retained: the heap-based engine is O(number of pending events) memory with O(log) work per push. That is what is actually true, so that is what we ship. -
Escape-Noise Regularization — TRUE with a name correction. The implemented regularizer is a deterministic peak-margin penalty (max of the differentiable soft-weighted probe peak below threshold) that provably removes dead/silent-neuron gradient collapse: gradient descent raises silent weights. This is not the stochastic escape-rate functional; we therefore call it the existence regularization (
ExactAdjointFunction(use_existence=True)orexistence_loss_and_grads). -
Full Layer Suite — TRUE.
ExactAdjointLinear,ExactAdjointConv2d,ExactAdjointMultiSpike,ExactAdjointRecurrent/ExactAdjointTTFSRnn,ExactAdjointAttention,ExactAdjointMultiHeadAttention(genuine per-head split,sqrt(head_dim)scaling),ExactAdjointFFN, withAdjointSpikeNorm, latency / spike-count losses. All constructed layers run forward and backward.
Quickstart
import torch
from exact_event_adjoint import (
ExactAdjointLinear, ExactAdjointMultiSpike,
latency_cross_entropy, AdjointSpikeNorm,
)
torch.manual_seed(0)
encoder = ExactAdjointLinear(4, 6, tau_m=15.0, tau_s=4.0, theta=1.0, t_max=50.0)
decoder = ExactAdjointMultiSpike(6, 3, max_spikes=4)
norm = AdjointSpikeNorm(6)
t_in = torch.rand(8, 4) * 40 + 1 # latency-encoded input spikes
t1 = encoder(t_in) # (B, O) exact first-spike times
h = norm(t1)
train = decoder(h) # (O, B, K) reset-aware spike trains
loss = latency_cross_entropy(t1, torch.randint(0, 6, (8,)))
loss.backward() # exact IFT gradients, no surrogates
Mixed firing/silent training uses the existence flag:
layer = ExactAdjointLinear(4, 3, use_existence=True) # dead neurons get gradient pressure
The exact per-layer alternative with the same maths lives in ExactEventLinear /
ExactEventFunction (bit-identical to ExactAdjointFunction).
Running the verification suite
python -m pytest exact_event_adjoint/tests -q
# 20+ tests: finite-difference Jacobians, brute-force simulations, engine-vs-solver,
# existence direction, head split, layer suite, grid-free static guard
Integrity notes
- The solver (
solver.py) performs no autograd; all broadcast arithmetic is ordinary tensor ops and every gradient is provided analytically by the autogradFunctionbackward passes. - Recurrent loops connect spike-latency outputs to latency-encoded inputs
(
w_in+w_rec); this is differentiable TTFS feedback, not a continuous dynamical-system solver for the recurrent loop (scope note). EventDrivenO1Engine.simulate_eventsimplements feedforward receptive fields (static inputs). Causal recurrent rescheduling is future work.
License
MIT — see LICENSE.
Metadata
Release files for exact-event-adjoint 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| exact_event_adjoint-0.2.0.tar.gz | 26.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| exact_event_adjoint-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.7 kB
Release files / exact_event_adjoint-0.2.0.tar.gz
| Download URL | exact_event_adjoint-0.2.0.tar.gz |
|---|---|
| Size | 26.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a32e967596c116fc236381c54063ad9de52c1b16146e3e5e91924b702c8e6ace
|
|
BLAKE2b-256 checksum How to use checksums |
379c1de799bc97be73a255ef0c3974f10f624c350de142449cb2fcb0670f088b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|
Release files / exact_event_adjoint-0.2.0-py3-none-any.whl
| Download URL | exact_event_adjoint-0.2.0-py3-none-any.whl |
|---|---|
| Size | 21.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aa952917e5eccf4ce198954b8c7e7892af25aee1c8a3c226b3e041a6e567da01
|
|
BLAKE2b-256 checksum How to use checksums |
f98f6dfa757b3d0e133876fb0d1d608ffde75e3709cd814e96fab4284974ca2b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|
Security for exact-event-adjoint 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.