Skip to content

Optimize UMA SO2 and Wigner inference kernels - #2154

Merged
mlazos merged 7 commits into
mainfrom
uma-so2-wigner-fusions
Aug 25, 2026
Merged

mlazos merged 7 commits into
mainfrom
uma-so2-wigner-fusions

Conversation

@mlazos

@mlazos mlazos commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

UMA spends much of its inference time moving edge data between GPU operations. This PR removes several large temporary tensors and combines adjacent operations so the GPU reads and writes less memory.

In plain terms, the six optimization commits do the following:

  1. Keep edge features in the format already produced by the first layer instead of converting them to another format and immediately converting them back.
  2. Combine the gate activation into one GPU operation.
  3. Process more edges in parallel.
  4. Write backward gradients directly to atoms instead of first building a large per-edge gradient tensor.
  5. Rotate edge messages and add them to atoms in one GPU operation.
  6. Store only the nonzero parts of each rotation matrix.

On a 1000-atom UMA-S-1p2 inference workload, the complete stack improves GPU throughput from 19.722 to 25.881 QPS (+31.22%) and reduces steady peak allocated memory by 11.18%. CUDA graphs were not enabled for this comparison.

How this relates to #2073

This work builds on #2073, which is already included in the base revision.

#2073 added GPU kernels that combine the initial node-to-edge rotation and the final edge rotation. However, the model still created large temporary edge tensors around those kernels. This PR removes more of those temporary tensors and reduces the amount of rotation data stored for every edge.

A simplified view is:

Before this PR:

atom features
  -> rotate and gather them onto edges
  -> build a large edge tensor
  -> apply several gate operations
  -> convert the result back into blocks
  -> run the second layer
  -> build another large rotated edge tensor
  -> add edge messages to atoms

After this PR:

atom features
  -> rotate and gather them onto edges
  -> apply the gate directly to the existing blocks
  -> run the second layer
  -> rotate and add messages directly to atoms

The six optimizations

1. Avoid an unnecessary format conversion

The first convolution already produces three feature blocks in the format expected by the second convolution. Previously, UMA joined these blocks into one [edges, 9, channels] tensor for the gate activation and then split the tensor back into blocks.

The model now keeps the blocks in their original format across the gate. This removes a full-tensor join, split, and memory round trip.

For the representative 78,848-edge graph with 128 channels, one FP32 [edges, 9, channels] tensor occupies about 346.5 MiB.

Forward and backward results were compared with the old path using:

relative tolerance: 1e-6
absolute tolerance: 1e-7

2. Run the gate as one GPU operation

The gate previously launched separate operations for sigmoid, SiLU, multiplication, splitting, and joining. The new Triton kernel performs those steps together and writes the block-form output directly.

The fused operation supports normal force inference and its first derivative. When a caller requests second derivatives through this gate, it uses a differentiable PyTorch implementation rather than the specialized first-derivative kernel. This fallback applies to the gate itself; the other existing umas_fast_gpu Triton operations retain the backend's existing higher-order derivative restrictions.

Measured agreement with the original gate:

Result Relative tolerance Absolute tolerance
Forward output 1e-5 1e-6
First derivative 1e-5 1e-6
Second derivative 2e-5 2e-6

Compiled and uncompiled executions of the new kernel matched exactly in the dynamic-shape tests.

3. Process more edges in parallel

The existing Wigner kernels always launched 2,048 GPU programs. A graph with 78,848 edges therefore made each program process many edges one after another.

The launch size now follows the number of edges, up to a limit of 131,072 programs. For the representative graph, this allows roughly one program per edge.

This does not change the formula or combine results from different edges; it only exposes more independent work to the GPU. The standard forward, backward, empty-graph, and dynamic-shape correctness tests pass. Across the four affected kernels at 78,848 edges, combined kernel time decreased from 2.8196 ms to 2.7111 ms.

4. Write input gradients directly to atoms

Backward previously created an [edges, 9, 2 * channels] tensor and then ran two additional reductions to add its values to source and target atoms.

The new kernel writes those contributions directly into the atom-gradient tensor. At 78,848 edges and 128 channels, this avoids an approximately 693.1 MiB temporary tensor. The isolated backward operation improved from 2.361 ms to 0.935 ms.

Because multiple edges may update the same atom concurrently, the order of FP32 additions can change. The measured numerical effect was:

atom-feature gradient relative L2 difference: 2.97e-7
rotation gradient: bitwise equal
radial gradient: bitwise equal

Here, relative L2 means the total error magnitude divided by the reference result magnitude.

When deterministic algorithms are enabled, UMA keeps the previous reduction path. Repeated forward and backward executions of that path are bitwise identical.

5. Rotate and add edge messages in one operation

Previously, UMA wrote an inverse-rotated [edges, 9, channels] tensor and then read it again to add edge messages to their target atoms. Backward similarly gathered atom gradients into another edge tensor.

The new kernel performs the rotation and atom update together. The isolated forward operation improved from 0.565 ms to 0.201 ms.

This operation also changes the order of some FP32 additions:

output relative L2 difference: 2.15e-7
input gradients: bitwise equal
rotation gradients: bitwise equal

When deterministic algorithms are enabled, UMA uses the previous materialized reduction. Its forward and backward results are bitwise repeatable.

6. Store only the nonzero rotation values

UMA-S uses a 9 by 9 block-diagonal rotation matrix. Only three diagonal blocks can contain nonzero values:

1 by 1 block:  1 value
3 by 3 block:  9 values
5 by 5 block: 25 values
               ---------
               35 values

The old representation stored all 81 values, including 46 values that are always zero. The new representation stores only the 35 useful values. For 78,848 edges, this saves approximately 13.8 MiB per rotation tensor, or 27.7 MiB for the forward and inverse tensors before counting gradients.

The compact rotation generator is bitwise equal to the corresponding blocks of the dense generator, including its position gradients. The edge-degree output and input gradient are also bitwise equal. Double-precision radial and rotation gradient comparisons were within 2e-15 and 1e-14 relative tolerance respectively.

Legacy entry points still accept compact rotations by reconstructing the dense matrix when needed.

Independent review follow-up

An independent review found two compatibility gaps, fixed in follow-up commit 802979b9:

  • The backend can be selected for non-power-of-two channel widths or under autocast, while the fused gate kernel requires power-of-two FP32 inputs. The gate now checks the actual convolution outputs and uses the existing block-form PyTorch activation when the Triton kernel does not apply. This preserves the packed convolution results rather than rerunning the convolutions.
  • The exported fused Wigner operations had stopped accepting their previous dense [edges, 9, 9] rotation input. They now pack dense block-diagonal inputs before custom autograd, preserving the public input shape, dense gradient shape, and zero off-block gradients.

The added tests cover 96-channel FP32 fallback, 128-channel BF16 fallback, dynamic full-graph compilation, dense forward and backward compatibility, and changing edge counts. An independent re-review found no remaining blocking issues.

The fixed branch measured 25.946 QPS with a 38.541 ms GPU median and 5.956 GiB steady peak allocation under the same configuration below. This is effectively unchanged from the published 25.881 QPS result and produced no timed recompilations.

When these optimizations run

These changes are used by the umas_fast_gpu backend. That backend is selected explicitly with:

InferenceSettings(
    activation_checkpointing=False,
    merge_mole=True,
    execution_mode="umas_fast_gpu",
)

It can also be selected automatically when execution_mode=None and all of the following are true:

  • CUDA is available
  • the model uses lmax=2 and mmax=2
  • activation checkpointing is disabled
  • merge_mole=True
  • the requested Hessian configuration is supported

The optimized kernels are not selected by execution_mode="general".

Additional fallbacks remain available:

  • non-FP32 and non-power-of-two-channel gate inputs use the block-form PyTorch implementation
  • S2 activation uses the existing materialized path
  • deterministic algorithms use the previous deterministic reductions
  • second-order gate differentiation uses differentiable PyTorch operations
  • legacy backends can reconstruct dense rotation matrices
  • exported fused Wigner operations accept either compact or dense rotation inputs

CUDA graphs are not required to use these kernels.

End-to-end performance

Standalone H100 measurements compare clean detached checkouts of base a3fa357c3 and optimization head 522ce9df. Follow-up head 802979b9 contains the compatibility fixes described above and has unchanged performance.

Configuration:

model: uma-s-1p2
system: 1000-atom Cu
GPU: NVIDIA H100
PyTorch: 2.14.0a0+gitc0c824e
CUDA: 12.9
TF32: enabled
activation checkpointing: disabled
merge_mole: enabled
torch.compile: enabled, dynamic shapes
execution mode: umas_fast_gpu
external graph generation: disabled
internal graph generation: NVIDIA v3
CUDA graphs: disabled
Measurement Base This PR Change
GPU median latency / QPS 50.704 ms / 19.722 38.639 ms / 25.881 +31.22% QPS
GPU minimum latency 49.121 ms 36.905 ms -24.87%
Wall median latency / QPS 50.523 ms / 19.793 39.256 ms / 25.474 +28.70% QPS
Wall minimum latency 49.272 ms 37.053 ms -24.80%
Overall peak allocated memory 6.705 GiB 5.970 GiB -10.95%
Steady peak allocated memory 6.705 GiB 5.955 GiB -11.18%
Reserved memory after warmup 8.219 GiB 7.561 GiB -8.01%

Each process performed 24 warmup predictions followed by eight blocks of 100 timed predictions. Base and head processes were interleaved in base/head/head/base order, and each process used a fresh TorchInductor cache. No recompilation occurred during timed blocks. QPS is 1000 / median_latency_ms.

End-to-end numerical comparison

Five frames used deterministic position displacements of -0.02, -0.003, 0, 0.003, and 0.02. The table compares the complete optimization stack with the base. The final column shows the normal difference observed when the same revision is run twice.

Output Maximum absolute difference Relative L2 difference Same-revision maximum difference
Energy 3.813e-3 6.270e-7 4.073e-3
Forces 2.199e-4 5.778e-4 2.407e-4
Stress 1.518e-5 1.279e-4 1.467e-5

The complete-stack differences are comparable to the run-to-run variation already observed from the same revision. The two operations that intentionally change FP32 addition order were also tested directly, producing relative L2 differences of 2.97e-7 and 2.15e-7.

Suggested review order

The first six commits follow the model's execution order:

  1. preserve the existing block format
  2. combine the gate operations
  3. increase edge parallelism
  4. remove the large backward temporary tensor
  5. combine rotation and atom accumulation
  6. compact the rotation representation

The seventh commit contains the independent-review compatibility fixes and their regression tests.

Test plan

PYTHONPATH="$PWD/src" python -m pytest -q   tests/core/models/uma/nn/test_so2_layers.py   tests/core/models/uma/test_quaternion_wigner.py   tests/core/models/uma/uma_fast/test_execution_backends.py   tests/core/models/uma/uma_fast/test_fused_edgewise.py   -c packages/fairchem-core/pyproject.toml

/home/mlazos/.conda/envs/pytorch-3.12/bin/pre-commit run   --files $(git diff --name-only a3fa357c3..HEAD)

All selected correctness tests and all 21 fused-edgewise tests reached pass status. The final review-focused run passed four gate/backend cases and three dense/compact dynamic Wigner cases. In this local custom PyTorch/Triton environment, one Triton-heavy test process subsequently hit a known interpreter teardown abort after pytest reported 100% passed.

Authored with assistance from an AI coding tool.

mlazos added 6 commits August 12, 2026 13:29
The fast GPU edgewise path previously materialized an [E, 9, C] message between conv1, gate activation, and conv2. Preserve the existing m-major GEMM blocks across that boundary so the gate and conv2 consume them directly, while retaining the materialized path for S2 activation.

For UMA-S-1p2 at 1000 atoms on one H100 with internal graph generation, merge_mole=True, compile dynamic shapes, the fast GPU backend, a 1024-edge CUDA graph bucket, 12 warmups, and 200 timed predictions, trajectory throughput increased from 23.8327 QPS to 25.8706 QPS across two repeats (+8.55%).

Test Plan:
```
PYTHONPATH="$PWD/src" pytest tests/core/models/uma/nn/test_so2_layers.py -c packages/fairchem-core/pyproject.toml -q
pre-commit run --files src/fairchem/core/models/uma/escn_md_block.py src/fairchem/core/models/uma/nn/activation.py src/fairchem/core/models/uma/nn/so2_layers.py tests/core/models/uma/nn/test_so2_layers.py
```

Authored with an AI coding assistant.
The fast GPU path still launched separate sigmoid, SiLU, multiply, split, and concatenate work between the packed conv1 and conv2 GEMMs. Add an FP32 Triton primitive that activates the three m-major blocks directly and provides a fused first-order VJP. Non-FP32 inference retains the packed PyTorch path.

When autograd requests a higher-order graph, the VJP switches to an algebraically equivalent differentiable PyTorch implementation so loop-based UMA Hessian inference remains supported. Tests cover empty and strided inputs, dynamic full-graph compilation at multiple edge counts, and first- and second-order parity.

For the same UMA-S-1p2 1000-atom H100 endpoint, the primitive increased trajectory throughput from 25.8706 QPS to 27.0886 QPS across two repeats (+4.71%). The exact final stack, including the following Wigner launch commit, reproduced at 27.4831 QPS over 200 timed predictions.

Test Plan:
```
PYTHONPATH="$PWD/src" pytest tests/core/models/uma/uma_fast/test_fused_edgewise.py -c packages/fairchem-core/pyproject.toml -q -k packed_gate
PYTHONPATH="$PWD/src" python /data/users/mlazos/pytorch/agent_space/bench_uma_internal_padding.py --variant captured --bucket-size 1024 --repeat 9 --warmups 12 --batches 10 --steps-per-batch 20
pre-commit run --files src/fairchem/core/models/uma/escn_md_block.py src/fairchem/core/models/uma/nn/execution_backends.py src/fairchem/core/models/uma/triton/__init__.py src/fairchem/core/models/uma/triton/kernels.py src/fairchem/core/models/uma/triton/packed_gate.py tests/core/models/uma/uma_fast/test_fused_edgewise.py
```

Authored with an AI coding assistant.
The fused Wigner kernels used a fixed 2,048-program grid and serially processed the remaining edges in each program. Size the grid from the symbolic edge count, capped at 131,072 programs, so the 78,848-edge inference bucket can expose one program per edge while retaining a grid-stride loop for larger graphs. A one-program minimum and explicit Wigner width preserve empty-graph behavior.

Across the four forward/backward kernels at 78,848 edges, the selected one-warp launch reduced aggregate kernel time from 2.8196 ms to 2.7111 ms. On the UMA-S-1p2 1000-atom endpoint, trajectory throughput increased from 27.0886 QPS to 27.4774 QPS across two repeats (+1.44%).

All five selected correctness tests reached pass status. This local development PyTorch build then aborted during interpreter teardown, after pytest reported 100%, which is a pre-existing shutdown issue for the Wigner Triton test module.

Test Plan:
```
PYTHONPATH="$PWD/src" python /data/users/mlazos/pytorch/agent_space/bench_rebased_wigner_launch.py
PYTHONPATH="$PWD/src" pytest tests/core/models/uma/uma_fast/test_fused_edgewise.py -c packages/fairchem-core/pyproject.toml -q -k "matches_pytorch or empty_graph"
PYTHONPATH="$PWD/src" python /data/users/mlazos/pytorch/agent_space/bench_uma_internal_padding.py --variant captured --bucket-size 1024 --repeat 9 --warmups 12 --batches 10 --steps-per-batch 20
pre-commit run --files src/fairchem/core/models/uma/triton/constants.py src/fairchem/core/models/uma/triton/fused_wigner.py tests/core/models/uma/uma_fast/test_fused_edgewise.py
```

Authored with an AI coding assistant.
The fused Wigner conv1 backward previously materialized an [E, 9, 2C]
gradient buffer and launched two index_add reductions to return gradients to
the source and target nodes. Accumulate those contributions directly into the
[N, 9, C] node-gradient tensor in the producer kernel when deterministic
algorithms are disabled. When deterministic algorithms are enabled, specialize
the same kernel to retain the per-edge buffer and established index_add path.

On UMA-S-1p2 with 1000 atoms, internal NVIDIA graph generation v3,
external_graph_gen=False, merge_mole=True, dynamic torch.compile, fast math,
the umas_fast_gpu backend, a 1024-edge bucket, and full outer CUDA graph
capture, three trajectory repeats average 29.575 QPS versus 27.466 QPS for the
committed predecessor (+7.68%). Reserved memory falls from 8.135 GiB to 7.908
GiB. An isolated backward measurement improves from 2.361 ms to 0.935 ms
(2.53x). The direct reduction differs from the prior atomic reduction by
2.97e-7 relative L2 in grad_x; grad_wigner and grad_radial are bitwise equal.
The deterministic compiled fallback is bitwise repeatable.

Test Plan:

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/uma_fast/test_fused_edgewise.py::test_wigner_conv1_fused_deterministic_backward -c packages/fairchem-core/pyproject.toml
```

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/uma_fast/test_fused_edgewise.py::test_wigner_conv1_fused_dynamic_compile -c packages/fairchem-core/pyproject.toml
```

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/uma_fast/test_fused_edgewise.py::test_wigner_conv1_fused_gradcheck -c packages/fairchem-core/pyproject.toml
```

Both gradcheck parameterizations pass before the local Triton runtime aborts
during interpreter teardown.

```bash
PYTHONPATH="$PWD/src:/data/users/mlazos/pytorch/agent_space:$PYTHONPATH" CUDA_VISIBLE_DEVICES=0 python /data/users/mlazos/pytorch/agent_space/bench_uma_internal_padding.py --variant captured --bucket-size 1024 --repeat 12
```

```bash
pre-commit run --files src/fairchem/core/models/uma/triton/fused_wigner.py src/fairchem/core/models/uma/triton/kernels.py tests/core/models/uma/uma_fast/test_fused_edgewise.py
```

This commit was authored with assistance from Codex.
The fast GPU consumer previously wrote an [E, 9, C] inverse-rotated edge
tensor, reread it in index_add to produce node embeddings, and gathered node
gradients back into another edge tensor during backward. Add a compile-safe
custom operation that accumulates the inverse rotation directly into target
nodes and gathers node gradients inside the existing backward kernel.

The default path uses relaxed FP32 atomics. When deterministic algorithms are
enabled, it retains the materialized inverse rotation and PyTorch index_add so
the deterministic contract is preserved. Graph-parallel target remapping stays
an explicit input to the fused operation.

On UMA-S-1p2 with 1000 atoms, internal NVIDIA graph generation v3,
external_graph_gen=False, merge_mole=True, dynamic torch.compile, fast math,
the umas_fast_gpu backend, a 1024-edge bucket, and full outer CUDA graph
capture, two trajectory repeats average 32.066 QPS versus 29.575 QPS for the
committed predecessor (+8.42%). Latency falls from 33.813 ms to 31.186 ms and
reserved memory from 7.908 GiB to 7.169 GiB. An isolated forward measurement
improves from 0.565 ms to 0.201 ms (2.81x). The fused output differs from the
materialized atomic reduction by 2.15e-7 relative L2, while input and Wigner
gradients are bitwise equal. The deterministic compiled forward and backward
are bitwise repeatable.

Test Plan:

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/uma_fast/test_fused_edgewise.py::test_wigner_inv_conv2_scatter_matches_materialized tests/core/models/uma/uma_fast/test_fused_edgewise.py::test_fused_edgewise_empty_graph -c packages/fairchem-core/pyproject.toml
```

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/uma_fast/test_fused_edgewise.py::test_wigner_inv_conv2_scatter_dynamic_compile -c packages/fairchem-core/pyproject.toml
```

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/uma_fast/test_fused_edgewise.py::test_wigner_inv_conv2_scatter_deterministic -c packages/fairchem-core/pyproject.toml
```

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/uma_fast/test_execution_backends.py::test_umas_fast_gpu_forces_match_baseline_pbc tests/core/models/uma/uma_fast/test_execution_backends.py::test_umas_fast_gpu_forces_match_baseline_no_pbc -c packages/fairchem-core/pyproject.toml
```

Both force tests pass before the local Triton runtime aborts during interpreter
teardown.

```bash
PYTHONPATH="$PWD/src:/data/users/mlazos/pytorch/agent_space:$PYTHONPATH" CUDA_VISIBLE_DEVICES=0 python /data/users/mlazos/pytorch/agent_space/bench_uma_internal_padding.py --variant captured --bucket-size 1024 --repeat 13
```

```bash
PYTHONPATH="$PWD/src:/data/users/mlazos/pytorch/agent_space:$PYTHONPATH" CUDA_VISIBLE_DEVICES=0 python /data/users/mlazos/pytorch/agent_space/bench_uma_internal_padding.py --variant captured --bucket-size 1024 --repeat 14
```

```bash
pre-commit run --files src/fairchem/core/models/uma/nn/execution_backends.py src/fairchem/core/models/uma/triton/__init__.py src/fairchem/core/models/uma/triton/fused_wigner.py src/fairchem/core/models/uma/triton/kernels.py tests/core/models/uma/uma_fast/test_fused_edgewise.py
```

This commit was authored with assistance from Codex.
UMA-S lmax=2 Wigner matrices are block diagonal, but the fast GPU path materializes and carries 81 values per edge and direction while only 35 can be nonzero. Generate the three blocks directly, keep the compact layout through edge-degree and the fused Triton producer and consumer kernels, and reconstruct dense matrices only at legacy backend entry points.

This reduces Wigner traffic and gradient storage without changing precision. Dynamic compiled generator and fused operation tests cover changing edge counts, deterministic fallbacks retain their behavior, and PBC energy, forces, and stress remain within the existing fast-backend tolerance.

On UMA-S-1p2 with 1000 atoms, NVIDIA internal graph generation v3, external_graph_gen=False, merge_mole=True, dynamic torch.compile, fast math, the umas_fast_gpu backend, a 1024-edge bucket, and full outer CUDA graph capture, two trajectory repeats average 33.815 QPS versus 32.066 QPS for the committed predecessor (+5.45%). Reserved memory falls from 7.169 GiB to 7.096 GiB.

Test Plan:

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/test_quaternion_wigner.py -k "compact_l2_matches_dense" -c packages/fairchem-core/pyproject.toml
```

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/uma_fast/test_fused_edgewise.py -c packages/fairchem-core/pyproject.toml
```

```bash
PYTHONPATH="$PWD/src" CUDA_VISIBLE_DEVICES=0 pytest -q tests/core/models/uma/uma_fast/test_execution_backends.py::test_compact_edge_degree_matches_dense tests/core/models/uma/uma_fast/test_execution_backends.py::test_legacy_backend_rotations_accept_compact_wigner tests/core/models/uma/uma_fast/test_execution_backends.py::test_umas_fast_gpu_forces_match_baseline_pbc tests/core/models/uma/uma_fast/test_execution_backends.py::test_umas_fast_gpu_forces_match_baseline_no_pbc -c packages/fairchem-core/pyproject.toml
```

```bash
PYTHONPATH="$PWD/src:/data/users/mlazos/pytorch/agent_space:$PYTHONPATH" CUDA_VISIBLE_DEVICES=0 python /data/users/mlazos/pytorch/agent_space/bench_uma_internal_padding.py --variant captured --bucket-size 1024 --repeat 16
PYTHONPATH="$PWD/src:/data/users/mlazos/pytorch/agent_space:$PYTHONPATH" CUDA_VISIBLE_DEVICES=0 python /data/users/mlazos/pytorch/agent_space/bench_uma_internal_padding.py --variant captured --bucket-size 1024 --repeat 17
```

```bash
pre-commit run --files src/fairchem/core/models/uma/common/quaternion/wigner_d_hybrid.py src/fairchem/core/models/uma/escn_md.py src/fairchem/core/models/uma/nn/execution_backends.py src/fairchem/core/models/uma/triton/fused_wigner.py src/fairchem/core/models/uma/triton/kernels.py tests/core/models/uma/test_quaternion_wigner.py tests/core/models/uma/uma_fast/test_execution_backends.py tests/core/models/uma/uma_fast/test_fused_edgewise.py tests/core/models/uma/uma_fast/triton_test_utils.py
```

The Triton-heavy pytest processes report all selected tests passed, then hit the existing local interpreter teardown abort. The compact generator and legacy fallback tests exit cleanly.

This commit was authored with assistance from Codex.
@meta-cla meta-cla Bot added the cla signed label Aug 12, 2026
The fast GPU backend can be selected for channel widths and autocast dtypes that the packed FP32 Triton gate does not support. Keep the packed GEMM outputs, but route their activation through the existing PyTorch block implementation unless the actual outputs are FP32 with a power-of-two channel width. This avoids rejecting valid fast-backend configurations without repeating the GEMMs.

Compact Wigner storage also made the exported fused operations reject their prior dense [E, 9, 9] inputs. Pack dense block-diagonal inputs before entering custom autograd so the public interface, dense gradient shape, and zero off-block gradients are preserved.

Test Plan:
```bash
PYTHONPATH="$PWD/src" pytest -q tests/core/models/uma/uma_fast/test_execution_backends.py -k "gate_activation_fallback or compact_edge_degree_matches_dense"
PYTHONPATH="$PWD/src" pytest -q tests/core/models/uma/uma_fast/test_fused_edgewise.py -k "exported_fused_ops_accept_dense_wigner or wigner_conv1_fused_dynamic_compile"
pre-commit run --files src/fairchem/core/models/uma/escn_md_block.py src/fairchem/core/models/uma/nn/execution_backends.py src/fairchem/core/models/uma/triton/fused_wigner.py tests/core/models/uma/uma_fast/test_execution_backends.py tests/core/models/uma/uma_fast/test_fused_edgewise.py
```

Authored with assistance from an AI coding tool.
@mlazos
mlazos marked this pull request as ready for review August 12, 2026 23:22
@mlazos mlazos added the patch Patch version release label Aug 17, 2026
Comment thread src/fairchem/core/models/uma/common/quaternion/wigner_d_hybrid.py
@mlazos
mlazos requested a review from bkmi August 24, 2026 20:39

@bkmi bkmi left a comment

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.

this is all I got!

return torch.cat(out, dim=1)

def gemms_to_buffers(
def gemms_from_blocks(

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.

is there any backwards compatibility we should think about here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

g0, g1, g2 = self.so2_conv_2.gemms_to_buffers(x_message)
is the only usage, so I think we are good!

@mlazos
mlazos requested a review from bkmi August 25, 2026 21:25

@bkmi bkmi left a comment

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.

seems good to me... maybe it's worth having someone else look too.

@mlazos
mlazos added this pull request to the merge queue Aug 25, 2026
Merged via the queue into main with commit 8a570eb Aug 25, 2026
25 of 27 checks passed
@mlazos
mlazos deleted the uma-so2-wigner-fusions branch August 25, 2026 23:36
abagusetty added a commit to abagusetty/fairchem that referenced this pull request Aug 28, 2026
…kernels

Resolves the merge in favour of the accelerator-agnostic device layer, then
carries that treatment to the Triton work upstream added in facebookresearch#2154: packed_gate
required CUDA tensors outright, and the new tests hard-coded CUDA devices.
Both now follow whatever accelerator is present.

Upstream's execution_backends.py is kept verbatim apart from dropping the
XPU Triton opt-in; an earlier resolution had discarded its _dense_l2_wigner
and pack_blocks helpers, which broke the compact Wigner layout its kernels
expect.

Verified on Intel Data Center GPU Max: the gpu-marked suite is 93 passed,
0 failed, including the packed-gate and compact-Wigner tests that compare
against the materialized reference implementations and the torch.compile
dynamic-shape paths.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla signed patch Patch version release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants