Source code for xnn.common.featurizers.radial
"""Radial basis expansion shared across families (SchNet + equivariant GNN edge embedding)."""
from __future__ import annotations
import math
import torch
from torch import Tensor, nn
[docs]
class GaussianRBF(nn.Module):
"""Gaussian radial basis expansion (SchNet-style).
Expands scalar interatomic distances onto a set of ``n_rbf`` Gaussians
``e_k(r) = exp(-gamma (r - mu_k)^2)`` whose centers ``mu_k`` are fixed and
evenly spaced on ``[0, cutoff]``. Registered as a non-trainable buffer.
Parameters
----------
n_rbf : int, optional
Number of Gaussian basis functions (centers). Defaults to ``50``.
cutoff : float, optional
Upper bound of the center range, in the same units as the distances.
Defaults to ``5.0``.
gamma : float or None, optional
Width parameter of the Gaussians. ``None`` (default) sets the standard
deviation to the spacing between adjacent centers, i.e.
``gamma = 0.5 / spacing**2``. SchNet (Schuett et al., NIPS 2017) fixes
``gamma = 10`` per Angstrom^2 on a 0.1 Angstrom center grid.
Notes
-----
With ``gamma=None`` the width is the spacing between adjacent centers, or
``1.0`` when ``n_rbf == 1``.
"""
def __init__(self, n_rbf: int = 50, cutoff: float = 5.0,
gamma: float | None = None):
super().__init__()
centers = torch.linspace(0.0, cutoff, n_rbf)
self.register_buffer("centers", centers)
self.width = (centers[1] - centers[0]).item() if n_rbf > 1 else 1.0
self.gamma = float(gamma) if gamma is not None \
else 0.5 / self.width ** 2
[docs]
def forward(self, r: Tensor) -> Tensor:
"""Expand distances onto the Gaussian basis.
Parameters
----------
r : Tensor
Interatomic distances of arbitrary shape ``(...)``.
Returns
-------
Tensor
The radial basis expansion of shape ``(..., n_rbf)``.
"""
diff = r[..., None] - self.centers
return torch.exp(-self.gamma * diff ** 2)