A differentiable, GPU-native simulator for peripheral-nerve fiber models.
jaxon is a fully differentiable, GPU-native reimplementation of the canonical
peripheral-nerve fiber models in JAX /
Jaxley. It reproduces NEURON's
extracellular mechanism to sub-0.5 % threshold error and machine-precision
conduction velocity, runs whole fiber populations in parallel on one GPU, and
exposes gradients through the cable equation — so extracellular stimulation
parameters (per-contact amplitudes, waveform shape, electrode position) can be
optimized directly rather than grid-searched. It slots into existing pipelines
as a gradient-enabled, vectorized replacement for the NEURON forward solver.
The cohort-scale selectivity experiments run on FEM-derived per-contact lead fields and histology-segmented vagus-nerve geometries produced by the golgi platform [1, 2] (29 nerves: 18 pig, 11 human), applied to a 12-contact ring cuff.
✨ Highlights
- Differentiable through the cable equation. Reverse-mode autodiff and packed finite-difference gradients drive Adam optimization of per-contact amplitudes, arbitrary K×T waveforms, and joint amplitude + electrode-position search — no surrogate model, gradients of the true biophysics.
- GPU-native population scale.
vmap-batched forward passes simulate 100,000 fibers on a single GPU, reaching a geometric-mean ~820× speedup over single-thread NEURON at that scale (~200–300× at the ~1000-fiber operating point used in the paper). - Validated against NEURON / PyFibers. 99.6 % of 943 threshold configurations agree within 1 %; conduction velocity matches to machine precision. jaxon is checked against the reference solver, not a fit to it.
- Principled coupled solver. A custom backward-Euler integrator for the
two-state (V_i, V_pax) double-cable equation — the equivalent of NEURON's
extracellularmechanism, not the single-cable activating-function shortcut — solved by a 2×2 block-Thomas sweep verified to 5.6×10⁻¹¹ mV against dense LU. - Five model/parameterization variants. Myelinated MRG (McIntyre–Richardson–Grill, original + interpolated tables) and Sweeney; unmyelinated Sundt and Rattay C-fibers, each hand-translated from NMODL and rate-checked to machine precision.
- Realistic cohort fields. Drives golgi-derived FEM lead fields over histology-segmented swine and human vagus nerves, so selectivity is scored on anatomically realistic populations, not synthetic cross-sections.
Models
| Model | Type | Compartments | Validation status |
|---|---|---|---|
| MRG | Myelinated A-fiber (2002) | 21 nodes (double-cable) | ✓ <0.5 % thresholds, 1e-12 CV |
| MRG_Interp | MRG, polynomial geometry | same | scaling-only |
| Sweeney | Myelinated A-fiber (1987) | 21 nodes | ✓ <0.1 % thresholds, 1e-12 CV |
| Sundt | Unmyelinated C-fiber (2015) | 51 uniform | ✓ <0.5 % thresholds, CV fixed |
| Rattay | Unmyelinated HH C-fiber | 51 uniform | ✓ <0.5 % thresholds, CV fixed |
| Schild94 | Unmyelinated C-fiber (full Ca dynamics) | 51 uniform | code complete; validation pending |
| Schild97 | Unmyelinated C-fiber (mean) | 51 uniform | code complete; validation pending |
Installation
jaxon installs with conda (for the pinned scientific + NEURON stack) and runs on CPU by default; a single edit switches it to CUDA.
# 1. enter the project directory
cd jaxon
# 2. create the env (CPU by default; uncomment jax[cuda12] in environment.yml for GPU)
conda env create -f environment.yml
# 3. activate and compile the NEURON .mod files used by the PyFibers reference
conda activate jaxon
pyfibers_compile
For CUDA hosts, edit environment.yml per its header comments before step 2, then verify:
python -c "import jax; print(jax.devices())" # should list CudaDevice
Quick start
Run the validation suite (CPU, minutes):
conda activate jaxon
python experiments_v2/mrg_validation.py # thresholds, CV, traces (MRG)
python experiments_v2/sundt_validation.py # ditto, Sundt
python experiments_v2/rattay_validation.py # ditto, Rattay
python experiments_v2/sweeney_validation.py # ditto, Sweeney
python experiments_v2/scaling.py # scaling benchmark, all models
Optimize stimulation for selectivity:
python experiments_v2/selectivity_demo.py # local single-nerve selectivity demo
python experiments_v2/selectivity_sweep.py # full population sweep
python experiments_v2/selectivity_joint_opt.py # joint amplitudes + electrode positions
Outputs land in outputs/<experiment>/. Heavier sweeps are wired for SLURM under
slurm/ — see slurm/submit_all.sh. To reproduce every figure and number in the
paper from processed data, see REPRODUCE.md.
Reproducing the paper
The processed data (golgi lead fields + all sweep / validation / phenomena
outputs) is archived on Zenodo; REPRODUCE.md documents two paths — regenerating
every figure from that processed data (fast, CPU-only), or re-running the full
simulation pipeline from scratch (slow, GPU).
The Zenodo archive's top-level directories (outputs/, duke_Ves/) match the
repository layout, so it unpacks directly at the repo root — no mapping or
symlinks needed. From the repository root:
# unpack the Zenodo data archive -> populates outputs/ and duke_Ves/
tar -xzf jaxon_data_archive_v1.tar.gz
# regenerate all main + named-supplementary figures from processed data
bash reproduce_figures.sh
outputs/ supplies the processed sweep / validation / phenomena data; duke_Ves/
supplies the raw per-nerve lead-field bundles the gradient-check figure and the
from-scratch cohort sweep read. See REPRODUCE.md for the from-scratch (GPU) path.
The raw golgi FEM meshes are not bundled — they are golgi outputs, available via the golgi platform and the underlying SPARC datasets (swine doi:10.26275/MAQ2-EII4, human doi:10.26275/OFJA-GHOZ).
Core design
- Channels. Each NMODL mechanism is hand-translated into a pure-JAX
Channelclass withsolve_gate_exponentialupdates; rate constants are checked against the published.modformulas to machine precision. - Fibers. Each builder returns a
jaxley.Cellplus a geometry dataclass fully describing the per-compartment static parameters. - Coupled solver (
stim/extracellular_coupled.py). Backward-Euler integrator for the two-state (V_i, V_pax) double-cable equation, solved by a 2×2 block-Thomas sweep (verified to 5.6e-11 mV vs dense LU); back-substitution viajax.lax.associative_scanfor O(log n) depth. - Batching.
stack_fiber_staticspacks N fibers into vmapped arrays;batch_integrate_m_maxruns them in parallel on the GPU for the optimization loops. - Optimization (
optim/optimizer.py), three modes: rectangular per-contact amplitudes via a packed finite-difference gradient (K+1 configs per forward pass); arbitrary K×T waveforms via autodiff through the ODE scan (with gradient checkpointing); joint amplitudes + electrode positions via FD over both, with the field recomputed differentiably each step.
Repository layout
jaxon/
├── README.md this file
├── REPRODUCE.md step-by-step paper reproduction
├── reproduce_figures.sh one-command figure driver (processed data)
├── environment.yml pinned conda env
├── jaxon/ core package
│ ├── channels/ Jaxley Channel translations (MRG, Sundt, Rattay,
│ │ Sweeney, Schild94, Schild97)
│ ├── fibers/ morphology builders, one per model
│ ├── stim/
│ │ ├── intracellular.py rectangular intracellular pulse helper
│ │ ├── extracellular.py point-source Ve profile (demo option)
│ │ ├── extracellular_coupled.py coupled (V_i, V_pax) backward-Euler solver
│ │ ├── batch_solve.py vmapped multi-fiber forward pass
│ │ └── multichannel_field.py multi-contact ring-cuff field
│ ├── optim/
│ │ ├── losses.py activation proxy, WQ loss, WBCE, selectivity index
│ │ └── optimizer.py rect / waveform / joint Adam loops
│ ├── nerve/geometry.py synthetic nerve cross-section (demos/tests)
│ └── nrn_baseline.py thin PyFibers / NEURON wrappers
├── experiments_v2/ paper-relevant runs (see EXPERIMENTS.md, FIGURE_DATA_MAP.md)
│ ├── <model>_validation.py thresholds + CV + traces, one per fiber model
│ ├── scaling.py PyFibers vs Jaxley CPU vs Jaxley GPU
│ ├── selectivity_sweep_duke.py cohort selectivity sweep on golgi fields
│ ├── figures_*_main.py paper figure generators
│ └── analyze_*.py statistics + tables
├── duke_Ves/ per-nerve golgi FEM lead fields + geometry (gitignored)
├── outputs/ validation JSONs + figures + sweep data (gitignored)
├── slurm/ SLURM sbatch drivers for the cluster
└── reference_code/pyfibers/ upstream PyFibers, cloned for reference
Headline numbers
- MRG threshold error vs NEURON: median +0.04 %, max 0.39 % across 432 cases (9 diameters × 8 pulse shapes × 6 pulse widths). Sweeney: max 0.09 % across 240 cases. Overall, 99.6 % of 943 configurations within 1 %.
- MRG / Sweeney conduction velocity: machine precision (1e-12 %) across all diameters.
- Scaling vs single-thread NEURON: ~200–300× at N = 1000 fibers, geometric-mean ~820× at N = 100,000 on one A100. The value here is accuracy and differentiability, not raw forward speed against fitted surrogates.
Caveats
- Extracellular field. The cohort selectivity experiments use FEM-derived
per-contact lead fields (the pre-baked
Ve_VperAtensor) computed by golgi [1, 2] for a multi-contact ring cuff on histology-segmented vagus nerves — not a point source. The analytic point-source potential in a homogeneous medium (σ = 0.3 S/m,stim/extracellular.py) is retained as a lightweight option for local single-nerve demos. - Nerve geometry. The cohort experiments use histology-derived vagus-nerve
morphology segmented and meshed by golgi [1, 2] from the Pelot 2020 pig /
2021 human SPARC datasets (29 nerves: 18 pig, 11 human). The synthetic circular
cross-section (
nerve/geometry.py) is retained for local demos and unit tests. - Schild94 / Schild97 are not yet validated. Channel and fiber code exist and are wired into the scaling benchmark, but the dedicated validation runs have not been executed.
- Sundt at D = 1.2 µm gives a non-physiological CV (~19 m/s) in the current validation script because the bisection lands on a suprathreshold pulse that fires multi-site. Reported identically by JAX and PyFibers — a measurement artifact, not a model error.
Reproducibility
Versions pinned in environment.yml / requirements.txt:
python 3.11 jaxley 0.13.0 optax 0.2.8
jax 0.6.2 neuron 9.0.1 numpy 2.2.6
pyfibers 0.8.5 matplotlib 3.10.9
Cluster validation hardware: MedUni Vienna HPC, NVIDIA A16 (16 GB VRAM), SLURM.
NEURON .mod artifacts are platform-specific and gitignored — re-run
pyfibers_compile after creating the conda env on a new host.
License
jaxon is released under the MIT License — see LICENSE. The golgi FEM datasets and the manuscript reproduction archive are released separately on Zenodo under CC-BY-4.0.
Citation
If you use jaxon, please cite the accompanying manuscript (Lung & Haberbusch, Medical University of Vienna; citation to be updated on publication) and the golgi platform that supplies the cohort fields:
- Lung D, Jia Y, Moro A, Fachino M, Haberbusch M. golgi: open-source software for automated nerve model generation and recruitment simulation. bioRxiv 2026.07.10.737846. https://doi.org/10.64898/2026.07.10.737846
- Lung D, Jia Y, Blumer R, Reissig L, Zopf LM, Heimel P, Kraus C, Moro A, Fachino M, Haberbusch M. golgi: an open-source graphical platform for image-to-recruitment modeling of peripheral nerve stimulation. bioRxiv 2026.07.10.737529. https://doi.org/10.64898/2026.07.10.737529
Built with
| Role | Built on |
|---|---|
| Differentiable cable simulation | JAX · Jaxley |
| Optimization | Optax |
| Reference solver | NEURON via PyFibers |
| Cohort FEM fields & anatomy | golgi |
Metadata
Release files for jaxon-nerve 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jaxon_nerve-1.0.1.tar.gz | 85.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jaxon_nerve-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 176.1 kB
Release files / jaxon_nerve-1.0.1.tar.gz
| Download URL | jaxon_nerve-1.0.1.tar.gz |
|---|---|
| Size | 85.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
05f1fa85175a7504a4f2a9aa67825dcd3e8726e23c5df7084ed3de93bde730f6
|
|
BLAKE2b-256 checksum How to use checksums |
9d56e0dba2eeb8814d4b2323746a071c313df0f9da2843f7c3366d994d250a91
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / jaxon_nerve-1.0.1-py3-none-any.whl
| Download URL | jaxon_nerve-1.0.1-py3-none-any.whl |
|---|---|
| Size | 90.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5175a55cca495dd04d6872c4f54df48e3b52f7d5828467be1ede12b35f6beb62
|
|
BLAKE2b-256 checksum How to use checksums |
9a2a619e271dfa7e4bf96ca523183d5658c886cb38c73b72e32530a4cf493af4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|