A reimplementation of the Spinach spin dynamics kernel, verified stage by stage against the MATLAB original: same function names, parameter structures and tolerances, so that an existing Spinach script maps onto Spynach line by line.
Spynach derives from Spinach 2.13, the spin dynamics package of Ilya Kuprov and co-workers, released under the MIT licence. The translation keeps the Spinach attribution and notice in LICENSE, and keeps Spinach's function names, parameter structures and tolerances.
The deliberate differences are small: spin indices are 0-based, product and sum operators are told apart by the type of the first argument, spin system objects are immutable, and time series are 1-D arrays. The guide Coming from Spinach lists, feature by feature, what is the same, what differs and what is not yet ported.
The translation from MATLAB to Python was carried out largely by an AI coding agent working under the direction of Alexej Jerschow, who set the goals and reviewed the results; correctness rests on the test suite against Spinach reference data.
fast extra) with the same numbers as the SciPy code, and the hot loops go through one backend adapter so that a GPU array library can be plugged in; that step is deferred until a target device is chosen.zeeman-hilb)sphten-liouv): restricted basis, superoperators, states, propagation with zero track elimination and path tracing, chemical exchangecrystal and powder contexts and Spinach's gridssinglerot)zeeman-liouv formalismThe function-by-function table is in the README.
After installing: two protons at 14.1 T, pulse-acquire, exponential apodisation, two doublets.
import numpy as np
import spynach
spin_system = spynach.create(
{"isotopes": ["1H", "1H"], "magnet": 14.1},
{"zeeman": {"scalar": [1.00, 1.10]}, "coupling": {"scalar": [[0, 12.0], [0, 0]]}},
)
spin_system = spynach.basis(spin_system, {"formalism": "zeeman-hilb", "approximation": "none"})
pars = {"spins": ["1H"], "offset": 630, "sweep": 200, "npoints": 1024,
"rho0": spynach.state(spin_system, "L+", "1H"),
"coil": spynach.state(spin_system, "L+", "1H")}
fid = spynach.liquid(spin_system, spynach.acquire, pars, "nmr")
fid = spynach.apodisation(fid, [("exp", 6)])
spectrum = np.fft.fftshift(np.fft.fft(fid, 4096))
The parity suites compare Spynach with Spinach 2.13 fixtures at every stage of a calculation. Of the 540 tests, 323 are fixture comparisons over 45 fixture cases: Liouville-space setup (132), Liouville-space propagation (54), Hilbert-space pulse-acquire (64), anisotropic interactions (25), relaxation (20), scalar relaxation (9), magic angle spinning (16), pulse sequences (3). Comparisons are relative with zero absolute tolerance; the largest measured difference is 8e-11 for quantities compared at 1e-9 (time evolution and FIDs) and 9e-15 for everything compared at 1e-10 or tighter (operators, states, Hamiltonians, propagators). Every case, tolerance and measured difference is listed in docs/parity.md (regenerated from the suite by oracle/parity_summary.py).
Full-size calculations are run on both sides under a profiler, on the same machine, and summarised in an HTML report with the stage timings, the normwise difference of every saved array, the figures and the profiler tops. The timings are wall clock for each implementation as shipped: Spinach with its parallel pool and multithreaded BLAS, Spynach as a single process with multithreaded BLAS inside dense calls. The table is generated from the reports' summary.json files.
| Case | Spinach / s | Spynach / s | Spynach / Spinach | Agreement | Report |
|---|---|---|---|---|---|
li_exchange_equilibriumtwo 7Li sites with quadrupolar and shift tensors in chemical exchange: thermal state equilibrated under exchange, thermalised Redfield relaxation, a 2 s trajectory analysed by tensor projections and state classes (31 states) | 2.57 | 0.59 | 0.23x | 3.9e-13 | report |
maz_noesy_2methylaziridine, 7 protons + 14N with DFT shielding tensors and the 14N quadrupole: Redfield theory with scalar relaxation of both kinds, IK-2 basis (19 314 states), 2 s NOESY | 846.15 | 282.04 | 0.33x | 1.2e-13 | report |
noesy_strychnine22 protons, IK-2 basis (12 316 states), Redfield relaxation, 500 ms NOESY, 512 x 512 points | 113.24 | 68.54 | 0.61x | 2.3e-12 | report |
zulf_pf6_1d31P and six 19F at 2.7 µT, complete basis (16 384 states), lab frame, selective pulse, 16 384-point acquisition | 51.83 | 42.39 | 0.82x | 8.1e-11 | report |
Wall clock of the whole case on each side, Spynach in its fastest configuration; agreement is the largest relative difference over the arrays both sides saved (FIDs, spectra, trajectories).
Apple silicon Mac, 12 cores; MATLAB R2026b with an 11-worker pool; Python 3.12 with NumPy and SciPy on Accelerate. Spynach is timed with its compiled kernels (the fast extra), which thread the sparse products; the methylaziridine NOESY spends nine tenths of its time in them (26 squarings of a 38 628-dimensional sparse matrix), while the strychnine and ZULF cases are dominated by dense matrix products at BLAS speed. The protocol, including how to add a case, is in comparison/README.md.
The guides run reduced versions of Spinach examples in seconds. The scripts in examples/ are the same calculations at the Spinach size, for side-by-side comparison with MATLAB: each writes its results next to itself and can report the normwise error against a .mat file saved by the Spinach script. Python code contributed by users lives in user_py/ as notebooks saved with their outputs.
| Example | What it calculates | Open |
|---|---|---|
| 23Na triple-quantum build-up | contributed notebook: one 23Na spin under quadrupolar Redfield relaxation towards thermal equilibrium (Cq = 200 kHz, τc = 1 ns, 14.1 T); from the pulsed thermal state, the rank-3 single-quantum tensor, the triple-quantum coherence after a second pulse and the longitudinal recovery over 10 ms, against the biexponential form of Jaccard, Wimperis and Bodenhausen | notebook · source |
| noesy_strychnine | Spinach's examples/nmr_liquids/noesy_strychnine.m: 22 protons, IK-2 basis, Redfield relaxation, 500 ms NOESY at 512 x 512 points | script |
| maz_noesy_2 | Spinach's examples/relaxation_theory/maz_noesy_2.m: methylaziridine with CSA tensors, a 14N quadrupole and scalar relaxation of the first and second kind, 2 s NOESY | script |
| li_exchange_equilibrium | contributed Spinach script (Florin Teleanu): two-site 7Li exchange with quadrupolar and shift tensors, thermal state equilibrated under exchange, thermalised Redfield relaxation, 2 s trajectory analysed with trajan and stateinfo | script · original |
| zulf_pf6_1d | contributed Spinach script (Florin Teleanu): PF6− at 2.7 µT, adiabatic-drop state from file, selective pulse, 16 384-point zero- to ultralow-field spectrum | script · original |
The four scripts are the cases of the timed comparisons above. Contributions to user_py/ keep their author's name in the file and in its README. The guides and notebooks are listed on the documentation page.