Metadata-Version: 2.4
Name: torchspin
Version: 0.5.0
Summary: Differentiable PyTorch port of EasySpin - EPR/ESR spin Hamiltonian and spectrum simulation
Author-email: Alec Follmer <ahfollmer@ucdavis.edu>
Maintainer-email: Alec Follmer <ahfollmer@ucdavis.edu>
License-Expression: MIT
Project-URL: Homepage, https://github.com/follmerlab/torchspin
Project-URL: Source, https://github.com/follmerlab/torchspin
Project-URL: Issues, https://github.com/follmerlab/torchspin/issues
Project-URL: Upstream (EasySpin), https://easyspin.org
Keywords: EPR,ESR,spectroscopy,spin-Hamiltonian,pytorch,autograd,magnetic-resonance,quantum,EasySpin,simulation
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Chemistry
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE.md
Requires-Dist: torch<3,>=2.0
Requires-Dist: numpy<3,>=1.23
Requires-Dist: scipy>=1.10
Provides-Extra: plot
Requires-Dist: matplotlib>=3.6; extra == "plot"
Provides-Extra: fit
Requires-Dist: cloudpickle>=2.0; extra == "fit"
Provides-Extra: gui
Requires-Dist: ipywidgets>=7.6; extra == "gui"
Requires-Dist: matplotlib>=3.6; extra == "gui"
Requires-Dist: ipympl>=0.9; extra == "gui"
Requires-Dist: ipython>=7; extra == "gui"
Requires-Dist: cloudpickle>=2.0; extra == "gui"
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: matplotlib>=3.6; extra == "test"
Requires-Dist: cloudpickle>=2.0; extra == "test"
Requires-Dist: ipywidgets>=7.6; extra == "test"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Requires-Dist: matplotlib>=3.6; extra == "dev"
Requires-Dist: cloudpickle>=2.0; extra == "dev"
Requires-Dist: ipywidgets>=7.6; extra == "dev"
Dynamic: license-file

# torchspin

**Simulate and fit EPR/ESR spectra in Python — differentiable, GPU-capable, and validated against [EasySpin](https://easyspin.org).**

[![PyPI](https://img.shields.io/pypi/v/torchspin)](https://pypi.org/project/torchspin/)
[![Python](https://img.shields.io/pypi/pyversions/torchspin)](https://pypi.org/project/torchspin/)
[![Tests](https://github.com/follmerlab/torchspin/actions/workflows/python-tests.yml/badge.svg)](https://github.com/follmerlab/torchspin/actions/workflows/python-tests.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE.md)

torchspin reimplements the core physics engine of EasySpin (MATLAB) in
Python/PyTorch. You get the simulators you already know — `pepper`, `garlic`,
`chili`, `salt`, `saffron`, `curry`, `cardamom`, `esfit` — with the same units
and conventions, plus three things MATLAB cannot give you:

- **Gradients.** Spectra are differentiable end to end, so you can fit with
  gradient descent, propagate uncertainty, or put a simulator inside a neural
  network.
- **A Python workflow.** NumPy and torch tensors in and out, no license server,
  installable with `pip`.
- **Optional GPU.** `Options(device='cuda')` for batched and gradient-based
  work.

torchspin is a reimplementation, not a drop-in replacement for every EasySpin
feature. Before relying on it for published results, read
[KNOWN_LIMITATIONS.md](KNOWN_LIMITATIONS.md) — it states per simulator what is
validated against MATLAB and what is not.

---

## Installation

```bash
pip install torchspin                 # core
pip install "torchspin[plot]"         # + matplotlib for the plotting helpers
pip install "torchspin[gui]"          # + the interactive fitting panel (torchspin.fitgui)
```

CPU-only PyTorch, to avoid the large CUDA wheels:

```bash
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install torchspin
```

Requires Python 3.10+, PyTorch 2.0+, NumPy 1.23+, SciPy 1.10+.

---

## Quick start

### CW powder spectrum (`pepper`)

```python
from torchspin import SpinSystem, Experiment, Options, pepper

sys = SpinSystem(
    S=[0.5],
    g=[[2.009, 2.006, 2.002]],
    Nucs=['14N'],
    A=[[10.0, 10.0, 95.0]],   # MHz
    lw=[1.0, 0.0],            # Gaussian FWHM (mT)
)
exp = Experiment(mwFreq=9.5, Range=[330, 350], nPoints=1024, Harmonic=1)
B, spec = pepper(sys, exp, Options(GridSize=50))
```

### Solution EPR (`garlic`)

```python
from torchspin import SpinSystem, Experiment, garlic

sys = SpinSystem(S=[0.5], g=2.003, Nucs='1H', A=10.0, lw=[0.2, 0.0])
exp = Experiment(mwFreq=9.5, Range=[336, 342], nPoints=512, Harmonic=1)
B, spec = garlic(sys, exp)
```

### Fitting (`esfit`)

```python
import numpy as np
from torchspin import esfit, FitOptions

def model(p):
    sys_mod = SpinSystem(S=[0.5], g=[[p[0], p[1], p[2]]], lw=[1.0, 0.0])
    _, spc = pepper(sys_mod, exp, Options(GridSize=31, Verbosity=0))
    return spc.numpy()

result = esfit(
    data_measured, model,
    p0=np.array([2.00, 2.10, 2.20]),
    lb=np.array([1.95, 2.05, 2.15]),
    ub=np.array([2.05, 2.15, 2.25]),
    options=FitOptions(method='global', progress='text'),
)
print('Best-fit g:', result.pfit)
```

`progress='text'` prints RMSD, evaluation count and ETA as it runs, and
interrupting the kernel returns the best fit so far. Population-based methods
parallelize over processes with `FitOptions(n_workers='auto')`.

### Differentiable spectrum

```python
import torch
from torchspin import differentiable_spectrum

g = torch.tensor([2.0, 2.1, 2.2], dtype=torch.float64, requires_grad=True)
B, spec = differentiable_spectrum(g, mwFreq_GHz=9.5, B_range=(300, 380),
                                  nPoints=512, lw_mT=1.0)
loss = ((spec - target_spec) ** 2).sum()
loss.backward()
print('dL/dg:', g.grad)
```

---

## Units and conventions

These match EasySpin, so parameters transfer directly:

| | |
|---|---|
| Energy | MHz |
| Magnetic field | mT |
| Euler angles | radians, z-y'-z'' **passive** rotation |
| Basis ordering | m = S, S−1, …, −S (descending) |
| Default dtype | `torch.complex128` |

---

## What's included

| Category | Modules |
|----------|---------|
| CW EPR | `pepper` (powder; `matrix`, `perturb` and `hybrid` resonance solvers, `grid_convergence` to check `GridSize`), `garlic` (solution), `chili` (slow-motion), `salt` (ENDOR) |
| Pulse EPR | `saffron` (ESEEM/HYSCORE), `spidyan` (arbitrary sequences), `saffron_thyme` (real pulses) |
| Trajectory | `cardamom` (MD/diffusion/jump), `mdload`, `mdhmm` |
| Magnetometry | `curry` (susceptibility, magnetization) |
| Fitting | `esfit` — 10 methods (simplex, L-BFGS-B, Powell, Levenberg–Marquardt, trust-region reflective, grid, Monte Carlo, genetic, swarm, and `global` = swarm + simplex polish); `torchspin.fitgui` interactive panel |
| Autograd | `differentiable_spectrum`; `pepper`, `garlic`, `salt`, `saffron` and `curry` are differentiable |
| Machine learning | `torchspin.ml` — parameter packing, batched simulation, datasets, differentiable losses |
| Data I/O | `eprload` (14 vendor formats), `eprsave` (BES3T), `orca2torchspin` |
| Batch / GPU | `batch_pepper`, `batch_simulate`; `Options(device='cuda')` |

---

## Tutorials

Start with `01_torchspin_quickstart.ipynb`, then branch to autograd or fitting
depending on what you need. Full index: [docs/index.md](docs/index.md).

| Notebook | What it covers |
|---|---|
| `01_torchspin_quickstart` | Core API: spin systems, Hamiltonians, powder and solution spectra |
| `02_gpu_and_autograd` | GPU execution and differentiable spectra |
| `03_spectral_fitting` | Parameter recovery with `esfit` |
| `04_benchmarks` | Timings against MATLAB/EasySpin |
| `05_fitting_real_world` | Bad starting guesses, loss landscapes, global vs local search |
| `06_blue_copper_fitting` | End-to-end Cu(II) case study |
| `07_easyspin_parity` | Overlays against stored EasySpin references |
| `08_pepper_playground` | Simulate, add noise, fit with every `esfit` method |
| `09_fit_gui` | Interactive fitting panel: sliders, live fit, RMSD trace |

They live in `examples/notebooks/`, with scripted equivalents of EasySpin's
example collection in `examples/{solidstate,liquids,slowmotion,endor,fitting,magnetometry}`.

---

## Accuracy and performance

Every simulator with an EasySpin counterpart is checked against stored MATLAB
reference output on each commit. Agreement is cosine ≥ 0.999 (most
0.9999–1.0000) for `pepper`, `garlic`, `salt`, `saffron`, `spidyan` and
`curry`, with absolute intensities matched where EasySpin defines them.
`chili` and the stochastic `cardamom` have documented caveats. Details per
simulator, with the evidence: [KNOWN_LIMITATIONS.md](KNOWN_LIMITATIONS.md).

On speed, torchspin is competitive rather than uniformly faster. On an audited
nine-workload comparison it beat EasySpin on six — most strongly on
slow-motion `chili` (7.3×) and trajectory `cardamom` (2.0×) — and was slower on
three, including matrix-method Cu(II) powders (0.34×). The GPU path pays off
for batched and gradient-based work, not for single simulations. Numbers,
replicates and a per-workload device audit:
[BENCHMARK_VERIFICATION.md](benchmarks/results/workstation_20260904_verified/BENCHMARK_VERIFICATION.md).

---

## Citation

If you use torchspin in published work, please cite both:

- **TorchSpin** — Follmer, A.H. *arXiv:2610.18924* (2026) https://arxiv.org/abs/2610.18924.
- **EasySpin**, the MATLAB toolbox this port is based on —
  Stoll, S. & Schweiger, A. *J. Magn. Reson.* **178** (2006) 42–55.


---

## Contributing

Bug reports, documentation fixes and pull requests are welcome. Development
install and the test layout are in [CONTRIBUTING.md](CONTRIBUTING.md):

```bash
git clone https://github.com/follmerlab/torchspin.git
cd torchspin
pip install -e ".[dev]"
pytest -q -m "not slow" -k "not matlab_validation"   # fast pass, ~4 min
```

The MATLAB reference data ships in the repository but not in the PyPI package,
so the cross-validation suite only runs from a checkout.

---

## Related

- [EasySpin](https://easyspin.org) — the MATLAB reference implementation
- [PyTorch](https://pytorch.org) — the autograd engine

## License

MIT — see [LICENSE.md](LICENSE.md). torchspin is derived from EasySpin, which is
also MIT licensed; both copyrights are reproduced there.
