kups.potential.mliap.torch.interface
¶
Universal PyTorch MLFF interface.
Mirrors the JAX tojax interface for PyTorch
models. Each MLFF backend only needs to provide a torch.nn.Module whose
forward consumes the universal AtomGraphInput
and returns a dict with "energy", "position_gradients", and
optionally "cell_gradients". All graph extraction, padding, and kUPS
Potential wiring is handled here.
Example
from kups.application.potential.filter import POSITIONS_AND_CELL
from kups.application.potential.mliap.torch import make_torch_mliap_from_state
from kups.potential.mliap.torch.interface import TorchMliap
# A backend provides a Module with the universal forward contract:
model = TorchMliap.from_module(my_module, cutoff=6.0, compute_cell_gradients=True)
# Wire into a kUPS Potential:
potential = make_torch_mliap_from_state(state_lens, gradient=POSITIONS_AND_CELL)
Requires the torch_dev dependency group: uv sync --group torch_dev.
AtomGraphInput
¶
Bases: TypedDict
Universal input schema shared by all torch MLFF backends.
Mirrors the JAX AtomGraphInput.
Shapes use N atoms, B systems, and E edges (each padded by one
extra atom/system to work around backends that cannot handle empty graphs).
Source code in src/kups/potential/mliap/torch/interface.py
IsTorchMliapParticles
¶
Bases: IsRadiusGraphPoints, HasAtomicNumbers, Protocol
Particle protocol for torch MLFF models.
Source code in src/kups/potential/mliap/torch/interface.py
TorchMliap
¶
Container for a torch MLFF wired into JAX.
Attributes:
| Name | Type | Description |
|---|---|---|
cutoff |
Table[SystemId, Array]
|
Per-system cutoff radius [Å]. |
wrapper |
TorchModuleWrapper
|
|
compute_cell_gradients |
bool
|
Whether the module returns |
Source code in src/kups/potential/mliap/torch/interface.py
call(input)
¶
from_module(module, cutoff, compute_cell_gradients=False)
staticmethod
¶
Wrap a torch.nn.Module that returns energy and gradients.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
torch |
required |
cutoff
|
float
|
Interaction cutoff radius [Å]. |
required |
compute_cell_gradients
|
bool
|
Whether the module returns
|
False
|
Returns:
| Type | Description |
|---|---|
'TorchMliap'
|
Configured |
Source code in src/kups/potential/mliap/torch/interface.py
TorchMliapForward
¶
Bases: Protocol
Forward contract for a torch MLFF module.
The module must accept an AtomGraphInput dict and return a dict with:
"energy":(B,)per-system total energies."position_gradients":(N, 3):math:\partial E / \partial r."cell_gradients":(B, 3, 3):math:\partial E / \partial h, required only whencompute_cell_gradients=True.
Outputs are gradients (not forces); adapters around models that natively produce forces/virials negate appropriately inside the module.
Source code in src/kups/potential/mliap/torch/interface.py
lattice_gradient_from_virial(forces, positions, batch, cell, virial)
¶
Recover ∂E/∂h from a symmetric-strain virial.
Many torch MLFF backends (MACE, UMA, …) return a virial or stress quantity
that encodes the gradient of energy under a symmetric infinitesimal strain
applied jointly to positions and cell. In kUPS's row convention
(r = frac @ h; lattice vectors are the rows of h) that virial is
virial = pos_virial + cell_virial (exactly symmetric)
where
pos_virial[s, j, k] = Σ_{b∈s} (∂E/∂r_b)_j · (r_b)_k
cell_virial = (∂E/∂h)^T @ h
Rotational invariance makes the total symmetric, but cell_virial on its
own is not. Its antisymmetric part is pinned by pos_virial, known exactly
from forces and positions, so the raw lattice gradient (antisymmetric part
included) is recovered by
∂E/∂h = h^-T @ (virial - pos_virial^T).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
forces
|
'torch.Tensor'
|
|
required |
positions
|
'torch.Tensor'
|
|
required |
batch
|
'torch.Tensor'
|
|
required |
cell
|
'torch.Tensor'
|
|
required |
virial
|
'torch.Tensor'
|
|
required |
Returns:
| Type | Description |
|---|---|
'torch.Tensor'
|
|
Source code in src/kups/potential/mliap/torch/interface.py
make_torch_mliap_potential(particles_view, systems_view, neighborlist_view, model, patch_idx_view=None, out_cache_lens=None, gradient=None)
¶
make_torch_mliap_potential(
particles_view: View[State, Table[ParticleId, P]],
systems_view: View[State, Table[SystemId, S]],
neighborlist_view: View[State, NNList],
model: View[State, TorchMliap] | TorchMliap,
patch_idx_view: View[
State, PotentialOut[PositionsAndCell, EmptyType]
]
| None = None,
out_cache_lens: Lens[
State,
KahanSummand[
PotentialOut[PositionsAndCell, EmptyType]
],
]
| None = None,
) -> Potential[
State, PositionsAndCell, EmptyType, Patch[State]
]
make_torch_mliap_potential(
particles_view: View[State, Table[ParticleId, P]],
systems_view: View[State, Table[SystemId, S]],
neighborlist_view: View[State, NNList],
model: View[State, TorchMliap] | TorchMliap,
patch_idx_view: View[
State, PotentialOut[PositionsAndCell, EmptyType]
]
| None = None,
out_cache_lens: Lens[
State,
KahanSummand[
PotentialOut[PositionsAndCell, EmptyType]
],
]
| None = None,
*,
gradient: Lens[Geometry, PositionsAndCell],
) -> Potential[
State, PositionsAndCell, EmptyType, Patch[State]
]
Create a kUPS Potential from a TorchMliap.
Forces and stress are computed inside the torch module; the kUPS side just
routes the precomputed PositionsAndCell gradients through
DirectPotential. Without a gradient the raw PositionsAndCell
gradients pass through; with one they are pulled back through gradient.set
into ∂E/∂u — the pullback is hooked here, where the gradients are concretely
PositionsAndCell.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
particles_view
|
Any
|
Extracts particle data from state. |
required |
systems_view
|
Any
|
Extracts system data (cell) from state. |
required |
neighborlist_view
|
Any
|
Extracts a cutoff-bound neighbor list from state. |
required |
model
|
Any
|
|
required |
patch_idx_view
|
Any | None
|
Cached output index structure (optional). |
None
|
out_cache_lens
|
Any | None
|
Cache location lens (optional). |
None
|
gradient
|
Lens[Geometry, PositionsAndCell] | None
|
Relaxation filter |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
Configured |
Source code in src/kups/potential/mliap/torch/interface.py
torch_mliap_model_fn(inp)
¶
Run a TorchMliap on a graph input and package the result.
Always packages "cell_gradients" into a PositionsAndCell gradients
structure (the module must produce them); downstream consumers that only
need forces let XLA prune the unused cell-gradient ops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inp
|
TorchMliapInput[P, S]
|
Graph potential input bundling the model and graph. |
required |
Returns:
| Type | Description |
|---|---|
WithPatch[PotentialOut[PositionsAndCell, EmptyType], IdPatch[Any]]
|
|
WithPatch[PotentialOut[PositionsAndCell, EmptyType], IdPatch[Any]]
|
gradients, and an identity patch. |