Skip to content

Compile potentials with their derivatives, and generative cleanups - #811

Merged
stefaanhessmann merged 6 commits into
v3from
sh/v3
Oct 26, 2026
Merged

stefaanhessmann merged 6 commits into
v3from
sh/v3

Conversation

@stefaanhessmann

Copy link
Copy Markdown
Collaborator

Summary

Compiled potentials with their derivatives, plus generative cleanups from sh/v3.

Compile (opt-in everywhere). torch.compile alone cannot train on forces (the force loss needs the gradient of the forces). CompiledPotential records the forward together with the response modules' autograd.grad into one graph, then compiles it:

CompiledPotential(model)        # TypeError: not an AtomisticModel; RuntimeError: torch < 2.14
  __call__(inputs)
    key = (train/eval, postprocessing, input keys/dtypes/devices, sizes 0 or 1)
    once per key: make_fx(forward incl. forces/stress) -> torch.compile(dynamic=True)
    train: graph(params, buffers, inputs)   # force loss needs one backward
    eval:  graph under no_grad
where option (default off)
AtomisticTask / spktrain task.compile=True (distillation runs uncompiled, with a warning)
spkpredict compile=True
spkmd SchNetPackCalculator calculator.compile_model=True
ASE SpkCalculator compile_model=True
spk.dynamics.Calculator compile=True

Coulomb, Ewald and ZBL count molecules from n_atoms instead of int(idx_m[-1]) + 1 (a GPU sync and a data-dependent size).

Generative.

  • e8855064: the sampler's calculator only runs the model (JointCalculator renamed FieldCalculator); updates convert their head and add guidance. GenerativeCalculator, the Score/Noise/Velocity/X0/PseudoForce calculators and ParametrizedModel are removed.
  • 05903841: ReverseODE removed, differential_equations renamed sde.
  • 688e6cb2: MatchingLoss removed; training goes through Diffuse.
  • Merge of v3 (040aca4f): the new docs/api/generative.rst drops the removed names.

Evidence

  • tests/atomistic/test_compile.py (float64, default assert_close), all marked slow. These checks passed on 2026-10-26 (laptop GPU, torch 2.14.1); they have not been rerun since the last commit (2f2a1033):
    • SchNet, PaiNN, SO3net: compiled energy, forces and force-loss parameter gradients equal eager, in train and eval mode.
    • Stress on periodic silicon cells.
    • One-molecule batches get their own graph.
    • AtomisticTask.training_step gradients equal eager; the task pickles.
    • The three calculators give eager forces.
  • Default suite on 2f2a1033: 788 passed, 0 failed, 13 skipped (12 need mace-torch, 1 by design).
  • Compiled vs eager runs:
    • spktrain experiment=md17, synthetic 80-molecule db, 3 epochs: Before (eager) val_loss 190646 / 161211 / 152163, test_forces_mae 332.089; After (task.compile=True) identical to 6 digits.
    • spkpredict compiled vs eager: max relative difference 2e-8 for energy and 3e-7 for forces (float32).
  • Speed (earlier probes, H100 PCIe, SchNet 6 × 128, 21 to 8192 atoms): compiled vs eager 1.8–3.1× for energy+forces, 1.7–2.2× for a training step; about 10–25 s to compile on the first step. A timed end-to-end QM9 training run is still to do.

Merge Danger

Door: two-way

Compile is off by default, and the code paths are unchanged unless an option is set.

Blast Radius: generative API

Code that uses the removed generative classes breaks: MatchingLoss, ReverseODE, ParametrizedModel, GenerativeCalculator, JointCalculator and the per-field score calculators. The new compile options need torch ≥ 2.14 when switched on; the package floor stays at torch 2.5.

🤖 Generated with Claude Code

stefaanhessmann and others added 6 commits October 23, 2026 16:35
The sampler's calculator now only runs the model: FieldCalculator (formerly
JointCalculator) returns the raw outputs at time t. Each update reads its
variable's head and converts it with the variable's parametrization into
the field it steps on, and adds its own guidance to that field as it is.

- dynamics/calculator: JointCalculator renamed FieldCalculator, without
  guidance; GenerativeCalculator and the Score/Noise/Velocity/X0/
  PseudoForce calculators removed; guidance moved from the Calculator base
  onto ForceCalculator.
- dynamics/sample/sampler: updates take no field key; head() reads
  variable.output_key, field() converts it (to_score for SDEUpdate,
  to_velocity for ODEUpdate, to_x0 for AncestralUpdate), guide() adds the
  weighted guidance. The sampler writes the stage time into the batch
  before each model call. ODEUpdate refuses a non-velocity head on a
  non-Gaussian interpolant at assembly.
- dynamics/guidance: a term returns a tensor shaped like the field it is
  added to, for any variable; no position unit handling. guidance_terms()
  and weighted_sum() shared by ForceCalculator and the updates.
- generative/parametrizations: ParametrizedModel removed.
- dynamics/base, dynamics/__init__, differential_equations, docs/api:
  docstrings and API listing follow.
- tests: ported to FieldCalculator and update guidance; new tests for the
  per-update conversion, guidance on every field and on a per-structure
  variable, and the shape check.

docs_new/generation/generation.md §3.2 (guidance converted through the
score) and new_variables.md §3.6 (guidance on the calculator, positions
only) no longer match the code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ReverseODE was used nowhere: ODEUpdate steps along the velocity itself, its
drift the velocity and no diffusion. What is left of the module is the SDE
of a Gaussian schedule and its reversal, so it is now generative/sde.py.

- generative/sde (was differential_equations): ReverseODE removed; module
  docstring covers SDE and ReverseSDE only.
- generative/__init__, generative/parametrizations,
  dynamics/sample/sampler: import from schnetpack.generative.sde.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generative package imports are sorted on v3 and use the renamed sde
module from sh/v3. The new generative API page no longer lists
ReverseODE and ParametrizedModel, which sh/v3 removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generative training goes through the Diffuse transform and an ordinary
supervised loss per variable; the tensor-level MatchingLoss is gone.

- generative: delete losses.py and its package import; the docstrings of
  noising, times and transforms no longer point to it
- docs/api/generative.rst: drop MatchingLoss from the training section
- tests: the toy training helper in tests/dynamics/test_sampling.py
  writes the weighted target regression out; the VE score weighting test
  moves to test_parametrizations.py; the MatchingLoss tests are removed

docs_new/legacy/ still describes MatchingLoss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
compile_potential traces a model's forward together with the
autograd.grad of its response modules (forces, stress) and compiles the
graph with dynamic shapes, so training on forces needs one backward and
inference runs without autograd. Off by default everywhere.

- model.compile: CompiledPotential / compile_potential /
  compile_supported (torch >= 2.14); one graph per train/eval mode,
  postprocessing flag, input keys/dtypes/devices and sizes 0 or 1; no
  shared symbols for sizes that happen to be equal; pickles without its
  graphs
- lightning.AtomisticTask and spktrain: compile (task.compile=True);
  eager with a teacher and on older torch, with a warning
- spkpredict: compile=True
- calculators: compile_model on the spkmd SchNetPackCalculator (config
  calculator.compile_model) and the ASE SpkCalculator, compile on
  dynamics.Calculator
- Coulomb, Ewald, ZBL: count molecules from n_atoms instead of
  int(idx_m[-1]) + 1 (a GPU sync and a data-dependent size)
- docs: model API page, configs and MD user guide
- tests: compiled vs eager in float64 for SchNet, PaiNN and SO3net,
  stress on periodic cells, one-molecule batches, the task's training
  step and pickling, the three calculators; every test that compiles is
  marked slow

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntial

CompiledPotential now raises a TypeError for a model that is not an
AtomisticModel and a RuntimeError on torch < 2.14, instead of warning and
compiling anyway. The call sites no longer check on their own, and
compile_potential, which only constructed the class, is gone.

- model.compile: both checks in CompiledPotential.__init__; remove
  compile_potential
- AtomisticTask, spkpredict: no version fallback; compile=True on older
  torch raises. Distillation still runs uncompiled with a warning
- calculators (spkmd, ASE, dynamics): construct CompiledPotential; the
  dynamics calculator drops its own type check
- docs: model API page and configs guide
- tests: one fast test per check; the compiling tests use the class

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@stefaanhessmann
stefaanhessmann merged commit 328101c into v3 Oct 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant