Skip to content

perf(pt): assemble Wigner-D behind a custom op; inverse rotation as a view - #6070

Open
long-yi-2019 wants to merge 2 commits into
deepmodeling:masterfrom
long-yi-2019:pr/wignerd-assembly-op
Open

long-yi-2019 wants to merge 2 commits into
deepmodeling:masterfrom
long-yi-2019:pr/wignerd-assembly-op

Conversation

@long-yi-2019

@long-yi-2019 long-yi-2019 commented Oct 10, 2026 •

Copy link
Copy Markdown

Motivation

Each eval frame assembles the block-diagonal Wigner-D rotation matrices by
scattering the per-degree blocks into a dense (E, D, D) tensor. Traced
through Inductor, those in-place slice assignments functionalize into a
chain of full-size zeros + index_put + add tensors plus a realized
transpose -- five (E, D, D) surfaces combined by one kernel, ~4 GB of HBM
traffic per frame at the SeZM N=4096 workload (E ~ 646k, D = 16). On top
of that, the inverse rotation Dt_full was produced as a full contiguous
copy even though every consumer can address it through strides.

Change

Two commits, one battle:

  1. Assembly behind a custom op. sezm_nn::wigner_d_assemble takes the
    per-degree blocks (plus the polynomial tail when lmax >= 11) and does
    the eager scatter: one zeros buffer, in-place block writes. The traced
    forward takes the op whenever a fake-tensor mode is active (is_compiling()
    is not enough -- this module is traced through make_fx, where the flag
    stays False; a fake tensor carries its mode, so the forward probes for
    it). The eager path is untouched. An exact autograd formula returns
    each block its D-slice.

  2. The inverse rotation is a view. The flash and rotation-back kernels
    already address the rotation through explicit stride arguments
    (_rotation_strides / w_se, w_sr, w_sk), verified bit-for-bit against
    the contiguous copy with non-symmetric matrices. The op now returns
    D_full alone and the caller hands out D_full.transpose(-1, -2); the
    transpose copy never exists. The one consumer that requires contiguous
    storage (so2_rotate_mix) reads D_full itself, which stays contiguous.

Measurements

SeZM model, N=4096 atoms (~646k edges), RTX 5090, torch 2.12.1+cu130,
DP_COMPILE_INFER=1, DP_TRITON_INFER=3, throughput mode (16 evals, P50):

build per-eval (ms) steady peak VRAM alloc (MB) reserved (MB)
master 153.2 22740 26510
this PR 149.4 (-2.5%) 21888 (-852 MB, -3.7%) 24539 (-2.0 GB)

The step-1 saving is time (the ~4 GB/frame add-chain disappears; the
compiler-fragment family drops 38.3 -> 25.8 ms of kernel time). The step-2
saving is VRAM: deleting the 660 MB transpose copy costs nothing overall
because the stride-read overhead and the removed copy cancel out. Two-state
compiled-eval accuracy at N=4096: energy rel diff 5.8e-10, max force diff
4.8e-6 with max |f| ~ 3.9.

Tests

  • source/tests/pt/model/test_descriptor_sezm.py: 61 passed (default config).
  • source/tests/pt/model/test_descriptor_sezm_triton.py: 37 passed, 1 skipped (pre-existing).
  • source/tests/pt/model/test_sezm_model.py::TestSeZMModelCompile: 9 passed on this branch head (RTX 5090, torch 2.12.1), including the eval cache/shape-change, first-frame and force-loss double-backward tests.
  • The eager path is byte-identical to master: the two-state eager run
    (view vs materialized inverse) matches exactly.

Summary by CodeRabbit

  • Improvements
    • Updated Wigner-D matrix generation to support both traced and eager execution, while retaining low-order and polynomial components in the assembled result.

longjunhao added 2 commits October 10, 2026 15:44
Functionalization expands the in-place block scatter into a chain of
full-size zeros/index_put adds plus a realized transpose (~4 GB of HBM
traffic per frame at the SeZM N=4096 workload).  A wigner_d_assemble
custom op keeps the eager scatter and hands the graph two contiguous
tensors; the traced assembly takes it whenever a fake-tensor mode is
active, so the eager path is untouched.
The flash and rotation-back kernels already address the rotation
through explicit strides, so the contiguous transpose the assembly
produced was pure storage: one 660 MB copy per frame that existed only
to be read back.  The assemble op now returns D_full alone and the
caller hands out a transpose view; consumers read it in place and no
transpose copy ever exists.
Copilot AI balanced review requested due to automatic review settings October 10, 2026 08:14

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ca4a7c3a-fa26-4c34-bff0-00bab6403773

📥 Commits

Reviewing files that changed from the base of the PR and between 64d3266 and aea9947.


📒 Files selected for processing (1)
  • deepmd/pt/model/descriptor/sezm_nn/wignerd.py

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.



📝 Walkthrough

Walkthrough

WignerDCalculator.forward now collects degree blocks before assembling D_full. During compilation or fake-tensor tracing, a custom operation assembles the blocks and routes gradients to them. Otherwise, the calculator uses eager slice assignments.

Changes

Wigner D block assembly

Layer / File(s) Summary
Custom assembly and gradient routing
deepmd/pt/model/descriptor/sezm_nn/wignerd.py
A custom operation assembles the identity, regular blocks, and optional polynomial block into D_full. It provides a fake implementation and extracts block gradients during backward.
Forward block collection and assembly
deepmd/pt/model/descriptor/sezm_nn/wignerd.py
WignerDCalculator.forward collects degree blocks. It uses the custom operation during tracing when blocks exist, and uses slice assignments otherwise. The polynomial block variable is renamed to poly_block.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Refactor

Suggested reviewers: outisli


Merge Risk: ⚪ Minimal · up to aea99

No actionable merge-blocking defect was established. The untested PyTorch 2.11 compiled path remains a validation consideration, not a demonstrated failure.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the two main changes: custom-op Wigner-D assembly and inverse-rotation handling through a view.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🧪 Generate unit tests (beta)
  • Create a new PR

🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants