elfes.modules.nn
Reusable operations for constructing and transforming network features.
Irreps
Bases: tuple
slices
slices()
List of slices corresponding to indices for each irrep.
Examples:
Irreps('2x0e + 1e').slices() [slice(0, 2, None), slice(2, 5, None)]
count
count(ir: Irrep) -> int
:returns: total multiplicity of ir.
ComplexLinear
ComplexLinear(
in_channels: int,
out_channels: int,
*,
device: device | str | None = None,
dtype: dtype | None = None,
)
Bases: Module
Apply a bias-free complex matrix to real/imaginary channel pairs.
For \(z=x_0+i x_1\) and \(W=A+iB\), compute \(y=Wz\) as
Inputs are real tensors shaped [..., 2, C_in]; axis -2 holds the
real and imaginary parts. Outputs use the same layout, [..., 2, C_out].
The weight stores \((A,B)\) with shape [2, C_out, C_in].
Each call builds the real block matrix \([[A,-B],[B,A]]\) and applies one real linear map to the flattened pair. Only the final real/imaginary output is materialized, without storing the four individual products.
The map commutes with any common phase multiplication \(z\mapsto e^{i\phi}z\).
This makes it suitable for copies of a nonzero SO(2)/U(1) mode, without
depending on the mode index or the origin of the channels. A nonzero
additive bias would break this phase equivariance and is not supported.
Real invariant channels, such as the \(m=0\) block of SO2Linear, use an
ordinary real nn.Linear instead.
Parameters:
-
(in_channelsint) –Number of complex input channels.
-
(out_channelsint) –Number of complex output channels.
-
(devicedevice | str | None, default:None) –Device on which to create the weight.
-
(dtypedtype | None, default:None) –Real dtype used to store weights and channel pairs.
forward
forward(complex_tensor: Tensor) -> Tensor
Parameters:
-
(complex_tensorTensor) –[..., 2, C_in].
Returns:
-
output_complex_tensor(Tensor) –[..., 2, C_out].
ComplexLinear3M
ComplexLinear3M(
in_channels: int,
out_channels: int,
*,
device: device | str | None = None,
dtype: dtype | None = None,
)
Bases: Module
Apply a bias-free complex linear map using three real matrix products.
This module has the same API and parameter layout as ComplexLinear. For the
complex input \(z=x_0+i x_1\) and complex weight \(W=A+iB\), it computes
and recovers the complex product as
Inputs have shape [..., 2, C_in] and outputs have shape
[..., 2, C_out], with axis -2 containing \((x_0,x_1)\). The
weight has shape [2, C_out, C_in] and stores \((A,B)\).
Parameters:
-
(in_channelsint) –Number of complex input channels.
-
(out_channelsint) –Number of complex output channels.
-
(devicedevice | str | None, default:None) –Device on which to create the weight.
-
(dtypedtype | None, default:None) –Dtype with which to create the real weight.
forward
forward(complex_tensor: Tensor) -> Tensor
Parameters:
-
(complex_tensorTensor) –[..., 2, C_in].
Returns:
-
output_complex_tensor(Tensor) –[..., 2, C_out].
IrrepLayoutTransform
Bases: Module
Reorder SO(3) spherical tensors between l-major and m-major layouts.
Both layouts contain the same components with 0 <= l <= l_max and |m| <= min(l,m_max). The m_max cutoff does not remove any l block; it removes the components with |m| > m_max from every higher-l block. Thus with l_max=3 and m_max=2, the l=3 block retains m=-2,...,+2 but omits m=-3 and m=+3.
For this example, the l-major layout, written as (l,m), is:
l=0: [(0,0)] l=1: [(1,-1), (1,0), (1,+1)] l=2: [(2,-2), (2,-1), (2,0), (2,+1), (2,+2)] l=3: [(3,-2), (3,-1), (3,0), (3,+1), (3,+2)]
The corresponding m-major layout, written as (m,l), is:
m=0: [(0,0), (0,1), (0,2), (0,3)] m=+1: [(+1,1), (+1,2), (+1,3)] m=-1: [(-1,1), (-1,2), (-1,3)] m=+2: [(+2,2), (+2,3)] m=-2: [(-2,2), (-2,3)]
This transform does not truncate m components. It accepts tensors already
truncated to |m| <= m_max and only reorders them. to_m_major and
to_l_major are exact inverses.
Parameters:
-
(l_maxint) –Highest non-negative l.
-
(m_maxint | None, default:None) –Highest retained |m|. Defaults to
l_max. -
(devicedevice | str | None, default:None) –Initial device of the permutation indices.
Shape
- Input:
[..., K, C] - Output:
[..., K, C]
Here
K = (m_max+1)**2+(l_max-m_max)*(2*m_max+1).
Attributes:
-
m_sizes–Number of retained l values for each non-negative m.
-
n_components–Number of retained spherical components.
to_m_major
to_m_major(l_major_tensor: Tensor) -> Tensor
Parameters:
-
(l_major_tensorTensor) –[..., K, C].
Returns:
-
so2_tensor(Tensor) –[..., K, C].
to_l_major
to_l_major(so2_tensor: Tensor) -> Tensor
Parameters:
-
(so2_tensorTensor) –[..., K, C].
Returns:
-
l_major_tensor(Tensor) –[..., K, C].
EquivariantRMSNorm
EquivariantRMSNorm(
l_max: int,
channels: int,
*,
eps: float = 1e-05,
learnable: bool = True,
center: bool = True,
weighting: Literal["l_balanced", "component"] = "l_balanced",
device: device | str | None = None,
dtype: dtype | None = None,
)
Bases: Module
Normalize complete SO(3) tensors with one RMS per sample.
Inputs contain one complete real SO(3) irrep for every l = 0, ..., l_max
and use the l-major shape (..., (l_max + 1) ** 2, channels). When
center is true, the scalar channels are first centered as
The default l_balanced weighting gives every l block equal weight:
component weighting instead averages all (l, m, c) entries uniformly.
The normalized tensor shares the sample-dependent scale
\((q+\varepsilon)^{-1/2}\). When learnable is true, the result is multiplied
by a learned weight[l, c] shared across m; centered scalar channels also
receive a learned bias[c]:
Sharing parameters across m and restricting the bias to l = 0 makes
the operation commute with rotations. Contiguous CUDA float16, bfloat16,
and float32 inputs use fused Triton forward and first-order backward
kernels; higher-order gradients and other inputs use differentiable
PyTorch expressions.
Parameters:
-
(l_maxint) –Highest included non-negative
l. -
(channelsint) –Number of channels shared by every spherical component.
-
(epsfloat, default:1e-05) –Positive value added before the reciprocal square root.
-
(learnablebool, default:True) –Learn the equivariant scale and, when centering, scalar bias.
-
(centerbool, default:True) –Subtract the mean of the
l = 0channels for every sample. -
(weightingLiteral['l_balanced', 'component'], default:'l_balanced') –Give each
lblock or each spherical component equal weight. -
(devicedevice | str | None, default:None) –Initial device of parameters and buffers.
-
(dtypedtype | None, default:None) –Initial floating dtype of parameters and weights.
Attributes:
-
weight(Tensor | None) –Learned scale with shape
(l_max + 1, channels), orNone. -
bias(Tensor | None) –Learned scalar bias with shape
(channels,), orNone.
forward
forward(so3_tensor: Tensor) -> Tensor
Parameters:
-
(so3_tensorTensor) –[..., (l_max+1)**2, C].
Returns:
-
normalized_so3_tensor(Tensor) –[..., (l_max+1)**2, C].
GaussianBasis
GaussianBasis(cutoff: float, num_basis: int, *, width_scale: float = 2.0)
Bases: Module
Fixed Gaussian basis with uniformly spaced centers.
For \(n = 0, \ldots, N - 1\), define
The basis functions are
The centers and widths are fixed rather than learned. Gaussian functions do not vanish at \(r_\mathrm{c}\), so a separate cutoff remains necessary when edge messages must go smoothly to zero.
Parameters:
-
(cutofffloat) –Cutoff radius \(r_\mathrm{c}\) and location of the final center.
-
(num_basisint) –Number of Gaussian functions \(N\); must be at least two.
-
(width_scalefloat, default:2.0) –Width scale \(s\) relative to the center spacing.
LearnableSphericalBesselBasis
Bases: _SphericalBesselBasis
Learnable zero-order spherical Bessel radial basis.
This basis has the same initial functions as :class:SphericalBesselBasis,
but optimizes their dimensionless frequencies, initialized to
\(n\pi\) for \(n = 1, \ldots, N\).
Parameters:
-
(cutofffloat) –Cutoff radius \(r_\mathrm{c}\).
-
(num_basisint) –Number of basis functions \(N\).
PolynomialEnvelope
Bases: Module
DimeNet polynomial envelope with two vanishing derivatives.
With \(x = r / r_\mathrm{c}\) and positive integer power \(p\), the cutoff is
Its value, slope, and curvature all reach zero at \(r_\mathrm{c}\), giving a smooth transition to zero. The default \(p = 6\) is the common NequIP-style choice.
Parameters:
-
(cutofffloat) –Cutoff radius \(r_\mathrm{c}\).
-
(powerint, default:6) –Polynomial power \(p\).
SphericalBesselBasis
Bases: _SphericalBesselBasis
Fixed orthonormal zero-order spherical Bessel radial basis.
For cutoff radius \(r_\mathrm{c}\) and \(n = 1, \ldots, N\), the basis functions are
They are orthonormal on \([0, r_\mathrm{c}]\) with radial measure \(r^2\,\mathrm{d}r\). At the origin, the implementation uses the analytic limit
The frequencies \(n\pi\) are fixed.
Parameters:
-
(cutofffloat) –Cutoff radius \(r_\mathrm{c}\).
-
(num_basisint) –Number of basis functions \(N\).
S2GridTransform
S2GridTransform(
l_max: int,
m_max: int | None = None,
grid_shape: tuple[int, int] | None = None,
*,
component_layout: Literal["l_major", "m_major"] = "l_major",
device: device | str | None = None,
dtype: dtype | None = None,
)
Bases: Module
Transform band-limited real spherical functions to and from an S2 grid.
The component basis is the normalized Wikipedia real spherical-harmonic
basis used throughout ELFES. Setting m_max < l_max keeps only
|m| <= m_max in every \(l\) block. Components use either l_major layout,
with consecutive \(l\) blocks ordered by m = -l, ..., l, or m_major
layout, with the \(m=0\) block followed by the positive and negative blocks
for each \(|m|\) and \(l\) increasing within every block.
The equiangular grid uses polar angles
beta_i = pi * (i + 1/2) / n_polar and azimuthal angles
alpha_j = 2 * pi * j / n_azimuthal. Its Cartesian directions are
(sin(beta) * cos(alpha), sin(beta) * sin(alpha), cos(beta)). The inverse
uses the Kostelec–Rockmore quadrature for this grid.
Parameters:
-
(l_maxint) –Highest non-negative \(l\).
-
(m_maxint | None, default:None) –Highest retained \(|m|\). Defaults to
l_max. -
(grid_shapetuple[int, int] | None, default:None) –Number of polar and azimuthal samples. Defaults to
(2 * (l_max + 1), 2 * m_max + 1). -
(component_layoutLiteral['l_major', 'm_major'], default:'l_major') –Layout of the spherical components.
-
(devicedevice | str | None, default:None) –Initial buffer device.
-
(dtypedtype | None, default:None) –Initial floating dtype. Defaults to PyTorch's default dtype.
Shape
- Components:
(..., n_components, channels) - Grid values:
(..., n_polar, n_azimuthal, channels)
Here
n_components = sum(2 * min(l, m_max) + 1 for l in range(l_max + 1)).
forward
forward(components: Tensor) -> Tensor
Alias for to_grid.
to_grid
to_grid(components: Tensor) -> Tensor
Evaluate spherical components on the configured grid.
from_grid
from_grid(values: Tensor) -> Tensor
Project values on the configured grid back to components.
SO2Linear
SO2Linear(
in_channels: int,
out_channels: int,
l_max: int,
m_max: int | None = None,
*,
bias: bool = True,
algorithm: Literal["direct", "3m"] = "direct",
device: device | str | None = None,
dtype: dtype | None = None,
)
Bases: Module
Apply an SO(2)-equivariant linear map to SO(3) spherical tensors.
The input and output use m-major layout. They first store every \(m=0\)
component in increasing \(l\) order. For each \(m>0\), they then store every
\((l,+m)\) component followed by every \((l,-m)\) component, both in increasing
\(l\) order. The operation views these tensors after restricting rotations
to the SO(2) subgroup about the local z axis. It preserves \(|m|\) while
freely mixing all \((l,c)\) copies within each fixed \(|m|\).
For \(m=0\), the components are invariant under SO(2), so the block is an ordinary real linear map over the combined \((l,c)\) axis:
For each \(m>0\), define one complex value from the two real components
The corresponding block is the complex linear map
implemented by either ComplexLinear or ComplexLinear3M on real
tensors. A bias is allowed only in the invariant \(m=0\) block.
This module is not SO(3)-equivariant by itself because it may mix different \(l\). It is intended for tensors already expressed in a local frame; the composition of rotation to that frame, this SO(2)-equivariant map, and rotation back to the global frame can be SO(3)-equivariant.
Parameters:
-
(in_channelsint) –Number of input channels for every spherical component.
-
(out_channelsint) –Number of output channels for every spherical component.
-
(l_maxint) –Highest non-negative \(l\).
-
(m_maxint | None, default:None) –Highest retained \(|m|\). Defaults to
l_max. -
(biasbool, default:True) –Whether the \(m=0\) block has a bias.
-
(algorithmLiteral['direct', '3m'], default:'direct') –Real multiplication algorithm for every \(m>0\) block. Defaults to
direct, the block-weightComplexLinear. -
(devicedevice | str | None, default:None) –Initial device of parameters and layout indices.
-
(dtypedtype | None, default:None) –Initial floating dtype of the parameters.
Shape
- Input:
[..., K, C_in]inm-major layout - Output:
[..., K, C_out]inm-major layout
Here
K = (m_max+1)**2+(l_max-m_max)*(2*m_max+1).
Attributes:
-
m_sizes–Number of retained \(l\) values for each non-negative \(m\).
-
n_components–Number of retained spherical components.
forward
forward(so2_tensor: Tensor) -> Tensor
The K axis uses an m-major layout: all m=0 components come first,
followed for each m>0 by all +m components and then all -m components,
with l increasing inside each group. Written as (m,l), the order for
l_max=3 and m_max=2 is:
m=0: [(0,0), (0,1), (0,2), (0,3)] m=+1: [(+1,1), (+1,2), (+1,3)] m=-1: [(-1,1), (-1,2), (-1,3)] m=+2: [(+2,2), (+2,3)] m=-2: [(-2,2), (-2,3)]
Parameters:
-
(so2_tensorTensor) –[..., K, C_in], whereK = (m_max+1)**2+(l_max-m_max)*(2*m_max+1).
Returns:
-
output_so2_tensor(Tensor) –[..., K, C_out].
SO3Linear
SO3Linear(
in_channels: int,
out_channels: int,
l_max: int,
*,
bias: bool = True,
device: device | str | None = None,
dtype: dtype | None = None,
)
Bases: Module
Mix channels independently within each spherical l block.
For every l = 0, ..., l_max and m = -l, ..., l, the layer applies
Each \(W^{(l)}\) is shared by every \(m\) in the same \(l\) block, so the map
commutes with SO(3) rotations. The bias \(b\) is present only when bias is
true and acts only on \(l = 0\). Inputs use the complete \(l\)-major layout
[..., (l_max+1)**2, C_in]; outputs have shape
[..., (l_max+1)**2, C_out].
Parameters:
-
(in_channelsint) –Number of input channels shared by every \(l\).
-
(out_channelsint) –Number of output channels shared by every \(l\).
-
(l_maxint) –Highest included non-negative \(l\).
-
(biasbool, default:True) –Add a learned scalar bias when true.
-
(devicedevice | str | None, default:None) –Device on which to create parameters.
-
(dtypedtype | None, default:None) –Dtype with which to create parameters.
forward
forward(so3_tensor: Tensor) -> Tensor
Parameters:
-
(so3_tensorTensor) –[..., (l_max+1)**2, C_in].
Returns:
-
output_so3_tensor(Tensor) –[..., (l_max+1)**2, C_out].
SO3TensorProduct
SO3TensorProduct(
l_max_left: int,
l_max_right: int,
l_max_out: int | None = None,
*,
kind: Literal["gaunt", "cg"],
device: device | str | None = None,
dtype: dtype | None = None,
)
Bases: Module
Unweighted channelwise Gaunt or CG contraction on CUDA.
Inputs use integral-normalized Wikipedia real spherical harmonics,
ordered by degree, then m=-l,...,l, with shape
[..., (l_max + 1)**2, channels]. Batch shapes and channel counts
must agree. Each channel is contracted independently, without weights.
kind="gaunt" computes a pointwise spherical-function product, projected
to the output bandlimit. kind="cg" sums all triangle-allowed paths,
including odd degree sums, with sqrt(2*lout+1) times OEQ's unit-norm
real Wigner-3j coefficients and no additional path normalization.
Supports CUDA float32/float64 and differentiable adjoint contractions through the vendored OEQ JIT runtime.
Parameters:
-
(l_max_leftint) –Highest degree of the first input.
-
(l_max_rightint) –Highest degree of the second input.
-
(l_max_outint | None, default:None) –Highest output degree; defaults to the sum of input degrees.
-
(kindLiteral['gaunt', 'cg']) –Required coefficient choice: "gaunt" or "cg".
-
(devicedevice | str | None, default:None) –Initial module device; move to the input device before use.
-
(dtypedtype | None, default:None) –Floating precision, defaulting to Torch's default dtype.
forward
forward(left: Tensor, right: Tensor) -> Tensor
Contract the two inputs independently in each channel.
WeightedTensorProduct
WeightedTensorProduct(
irreps_in1: Irreps,
irreps_in2: Irreps,
irreps_out: Irreps,
instructions: Sequence[TensorProductInstruction],
)
Bases: Module
Weighted tensor product with an OpenEquivariance-only CUDA implementation.
The provider supports CUDA float32/float64 tensor products whose
instructions are all weighted and use one connection mode, uvu or
uvw. Irreps metadata comes from vendored e3nn-lite, not external e3nn.
Weights are external and unshared, in canonical instruction/path order
(compatible with e3nn weight packing). Component irrep normalization and
element path normalization are fixed. Features use Wikipedia real SH;
weight packing does not impose e3nn's spherical-harmonic coordinate convention.
CPU construction and state loading are supported, but CPU execution is not.
Moving the module to CUDA prepares its static schedule so forward is a
traceable weight permutation followed by a registered Torch custom op.
weight_numel
property
weight_numel: int
Number of external weights expected for each input row.
build_local_frame
build_local_frame(z_direction: Tensor, x_reference: Tensor) -> Tensor
Build proper global-to-local frames from z directions and x references.
Each nonzero z_direction is normalized to form the local z axis. The
corresponding x_reference is projected onto its orthogonal plane and
normalized to form the local x axis; the local y axis completes a
right-handed frame. A stable Cartesian reference is used when the supplied
reference is nearly parallel to z.
Parameters:
-
(z_directionTensor) –Nonzero Cartesian directions with shape
(..., 3). -
(x_referenceTensor) –Cartesian reference directions broadcastable with
z_directionand on the same device with the same dtype. The projected direction fixes the remaining SO(2) gauge freedom around z.
Returns:
-
Tensor–Proper rotation matrices with shape
(..., 3, 3), whose rows are the -
Tensor–local x, y, and z axes in global coordinates. Therefore the matrices
-
Tensor–map global Cartesian coordinates into the constructed local frames.
to_global_frame
to_global_frame(
so2_tensor: Tensor,
wigner_matrices: Tensor,
l_max: int,
m_max: int | None = None,
*,
so2_layout: Literal["l_major", "m_major"] = "l_major",
) -> Tensor
Zero-fill local features and rotate them into the global frame.
Restore l-major ordering, fill omitted m components with zeros, and
apply the transpose Wigner rotation. This is the adjoint of
to_local_frame: without truncation it undoes that transform, but with
truncation it cannot recover discarded components. Channels are not mixed.
Parameters:
-
(so2_tensorTensor) –Local features shaped
(..., K, channels)inso2_layout, whereK = sum(2*min(l, M)+1 for l in range(l_max+1))andM = l_max if m_max is None else m_max. -
(wigner_matricesTensor) –The same global-to-local Wigner matrices used by
to_local_frame; do not transpose them yourself. Shape is(..., sum((2*l+1)**2 for l in range(l_max+1))); leading dimensions, dtype, and device must matchso2_tensor. -
(l_maxint) –Highest included non-negative degree.
-
(m_maxint | None, default:None) –Highest absolute order present in the local input;
Nonemeans all orders are present. -
(so2_layoutLiteral['l_major', 'm_major'], default:'l_major') –Local input layout, as described by
to_local_frame. This must match the layout used to constructso2_tensor.
Returns:
-
Tensor–Complete global
so3_tensorshaped(..., (l_max+1)**2, channels) -
Tensor–in l-major order. Leading dimensions, channels, dtype, and device
-
Tensor–are preserved.
to_local_frame
to_local_frame(
so3_tensor: Tensor,
wigner_matrices: Tensor,
l_max: int,
m_max: int | None = None,
*,
so2_layout: Literal["l_major", "m_major"] = "l_major",
) -> Tensor
Rotate global SO(3) features into local frames and optionally truncate m.
Apply the Wigner rotation, retain components with \(|m|\leq m_{\max}\),
and pack the local features in so2_layout. The complete global input
is always l-major. Channels are not mixed by this operation.
Parameters:
-
(so3_tensorTensor) –Complete global features shaped
(..., (l_max+1)**2, channels)in l-major order. -
(wigner_matricesTensor) –Flattened matrices from
wigner_D(l_max, frames), whereframesmaps global Cartesian coordinates to local ones, as returned bybuild_local_frame. Shape is(..., sum((2*l+1)**2 for l in range(l_max+1))); leading dimensions, dtype, and device must matchso3_tensor. -
(l_maxint) –Highest included non-negative degree.
-
(m_maxint | None, default:None) –Highest retained absolute order;
Noneretains all orders. -
(so2_layoutLiteral['l_major', 'm_major'], default:'l_major') –Local output layout.
l_majororders by ascending l, then ascending m within each l block.m_majororders blocks asm=0,+1,-1,+2,-2,..., with ascending l inside each block.
Returns:
-
Tensor–Local
so2_tensorshaped(..., K, channels)inso2_layout, where -
Tensor–K = sum(2*min(l, M)+1 for l in range(l_max+1))and -
Tensor–M = l_max if m_max is None else m_max. Leading dimensions, -
Tensor–channels, dtype, and device are preserved.
Use to_global_frame with the same matrices and settings to rotate back.
If orders were truncated, their original values cannot be recovered.