Skip to content

Add an O(3)-symmetrized atomistic model wrapper - #294

Open
MichelangeloDomina wants to merge 11 commits into
metatensor:mainfrom
MichelangeloDomina:review/symmetrized-model-progressive
Open

Add an O(3)-symmetrized atomistic model wrapper#294
MichelangeloDomina wants to merge 11 commits into
metatensor:mainfrom
MichelangeloDomina:review/symmetrized-model-progressive

Conversation

@MichelangeloDomina

@MichelangeloDomina MichelangeloDomina commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds metatomic.torch.symmetrized_model: wrap an exported AtomisticModel to
average its predictions over O(3) with a finite rotation quadrature, and to
measure how far the underlying model is from being equivariant.

Everything is selected through regular output requests:

Requested output Result
<name> O(3) average of the underlying output
o3::variance::<name> per-sample equivariance variance
o3::character_projection::<name> squared character-projection contribution per irrep

SymmetrizedModel.wrap() returns a regular exportable AtomisticModel
(metadata, capabilities, neighbor lists, and custom inputs are carried over),
so the wrapped model can be used anywhere the original could, including
TorchScript export.

The conventions (quadrature, variance definition, irrep bookkeeping in
TensorMap keys) are documented in docs/src/torch/reference/symmetrized-model.rst.

Limitations

  • the quadrature is exact only up to a maximal angular momentum; convergence
    should be checked by increasing max_o3_lambda_grid;
  • CPU and CUDA only, float32/float64;
  • explicit TensorBlock gradients are not supported (PyTorch autograd through
    returned values works).

Contributor (creator of pull-request) checklist

  • Tests updated (for new features and bugfixes)?
  • Documentation updated (for new features)?
  • [ ] Issue referenced (for PRs that solve an issue)?

Reviewer checklist

  • CHANGELOG updated with public API or any other important changes?

@ppegolo
ppegolo force-pushed the review/symmetrized-model-progressive branch from c2acacf to 7e23329 Compare July 28, 2026 09:06
@ppegolo
ppegolo marked this pull request as ready for review July 28, 2026 12:42
@ppegolo
ppegolo requested review from Luthaf and removed request for ppegolo July 28, 2026 13:20

@Luthaf Luthaf left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only checked the code, I'll try to do a pass on the tests ASAP

O(3)-symmetrized models
=======================

The :py:mod:`metatomic.torch.symmetrized_model` module wraps an exported

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's export it as metatomic.torch.SymmetrizedModel, and make the module private

O(3)-symmetrized models
=======================

The :py:mod:`metatomic.torch.symmetrized_model` module wraps an exported

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The :py:mod:`metatomic.torch.symmetrized_model` module wraps an exported
The :py:mod:`metatomic.torch.symmetrized_model` module wraps an existing

Comment on lines +8 to +11
averaging and equivariance diagnostics. Ordinary outputs are averaged over
rotated and inverted copies of each input. Additional output names request an
equivariance variance or squared character-projection contributions of the
model response.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
averaging and equivariance diagnostics. Ordinary outputs are averaged over
rotated and inverted copies of each input. Additional output names request an
equivariance variance or squared character-projection contributions of the
model response.
averaging and equivariance diagnostics. Pre-existing outputs of the model are
averaged over rotated and inverted copies of each input.
:py:class:`SymmetrizedModel` also add extra outputs to compute the equivariance
variance or squared character-projection contributions of the model response.

Comment on lines +13 to +33
Output requests
---------------

The requested output name selects both the source output and the calculation:

.. list-table::
:header-rows: 1

* - Requested and returned name
- Result
* - ``<name>``
- O(3) average of the underlying ``<name>`` output
* - ``o3::variance::<name>``
- component-averaged equivariance variance of ``<name>``
* - ``o3::character_projection::<name>``
- unnormalized squared character-projection contributions of ``<name>``

``<name>`` is preserved verbatim. It can therefore be a standard quantity, a
variant such as ``energy/pbe``, or a custom name such as
``mtt::feature::node``. For example,
``o3::variance::energy/pbe`` evaluates the underlying ``energy/pbe`` output.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not very clear, but I can rephrase when everything else is done here =)

.. autoclass:: SymmetrizedModel
:members:

.. autofunction:: get_rotation_quadrature

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this part of the public API?

import torch
from metatensor.torch import Labels, TensorBlock, TensorMap

from ._utils import (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functions in ._utils (or this file) that are intended to be imported by other files should not start by an underscore.

Function in a class starting with underscore => the function is private to the class/the module that defines the class

Function in a module starting with underscore => the function is private to the module and should not be imported outside of it.

The module itself is private and should not be used by code outside of metatomic, but it can still provide public functions to be used outside of it.

This way, we easily see if a function is just a current implementation detail or something used outside of the current module/file.

_quadrature.py does this well

return TensorMap(Labels(key_names + ["chi_lambda"], values), blocks)


def _character_projection_tensormap_from_cosets(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain to me what a coset is?

@ppegolo ppegolo Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are in general subsets of groups that can be used to split a group in some interesting way. In this case into proper (SO(3)) and improper (SO(3) * inversion) rotations. Coset is the correct word here (subgroup would be wrong and apply to the SO(3) case only). We could call it "subset", but we would be using a generic word instead of the specific one. I can add a comment or update the docs to explain it

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cosets is fine, just might be worth a small comment for people like me who forgot all their group theory

from ..o3 import O3Transformation


def _build_packed_wigner_matrices(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love that we have a different representation here and in O3Transformation. This is just putting all matrices in the same tensor, right? I assume this is for efficiency purposes?

Could we change the format used by O3Transformation to be the same as this one?

Comment on lines +645 to +647
max_o3_lambda_target: int,
max_o3_lambda_input: int = 0,
max_o3_lambda_character: Optional[int] = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we auto-guess these from the model by default, by checking the capabilities and extra inputs against the list of standard quantities? And then allow to override for custom inputs/outputs?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For non-standard outputs/more complex things down the line, this would be another use case for #271

Comment thread python/metatomic_torch/metatomic/torch/symmetrized_model/_model.py Outdated
@ppegolo
ppegolo force-pushed the review/symmetrized-model-progressive branch from f551943 to 8f87505 Compare July 30, 2026 11:26
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.

3 participants