xnn
Machine-learning interatomic potentials for molecules and materials behind a single coherent PyTorch interface.
Installation
From PyPI, where the distribution is called xnns (the import name and the
command line are xnn):
pip install xnns # core (torch, numpy, pyyaml)
pip install "xnns[gnn,ase]" # + e3nn for NequIP/MACE/Allegro, + ASE calculator
From a clone, for development:
pip install -e . # core (torch, numpy, pyyaml)
pip install -e ".[ase]" # + ASE calculator
pip install -e ".[gnn]" # + e3nn for NequIP/MACE/Allegro
pip install -e ".[hydra]" # + Hydra/OmegaConf config
pip install -e ".[examples]" # + ASE, e3nn, mace-torch, nequip, jupyter (runs the notebooks)
pip install -e ".[all]"
The Allegro reference implementation used by one fidelity notebook is not on
PyPI; install it separately with
pip install "git+https://github.com/mir-group/allegro@v0.3.0".
Quick start
# builds trains a model on toy data and predicts on a test set
python examples/quickstart.py
# smoke tests for all models and families
pytest tests/
from xnn.common.config import Config
from xnn.common.data import AtomicDataset
from xnn.common.train import Trainer
cfg = Config()
# E.g. schnet | hdnnp | ani | physnet | nequip | mace | allegro | cace | reaxff
cfg.model.name = "nequip"
# Unshared model specific hyperparameters go here
cfg.model.extra = {"species": [1, 6, 8], "l_max": 2}
# batch_size = 1 disables batch training
cfg.data.batch_size = 16
# auto | cpu | cuda | cuda:0
cfg.device = "auto"
Trainer(cfg, AtomicDataset(structures, cfg.model.cutoff)).fit()
structures is a list of plain dictionaries (pos, atomic_numbers, and
optionally, cell/pbc and energy/forces/stress targets). Data in any
ASE-native format loads directly and targets can also be included. So, no
pre-wrapping is required:
# xnn supports ase-io native formats such as .extxyz, .cif, VASP, ...
train_set = AtomicDataset.from_file("trajectory.extxyz", cutoff=4.0)
Design principles
Four ideas hold xnn together:
- Unified data object. Every model consumes an
AtomicGraphand returns{"node_energy", "energy"}. Molecular vs. periodic is invisible to models: periodicity lives only inedge_vectors()(r_ij = pos[dst] - pos[src] + cell_shift @ cell), keeping forces and stress differentiable. - Featurizers are first-class. A
Featurizer(subclass ofnn.Module) turns a graph into invariant descriptors (symmetry functions, AEV) or equivariant edge attributes (spherical harmonics, Cartesian monomials). Descriptor models (HDNNP, ANI) and GNNs (NequIP/MACE/Allegro/CACE) are thin compositions over featurizers, so the featurization is reusable and inspectable on its own. - Forces/stress in one place.
ForceStressOutputwraps any model and differentiates energy w.r.t. positions (forces) and a symmetric strain (stress). Models never implement them. The same wrapper idea powersLatentEwald(Latent Ewald Summation, Cheng 2025): every model exposes invariant"node_features", so long-range electrostatics/dispersion can be added to any short-range model withextra: {long_range: {...}}— a faithful port of the CACE-LR reference implementation (seetests/test_les.pyandexamples/gnn/les/).D4DispersionandD3Dispersiondo the same for London dispersion: the charge-dependent DFT-D4 model (Caldeweyher 2019) and the DFT-D3 model (Grimme 2010/2011, all four damping functions),extra: {dispersion: {name: d4 | d3, ...}}, each reproducing its reference code (dftd4,simple-dftd3) to machine precision (tests/test_d4.py,tests/test_d3.py,examples/common/). - Extensibility via registry + one config, three frontends.
@register_model("name")+ afrom_configclassmethod makes a model usable from any of YAML / argparse / Hydra, which all funnel into oneConfigdataclass.
# Data (structures dict -> AtomicGraph)
from xnn.common.data import AtomicDataset, build_neighbor_list
ds = AtomicDataset(structures, cutoff=5.0)
graph = ds[0]
# Featurizers (AtomicGraph -> model inputs)
from xnn.dnn.featurizers import AEV, RadialSymmetryFunctions
from xnn.gnn.featurizers import SphericalHarmonicEdgeEmbedding
# (N, D) invariant per-atom AEV
descriptor = AEV(species=[1, 6, 8])(graph)
# Equivariant edge attributes
edges = SphericalHarmonicEdgeEmbedding(l_max=2)(graph)
# Models (model inputs -> energy)
from xnn.common.models import build_model, ForceStressOutput, available_models
# Any registered model + autograd forces/stress
model = ForceStressOutput(build_model(cfg.model))
Config frontends (interchangeable)
from xnn.common.config import from_yaml, from_argparse, from_hydra
cfg = from_yaml("configs/train.yaml")
cfg = from_argparse(["--config", "configs/train.yaml", "--set", "model.cutoff=6.0"])
CLI: xnn train --config configs/train.yaml --set optim.epochs=50
(also xnn benchmark --config configs/benchmark.yaml and
xnn export --config ... --ckpt ... --to lammps|torchscript).
Model keys copied verbatim from an upstream code's yaml also work: a per-model
key-translation registry (xnn.common.config.translate) rewrites the foreign
spellings (MACE-CLI r_max/num_radial_basis/atomic_numbers/E0s, NequIP
num_layers, ...) to the xnn canonical names at config-load time; the xnn
spelling wins if both are given. Extend it for another code with
register_key_translation("name", {...}).
Deployment
from xnn.common.deploy import XNNCalculator, export_to_lammps
atoms.calc = XNNCalculator(model, cutoff=5.0) # ASE
export_to_lammps(model, cutoff=5.0, path="deployed.pt") # TorchScript for LAMMPS
Pair the exported .pt with the matching C++ pair style (pair_nequip /
pair_mace / pair_allegro pattern). The LAMMPSWrapper in
common/deploy/lammps.py defines the tensor ABI. A model is exportable when it
provides the scriptable node_energy(atomic_numbers, edge_index, edge_vec)
core -- SchNet, NequIP, MACE and Allegro all do (the scripted models reproduce
the eager ones to ~1e-15, verified in tests/test_schnet.py /
tests/test_mace.py / tests/test_nequip.py / tests/test_allegro.py). For NequIP this required a scriptable, bit-exact stand-in
for e3nn's Gate (xnn.gnn.models.nequip._Gate), which the e3nn 0.4.4
original cannot do on torch 2.x.
Benchmarking
Score a set of pre-trained models on one dataset with
xnn.common.benchmark and tabulate their errors. A single config lists the
models (each an architecture plus the checkpoint to load) and the
metrics mapping, which ties each target quantity (energy / forces /
stress) to the error metrics reported for it (mae / mse / rmse, or
custom callables). Results are tabulated per model and
written to CSV / JSON / Markdown (or a user-registered format). Energy can be
scored per atom or, with atomic_energies (a {Z: E0} map or average to fit
from data), as the physically meaningful atomization (interaction) energy.
Benchmarking does not train — produce the checkpoints first with xnn train.
xnn benchmark --config configs/benchmark.yaml
from xnn.common.benchmark import from_yaml, run_benchmark
rows = run_benchmark(from_yaml("configs/benchmark.yaml"))
The benchmark builds models with the same Config and model registry as a
single run; new metrics and output formats plug in via @register_metric and
@register_writer, mirroring @register_model.
Available models
| Model | Family | Featurizers | State |
|---|---|---|---|
| SchNet | cnn | Gaussian RBF | Under development |
| PhysNet | dnn | exp-Gaussian rbf + attention masks | Complete: Training, Evaluation, Deployment (ASE only) |
| HDNNP | dnn | radial symmetry functions (G2) | Under development |
| ANI | dnn | AEV (radial + angular symmetry functions) | Complete: Training, Evaluation, Deployment (ASE only) |
| NequIP | gnn | spherical-harmonic edges | Complete: Training, Evaluation, Deployment (TorchScript, LAMMPS, ASE) |
| MACE | gnn | spherical-harmonic edges | Complete: Training, Evaluation, Deployment (TorchScript, LAMMPS, ASE); loads the pretrained MACE-MP / MACE-OFF foundation models via MACE.from_foundation() |
| CACE | gnn | Cartesian monomial edges | Complete: Training, Evaluation, Deployment (ASE only) |
| Allegro | gnn | spherical-harmonic edges | Complete: Training, Evaluation, Deployment (TorchScript, LAMMPS, ASE) |
| BAMBOO | hybrid | exp-normal rbf + multi-head edge attention | Complete: Training, Evaluation, Deployment (ASE only) |
| ReaxFF / ReaxFF-nn | ffnn | bond orders + EEM charges (the force field is the model) | Complete: Training, Evaluation, Deployment (ASE only) |
| OPLS / OPLS-AA / L-OPLS | ffnn | fixed valence topology (the force field is the model) | Complete: Training, Evaluation, Deployment (ASE only) |
| DREIDING / DREIDING-X6 | ffnn | rule-generated valence terms (the force field is the model) | Complete: Training, Evaluation, Deployment (ASE only) |
Package layout
The package is organized by model family (gnn, cnn, dnn, ffnn, hybrid),
with shared resources factored into the common modules and reusable
transformer building blocks in transformer. An object (e.g., function, module
etc.) lives with the model family that uses it, or with common if more than
one family needs it. Of course, layers are designed as stand-alone entities and
can be imported on their own.
src/xnn/
├── __main__.py `python -m xnn` entry point
├── common/ shared across all model families
│ ├── data/ - common data abstractions
│ │ └── … + AtomicGraph (the one data object), PBC neighbor list, AtomicDataset, ASE I/O
│ ├── featurizers/ - common featurizers
│ │ └── … + Featurizer base + shared basis functions (GaussianRBF, CosineCutoff)
│ ├── config/ - one dataclass schema; loaders for yaml / argparse / hydra
│ │ └── … + schema, loaders, translate, coerce
│ ├── models/ - InteratomicPotential interface, registry, ForceStressOutput, ops (scatter_sum, shifted_softplus)
│ │ └── … + base, registry, outputs, ops, les (Latent Ewald), dispersion (shared D3/D4 machinery), d4 (DFT-D4), d3 (DFT-D3)
│ ├── train/ - Trainer (batch + device aware), weighted energy/force/stress loss
│ │ └── … + trainer, losses
│ ├── benchmark/ - score pre-trained models on a dataset (metrics, atomization energy, report writers)
│ │ └── … + config, runner, metrics, energy, report
│ ├── deploy/ - ASE Calculator, and LAMMPS/TorchScript export
│ │ └── … + ase_calculator, lammps
│ └── cli/ - the `xnn` command-line interface
│ └── main.py
├── gnn/ graph potentials
│ ├── featurizers/ - GNN featurizers
│ │ └── … + spherical, cartesian, radial, cutoff
│ └── models/ - base (GNNPotential, EquivariantGNN), blocks, nequip, mace, allegro, cace
│ └── … + base, blocks, nequip, mace, allegro, cace
├── cnn/ continuous-filter conv net
│ └── models/ - schnet
├── dnn/ descriptor + per-element networks, and PhysNet
│ ├── featurizers/ - DNN featurizers
│ │ └── … + symmetry functions, AEV
│ └── models/ base (DescriptorPotential), hdnnp, ani, physnet
│ └── …
├── ffnn/ learnable classical force fields
│ ├── common/ - frc (SEAMM .frc force-field files: reader, resolver, writer, registry),
│ │ typing (SMARTS atom typing), elements
│ ├── data/ - shipped parameter files: oplsaa.frc, lopls.frc, oplsaa_1996.frc, reaxff/*.frc
│ └── models/ - reaxff, ffield, opls, oplslib, topology
│ └── … + ReaxFF / ReaxFF-nn reactive force field, OPLS / L-OPLS and DREIDING fixed-topology
│ force field, the .frc <-> model parameter bridges
├── transformer/ shared graph-transformer building blocks
│ ├── attention.py - EdgeMultiheadAttention (multi-head QKV attention on edges)
│ └── featurizers/ - ExpNormalSmearing radial basis
└── hybrid/ GNN + transformer potentials with a physics energy split
└── models/ - bamboo (BAMBOO graph equivariant transformer), dispersion (D3(CSO))
└── …
Examples
Runnable, pre-executed notebooks live under examples/ (pip install -e ".[examples]"). They are grouped by model family, where the leading letter of
<xnn> names the architecture type (gnn graph, cnn convolutional, dnn
deep/descriptor, ffnn force-field, hybrid mixed):
- Per-model training and MD (
examples/<xnn>/<model>/):<model>_argon_train_test.ipynband<model>_argon_density_md.ipynbfor MACE, NequIP, Allegro, CACE and PhysNet.schnet_rmd17_train.ipynbandschnet_ethanol_md.ipynbfor SchNet.ani_rmd17_train.ipynbplus theani1*_dataset.ipynb/ani2x_dataset.ipynbdataset walk-throughs for ANI.mace_foundation_molecules.ipynbandmace_foundation_materials.ipynbfor the pretrained MACE foundation models (MACE.from_foundation()): MACE-OFF23 on torsions / hydrogen bonds /Trainerfine-tuning, and equations of state across the MACE-MP generations. Every published checkpoint's conversion is verified inexamples/fidelity_checks/mace_foundation_verification.ipynb.les_molecular_dimers.ipynbfor the LES long-range wrapper (charged and polar dimers) andrecreate_mace_architecture.ipynbfor a block-by-block MACE rebuild.bamboo_dimer_electrostatics.ipynbandbamboo_charge_analysis.ipynbfor the hybrid BAMBOO model.common/d4/d4_paper_examples.ipynbandcommon/d4/d4_benchmark.ipynbfor the DFT-D4 dispersion add-on (paper reproductions, benchmark againstdftd4, deployment of a D4-corrected MLIP);common/d3/likewise for DFT-D3 (againstsimple-dftd3).
- Force fields (
examples/ffnn/):reaxff/reaxff_rmd17_train_test.ipynbtrains the ReaxFF-nn reactive force field on rMD17 andreaxff/reaxff_md_bond_orders.ipynbanalyses its bond orders, EEM charges and bond dissociation in MD. Both benchmark against the published classical Chenoweth 2008 C/H/O field, loaded from the shipped SEAMM.frcfile (ReaxFF("CHO_cho_2008")).opls/opls_conformational_energetics.ipynbreproduces Table 1 of the 1996 OPLS-AA paper with a relaxed dihedral driver, typing every molecule from coordinates with the SMARTS templates ofoplsaa.frc.opls/opls_lopls_torsion_refit.ipynbre-derives the L-OPLS hydrocarbon torsion refit of Siu et al. (2012) by gradient descent (trainable=("dihedral_v",)) and writes the trained field back out as a.frcfile.dreiding/dreiding_conformational_energetics.ipynbreproduces the rotational barriers of Table XI and the conformational energies of Table XII of the 1990 DREIDING paper (14 molecules, mean difference ~0.01 kcal/mol from the paper's own calculated values) from relaxed scans.dreiding/dreiding_refit_aromatics.ipynbrefits DREIDING's generators on benzene against rMD17 PBE forces and tests whether they transfer to naphthalene and toluene, neither of which is trained on.
- Data and deployment:
data/load_dataset_tutorial.ipynbcovers the one-line dataset hub used by every training notebook (load_dataset("argon_md"),load_dataset("rmd17", ...),load_dataset("ani1", ...),load_dataset("lode_dimers", subset="bio_scan")).deploy/mdi_argon_md.ipynbanddeploy/mdi_argon_lammps.ipynbdrive a trained model from an external MD code through the MDI engine.examples/quickstart.pyis a minimal train/predict script on toy data.
- Fidelity checks (
examples/fidelity_checks/<model>_verification.ipynb):- Block-by-block numerical comparisons against the upstream codes for MACE, NequIP, Allegro, CACE, SchNet, PhysNet, ANI, BAMBOO, LES, DFT-D4 and DFT-D3.
- OPLS is verified against OpenMM (an independent MD engine, optional
dependency) in
opls_verification.ipynbandtests/test_opls.py. - DREIDING is verified term by term against LAMMPS's DREIDING styles
(an independent MD engine, optional dependency) in
dreiding_verification.ipynb, and against the paper's own tables intests/test_dreiding.py. - ReaxFF is the exception: the authors' reference implementation of
ReaxFF-nn is AGPL-licensed, so no verification notebook or test depending
on it (and no code derived from it) is distributed with this MIT-licensed
code base. The implementation follows the published equations, was checked
against that implementation during development without redistributing
anything from it, and ships with self-contained equation-by-equation tests
instead (
tests/test_reaxff.py; see the fidelity notes in the documentation).
Extension points
- Implement a new model:
- Pick your model family package (
gnn/cnn/dnn/ffnn, or add one) and add a module under<family>/models/. - Subclass
xnn.common.models.InteratomicPotential(or the family base, e.g.,gnn.models.base.EquivariantGNN) and implement itsforward(data)method. - register your model implementation using
@register_modeldecorator. - Add a YAML config file for your model
configs/model/<name>.yaml. - Import the family package so the model registers.
- For TorchScript/LAMMPS export, it is important to expose a scriptable
node_energy(atomic_numbers, edge_index, edge_vec)core (SchNet shows the pattern; e3nn models need e3nn's JIT support for this).
- Pick your model family package (
- Add a new featurizer:
- Subclass
xnn.common.featurizers.Featurizerand implementoutput_dimandforward(data). Put the resulting featurizer module in thecommon/featurizers/if shared by more than one model family or under the using family'sfeaturizers/if it is only used by that one model family.
- Subclass
Release files for xnns 0.3.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 | |
|---|---|---|---|
| xnns-0.3.0.tar.gz | 1.3 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xnns-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.5 MB
Release files / xnns-0.3.0.tar.gz
| Download URL | xnns-0.3.0.tar.gz |
|---|---|
| Size | 1.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83db24e075fa099f1f6414c6377239ecadaf9d3892cb67d989b34808162d90b3
|
|
BLAKE2b-256 checksum How to use checksums |
ed7bced8a3c1baf08c0382452f57eb0653843128ca1207d8d59dd60250e97cfd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency logRelease files / xnns-0.3.0-py3-none-any.whl
| Download URL | xnns-0.3.0-py3-none-any.whl |
|---|---|
| Size | 1.2 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
14bacbd7b2ef8c57612965eb20081273299558f9527c4be91cdd8a303a1e754c
|
|
BLAKE2b-256 checksum How to use checksums |
17a4b449450869763377cfa6d096bdaaefb6f80d30426ef61b754f3539a9c052
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency log