Skip to content

[PyTorch] [torch.compile] torch.compile support for LayerNormLinear and LayerNormMLP - #43

Open
pggPL wants to merge 20 commits into
mainfrom
layernorm_compile
Open

pggPL wants to merge 20 commits into
mainfrom
layernorm_compile

Conversation

@pggPL

@pggPL pggPL commented Sep 3, 2026 •

Copy link
Copy Markdown
Owner

Description

Add full-graph torch.compile support for LayerNormLinear and LayerNormMLP, including backward, dynamic batch dimensions and normalization outputs.

Targets main. This branch includes the NVFP4 packed-buffer, TensorSpec and Bias correctness fixes from NVIDIA/TransformerEngine#3628.

Changes

  • Describe forward outputs, saved tensors, aliases and backward gradients for both LayerNorm modules; retain eager fallbacks for unsupported configurations.

  • Share output-gradient setup and quantizer validation with Linear. Adapt the newly inherited fused-attention backward container to the same gradient setup contract, preserving its existing first-output gradient assignment.

  • Validate both MLP GEMMs, including gated activations and sequence-parallel dimensions.

  • Support compiled FP8 weight caching in both LayerNorm modules, following Linear: replace the cache on the first microbatch and reuse it on subsequent microbatches, including both MLP weights. GPU-controlled cache updates and fused wgrad accumulation retain eager fallbacks.

  • Share the cache regression across Linear, LayerNormLinear and LayerNormMLP, checking independent eager outputs/gradients, cache refresh/reuse, optimizer updates, immediate/deferred backward and CUDA Graph replay.

  • Describe LayerNorm/RMSNorm output, mean and reciprocal standard deviation through dynamo.fake.fake_norm, used by both LayerNorm modules. Share the fused-quantization layout predicate with activation output metadata, including the RHT-fusion disable flag and output device; remove duplicate layout helpers. Preserve MXFP8/cuDNN normalization layouts and gathered LayerNormMLP output shapes.

  • Parameterize module compile tests across recipes, shapes and modes, with LayerNorm-specific outputs, gradients and eager fallbacks. Retain the non-matrix Linear regressions and expanded cold CUDA Graph recipe coverage inherited from [PyTorch] Fix NVFP4 metadata in compiled Linear operations NVIDIA/TransformerEngine#3628.

  • Preserve the native RMSNorm MXFP8 2D scale layout in fake normalization metadata; cover metadata reconstruction and add CustomRecipe MXFP8 2D cases to the compiled normalization scale-layout matrix.

Validation

2026-10-23 RMSNorm follow-up: the new metadata regression detects two failures on the old helper. The corrected selection passes 113 tests, with 83 hardware skips on Ada. The final helper also passes all 32 metadata cases after the lint-only condition refactor. Native MXFP8 2D compiled forward/backward cases are added for supported hardware but remain unverified on Ada. Existing native libraries reused.

2026-10-23, workstation RTX 5880 Ada, PyTorch 2.15.0a0+875d815502.nvinternal.main; existing native libraries reused, no fresh native build:

  • After merging NVIDIA/main dd02f3f7: fake-versus-real outputs, LayerNorm variants and inherited Linear cache selection: 85 passed, 15 skipped.
  • Final cache and eager-fallback selection: 21 passed, 36 hardware skips. The cache tests cover all three modules, five minibatches with optimizer updates, three microbatches each, both compile modes and immediate/deferred backward. Both MLP caches are checked. Float8CurrentScaling passes; Float8BlockScaling, MXFP8 and NVFP4 cache cases require supported hardware and remain unverified for this change.
  • Black 24.4.2, changed-module pylint, license, AST and whitespace checks pass. Full Python lint retains the pre-existing fused_mla_q_uproj.py:19 C0411 import-order diagnostic.

Historical validation before this merge/cache extension:

B100/SM100, PyTorch 2.15.0a0+875d815502.nvinternal.main, CUDA 13.4; existing October 7 native libraries reused, no fresh native build.

  • Focused LayerNorm compile selection: 180 passed, 4 expected skips for unsupported LayerNormMLP backward overrides. Includes full-graph forward/backward, non-matrix inputs, normalization output gradients, variants and eager fallbacks.
  • The selection includes 60 normalization scale-layout cases: both modules, LayerNorm/RMSNorm, MXFP8 and NVFP4, cuDNN settings, shapes, returned norm output, and NVFP4 RHT fusion enabled/disabled.
  • The inherited cold CUDA Graph regression is identical to the version validated in [PyTorch] Fix NVFP4 metadata in compiled Linear operations NVIDIA/TransformerEngine#3628: 48 passed with an independent eager model and no implicit recipe construction for unquantized inputs.
  • Pre-commit, license, syntax and whitespace checks passed. Full PyTorch Python lint retains an unchanged C0411 import-order diagnostic in attention/fused_mla_q_uproj.py in the GPU container; no unrelated source cleanup is included. Pylint on all changed production files passed.

No complete compile-suite rerun or distributed TP/SP/Userbuffers validation. The gathered-output test emulates the collective and does not establish multi-GPU correctness.

@pggPL
pggPL force-pushed the layernorm_compile branch from 90f05e3 to 9422845 Compare September 3, 2026 12:42
@pggPL
pggPL changed the base branch from layernorm_compile_refactor to layernorm_compile_prep September 3, 2026 12:42
@pggPL
pggPL force-pushed the layernorm_compile branch from 9422845 to 4cfffaf Compare September 3, 2026 12:49
@pggPL
pggPL requested a review from cyanguwa as a code owner September 17, 2026 11:00
@pggPL
pggPL changed the base branch from layernorm_compile_prep to main September 17, 2026 11:33
@pggPL
pggPL changed the base branch from main to fix_nvfp4_compile_nd October 12, 2026 15:32
@pggPL
pggPL changed the base branch from fix_nvfp4_compile_nd to main October 12, 2026 15:43
xrennvidia and others added 19 commits October 26, 2026 10:55
…the fused grouped MLP (NVIDIA#3545)

* [PyTorch] Quantize distributed BF16 weights in the fused grouped MLP

A distributed (e.g. GTP) weight whose shards are not natively quantized is
re-gathered as BF16. The fused grouped-MLP op assumed gathered weights were
already quantized: forward quantized the shards before the gather, and backward
read _columnwise_data / the NVFP4 amax straight off the gathered tensor, which
fails for a module whose per-module recipe (e.g. MXFP8 experts under a global
NVFP4 recipe) does not create native-quantized params.

Gather first, then quantize whatever is still unquantized with the op's
per-expert weight quantizers, mirroring module/linear.py's handling of a
re-gathered BF16 distributed weight. Native-quantized shards gather as quantized
tensors and pass through unchanged. The FC2 backward re-gather moves ahead of
the NVFP4 amax / global-scale computation so that path no longer reads
un-gathered shards.

The per-expert weight quantizer lists are built once in forward and saved on
the FC1/FC2 contexts; backward quantizes the re-gathered weights with those
rather than reading the live op. A later forward on the same module under a
different recipe, or with quantization disabled, resets the ops' quantizers
before the earlier backward runs (pipelined microbatches, or a module shared
across recipe scopes), which would otherwise hit None or quantize in the wrong
format.

Regression tests: a fake routed-expert DistributedWeight group whose materialize
hooks return BF16 tensors (or MXFP8 tensors for the native-quantized case)
drives the CuTeDSL fused grouped MLP under per-module MXFP8, optionally inside a
global NVFP4 autocast. Distinct power-of-two scales in the forward/backward
hooks make the gathered weights observable: output, dgrad and the main_grad
wgrads must match a plain reference bit-exactly up to those scales. A unit
check confirms already-quantized gathered weights pass through the helper
unchanged. A further case runs a second forward under no recipe or NVFP4
between an MXFP8 forward and its backward and requires bit-exact agreement
with a reference. Fails on the unpatched op with AttributeError on
_columnwise_data in backward; passes with the fix.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Xiaowei Ren <xren@nvidia.com>

* [PyTorch] Drop NVFP4 rowwise input copies from the fused grouped MLP saved state

Every wgrad path reads only the columnwise tiles of the saved FC1/FC2 inputs:
the cuDNN grouped wgrad kernel, the single-group dense GEMM (general_gemm with
the NT layout unwraps both operands columnwise) and the generic grouped-GEMM
fallback (NVFP4 forces a TN layout by taking the columnwise data for a
non-transposed A and a transposed B, which is the wgrad case). The save path
already pruned the rowwise data and scales for MXFP8 but kept them for NVFP4,
citing the generic fallback, so each quantized NVFP4 input was held (and, under
activation offloading, copied to host and back) twice.

Prune them for NVFP4 as well, unconditionally. The amax tensors are untouched.
Per decoder expert layer of a Nemotron hybrid proxy (83k tokens, hidden 3072)
this removes a 121 MiB data and a 15 MiB scale tensor from the ~1.9 GiB saved
set, with unchanged numerics on both the cuDNN kernel and the forced generic
fallback (NVTE_DISABLE_CUTEDSL_WGRAD_FUSED_GROUPED_MLP=1).

Adds a saved_tensors_hooks test asserting exactly one FP4 data and one scale
tensor is saved per quantized input on the fused NVFP4 path, for group sizes
1 and 4, with token counts chosen so the inputs cannot be confused with weights
in the count, and registers it in the L0 unit-test runner. Grouped storage is
saved as raw buffers; the single-group dense path (cuDNN frontend >= 1.27)
saves a GroupedTensor wrapper whole, so the test inspects the wrapper's buffers
directly and requires its rowwise data and scales to be None.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Xiaowei Ren <xren@nvidia.com>

* Update transformer_engine/pytorch/ops/fused/grouped_mlp.py

Co-authored-by: Tim Moon <4406448+timmoon10@users.noreply.github.com>
Signed-off-by: Xiaowei Ren <103958965+xrennvidia@users.noreply.github.com>

* [PyTorch] Move the NVFP4 saved-state test into test_grouped_mlp.py

The check that the fused grouped MLP saves only the columnwise NVFP4 input
tiles lives naturally with the other fused grouped MLP tests. Move it into
TestGroupedMLPFusedOp as test_nvfp4_saves_columnwise_inputs_only, drop the
standalone file, and remove its separate line from the L0 launcher, which
already runs test_grouped_mlp.py.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Xiaowei Ren <xren@nvidia.com>

* [PyTorch] Move the fused grouped MLP distributed-weight tests into test_grouped_mlp.py

The DistributedWeight dispatch tests for the fused grouped MLP (BF16 and
native-quantized shard gather, the quantizer pass-through unit check, and the
backward-after-recipe-change regression) move into test_grouped_mlp.py as
TestGroupedMLPDistributedWeight, with their fake DistributedWeight helpers.
Drop the standalone file and its separate L0 launcher line, which already
runs test_grouped_mlp.py.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Xiaowei Ren <xren@nvidia.com>

* [PyTorch] Build fused grouped MLP weight quantizer lists lazily

The per-expert weight quantizer lists were built unconditionally at the top
of the fused grouped MLP forward, costing 2 * num_groups quantizer lookups on
every call, including the single grouped weight path that never uses them.

Build them only where they are consumed: the discrete-weight FC1/FC2
quantization and the distributed-weight gather-then-quantize sites. Save the
full list on the backward context only for distributed weights, which is the
one path that re-quantizes in backward; every other path keeps saving just the
first expert's quantizer as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Xiaowei Ren <xren@nvidia.com>

* Apply suggestion from @ksivaman

Co-authored-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>
Signed-off-by: Xiaowei Ren <103958965+xrennvidia@users.noreply.github.com>

* [PyTorch] Skip columnwise quantization of gathered distributed FC1 weights in forward

The forward GEMMs read only the rowwise weight data; the columnwise copy
exists for the dgrad GEMM in backward, which reads it from the saved weight.
A distributed weight's gathered copy is not saved (the shards are, and
backward re-gathers and quantizes columnwise itself), so the columnwise
tiles produced in forward were built and immediately discarded. Match the
FC2 distributed site and skip them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Xiaowei Ren <xren@nvidia.com>

---------

Signed-off-by: Xiaowei Ren <xren@nvidia.com>
Signed-off-by: Xiaowei Ren <103958965+xrennvidia@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Tim Moon <4406448+timmoon10@users.noreply.github.com>
Co-authored-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>
* [PyTorch] Keep None sentinels on the custom op device

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>

* [PyTorch] Remove the sentinel device test

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>

* [PyTorch] Restore the original test file ending

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>

* [PyTorch] Scope the device to custom op result packing

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>

* [PyTorch] Make result device selection easier to read

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>

---------

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…3721)

* Run minimum-versions build on pushes to main to warm sccache

The workflow only runs on pull_request, so sccache's GHA backend stores
every object in a PR-scoped cache that other PRs can't read. Running it
on pushes to main populates the base-branch scope, which PR runs can
restore, so a new PR's first build no longer compiles from scratch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rt4UYn6zqR9uS7DWNGEBYx
Signed-off-by: Apoorva Garg <271577+agarg21@users.noreply.github.com>

* Restrict minimum-version workflow token permissions

Signed-off-by: Przemek Tredak <ptredak@nvidia.com>

---------

Signed-off-by: Apoorva Garg <271577+agarg21@users.noreply.github.com>
Signed-off-by: Przemek Tredak <ptredak@nvidia.com>
Co-authored-by: Apoorva Garg <271577+agarg21@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Przemek Tredak <ptredak@nvidia.com>
* Support row-only MXFP8 distributed master-weight casts

Signed-off-by: xiuhu17 <zhihao.wang@perplexity.ai>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Share the NCCL process group across row-only cast tests

Signed-off-by: xiuhu17 <zhihao.wang@perplexity.ai>

* Address row-only MXFP8 review and preserve bidirectional performance

Signed-off-by: Zhihao Wang <zhihao.wang@perplexity.ai>

* Select MXFP8 partial-cast directions from output storage

Use columnwise output presence to select partial-cast computation in the common API and PyTorch binding. Require scales for requested columnwise output and ignore them when that output is omitted.

Document the contract and cover unused scales, empty shards, and missing required scales in regression tests.

Signed-off-by: Codex <codex@openai.com>

---------

Signed-off-by: xiuhu17 <zhihao.wang@perplexity.ai>
Signed-off-by: Zhihao Wang <zhihao.wang@perplexity.ai>
Signed-off-by: Codex <codex@openai.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
…A#3744)

Set device during NCCL init in symmetric memory test

Signed-off-by: Tim Moon <tmoon@nvidia.com>
…ath (NVIDIA#3649)

* [Common][PyTorch] Add NVFP4 row-scaled grouped cast with graph-safe path

Single-launch grouped row-scaled NVFP4 cast + fused amax, plus graph-safe amax/cast variants driven by device first_dims/tensor_offsets for capture-once/replay MoE. Wired into PyTorch (ep.nvfp4_group_row_scaled_cast) with C++ and PyTorch capture/replay tests.

Signed-off-by: Cael Ling <caell@nvidia.com>

* Address review: guard grouped row-scaled cast dispatch and inputs

Restrict the FUSED_NVFP4_ROW_SCALED selection to shapes the grouped kernels accept (128-aligned last dim, <= 64 experts) so unsupported requests fall back to the per-expert path. Return early for empty routing, make the graph-safe binding's input contiguous, and reject with_rht/with_2d_quantization/with_amax_reduction on both row-scaled entries. Add regression tests for the four guarded paths.

Signed-off-by: Cael Ling <caell@nvidia.com>

* Guard row-scaled grouped cast against non-2D, partial, and strided routing

The grouped row-scaled cast treats its input as 2D [rows, last_dim] indexed by
split counts, so route higher-rank inputs and partial splits (split sum below
the row count) to the per-expert path instead of the grouped kernel. Make
first_dims and tensor_offsets contiguous in the graph-safe cast so a strided
routing view is not misread. Add fallback and strided-routing tests.

Signed-off-by: Cael Ling <caell@nvidia.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fall back to per-expert cast for mixed NVFP4 row/tensor-scaled splits

The bulk NVFP4 split_quantize allocation sizes its amax buffer from the
first quantizer's row-scale setting, so a group mixing row-scaled and
tensor-scaled quantizers writes per-row amax into a per-tensor buffer and
corrupts neighbors. Detect the mixed case and fall back to the unfused
per-split path.

Signed-off-by: Cael Ling <caell@nvidia.com>

* Route row-scaled NVFP4 grouped quantize through nvte_group_quantize

Add the kNVTEGroupedRowScaledNVFP4 grouped tensor param and dispatch
row-scaled NVFP4 outputs in nvte_group_quantize to the graph-safe amax
and cast kernels. Handle row-scaled quantizers in tex.group_quantize.

Remove the row-scaled-specific C APIs, the host-split row-scaled cast
kernel, tex.nvfp4_group_row_scaled_cast_graph_safe, the ep.py helper,
and the fused row-scaled split_quantize path.

Add a top-level static_assert for unregistered types in TypeInfo.

Signed-off-by: Cael Ling <caell@nvidia.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Require 128-row experts for uniform row-scaled NVFP4 group_quantize

Signed-off-by: Cael Ling <caell@nvidia.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fold row-scaled NVFP4 amax into nvte_group_amax

Dispatch nvte_group_amax and nvte_group_amax_graph_safe on the output
tensor format so that row-scaled NVFP4 outputs get per-row amaxes, and
deprecate nvte_group_nvfp4_compute_amax in favor of nvte_group_amax.
Rename the row-scaled amax helpers to group_row_amax(_graph_safe).

In split_quantize, fall back to unfused allocation and quantization
whenever any NVFP4 quantizer is row-scaled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tim Moon <tmoon@nvidia.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix missing include

Review suggestion from @greptile-apps

Signed-off-by: Tim Moon <tmoon@nvidia.com>

* Add missing header include

Signed-off-by: Tim Moon <tmoon@nvidia.com>

---------

Signed-off-by: Cael Ling <caell@nvidia.com>
Signed-off-by: Tim Moon <tmoon@nvidia.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tim Moon <tmoon@nvidia.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…IA#3596)

Support user-provided routing and return expert counts

Add RouterComputationInfo and RoutingMapInfo to the JAX MoE API. moe() takes
one router_info argument: RouterComputationInfo has TE compute the gate and
top-k, while RoutingMapInfo supplies caller-computed expert indices and
differentiable routing weights (e.g. DeepSeek-V4 hash routing) and skips
TE's router.

moe() and _MoEBlock also return global per-expert assignment counts as a
fourth output. collect_expert_counts=False skips the associated all-reduce.

Signed-off-by: Deep Patel <deepatel@nvidia.com>
* [Common] Keep cuda_driver::call's symbol cache alive through exit

rtc::KernelManager's static destructor unloads its CUDA modules through
cuda_driver::call. Each instantiation of call caches driver entry points in
function-local statics, and those caches are usually first constructed after
the KernelManager, so they are destroyed before it. ~Kernel then reads a
destroyed unordered_map: with GCC 16's libstdc++ the process segfaults at exit;
with GCC 13's it writes into freed memory instead.

Allocate the cache and its mutex with new and never destroy them, and add a
test that the process exits cleanly after running NVRTC kernels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Connor Baker <connorb@nvidia.com>

* [Common] Never destroy rtc::KernelManager or the fused-attention graph caches

Both were function-local statics whose destructors call into CUDA after main
returns: ~Kernel unloads modules through the driver, and destroying a cached
cuDNN plan that a CUDA graph retains releases a CUDA user object. The CUDA
Programming Guide makes any use of CUDA during program termination undefined
behavior, and both crashed at exit depending on static destruction order.

Allocate both with new and never delete them, as TVMFFIConfigCache::create()
and the fused-attention debug registry already do. Both caches only ever
insert, so nothing changes while the process runs.

Replace tests/pytorch/test_process_exit.py, whose LayerNorm only runs NVRTC
kernels while the NVTE_NORM_FWD_USE_CUDNN and NVTE_NORM_BWD_USE_CUDNN switches
are off, with MALLOC_PERTURB_=165 for the C++ util tests. UtilTest.NVRTC drives
rtc::KernelManager directly, and ctest checks each test's exit status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Connor Baker <connorb@nvidia.com>

---------

Signed-off-by: Connor Baker <connorb@nvidia.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Fix DistributedWeight handling under saved-tensor hooks

Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>

* Reduce CPU overhead of the DistributedWeight check in prepare_for_saving

prepare_for_saving and restore_from_saved run for every tensor every TE
module saves, so the DistributedWeight check sits on a hot path for all
users, not just GTP. Test None and torch.Tensor first, read the marker
with an inline getattr (DistributedWeight implementers must be Tensor
subclasses), and only query torch.compiler.is_compiling() for a flagged
weight. On restore, test the common None/Tensor case first.

On a CPU microbenchmark this brings the non-distributed cost back to
roughly main's, e.g. 4.5 -> 5.2 us (was 10.0 us) per prepare_for_saving
for a 32-expert bf16 GroupedLinear.

Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add saved-tensor hook tests for DistributedWeight modules

Port the Linear and GroupedLinear saved-tensor hook tests from NVIDIA#3545 to
the prepare_for_saving fix, and extend them:

- Run each module case both under identity saved_tensors_hooks and under
  te.checkpoint(use_reentrant=False), whose pack hook discards saved
  tensors and returns recomputed ones in backward.
- Cover LayerNormLinear, which also gates its backward on the saved
  weight being a DistributedWeight.
- Add CPU-only checks that prepare_for_saving keeps a DistributedWeight
  by reference and that backward gets the same object back with no
  hooks, identity hooks, and torch.utils.checkpoint.

Co-authored-by: Xiaowei Ren <xren@nvidia.com>
Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Still use the autograd checker for the dist weight

Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>

* Error for distweight + torch.compile

Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>

* Rename _SavedDistributedWeight to _SavedTensorRef and drop premise test

Remove test_saved_tensor_hooks_unpack_subclass_as_plain_tensor, which
only checked PyTorch's saved-tensor-hook behavior rather than TE's.

Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Xiaowei Ren <xren@nvidia.com>
* Remove QB bounds device side validation

Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* [PyTorch] Test grouped FP8 block-scaling GEMM against the split-tensor path

MXFP8 GEMM on Blackwell accumulates at higher precision than FP8 block-scaling GEMM on Hopper.
The grouped FP8 block-scaling GEMM tests compared against an FP32 reference, so they passed with
the MXFP8 emulation on Blackwell but failed on Hopper.

Compare against the legacy split-tensor grouped GEMM instead, consistent with the existing MXFP8
grouped GEMM tests. Also remove the FP8 block-scaling zero-size-expert and mixed-operand tests,
which no other recipe has.

Signed-off-by: Alp Dener <adener@nvidia.com>

* [PyTorch] Test grouped FP8 block-scaling GEMM with uneven and empty experts

Dropping the varying splits and the zero-size-expert test left gaps: the remaining tests no longer
run grouped FP8 block-scaling GEMM with uneven or empty experts when the weights or outputs are
per-expert lists, when accumulating, or when K is not a multiple of 128.

Parametrize the grouped FP8 block-scaling GEMM test over uniform, varying and zero-expert splits,
checked against the split-tensor reference.

Signed-off-by: Alp Dener <adener@nvidia.com>

---------

Signed-off-by: Alp Dener <adener@nvidia.com>
NVIDIA#3513)

* fix: patch cuda hardcodes for plugin compatibility (complete debug directory coverage)

This commit completes the CUDA plugin compatibility patches across the entire codebase:

1. Debug directory (transformer_engine/debug/):
   - fake_quant.py: Replace is_cuda checks with te_device_type()
   - log_fp8_tensor_stats.py: Replace torch.device('cuda') and torch.cuda.get_device_capability()
   - per_tensor_scaling.py: Replace is_cuda checks with te_device_type()
   - utils/stats_buffer.py: Replace .cuda() calls with .to(te_device_type())

2. Additional missing files from cuda_patch_rc2.14:
   - permutation.py: Replace all is_cuda checks with device.type comparisons
   - tensor/float8_tensor.py: Replace is_cuda checks with device.type comparisons
   - tensor/float8_blockwise_tensor.py: Replace is_cuda checks with device.type comparisons
   - tensor/mxfp8_tensor.py: Replace is_cuda checks with device.type comparisons

3. Core infrastructure:
   - transformer_engine/__init__.py: Export te_platform and te_device_type from pytorch module
   - transformer_engine/pytorch/__init__.py: Define te_platform() and te_device_type() functions

All runtime CUDA hardcodes have been replaced with plugin-compatible abstractions.
Total: 64 files modified (+405/-289 lines)

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* fix: remove explicit import to avoid black parsing error

The te_platform and te_device_type functions are dynamically assigned to
the transformer_engine module namespace via:
  transformer_engine.te_platform = te_platform
  transformer_engine.te_platform = te_platform

in transformer_engine/pytorch/__init__.py, so explicit imports in
transformer_engine/__init__.py cause black to fail parsing due to
circular import detection.

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* fix: correct malformed imports that prevent module loading

Move te_platform and te_device_type imports outside of parenthesized
import lists to fix SyntaxError. The imports were incorrectly placed
inside multi-line import statements, preventing the modules from loading.

Fixed files:
- transformer_engine/debug/features/log_fp8_tensor_stats.py
- transformer_engine/pytorch/attention/dot_product_attention/backends.py
- transformer_engine/pytorch/attention/dot_product_attention/context_parallel.py
- transformer_engine/pytorch/attention/dot_product_attention/dot_product_attention.py
- transformer_engine/pytorch/attention/inference.py
- transformer_engine/pytorch/graph.py
- transformer_engine/pytorch/quantization.py
- transformer_engine/pytorch/quantized_tensor.py
- transformer_engine/pytorch/transformer.py

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* fix: add missing te_device_type import to files using it

Several files imported only te_platform but also called te_device_type(),
causing NameError at runtime. Add te_device_type to imports.

Fixed files:
- transformer_engine/pytorch/attention/dot_product_attention/flex_attention.py
- transformer_engine/pytorch/attention/rope.py
- transformer_engine/pytorch/cpp_extensions/gemm.py
- transformer_engine/pytorch/optimizers/fused_adam.py
- transformer_engine/pytorch/utils.py

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* fix: correct malformed imports in additional files

Move te_platform and te_device_type imports outside of parenthesized
import lists to fix SyntaxError across 20 more files.

Fixed files:
- transformer_engine/pytorch/attention/dot_product_attention/utils.py
- transformer_engine/pytorch/attention/multi_head_attention.py
- transformer_engine/pytorch/cpu_offload.py
- transformer_engine/pytorch/module/base.py
- transformer_engine/pytorch/module/grouped_linear.py
- transformer_engine/pytorch/module/layernorm_linear.py
- transformer_engine/pytorch/module/layernorm_mlp.py
- transformer_engine/pytorch/module/linear.py
- transformer_engine/pytorch/ops/basic/basic_linear.py
- transformer_engine/pytorch/ops/basic/grouped_linear.py
- transformer_engine/pytorch/ops/basic/l2normalization.py
- transformer_engine/pytorch/ops/fused/forward_linear_scale_add.py
- transformer_engine/pytorch/ops/fused/grouped_mlp.py
- transformer_engine/pytorch/ops/fused/userbuffers_backward_linear.py
- transformer_engine/pytorch/ops/fused/userbuffers_forward_linear.py
- transformer_engine/pytorch/ops/op.py
- transformer_engine/pytorch/tensor/nvfp4_tensor.py
- transformer_engine/pytorch/tensor/storage/mxfp8_tensor_storage.py
- transformer_engine/pytorch/triton/mhc.py
- transformer_engine/pytorch/triton/permutation.py

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* fix: use routing_map device instead of hardcoded device type

In make_row_id_map, use routing_map.device for tensor allocation
instead of te_device_type() to respect the input tensor's device.

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* revert: remove te_platform() replacements, keep te_device_type()

Revert all te_platform() method calls back to torch.cuda.* calls.
Keep te_device_type() string replacements for device type parameters.
Preserve te_platform definition in transformer_engine/pytorch/__init__.py.

Changes:
- Reverted 146 te_platform() calls back to torch.cuda.*
- Removed 'from transformer_engine import te_platform' imports
- Kept all te_device_type() replacements intact
- Kept te_platform definition in __init__.py for future use

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* fix: remove remaining te_platform() calls in utils, fused_adam, and distributed

Replace remaining te_platform() calls with torch.cuda equivalents:
- torch.cuda.nvtx.range() / range_push() / range_pop() for NVTX profiling
- torch.cuda.amp.autocast for autocast context manager
- Update docstring reference in fused_adam.py

All te_platform() usage has been removed except for the definition
in transformer_engine/pytorch/__init__.py.

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* keep sync with torch.cuda api usage

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* format

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Fix missing te_device_type imports and import ordering

Add the missing `te_device_type` import to eight modules that called it
without importing it, which would raise NameError at runtime: jit,
attention/inference, attention/dot_product_attention/{backends,
context_parallel,utils}, ops/fused/{grouped_mlp,
userbuffers_backward_linear}, and triton/mhc.

Move the `te_device_type` import ahead of relative imports where pylint
classifies it as a first-party import that must precede local ones. In
jit and distributed, place it so it does not split the torch import
group. Order `transformer_engine_torch` before `transformer_engine` in
ops/_common.

Add docstrings to `te_device_type` and `te_platform`.

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Query autocast state and dtype on the same device

The device-type patch had changed the autocast dtype lookups to the
configured device while leaving the enabled checks on CUDA. With a
non-CUDA device type, vendor autocast enabled, and CUDA autocast
disabled, these sites took the non-autocast branch and kept the input
dtype instead of the requested autocast dtype.

Route the operation sites through `maybe_autocast_dtype`, which already
queries one device for both calls and carries the torch version guard.
This covers the activation, SwiGLU, grouped linear, basic linear, and
fused linear forward operations.

Add `torch_is_autocast_gpu_enabled` next to `torch_get_autocast_gpu_dtype`
for the two sites outside the operation layer that had the same
mismatch: `set_activation_dtype`, which sets the activation dtype for
every module, and the TransformerLayer forward cast.

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Gate per-document THD partitioning on CUDA

`tex.thd_get_partitioned_indices` allocates a CUDA output tensor and
launches a CUDA kernel on the current CUDA stream, so it runs only on
CUDA. The device-type patch had changed its dispatch condition to the
configured device type, which sent packed sequences into that kernel on
a non-CUDA device instead of the device-capable fallback, failing the
`is_cuda` assertion before the fallback could run.

Restore the CUDA condition in `build_rank_indices` and in
`_get_thd_partition_cu_seqlens`, which had the same problem when
normalizing the boundaries that feed the kernel, and record why both
are gated on CUDA rather than on the configured device.

Keep the configured device in the NO_LOAD_BALANCE branch, which builds
indices with plain tensor operations and runs no CUDA kernel. Restore
the assertion message, which named the device type that triggers it.

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Drop incidental changes from the device-type patch

Narrow the diff against main to the device-type substitutions themselves:

- context_parallel.py: remove the comments on the THD partitioning
  device gates.
- cpp_extensions/gemm.py: restore the single-line
  Float8BlockwiseQTensorStorage import, which did not need wrapping.
- graph.py: remove an added blank line, leaving the file unchanged from
  main.

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Cover device-type checks missed in recently merged code

Two paths reached from main used CUDA device checks where the work below
them is not CUDA-specific:

- tensor/nvfp4_tensor.py: `NVFP4Tensor` kept CUDA checks in its
  `record_stream` dispatch and in `copy_`, while the sibling
  `Float8Tensor`, `MXFP8Tensor`, and `Float8BlockwiseQTensor` classes use
  the configured device type for the same logic. On another device type
  `copy_` would move the operand to the destination device and
  `record_stream` would skip stream tracking.
- cross_entropy.py: the input validation rejected non-CUDA tensors even
  though the Triton backend derives every allocation from the input
  device and contains nothing CUDA-specific.

Leave the checks that guard compiled CUDA kernels, the `device_types`
dispatch keys on custom operations, and the `is_cuda` properties that
implement the PyTorch tensor contract.

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Apply plugin device patches after the _nv alias is registered

The top-level hook in transformer_engine/__init__.py imported
<plugin>.patches before load_framework_extension() registered
transformer_engine_torch_nv. A plugin that imports that alias at package
level - a case the loader explicitly supports - failed there, and the
enclosing except only warned, so apply_patches() never ran. The loader
later imported the plugin successfully but never retried the patches,
leaving device patches unapplied while vendor calls still went through
CUDA.

Move the call into load_framework_extension(), after the alias is
registered and before the PyTorch submodules capture references to
torch.cuda entry points. Probe the submodule with find_spec so a plugin
that needs no patches may omit it, without masking real import failures
inside an existing patches module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Accept the configured device type in the shared forward input check

The modules whose `device` argument now defaults to `te_device_type()`
allocate their parameters on the configured device, but the
`prepare_forward()` input check still rejected anything whose `is_cuda`
was false. That property is a tensor attribute, so patching the
`torch.cuda` namespace does not change it, and forward raised before a
plugin's native operation could run.

Compare `device.type` against `te_device_type()` instead, and report the
configured device type in the error message. One check covers Linear,
LayerNormLinear, GroupedLinear, and LayerNormMLP, which share this base
method. Behavior on CUDA is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Restore the original spacing in the top-level package

Removing the plugin hook left an extra blank line behind. Drop it so the
file matches main and no longer appears in the diff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Skip patch discovery for single-file plugins

find_spec() raises ModuleNotFoundError instead of returning None when
the parent module is not a package, so a single-file plugin that
provides load_plugins() failed the optional-patches probe. The exception
handler then disabled the plugin and restored the original extension.

Check for __path__ before searching for the submodule. Genuine import
failures inside an existing patches module still propagate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Use the configured device type in GroupedTensor record_stream

The merged upstream GroupedTensor stream-tracking handler checked
`tensor.is_cuda`, which pins the path to CUDA. Match the device
abstraction used by the other quantized-tensor `record_stream`
handlers so plugin device types propagate allocator stream tracking.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Accept the configured device type in the attention input checks

The attention entry points asserted `is_cuda` on queries, keys, and
values, so a non-CUDA configured device failed validation before any
attention implementation could run, even though the surrounding state
was already created on the configured device.

Check the configured device type instead, in DotProductAttention and in
the matching FlashAttention and FusedAttention assertions. Apply the
same change to the per-document THD partitioning check in context
parallelism, and drop the redundant CUDA half of the mixed-THD
sequence_ids check, where the comparison against the Q/K/V device
already enforces the documented requirement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Keep the paged KV cache and EP buffer on CUDA

The paged KV cache page table and the EpBuffer default device fed
tensors into torch.library custom ops registered with
device_types="cuda". Allocating them on a configured non-CUDA device
left no matching registered implementation, so the dispatcher failed
the call before the wrapped tex function could run.

Restore the CUDA allocations so these tensors match their op
registrations, and drop the now-unused imports. Supporting another
device on these paths requires changing the op registrations, which
depends on how the device is registered with PyTorch and on when the
device type is configured relative to module import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Apply plugin device patches after the plugin finishes loading

apply_patches() rewrites process-global state in place - torch.cuda entry
points, TE_DEVICE_TYPE and TE_PLATFORM - with no way to undo it. Running it
before load_plugins() meant a load_plugins() failure left those patches
active while the except block restored only sys.modules, so import continued
with the CUDA extension under a vendor device type and failed later, far from
the cause.

Run load_plugins() first so everything that can fail is rolled back
completely. The patches still precede the PyTorch submodules that capture
torch.cuda entry points, and still follow the _nv alias registration that a
plugin package may need to import itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>

* Fix regex in test_cross_entropy

Signed-off-by: Przemyslaw Tredak <ptrendx@gmail.com>

---------

Signed-off-by: Xianduo Li <lixianduo@mail.nankai.edu.cn>
Signed-off-by: Przemyslaw Tredak <ptrendx@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Przemyslaw Tredak <ptrendx@gmail.com>
* Guard fused MoE auxiliary loss integer offsets

Signed-off-by: Srijan Keshri <212402043+arcusbuilds@users.noreply.github.com>

* Update tests/pytorch/test_fused_router.py

Co-authored-by: Alp Dener <adener@nvidia.com>
Signed-off-by: Srijan Keshri <212402043+arcusbuilds@users.noreply.github.com>

* Use 64-bit indexing for fused MoE auxiliary loss

Signed-off-by: Srijan Keshri <212402043+arcusbuilds@users.noreply.github.com>

* Unroll forward row loop in fused MoE aux loss

Signed-off-by: arcusbuilds <srijankeshri007@gmail.com>

* Revert 64-bit indexing, restore INT_MAX guard in fused MoE aux loss

Match the other fused router kernels, which reject
num_tokens * num_experts > INT_MAX. Keep #pragma unroll 4 on the
forward row loops.

Signed-off-by: arcusbuilds <srijankeshri007@gmail.com>

* Use 64-bit loop counters in fused MoE aux loss kernels

Shapes at the INT_MAX guard (e.g. INT_MAX x 1) overflowed the int loop
counters on their last increment and the backward grid-size math.
Flattened offsets stay 32-bit, which the guard bounds.

Signed-off-by: arcusbuilds <srijankeshri007@gmail.com>

---------

Signed-off-by: Srijan Keshri <212402043+arcusbuilds@users.noreply.github.com>
Signed-off-by: arcusbuilds <srijankeshri007@gmail.com>
Co-authored-by: Alp Dener <adener@nvidia.com>
* rely on recipe object for fusion, reduce cpu overhedads for deciding the grouped tensor path

Signed-off-by: Varun Thumbe <vthumbe@nvidia.com>

* Keep fusion decisions local to op fuser

Signed-off-by: Varun Thumbe <vthumbe@nvidia.com>

* Use recipe representation for fusion cache key

Signed-off-by: Varun Thumbe <vthumbe@nvidia.com>

* Include CUDA device in grouped tensor support cache

Signed-off-by: Varun Thumbe <vthumbe@nvidia.com>

* Declare derived quantizer settings as recipe fields

Signed-off-by: Varun Thumbe <vthumbe@nvidia.com>

* Update transformer_engine/pytorch/ops/fuser.py

Co-authored-by: Tim Moon <4406448+timmoon10@users.noreply.github.com>
Signed-off-by: vthumbe1503 <vthumbe@nvidia.com>

* Update transformer_engine/pytorch/module/grouped_linear.py

Co-authored-by: Tim Moon <4406448+timmoon10@users.noreply.github.com>
Signed-off-by: vthumbe1503 <vthumbe@nvidia.com>

* Update transformer_engine/pytorch/ops/basic/grouped_linear.py

Co-authored-by: Tim Moon <4406448+timmoon10@users.noreply.github.com>
Signed-off-by: vthumbe1503 <vthumbe@nvidia.com>

---------

Signed-off-by: Varun Thumbe <vthumbe@nvidia.com>
Signed-off-by: vthumbe1503 <vthumbe@nvidia.com>
Co-authored-by: Tim Moon <4406448+timmoon10@users.noreply.github.com>
…caled kernels (NVIDIA#3682)

Optimize scale swizzle kernels

Use optimized rowwise full-width and columnwise chunk kernels for MXFP8 and NVFP4 scales, including multi-tensor swizzling. Keep the 16-byte scale-buffer alignment contract and cover its validation in C++ tests.

Choose the rowwise full-width kernel after the fallback grid exceeds one SM wave, based on paired GB200 and VR200 measurements.

Signed-off-by: Varun Thumbe <vthumbe@nvidia.com>
Fix GH CI docs

Signed-off-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>
…re (NVIDIA#3698)

* [CUDA] Handle THD boundary capacity across CP utility kernels

THD metadata can exceed per-block shared memory even when the token tensors are valid. Use device-derived default and opt-in capacities before reading the existing global boundary arrays, without allocating scratch buffers.

Centralize staging and normalization so indexing helpers consume consistent boundaries while retaining pre-scaled shared reads. Preserve the current old_lse output-accumulation behavior and exercise its rescaling in boundary regression tests.

Focused native validation passed 348 boundary/dtype cases, 60 output-rescaling cases including 24 exact upstream comparisons, 33 graph captures with three replays each, and 324 partition stress cases up to one million documents. SM86 and SM100 compilation passed.

Signed-off-by: Sudhakar Singh <sudhakars@nvidia.com>

* [PyTorch] Guard THD boundary tests on available device limits

Older PyTorch builds do not expose the shared-memory properties used to choose exact boundary-test sizes. Check both capabilities inside the two affected tests and skip only those tests when either field is missing, without guessing a version cutoff or querying CUDA during import.

Include the running PyTorch version in the skip reason to make compatibility skips identifiable. Production kernels and the remaining functional tests are unchanged. Missing/present-property checks and both targeted native boundary tests passed.

Signed-off-by: Sudhakar Singh <sudhakars@nvidia.com>

* Use runtime storage selection for THD boundary tables

Avoid duplicating each dtype, layout, and functor specialization solely for
boundary storage. Pass a block-uniform storage flag and plain pointers with
an explicit read divisor, preserving shared pre-scaling and immutable
global metadata.

Use the device-reported default shared-memory budget and the existing
global table as fallback, removing kernel-specific opt-in state and the
boundary-view type. Retain per-boundary integer division and CP validation,
and test the two remaining storage regimes.

Signed-off-by: Sudhakar Singh <sudhakars@nvidia.com>

* Restore compile-time storage selection for THD boundaries

Runtime storage selection obscures the shared address space and unit read
divisor, adding work to boundary searches. Restore shared/global kernel
specializations so the compiler can eliminate global-only normalization
from the shared path.

Keep plain pointer and explicit divisor arguments instead of the boundary
view. Retain the default shared-memory budget, immutable global fallback,
and the existing default-boundary regression tests without restoring
shared-memory opt-in.

Signed-off-by: Sudhakar Singh <sudhakars@nvidia.com>

---------

Signed-off-by: Sudhakar Singh <sudhakars@nvidia.com>
* Fail fast on Hopper FA4 backward with affected Quack versions

Signed-off-by: Kshitij Lakhani <klakhani@nvidia.com>

* Remove redundant Quack guard documentation

Signed-off-by: Kshitij Lakhani <klakhani@nvidia.com>

---------

Signed-off-by: Kshitij Lakhani <klakhani@nvidia.com>
Co-authored-by: Kirthi Shankar Sivamani <ksivamani@nvidia.com>
…DIA#3769)

Link tests executables against cpython when CuTeDSL is enabled

Signed-off-by: Fred Heinecke <fheinecke@nvidia.com>
Register LayerNorm module forward and backward custom ops with matching fake tensor metadata, shared eligibility checks, and compiled weight caches. Extend the existing compile tests to cover the LayerNorm modules.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
@pggPL
pggPL force-pushed the layernorm_compile branch from 70e4b7d to 83641e8 Compare October 29, 2026 12:07

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.