Skip to content

[DRAFT] Jax Support for FE OSS API#351

Draft
mgoldfarb-nvidia wants to merge 35 commits into
NVIDIA:developfrom
mgoldfarb-nvidia:mgoldfarb/api-base-framework-split
Draft

[DRAFT] Jax Support for FE OSS API#351
mgoldfarb-nvidia wants to merge 35 commits into
NVIDIA:developfrom
mgoldfarb-nvidia:mgoldfarb/api-base-framework-split

Conversation

@mgoldfarb-nvidia

@mgoldfarb-nvidia mgoldfarb-nvidia commented Jul 6, 2026

Copy link
Copy Markdown

This is an experimental branch to show one possible integration of JAX using the FE OSS API.

Summary by CodeRabbit

  • New Features
    • Added JAX support and examples across many FE-OSS API docs, including attention, GEMM fusions, and grouped GEMM operations.
    • Introduced new JAX-facing APIs for several kernels and wrappers, expanding framework options for supported workflows.
  • Documentation
    • Clarified return values, layout/shape constraints, and JAX usage patterns.
    • Added higher-level design and overview docs for the new JAX integration.
  • Chores
    • Updated package loading and dependency setup to support the new JAX frontend.

…jax-fe-oss-poc

# Conflicts:
#	docs/fe-oss-apis/dsa.md
Use stable module-level CuTe launchers and shared TupleDict pytree results across the JAX wrappers. Consolidate dtype validation into ApiBaseJax and remove obsolete compatibility modules.
Add and refine JAX bindings across GEMM, DSA, NSA, and SDPA paths, including layout-aware descriptors and sliding-window attention.

Consolidate array and dtype validation, add optional JAX test selection, and refresh the design and operator documentation.
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds an optional cudnn.jax frontend namespace mirroring existing PyTorch APIs for attention, DSA, GEMM, and grouped-GEMM operations. It refactors the shared API base into framework-neutral (ApiBase/TensorDesc) and Torch-specific (ApiBaseTorch/TorchTensorDesc) layers, adds a JAX adapter (ApiBaseJax, call_cutedsl), centralizes GEMM/grouped-GEMM validation helpers, migrates Torch API classes to the new base, switches package __init__ modules to lazy exports, and updates documentation and pyproject.toml.

Changes

Documentation

Layer / File(s) Summary
SDPA attention docs
docs/fe-oss-apis/attention/sdpa_bwd_d256.md, sdpa_fwd_d256.md
Adds JAX tabs, examples, and updated TupleDict return-value documentation.
JAX FE-OSS design doc
docs/fe-oss-apis/cutedsl-jax-design.md
New document describing the cudnn.jax namespace, execution architecture, and OSS readiness gaps.
DSA docs
docs/fe-oss-apis/dsa.md
Expands documentation with JAX namespace usage, tabbed examples, and constraints.
GEMM fusion docs
docs/fe-oss-apis/gemm_fusions/*.md
Adds JAX tabs/examples and updated return-value tuple contracts across GEMM and grouped-GEMM pages.
NSA/overview/rmsnorm docs, config
docs/fe-oss-apis/nsa.md, overview.md, rmsnorm_rht_amax.md, pyproject.toml, python/cudnn/README.md
Adds JAX install/usage docs, a new jax optional dependency group, and updated package structure docs.

Core Infrastructure

Layer / File(s) Summary
Package wiring
python/cudnn/__init__.py, _experimental_warnings.py, _jax/__init__.py, _operation_api.py
Adds lazy cudnn.jax loading, thread-safe experimental warnings, and make_operation_api lazy-export helper.
ApiBase/ApiBaseTorch split
python/cudnn/api_base.py, api_base_torch.py
Rewrites the shared API base into framework-neutral validation plus Torch-specific tensor layout/lifecycle logic.
JAX adapter core
python/cudnn/_jax/api_base.py, _jax/gemm.py, _jax/grouped_gemm.py
Implements ApiBaseJax, call_cutedsl, and shared GEMM/grouped-GEMM JAX validation helpers.

GEMM and Grouped-GEMM Fusion Ops

Layer / File(s) Summary
gemm_amax/gemm_dsrelu/gemm_srelu/gemm_swiglu
python/cudnn/gemm_*/
Migrates Torch APIs to ApiBaseTorch, adds shared gemm_validation.py, adds new JAX API modules, switches to lazy exports.
grouped_gemm_*
python/cudnn/grouped_gemm/*
Migrates each grouped-GEMM Torch API to ApiBaseTorch, adds JAX modules per operation, switches to lazy exports.
discrete_grouped_gemm
python/cudnn/discrete_grouped_gemm/*/api.py
Migrates base classes to ApiBaseTorch.

DeepSeek Sparse Attention (DSA)

Layer / File(s) Summary
Indexer forward/top-k/backward, score recompute, sparse attention backward
python/cudnn/deepseek_sparse_attention/*
Migrates Torch APIs to ApiBaseTorch, refactors kernel internals (dense indexer backward, score-recompute config), and adds JAX API modules for each stage.

Estimated code review effort: 5 (Critical) | ~150 minutes

Suggested labels: cat-enhancements, cat-feature

Suggested reviewers: jhjpark, Anerudhan, saltyminty

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the PR’s main theme: adding JAX support across FE OSS APIs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/fe-oss-apis/dsa.md (1)

179-184: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use a defined head-dim in the backward example.

math.sqrt(D) references an undefined name here, so the snippet is not runnable as written. This section already fixes the head dimension at 512, so use that constant (or derive it from the tensor shape) instead.

🛠️ Proposed fix
 result = DSA.sparse_attention_backward_wrapper(
     q, kv, out, dout, lse, attn_sink, topk_idxs,
-    softmax_scale=1.0 / math.sqrt(D),
+    softmax_scale=1.0 / math.sqrt(512),
     topk_length=topk_length,
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/fe-oss-apis/dsa.md` around lines 179 - 184, The backward example uses an
undefined head-dim symbol in DSA.sparse_attention_backward_wrapper by passing
math.sqrt(D), so update this snippet to use the already established 512 head
dimension or derive it from q/k/v tensor shape instead. Keep the fix localized
to the sparse_attention_backward_wrapper call and the softmax_scale argument so
the example remains runnable and consistent with the rest of the section.
🧹 Nitpick comments (10)
python/cudnn/_jax/gemm.py (1)

183-197: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Sort __all__ per Ruff RUF022.

🧹 Proposed fix
 __all__ = [
     "GEMM_A_LAYOUTS",
     "GEMM_B_LAYOUTS",
     "GEMM_C_LAYOUTS",
     "as_gemm_tensor_desc",
     "block_scale_tensor_spec",
     "gemm_a_tensor_spec",
     "gemm_b_tensor_spec",
     "gemm_c_tensor_spec",
     "probability_tensor_spec",
-    "require_fp8_block_scales",
-    "require_gemm_inputs",
     "require_16_byte_extent",
+    "require_fp8_block_scales",
+    "require_gemm_inputs",
     "require_layout",
 ]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/_jax/gemm.py` around lines 183 - 197, The __all__ list in
gemm.py is unsorted and should be ordered to satisfy Ruff RUF022. Update the
exported names in the __all__ definition near as_gemm_tensor_desc and
require_layout so they are in sorted order, keeping all current symbols but
rearranging them alphabetically.

Source: Linters/SAST tools

python/cudnn/gemm_dsrelu/jax.py (2)

96-349: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add unit tests and expand docstrings for the new JAX API.

Per path instructions for python/cudnn/**, please confirm test coverage exists (or add it) under test/python/fe_api for this new GemmDsreluSm100/gemm_dsrelu_wrapper_sm100 JAX API, and consider expanding the class/__init__ docstrings with parameter documentation (currently a one-line class docstring only), consistent with the emphasis on documentation for this path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/gemm_dsrelu/jax.py` around lines 96 - 349, Add test coverage for
the new JAX squared-ReLU API by creating or updating tests under
test/python/fe_api to exercise both GemmDsreluSm100 and
gemm_dsrelu_wrapper_sm100 with valid inputs and basic signature/dispatch
behavior. Also expand the class and __init__ docstrings in GemmDsreluSm100 to
document the parameters and usage details, since the current one-line class
docstring is too sparse for this new API.

Source: Path instructions


142-214: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing explicit sf_vec_size/sf_dtype combination validation (parity gap vs Torch API).

_check_support validates block-scale shapes via require_fp8_block_scales but doesn't replicate the Torch API's explicit sf_vec_size in SF_VEC_SIZES check or the kernel's is_valid_dtypes_and_scale_factor_vec_size combo rules (e.g. Float8E4M3FN+sf_vec_size=32 or Float8E8M0FNU+sf_vec_size=16 are invalid). An invalid sf_vec_size/sf_dtype combo that happens to produce matching tensor shapes would surface as an opaque CUTLASS-level compile error rather than a clear ValueError.

Consider calling kernel.is_valid_dtypes_and_scale_factor_vec_size(...) (or an equivalent explicit check) in _check_support for parity with the Torch API's check_support.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/gemm_dsrelu/jax.py` around lines 142 - 214, The _check_support
method in the JAX squared-ReLU backward path only validates block-scale shapes,
but it still misses the explicit sf_vec_size/sf_dtype compatibility rules that
the Torch API enforces. Add an early validation in _check_support using
Sm100BlockScaledPersistentDenseGemmKernel (for example, its
is_valid_dtypes_and_scale_factor_vec_size check or equivalent) so invalid
sf_vec_size and scale-factor dtype combinations fail with a clear ValueError
before reaching CUTLASS compilation. Keep the new check alongside the existing
require_fp8_block_scales and dtype validation logic to preserve parity with the
Torch check_support behavior.
python/cudnn/gemm_srelu/jax.py (2)

137-217: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Same sf_vec_size/sf_dtype combination validation gap as gemm_dsrelu/jax.py.

_check_support doesn't replicate the kernel's is_valid_dtypes_and_scale_factor_vec_size combo validation (sf_vec_size membership + sf_dtype/sf_vec_size compatibility) that the Torch API enforces explicitly. Same recommendation applies here for parity and clearer error messages.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/gemm_srelu/jax.py` around lines 137 - 217, The _check_support
method in the JAX GEMM + squared-ReLU path is missing the same
sf_vec_size/sf_dtype combination validation that the Torch API enforces. Update
_check_support to mirror the kernel’s is_valid_dtypes_and_scale_factor_vec_size
checks so it rejects unsupported sf_vec_size values and incompatible
sf_dtype/sf_vec_size pairings before continuing. Use the existing JaxTensorDesc,
require_fp8_block_scales, and Sm100BlockScaledPersistentDenseGemmKernel flow as
the place to add this parity validation and keep the error messages explicit.

94-331: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Confirm test coverage under test/python/fe_api for this new JAX API.

As per path instructions, please confirm dedicated tests exist (or add them) for GemmSreluSm100/gemm_srelu_wrapper_sm100, and consider adding parameter-level docstrings similar to the underlying kernel class.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/gemm_srelu/jax.py` around lines 94 - 331, Confirm that dedicated
coverage exists under test/python/fe_api for the new JAX entrypoints, and add or
update tests for GemmSreluSm100 and gemm_srelu_wrapper_sm100 if they are
missing. Make sure the tests exercise the wrapper and class behavior through
their public APIs, including basic construction and call-path validation, so the
new symbols are covered by the FE API test suite. While touching
GemmSreluSm100/__init__ and gemm_srelu_wrapper_sm100, add brief parameter-level
docstrings or inline doc comments consistent with the underlying kernel class to
document the constructor and wrapper arguments.

Source: Path instructions

python/cudnn/gemm_validation.py (1)

12-181: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Solid, well-factored shared validation module; consider fuller docstrings and confirm test coverage.

Logic is sound (short-circuit or in require_mma_tiler correctly avoids IndexError on malformed inputs; length/positivity checks are ordered safely throughout). Given this module is now the single source of truth for GEMM shape/tiler/cluster validation shared across amax/dsrelu/srelu/swiglu, two suggestions:

  1. Docstrings here are one-liners; the downstream kernel classes use full Sphinx-style :param:/:return: docs. Aligning this shared module with that convention would help since it's now imported broadly.
  2. No test file is included for this new module. Given it centralizes correctness-critical validation for multiple GEMM fusion ops, dedicated unit tests (e.g., under test/python/fe_api) covering boundary cases (non-power-of-two clusters, sf_vec_size<=0, mismatched K/L, etc.) would materially reduce regression risk across all its consumers.

As per path instructions, this file should "Focus on documentation" and "Focus on whether there are test cases in test/python/fe_api."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/gemm_validation.py` around lines 12 - 181, The shared validation
helpers in the GEMM validation module are correct, but the review asks for
clearer documentation and dedicated coverage. Update the one-line docstrings in
the validation functions such as require_gemm_shapes,
require_block_scale_shapes, require_mma_tiler, require_cluster_shape, and
resolve_max_active_clusters to use fuller Sphinx-style parameter/return docs
consistent with the downstream kernel classes. Also add unit tests for this
module under test/python/fe_api covering key boundary cases like mismatched
shapes, non-power-of-two cluster dimensions, invalid sf_vec_size, and other
validation failures to protect all consumers.

Source: Path instructions

python/cudnn/gemm_amax/dense_blockscaled_gemm_persistent_amax.py (1)

163-201: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clarify the interaction between MMA_TILER_M and KNOWN_HANG_MMA_TILER_M.

MMA_TILER_M = (128, 256) advertises 256 as an allowed tile, but KNOWN_HANG_MMA_TILER_M = (256,) makes require_mma_tiler unconditionally reject any mma_tiler_mn[0] == 256 with NotImplementedError. Since this is the only public path (Torch/JAX APIs both call require_mma_tiler before instantiating the kernel), TWO_CTA_MMA_TILER_M and the entire 2-CTA code path in this kernel are currently unreachable. If this is an intentional temporary block on a known hang, consider a short comment on MMA_TILER_M itself cross-referencing KNOWN_HANG_MMA_TILER_M so future maintainers don't misread 256 as usable. As per path instructions, focus on documentation for this path.

def check_mma_tiler_reachability():
    from cudnn.gemm_amax.dense_blockscaled_gemm_persistent_amax import (
        Sm100BlockScaledPersistentDenseGemmKernel as K,
    )
    try:
        K.require_mma_tiler((256, 128))
        print("256 M tile is reachable")
    except NotImplementedError as e:
        print("256 M tile blocked:", e)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/gemm_amax/dense_blockscaled_gemm_persistent_amax.py` around
lines 163 - 201, The
`Sm100BlockScaledPersistentDenseGemmKernel.require_mma_tiler` path currently
blocks any M tile of 256 via `KNOWN_HANG_MMA_TILER_M`, while `MMA_TILER_M` still
advertises 256 as supported, which makes the 2-CTA path misleading. Update the
documentation/comments around `MMA_TILER_M`, `KNOWN_HANG_MMA_TILER_M`, and
`require_mma_tiler` to clearly state that 256 is intentionally disabled as a
known hang, and cross-reference the restriction so `require_cluster_shape` and
the `TWO_CTA_MMA_TILER_M` path are not misread as reachable.

Source: Path instructions

python/cudnn/grouped_gemm/grouped_gemm_dglu/jax.py (1)

420-544: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider documenting the public constructor parameters.

GroupedGemmDgluSm100 exposes ~25 constructor parameters (dtypes, layouts, tiler/cluster shapes, dGLU clamp/offset knobs) with only a one-line class docstring. A parameter-level docstring (mirroring the Torch GroupedGemmDgluSm100.__init__ doc) would make the JAX entry point self-documenting.

As per path instructions: "python/cudnn/**: Focus on documentation."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/grouped_gemm/grouped_gemm_dglu/jax.py` around lines 420 - 544,
The public constructor of GroupedGemmDgluSm100 is under-documented, making its
many configuration knobs hard to understand. Add a parameter-level docstring to
the __init__ method for this JAX wrapper, mirroring the Torch
GroupedGemmDgluSm100.__init__ documentation and covering the layout, dtype,
tiling, cluster, clamp, offset, and scheduling arguments so the API is
self-describing.

Source: Path instructions

python/cudnn/grouped_gemm/grouped_gemm_glu/moe_blockscaled_grouped_gemm_glu_bias.py (1)

289-294: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

get_workspace_bytes not updated to use the new get_dense_workspace_bytes classmethod.

The new get_dense_workspace_bytes classmethod (lines 289-294) centralizes the dynamic-scheduler workspace size, but the existing get_workspace_bytes instance method (lines 674-679) still hardcodes 4 if self.use_dynamic_sched else 0 instead of calling it. The sibling BlockScaledMoEGroupedGemmGluHadamardKernel in moe_blockscaled_grouped_gemm_glu_hadamard.py was updated to call self.get_dense_workspace_bytes(self.use_dynamic_sched) at the same location — this file appears to have been missed, leaving the two constants (DYNAMIC_SCHED_WORKSPACE_BYTES and the inline 4) to potentially drift apart.

♻️ Proposed fix
     def get_workspace_bytes(self) -> int:
         """Return descriptor workspace plus optional dynamic scheduler state."""
         desc_workspace_bytes = self.get_desc_workspace_bytes()
-        dynamic_sched_bytes = 4 if self.use_dynamic_sched else 0
+        dynamic_sched_bytes = self.get_dense_workspace_bytes(self.use_dynamic_sched)
         return desc_workspace_bytes + dynamic_sched_bytes

Also applies to: 674-679

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/grouped_gemm/grouped_gemm_glu/moe_blockscaled_grouped_gemm_glu_bias.py`
around lines 289 - 294, The workspace calculation in
`BlockScaledMoEGroupedGemmGluBiasKernel.get_workspace_bytes` still hardcodes the
dense-mode size instead of using the new shared helper. Update that method to
call `self.get_dense_workspace_bytes(self.use_dynamic_sched)` so it matches
`BlockScaledMoEGroupedGemmGluHadamardKernel` and keeps
`DYNAMIC_SCHED_WORKSPACE_BYTES` as the single source of truth.
python/cudnn/grouped_gemm/grouped_gemm_quant/grouped_gemm_quant.py (1)

97-136: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate MMA/cluster validation logic across kernel classes.

require_mma_tiler, require_cluster_shape, and get_dense_workspace_bytes here are byte-for-byte identical to the same three classmethods added to moe_blockscaled_grouped_gemm_srelu_quant.py (lines 109-148), and the same kernel.require_mma_tiler(...)/kernel.require_cluster_shape(...) pattern is repeated across gemm_amax, gemm_dsrelu, and presumably the other grouped-GEMM ops in this cohort. Since each kernel class re-declares its own MMA_TILER_M/MMA_TILER_N/CLUSTER_TILER_M/etc. constants and wrapper methods, any future change to the shared validation contract (e.g. a new allowed tile shape) requires updating every kernel class individually and risks silent drift.

Consider extracting this into a small mixin/base class (e.g. in gemm_validation.py) that kernel classes compose or inherit from, parameterized by the tiler/cluster constants.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/grouped_gemm/grouped_gemm_quant/grouped_gemm_quant.py` around
lines 97 - 136, The grouped-GEMM validation methods are duplicated across kernel
classes, so extract the shared logic from require_mma_tiler,
require_cluster_shape, and get_dense_workspace_bytes into a common mixin/base
helper (for example a gemm_validation module). Make the helper parameterized by
each kernel’s constants such as MMA_TILER_M, MMA_TILER_N, CLUSTER_TILER_M,
TWO_CTA_MMA_TILER_M, MAX_CLUSTER_CTAS, MAX_CLUSTER_DIMENSION, and
DYNAMIC_SCHED_WORKSPACE_BYTES, then update grouped_gemm_quant.py and the other
ops that currently call kernel.require_mma_tiler(...) and
kernel.require_cluster_shape(...) to inherit from or delegate to the shared
implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/fe-oss-apis/attention/sdpa_bwd_d256.md`:
- Around line 108-110: The JAX example in sdpa_bwd_d256 has an unpacking typo in
the result handling: result["dq"]_tensor is invalid Python and should be
corrected to the proper documented key access used by sdpa_bwd. Update the
example to reference result["dq_tensor"] alongside result["dk_tensor"] and
result["dv_tensor"] so the snippet is copy-paste runnable and consistent with
the return structure shown in the JAX API docs.

In `@docs/fe-oss-apis/cutedsl-jax-design.md`:
- Around line 45-52: The module tree under the operation bindings is currently
presented as a concrete layout, but it should be marked as illustrative or
updated to match the actual operation-specific module names used in the PR. In
the section describing each operation’s bindings, revise the tree near the
api.py/jax.py/kernel.py example so readers understand it is an example, or
replace the placeholder kernel.py entry with the real module names referenced by
the implementation.
- Around line 177-199: The gap list in the design doc is too mixed together and
does not clearly separate the two release blockers from the remaining scope
notes. Update the section around the grouped-GEMM, sliding-window, and DSA
paragraphs to use explicit bullets or a clearly numbered list, with each gap
described separately and the NSA/DSA follow-up items kept as distinct scope
notes. Keep the ownership and count unambiguous by ensuring each blocker maps to
one bullet and the remaining coverage notes are not folded into the blocker
text.

In `@docs/fe-oss-apis/dsa.md`:
- Around line 507-524: The example currently computes softmax_scale from an
undefined D, so update the recompute_target JIT wrapper to derive the scale from
the input tensor shape instead of relying on a free variable. Use the existing
symbols recompute_target and DSA.sparse_attn_score_recompute_wrapper to locate
the snippet, and make the scale expression self-contained by calculating it from
q_attn (or the relevant attention dimension) inside the function before calling
the wrapper.

In `@docs/fe-oss-apis/nsa.md`:
- Around line 771-774: The JAX availability note conflicts with the existing
Sliding Window section, so update the summary in the table description to match
the documented JAX support. In the nsa.md text that mentions Compression
Attention, Top-K Reduction, Selection Attention, and Sliding Window Attention,
remove the blanket “unavailable” claim for Sliding Window or narrow it to only
the unsupported subset, keeping the wording consistent with the JAX Sliding
Window section.

In `@python/cudnn/deepseek_sparse_attention/score_recompute/jax.py`:
- Around line 233-321: Dense score-recompute currently bypasses kernel-config
validation during the `_validate_only` path, so `_check_support()` can succeed
for unsupported configs. Update `_dense_score_recompute` to eagerly call
`resolve_dense_score_kernel_config` before the `_validate_only` early return,
using the same inputs passed later by `_launch_dense_with_q_causal_offsets` /
`_launch_dense_without_q_causal_offsets`. Ensure
`DenseIndexerScoreRecompute._check_support()` and
`DenseAttnScoreRecompute._check_support()` now reject invalid `head_dim` and
`qhead_per_kv_head` combinations up front, matching `_sparse_score_recompute`
behavior.

In `@python/cudnn/deepseek_sparse_attention/sparse_attention_backward/jax.py`:
- Around line 23-136: `max_topk` is not being forwarded into the JAX sparse
backward launch path, so `FlashAttentionDSABackwardSm100` is built with its
default value and KV loading can be wrong. Update `_launch_with_topk_length` and
`_launch_without_topk_length` to accept and pass `max_topk` when constructing
`FlashAttentionDSABackwardSm100`, and thread the computed `topk_shape[1]`
through `_sparse_attention_backward_impl` via `static_args`/`call_cutedsl` so
the kernel cache key includes it. Use the existing `topk_shape` and the
`_launch_*` helpers as the entry points for the fix.

In `@python/cudnn/gemm_amax/api.py`:
- Around line 228-234: The batch dimension from b_tensor is currently unpacked
as _b_batch in api.py’s c_tensor allocation path but never checked against
a_tensor’s batch before using batch to size c_tensor. In the gemm_amax
allocation logic, validate that both tensors share the same batch dimension
before calling torch.empty_strided, or discard the unused value with _ if the
batch is guaranteed to be checked elsewhere; keep the fix near the m, n, batch
unpacking and c_tensor setup so the mismatch is caught before allocation.

In `@python/cudnn/gemm_srelu/api.py`:
- Line 91: Rename the ambiguous loop/dimension variable `l` in
`require_gemm_shapes` usage within `api.py` to `batch` (or another clearer name)
to satisfy Ruff E741 and match the existing shape naming. Update all related
references in the same flow, including the later uses in the GEMM/SReLU API
logic at the spots where `l` is read again, so the renamed variable stays
consistent across the function.

In `@python/cudnn/gemm_swiglu/api.py`:
- Line 123: Rename the ambiguous loop variable in require_gemm_shapes assignment
within gemm_swiglu/api.py from l to batch to satisfy Ruff E741 and keep the
naming consistent with require_gemm_shapes; this change is localized because the
variable is not used afterward, so only update the unpacking in the relevant API
function.

---

Outside diff comments:
In `@docs/fe-oss-apis/dsa.md`:
- Around line 179-184: The backward example uses an undefined head-dim symbol in
DSA.sparse_attention_backward_wrapper by passing math.sqrt(D), so update this
snippet to use the already established 512 head dimension or derive it from
q/k/v tensor shape instead. Keep the fix localized to the
sparse_attention_backward_wrapper call and the softmax_scale argument so the
example remains runnable and consistent with the rest of the section.

---

Nitpick comments:
In `@python/cudnn/_jax/gemm.py`:
- Around line 183-197: The __all__ list in gemm.py is unsorted and should be
ordered to satisfy Ruff RUF022. Update the exported names in the __all__
definition near as_gemm_tensor_desc and require_layout so they are in sorted
order, keeping all current symbols but rearranging them alphabetically.

In `@python/cudnn/gemm_amax/dense_blockscaled_gemm_persistent_amax.py`:
- Around line 163-201: The
`Sm100BlockScaledPersistentDenseGemmKernel.require_mma_tiler` path currently
blocks any M tile of 256 via `KNOWN_HANG_MMA_TILER_M`, while `MMA_TILER_M` still
advertises 256 as supported, which makes the 2-CTA path misleading. Update the
documentation/comments around `MMA_TILER_M`, `KNOWN_HANG_MMA_TILER_M`, and
`require_mma_tiler` to clearly state that 256 is intentionally disabled as a
known hang, and cross-reference the restriction so `require_cluster_shape` and
the `TWO_CTA_MMA_TILER_M` path are not misread as reachable.

In `@python/cudnn/gemm_dsrelu/jax.py`:
- Around line 96-349: Add test coverage for the new JAX squared-ReLU API by
creating or updating tests under test/python/fe_api to exercise both
GemmDsreluSm100 and gemm_dsrelu_wrapper_sm100 with valid inputs and basic
signature/dispatch behavior. Also expand the class and __init__ docstrings in
GemmDsreluSm100 to document the parameters and usage details, since the current
one-line class docstring is too sparse for this new API.
- Around line 142-214: The _check_support method in the JAX squared-ReLU
backward path only validates block-scale shapes, but it still misses the
explicit sf_vec_size/sf_dtype compatibility rules that the Torch API enforces.
Add an early validation in _check_support using
Sm100BlockScaledPersistentDenseGemmKernel (for example, its
is_valid_dtypes_and_scale_factor_vec_size check or equivalent) so invalid
sf_vec_size and scale-factor dtype combinations fail with a clear ValueError
before reaching CUTLASS compilation. Keep the new check alongside the existing
require_fp8_block_scales and dtype validation logic to preserve parity with the
Torch check_support behavior.

In `@python/cudnn/gemm_srelu/jax.py`:
- Around line 137-217: The _check_support method in the JAX GEMM + squared-ReLU
path is missing the same sf_vec_size/sf_dtype combination validation that the
Torch API enforces. Update _check_support to mirror the kernel’s
is_valid_dtypes_and_scale_factor_vec_size checks so it rejects unsupported
sf_vec_size values and incompatible sf_dtype/sf_vec_size pairings before
continuing. Use the existing JaxTensorDesc, require_fp8_block_scales, and
Sm100BlockScaledPersistentDenseGemmKernel flow as the place to add this parity
validation and keep the error messages explicit.
- Around line 94-331: Confirm that dedicated coverage exists under
test/python/fe_api for the new JAX entrypoints, and add or update tests for
GemmSreluSm100 and gemm_srelu_wrapper_sm100 if they are missing. Make sure the
tests exercise the wrapper and class behavior through their public APIs,
including basic construction and call-path validation, so the new symbols are
covered by the FE API test suite. While touching GemmSreluSm100/__init__ and
gemm_srelu_wrapper_sm100, add brief parameter-level docstrings or inline doc
comments consistent with the underlying kernel class to document the constructor
and wrapper arguments.

In `@python/cudnn/gemm_validation.py`:
- Around line 12-181: The shared validation helpers in the GEMM validation
module are correct, but the review asks for clearer documentation and dedicated
coverage. Update the one-line docstrings in the validation functions such as
require_gemm_shapes, require_block_scale_shapes, require_mma_tiler,
require_cluster_shape, and resolve_max_active_clusters to use fuller
Sphinx-style parameter/return docs consistent with the downstream kernel
classes. Also add unit tests for this module under test/python/fe_api covering
key boundary cases like mismatched shapes, non-power-of-two cluster dimensions,
invalid sf_vec_size, and other validation failures to protect all consumers.

In `@python/cudnn/grouped_gemm/grouped_gemm_dglu/jax.py`:
- Around line 420-544: The public constructor of GroupedGemmDgluSm100 is
under-documented, making its many configuration knobs hard to understand. Add a
parameter-level docstring to the __init__ method for this JAX wrapper, mirroring
the Torch GroupedGemmDgluSm100.__init__ documentation and covering the layout,
dtype, tiling, cluster, clamp, offset, and scheduling arguments so the API is
self-describing.

In
`@python/cudnn/grouped_gemm/grouped_gemm_glu/moe_blockscaled_grouped_gemm_glu_bias.py`:
- Around line 289-294: The workspace calculation in
`BlockScaledMoEGroupedGemmGluBiasKernel.get_workspace_bytes` still hardcodes the
dense-mode size instead of using the new shared helper. Update that method to
call `self.get_dense_workspace_bytes(self.use_dynamic_sched)` so it matches
`BlockScaledMoEGroupedGemmGluHadamardKernel` and keeps
`DYNAMIC_SCHED_WORKSPACE_BYTES` as the single source of truth.

In `@python/cudnn/grouped_gemm/grouped_gemm_quant/grouped_gemm_quant.py`:
- Around line 97-136: The grouped-GEMM validation methods are duplicated across
kernel classes, so extract the shared logic from require_mma_tiler,
require_cluster_shape, and get_dense_workspace_bytes into a common mixin/base
helper (for example a gemm_validation module). Make the helper parameterized by
each kernel’s constants such as MMA_TILER_M, MMA_TILER_N, CLUSTER_TILER_M,
TWO_CTA_MMA_TILER_M, MAX_CLUSTER_CTAS, MAX_CLUSTER_DIMENSION, and
DYNAMIC_SCHED_WORKSPACE_BYTES, then update grouped_gemm_quant.py and the other
ops that currently call kernel.require_mma_tiler(...) and
kernel.require_cluster_shape(...) to inherit from or delegate to the shared
implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 442bcbd5-5425-4631-8205-449a46e81e78

📥 Commits

Reviewing files that changed from the base of the PR and between d380fab and ee72523.

📒 Files selected for processing (173)
  • docs/fe-oss-apis/attention/sdpa_bwd_d256.md
  • docs/fe-oss-apis/attention/sdpa_fwd_d256.md
  • docs/fe-oss-apis/cutedsl-jax-design.md
  • docs/fe-oss-apis/dsa.md
  • docs/fe-oss-apis/gemm_fusions/gemm_amax.md
  • docs/fe-oss-apis/gemm_fusions/gemm_dsrelu.md
  • docs/fe-oss-apis/gemm_fusions/gemm_srelu.md
  • docs/fe-oss-apis/gemm_fusions/gemm_swiglu.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_dglu.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_dsrelu.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_dswiglu.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_glu.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_glu_hadamard.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_quant.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_quant_unified.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_srelu.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_swiglu.md
  • docs/fe-oss-apis/gemm_fusions/grouped_gemm_wgrad.md
  • docs/fe-oss-apis/nsa.md
  • docs/fe-oss-apis/overview.md
  • docs/fe-oss-apis/rmsnorm_rht_amax.md
  • pyproject.toml
  • python/cudnn/README.md
  • python/cudnn/__init__.py
  • python/cudnn/_experimental_warnings.py
  • python/cudnn/_jax/__init__.py
  • python/cudnn/_jax/api_base.py
  • python/cudnn/_jax/gemm.py
  • python/cudnn/_jax/grouped_gemm.py
  • python/cudnn/_operation_api.py
  • python/cudnn/api_base.py
  • python/cudnn/api_base_torch.py
  • python/cudnn/deepseek_sparse_attention/indexer_backward/__init__.py
  • python/cudnn/deepseek_sparse_attention/indexer_backward/api.py
  • python/cudnn/deepseek_sparse_attention/indexer_backward/dense_indexer_backward_sm100.py
  • python/cudnn/deepseek_sparse_attention/indexer_backward/indexer_backward_sm100.py
  • python/cudnn/deepseek_sparse_attention/indexer_backward/jax.py
  • python/cudnn/deepseek_sparse_attention/indexer_forward/__init__.py
  • python/cudnn/deepseek_sparse_attention/indexer_forward/api.py
  • python/cudnn/deepseek_sparse_attention/indexer_forward/jax.py
  • python/cudnn/deepseek_sparse_attention/indexer_top_k/__init__.py
  • python/cudnn/deepseek_sparse_attention/indexer_top_k/api.py
  • python/cudnn/deepseek_sparse_attention/indexer_top_k/compactify.py
  • python/cudnn/deepseek_sparse_attention/indexer_top_k/indexer_top_k_decode_varlen.py
  • python/cudnn/deepseek_sparse_attention/indexer_top_k/indexer_top_k_varlen_util.py
  • python/cudnn/deepseek_sparse_attention/indexer_top_k/jax.py
  • python/cudnn/deepseek_sparse_attention/indexer_top_k/local_to_global_dsl.py
  • python/cudnn/deepseek_sparse_attention/score_recompute/__init__.py
  • python/cudnn/deepseek_sparse_attention/score_recompute/_interface_sm100.py
  • python/cudnn/deepseek_sparse_attention/score_recompute/api.py
  • python/cudnn/deepseek_sparse_attention/score_recompute/config.py
  • python/cudnn/deepseek_sparse_attention/score_recompute/jax.py
  • python/cudnn/deepseek_sparse_attention/sparse_attention_backward/__init__.py
  • python/cudnn/deepseek_sparse_attention/sparse_attention_backward/_interface_sm100.py
  • python/cudnn/deepseek_sparse_attention/sparse_attention_backward/api.py
  • python/cudnn/deepseek_sparse_attention/sparse_attention_backward/dsa_bwd_sm100.py
  • python/cudnn/deepseek_sparse_attention/sparse_attention_backward/jax.py
  • python/cudnn/deepseek_sparse_attention/utils/compiler.py
  • python/cudnn/discrete_grouped_gemm/discrete_grouped_gemm_dswiglu/api.py
  • python/cudnn/discrete_grouped_gemm/discrete_grouped_gemm_swiglu/api.py
  • python/cudnn/gemm_amax/__init__.py
  • python/cudnn/gemm_amax/api.py
  • python/cudnn/gemm_amax/dense_blockscaled_gemm_persistent_amax.py
  • python/cudnn/gemm_amax/jax.py
  • python/cudnn/gemm_amax/validation.py
  • python/cudnn/gemm_dsrelu/__init__.py
  • python/cudnn/gemm_dsrelu/api.py
  • python/cudnn/gemm_dsrelu/dense_blockscaled_gemm_persistent_dsrelu_quant.py
  • python/cudnn/gemm_dsrelu/jax.py
  • python/cudnn/gemm_srelu/__init__.py
  • python/cudnn/gemm_srelu/api.py
  • python/cudnn/gemm_srelu/dense_blockscaled_gemm_persistent_srelu_quant.py
  • python/cudnn/gemm_srelu/jax.py
  • python/cudnn/gemm_swiglu/__init__.py
  • python/cudnn/gemm_swiglu/api.py
  • python/cudnn/gemm_swiglu/dense_blockscaled_gemm_persistent_swiglu_interleaved_quant.py
  • python/cudnn/gemm_swiglu/dense_gemm_persistent_swiglu.py
  • python/cudnn/gemm_swiglu/jax.py
  • python/cudnn/gemm_swiglu/validation.py
  • python/cudnn/gemm_validation.py
  • python/cudnn/grouped_gemm/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_dglu/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_dglu/api.py
  • python/cudnn/grouped_gemm/grouped_gemm_dglu/jax.py
  • python/cudnn/grouped_gemm/grouped_gemm_dglu/moe_blockscaled_grouped_gemm_dglu_dbias.py
  • python/cudnn/grouped_gemm/grouped_gemm_dsrelu/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_dsrelu/api.py
  • python/cudnn/grouped_gemm/grouped_gemm_dsrelu/jax.py
  • python/cudnn/grouped_gemm/grouped_gemm_dsrelu/moe_blockscaled_grouped_gemm_dsrelu_quant.py
  • python/cudnn/grouped_gemm/grouped_gemm_dswiglu/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_dswiglu/api.py
  • python/cudnn/grouped_gemm/grouped_gemm_dswiglu/grouped_gemm_dswiglu_quant.py
  • python/cudnn/grouped_gemm/grouped_gemm_dswiglu/jax.py
  • python/cudnn/grouped_gemm/grouped_gemm_glu/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_glu/api.py
  • python/cudnn/grouped_gemm/grouped_gemm_glu/jax.py
  • python/cudnn/grouped_gemm/grouped_gemm_glu/moe_blockscaled_grouped_gemm_glu_bias.py
  • python/cudnn/grouped_gemm/grouped_gemm_glu_hadamard/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_glu_hadamard/api.py
  • python/cudnn/grouped_gemm/grouped_gemm_glu_hadamard/hadamard_utils.py
  • python/cudnn/grouped_gemm/grouped_gemm_glu_hadamard/jax.py
  • python/cudnn/grouped_gemm/grouped_gemm_glu_hadamard/moe_blockscaled_grouped_gemm_glu_hadamard.py
  • python/cudnn/grouped_gemm/grouped_gemm_quant/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_quant/api.py
  • python/cudnn/grouped_gemm/grouped_gemm_quant/grouped_gemm_quant.py
  • python/cudnn/grouped_gemm/grouped_gemm_quant/jax.py
  • python/cudnn/grouped_gemm/grouped_gemm_srelu/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_srelu/api.py
  • python/cudnn/grouped_gemm/grouped_gemm_srelu/jax.py
  • python/cudnn/grouped_gemm/grouped_gemm_srelu/moe_blockscaled_grouped_gemm_srelu_quant.py
  • python/cudnn/grouped_gemm/grouped_gemm_swiglu/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_swiglu/api.py
  • python/cudnn/grouped_gemm/grouped_gemm_swiglu/grouped_gemm_swiglu_quant.py
  • python/cudnn/grouped_gemm/grouped_gemm_swiglu/jax.py
  • python/cudnn/grouped_gemm/grouped_gemm_wgrad/__init__.py
  • python/cudnn/grouped_gemm/grouped_gemm_wgrad/api.py
  • python/cudnn/grouped_gemm/grouped_gemm_wgrad/jax.py
  • python/cudnn/grouped_gemm/grouped_gemm_wgrad/moe_blockscaled_grouped_gemm_wgrad.py
  • python/cudnn/grouped_gemm/moe_kernel_helpers.py
  • python/cudnn/grouped_gemm/utils.py
  • python/cudnn/jax/__init__.py
  • python/cudnn/native_sparse_attention/__init__.py
  • python/cudnn/native_sparse_attention/compression/__init__.py
  • python/cudnn/native_sparse_attention/compression/api.py
  • python/cudnn/native_sparse_attention/compression/jax.py
  • python/cudnn/native_sparse_attention/jax_utils.py
  • python/cudnn/native_sparse_attention/selection/NSA_select_attn_fwd_hmma.py
  • python/cudnn/native_sparse_attention/selection/__init__.py
  • python/cudnn/native_sparse_attention/selection/api.py
  • python/cudnn/native_sparse_attention/selection/jax.py
  • python/cudnn/native_sparse_attention/sliding_window_attention/__init__.py
  • python/cudnn/native_sparse_attention/sliding_window_attention/api.py
  • python/cudnn/native_sparse_attention/sliding_window_attention/jax.py
  • python/cudnn/native_sparse_attention/top_k/__init__.py
  • python/cudnn/native_sparse_attention/top_k/api.py
  • python/cudnn/native_sparse_attention/top_k/jax.py
  • python/cudnn/rmsnorm_rht_amax/__init__.py
  • python/cudnn/rmsnorm_rht_amax/api.py
  • python/cudnn/rmsnorm_rht_amax/config.py
  • python/cudnn/rmsnorm_rht_amax/jax.py
  • python/cudnn/sdpa/__init__.py
  • python/cudnn/sdpa/bwd/__init__.py
  • python/cudnn/sdpa/bwd/api.py
  • python/cudnn/sdpa/bwd/jax.py
  • python/cudnn/sdpa/fwd/__init__.py
  • python/cudnn/sdpa/fwd/api.py
  • python/cudnn/sdpa/fwd/jax.py
  • python/cudnn/sdpa/jax_utils.py
  • test/python/fe_api/conftest.py
  • test/python/fe_api/dsa/test_DSA_dense_indexer_backward.py
  • test/python/fe_api/test_api_base_framework_split.py
  • test/python/fe_api/test_gemm_amax_validation.py
  • test/python/fe_api/test_gemm_swiglu_validation.py
  • test/python/fe_api/test_gemm_validation.py
  • test/python/fe_api/test_jax_api_surface.py
  • test/python/fe_api/test_jax_cutedsl_adapter.py
  • test/python/fe_api/test_jax_dsa.py
  • test/python/fe_api/test_jax_dsa_contract.py
  • test/python/fe_api/test_jax_dsa_indexer_backward_contract.py
  • test/python/fe_api/test_jax_dsa_sparse_attention_backward_contract.py
  • test/python/fe_api/test_jax_gemm.py
  • test/python/fe_api/test_jax_gemm_contract.py
  • test/python/fe_api/test_jax_grouped_gemm_contract.py
  • test/python/fe_api/test_jax_grouped_gemm_wgrad_contract.py
  • test/python/fe_api/test_jax_import_contract.py
  • test/python/fe_api/test_jax_nsa.py
  • test/python/fe_api/test_jax_nsa_contract.py
  • test/python/fe_api/test_jax_rmsnorm_rht_amax.py
  • test/python/fe_api/test_jax_sdpa_contract.py
  • test/python/fe_api/test_jax_validation.py
  • test/python/fe_api/test_optional_jax_test_support.py
  • test/python/fe_api/test_rmsnorm_rht_amax.py
  • test/python/fe_api/test_rmsnorm_rht_amax_config.py

Comment on lines +108 to +110
result = sdpa_bwd(q, k, v, o, do, lse)
dq, dk, dv = result["dq"]_tensor, result["dk_tensor"], result["dv_tensor"]
```

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the unpacking typo in the JAX example.

result["dq"]_tensor is invalid Python, so the snippet won't run if copied. It should be result["dq_tensor"] to match the documented JAX return keys.

🛠️ Proposed fix
-dq, dk, dv = result["dq"]_tensor, result["dk_tensor"], result["dv_tensor"]
+dq, dk, dv = result["dq_tensor"], result["dk_tensor"], result["dv_tensor"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
result = sdpa_bwd(q, k, v, o, do, lse)
dq, dk, dv = result["dq"]_tensor, result["dk_tensor"], result["dv_tensor"]
```
result = sdpa_bwd(q, k, v, o, do, lse)
dq, dk, dv = result["dq_tensor"], result["dk_tensor"], result["dv_tensor"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/fe-oss-apis/attention/sdpa_bwd_d256.md` around lines 108 - 110, The JAX
example in sdpa_bwd_d256 has an unpacking typo in the result handling:
result["dq"]_tensor is invalid Python and should be corrected to the proper
documented key access used by sdpa_bwd. Update the example to reference
result["dq_tensor"] alongside result["dk_tensor"] and result["dv_tensor"] so the
snippet is copy-paste runnable and consistent with the return structure shown in
the JAX API docs.

Comment on lines +45 to +52
Each operation co-locates its bindings:

```text
cudnn/<operation>/
api.py # PyTorch API
jax.py # JAX API
kernel.py # shared CuTe DSL implementation
```

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Mark this tree as illustrative.

This reads like a concrete filesystem contract, but the PR summary shows operation-specific kernel modules rather than a shared kernel.py. Please either label the tree as an example or replace the placeholder with real module names so readers don't chase a path that doesn't exist.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/fe-oss-apis/cutedsl-jax-design.md` around lines 45 - 52, The module tree
under the operation bindings is currently presented as a concrete layout, but it
should be marked as illustrative or updated to match the actual
operation-specific module names used in the PR. In the section describing each
operation’s bindings, revise the tree near the api.py/jax.py/kernel.py example
so readers understand it is an example, or replace the placeholder kernel.py
entry with the real module names referenced by the implementation.

Comment on lines +177 to +199
The two current gaps need separate ABI work, not another generic adapter:

1. Two discrete grouped-GEMM wrappers consume device-resident tables of raw
addresses. JAX must receive the expert buffers as real operands and build
pointer tables in XLA-owned device workspace.

The sliding-window binding deliberately covers fixed-shape BHSD inference with
`right_bound=0`. Training statistics and packed THD/ragged inputs still need a
native frontend XLA FFI adapter; depending on JAX's private cuDNN APIs would be
an unstable OSS contract.

Dense DSA indexer backward now uses a runtime `grad_loss` operand, an
XLA-owned score-gradient workspace, and a kernel-cleared FP32 accumulation
buffer. Its first JAX binding deliberately covers fixed-shape SM100 BSHD
inputs; the broader PyTorch SM90 and packed-THD paths remain unchanged.

Only RMSNorm, the four dense GEMMs, five DSA entry points, and NSA
sliding-window inference currently have real GPU execution tests. These tests
include numerical reference coverage for dense indexer backward across multiple
query and key tiles, runtime loss scaling, and the registered cuDNN
sliding-window lowering. The other families have source and CPU contract
coverage but have not been qualified through real CUTLASS lowering and SM100
execution.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Split the gap list into explicit bullets.

The section says there are two gaps, but only one is numbered and the NSA/DSA notes are mixed into prose. Please separate release blockers from scope notes so the count and ownership stay unambiguous.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/fe-oss-apis/cutedsl-jax-design.md` around lines 177 - 199, The gap list
in the design doc is too mixed together and does not clearly separate the two
release blockers from the remaining scope notes. Update the section around the
grouped-GEMM, sliding-window, and DSA paragraphs to use explicit bullets or a
clearly numbered list, with each gap described separately and the NSA/DSA
follow-up items kept as distinct scope notes. Keep the ownership and count
unambiguous by ensuring each blocker maps to one bullet and the remaining
coverage notes are not folded into the blocker text.

Comment thread docs/fe-oss-apis/dsa.md
Comment on lines +507 to +524
```python
import math

import jax
from cudnn.jax import DSA

softmax_scale = 1.0 / math.sqrt(D)

@jax.jit
def recompute_target(q_attn, k_attn, lse, topk_indices, topk_length):
return DSA.sparse_attn_score_recompute_wrapper(
q_attn,
k_attn,
lse,
topk_indices,
softmax_scale,
topk_length=topk_length,
)

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the scale expression self-contained.

This example also uses D without defining it, so readers will hit a NameError if they copy/paste it. Compute the scale from the input shape inside the JIT wrapper instead.

🛠️ Proposed fix
 import math
 
 import jax
 from cudnn.jax import DSA
-
-softmax_scale = 1.0 / math.sqrt(D)
 
 `@jax.jit`
 def recompute_target(q_attn, k_attn, lse, topk_indices, topk_length):
     return DSA.sparse_attn_score_recompute_wrapper(
         q_attn,
         k_attn,
         lse,
         topk_indices,
-        softmax_scale,
+        softmax_scale=1.0 / math.sqrt(q_attn.shape[-1]),
         topk_length=topk_length,
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```python
import math
import jax
from cudnn.jax import DSA
softmax_scale = 1.0 / math.sqrt(D)
@jax.jit
def recompute_target(q_attn, k_attn, lse, topk_indices, topk_length):
return DSA.sparse_attn_score_recompute_wrapper(
q_attn,
k_attn,
lse,
topk_indices,
softmax_scale,
topk_length=topk_length,
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/fe-oss-apis/dsa.md` around lines 507 - 524, The example currently
computes softmax_scale from an undefined D, so update the recompute_target JIT
wrapper to derive the scale from the input tensor shape instead of relying on a
free variable. Use the existing symbols recompute_target and
DSA.sparse_attn_score_recompute_wrapper to locate the snippet, and make the
scale expression self-contained by calculating it from q_attn (or the relevant
attention dimension) inside the function before calling the wrapper.

Comment thread docs/fe-oss-apis/nsa.md
Comment on lines +771 to +774
The table above describes the PyTorch surface. In JAX, Compression Attention
and Top-K Reduction support fixed BHSD only, Selection Attention supports
packed THD self-attention only, and Sliding Window Attention is unavailable.

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the JAX Sliding Window availability note.

This contradicts the dedicated JAX Sliding Window section above, which documents a JAX binding and cuDNN lowering. Please remove the unavailable claim or narrow it to the unsupported subset.

🔧 Proposed fix
-The table above describes the PyTorch surface. In JAX, Compression Attention
-and Top-K Reduction support fixed BHSD only, Selection Attention supports
-packed THD self-attention only, and Sliding Window Attention is unavailable.
+The table above describes the PyTorch surface. In JAX, Compression Attention
+and Top-K Reduction support fixed BHSD only, Selection Attention supports
+packed THD self-attention only, and Sliding Window Attention is documented
+separately above.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The table above describes the PyTorch surface. In JAX, Compression Attention
and Top-K Reduction support fixed BHSD only, Selection Attention supports
packed THD self-attention only, and Sliding Window Attention is unavailable.
The table above describes the PyTorch surface. In JAX, Compression Attention
and Top-K Reduction support fixed BHSD only, Selection Attention supports
packed THD self-attention only, and Sliding Window Attention is documented
separately above.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/fe-oss-apis/nsa.md` around lines 771 - 774, The JAX availability note
conflicts with the existing Sliding Window section, so update the summary in the
table description to match the documented JAX support. In the nsa.md text that
mentions Compression Attention, Top-K Reduction, Selection Attention, and
Sliding Window Attention, remove the blanket “unavailable” claim for Sliding
Window or narrow it to only the unsupported subset, keeping the wording
consistent with the JAX Sliding Window section.

Comment on lines +233 to +321
def _dense_score_recompute(
q: Any,
k: Any,
per_head: Any,
*,
score_type: str,
per_head_name: str,
per_head_dtype: Any,
scale: float,
qhead_per_kv_head: Optional[int],
ratio: int,
q_causal_offsets: Any | None,
_validate_only: bool = False,
) -> TupleDict:
q_shape = require_array(q, name="q", rank=4, dtype=jnp.bfloat16)
k_shape = require_array(k, name="k", rank=4, dtype=jnp.bfloat16)

batch, seqlen_q, num_query_heads, head_dim = q_shape
k_batch, seqlen_k, num_kv_heads, k_head_dim = k_shape
dimensions = {
"batch": batch,
"S_q": seqlen_q,
"S_k": seqlen_k,
"H_q": num_query_heads,
"H_kv": num_kv_heads,
"head dimension": head_dim,
}
nonpositive = [f"{name}={value}" for name, value in dimensions.items() if value <= 0]
if nonpositive:
raise ValueError("Dense score-recompute dimensions must be positive, got " + ", ".join(nonpositive))
if k_batch != batch:
raise ValueError(f"q and k batch dimensions must match, got {batch} and {k_batch}")
if k_head_dim != head_dim:
raise ValueError("q and k head dimensions must match, got " f"{head_dim} and {k_head_dim}")
require_array(
per_head,
name=per_head_name,
shape=(batch, seqlen_q, num_query_heads),
dtype=per_head_dtype,
)
if num_query_heads % num_kv_heads:
raise ValueError(f"H_q ({num_query_heads}) must be divisible by H_kv ({num_kv_heads})")

inferred_qhead_per_kv_head = num_query_heads // num_kv_heads
if qhead_per_kv_head is None:
qhead_per_kv_head = inferred_qhead_per_kv_head
if qhead_per_kv_head != inferred_qhead_per_kv_head:
raise ValueError("qhead_per_kv_head must equal H_q / H_kv, got " f"{qhead_per_kv_head} and {num_query_heads} / {num_kv_heads}")
if ratio < 1:
raise ValueError(f"ratio must be at least 1, got {ratio}")

inputs = (q, k, per_head)
if q_causal_offsets is not None:
require_array(
q_causal_offsets,
name="q_causal_offsets",
shape=(batch,),
dtype=jnp.int32,
)
inputs += (q_causal_offsets,)

resolved_scale = float(scale)
if _validate_only:
return None

launcher = _launch_dense_with_q_causal_offsets if q_causal_offsets is not None else _launch_dense_without_q_causal_offsets
out, denom = call_cutedsl(
launcher,
inputs,
outputs=(
BufferSpec(
"out",
(batch, seqlen_q, seqlen_k),
jnp.float32,
fill_value=float("-inf"),
),
BufferSpec("denom", (batch, seqlen_q), jnp.float32),
),
static_args={
"score_type": str(score_type),
"head_dim": int(head_dim),
"qhead_per_kv_head": int(qhead_per_kv_head),
"ratio": int(ratio),
"max_seqlen_q": int(seqlen_q),
"max_seqlen_k": int(seqlen_k),
"scale": resolved_scale,
},
)
return TupleDict(out=out, denom=denom)

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Dense score-recompute skips kernel-config validation in _check_support.

_sparse_score_recompute validates feasibility via resolve_sparse_score_kernel_config before the _validate_only early return (lines 394-402), but _dense_score_recompute never calls resolve_dense_score_kernel_config — that call only happens inside the launcher helpers at actual trace/execute time. resolve_dense_score_kernel_config enforces head_dim % 64 == 0 and qhead_per_kv_head in {32, 64, 128}, neither of which _dense_score_recompute checks itself. As a result, DenseIndexerScoreRecompute._check_support() / DenseAttnScoreRecompute._check_support() can report success for configs that will only fail later during kernel launch, breaking the intended "gate invocation" contract.

🛠️ Proposed fix: validate config eagerly
     if ratio < 1:
         raise ValueError(f"ratio must be at least 1, got {ratio}")
 
     inputs = (q, k, per_head)
     if q_causal_offsets is not None:
         require_array(
             q_causal_offsets,
             name="q_causal_offsets",
             shape=(batch,),
             dtype=jnp.int32,
         )
         inputs += (q_causal_offsets,)
 
     resolved_scale = float(scale)
+    # Fail fast in check_support() on infeasible tile/SMEM configs, matching
+    # the eager validation done in `_sparse_score_recompute`.
+    resolve_dense_score_kernel_config(
+        score_type=score_type,
+        head_dim=head_dim,
+        qhead_per_kv_head=qhead_per_kv_head,
+    )
     if _validate_only:
         return None

Also applies to: 624-637, 683-696

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/deepseek_sparse_attention/score_recompute/jax.py` around lines
233 - 321, Dense score-recompute currently bypasses kernel-config validation
during the `_validate_only` path, so `_check_support()` can succeed for
unsupported configs. Update `_dense_score_recompute` to eagerly call
`resolve_dense_score_kernel_config` before the `_validate_only` early return,
using the same inputs passed later by `_launch_dense_with_q_causal_offsets` /
`_launch_dense_without_q_causal_offsets`. Ensure
`DenseIndexerScoreRecompute._check_support()` and
`DenseAttnScoreRecompute._check_support()` now reject invalid `head_dim` and
`qhead_per_kv_head` combinations up front, matching `_sparse_score_recompute`
behavior.

Comment on lines +23 to +136
def _launch_with_topk_length(
stream,
q,
kv,
output,
doutput,
lse,
attn_sink,
topk_idxs,
topk_length,
dq,
dkv,
d_sink,
workspace_lse_odo,
workspace_dkv,
*,
total_seqlen_q: int,
total_seqlen_kv: int,
num_heads: int,
head_dim: int,
block_tile: int,
softmax_scale: float,
):
from cutlass import Float32, Int32

from .dsa_bwd_sm100 import FlashAttentionDSABackwardSm100

kernel = FlashAttentionDSABackwardSm100(
head_dim=head_dim,
head_dim_v=head_dim,
block_tile=block_tile,
)

problem_shape = (
Int32(total_seqlen_q),
Int32(total_seqlen_kv),
Int32(head_dim),
(Int32(num_heads), Int32(1)),
)
kernel(
problem_shape,
q,
kv,
output,
doutput,
lse,
attn_sink,
topk_idxs,
topk_length,
dq,
dkv,
d_sink,
workspace_lse_odo,
workspace_dkv,
Float32(softmax_scale),
stream,
)


def _launch_without_topk_length(
stream,
q,
kv,
output,
doutput,
lse,
attn_sink,
topk_idxs,
dq,
dkv,
d_sink,
workspace_lse_odo,
workspace_dkv,
*,
total_seqlen_q: int,
total_seqlen_kv: int,
num_heads: int,
head_dim: int,
block_tile: int,
softmax_scale: float,
):
from cutlass import Float32, Int32

from .dsa_bwd_sm100 import FlashAttentionDSABackwardSm100

kernel = FlashAttentionDSABackwardSm100(
head_dim=head_dim,
head_dim_v=head_dim,
block_tile=block_tile,
)
problem_shape = (
Int32(total_seqlen_q),
Int32(total_seqlen_kv),
Int32(head_dim),
(Int32(num_heads), Int32(1)),
)
kernel(
problem_shape,
q,
kv,
output,
doutput,
lse,
attn_sink,
topk_idxs,
None,
dq,
dkv,
d_sink,
workspace_lse_odo,
workspace_dkv,
Float32(softmax_scale),
stream,
)

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.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Critical: max_topk is never forwarded to FlashAttentionDSABackwardSm100, silently corrupting KV loading.

Neither _launch_with_topk_length nor _launch_without_topk_length passes max_topk when constructing FlashAttentionDSABackwardSm100(head_dim=..., head_dim_v=..., block_tile=...), and _sparse_attention_backward_impl's static_args dict (lines 290-297) never includes it either — even though topk_shape[1] (the required value) is already computed at line 204-213. The kernel constructor defaults max_topk=0 (see dsa_bwd_sm100.py), and load_KV/_load_kv_rows in that same file guard every real K/V row load with if idx < self.max_topk: — with max_topk=0 this is always false, so topk_idx stays -1 for every row. That means real K/V rows are never loaded (silently zero-filled dKV/dQ gradients), or — in the topk_length-provided + first-tile branch — _copy_kv_row is invoked with topk_idx=-1, indexing mKV[-1, ...], a likely out-of-bounds access.

The Torch counterpart (sparse_attention_backward/_interface_sm100.py) correctly threads max_topk=max_topk (derived from topk_idxs.shape[1]) into the same kernel class, so this is a JAX-binding-only regression. This also means the call_cutedsl compile-cache key is missing a dimension that materially changes kernel behavior.

🐛 Proposed fix: thread `max_topk` through both launchers and static_args
 def _launch_with_topk_length(
     stream,
     q,
     kv,
     output,
     doutput,
     lse,
     attn_sink,
     topk_idxs,
     topk_length,
     dq,
     dkv,
     d_sink,
     workspace_lse_odo,
     workspace_dkv,
     *,
     total_seqlen_q: int,
     total_seqlen_kv: int,
     num_heads: int,
     head_dim: int,
     block_tile: int,
     softmax_scale: float,
+    max_topk: int,
 ):
     from cutlass import Float32, Int32

     from .dsa_bwd_sm100 import FlashAttentionDSABackwardSm100

     kernel = FlashAttentionDSABackwardSm100(
         head_dim=head_dim,
         head_dim_v=head_dim,
         block_tile=block_tile,
+        max_topk=max_topk,
     )
 def _launch_without_topk_length(
     stream,
     q,
     kv,
     output,
     doutput,
     lse,
     attn_sink,
     topk_idxs,
     dq,
     dkv,
     d_sink,
     workspace_lse_odo,
     workspace_dkv,
     *,
     total_seqlen_q: int,
     total_seqlen_kv: int,
     num_heads: int,
     head_dim: int,
     block_tile: int,
     softmax_scale: float,
+    max_topk: int,
 ):
     from cutlass import Float32, Int32

     from .dsa_bwd_sm100 import FlashAttentionDSABackwardSm100

     kernel = FlashAttentionDSABackwardSm100(
         head_dim=head_dim,
         head_dim_v=head_dim,
         block_tile=block_tile,
+        max_topk=max_topk,
     )
         static_args={
             "total_seqlen_q": int(total_seqlen_q),
             "total_seqlen_kv": int(total_seqlen_kv),
             "num_heads": int(num_heads),
             "head_dim": int(head_dim),
             "block_tile": int(block_tile),
             "softmax_scale": float(resolved_scale),
+            "max_topk": int(topk_shape[1]),
         },

Also applies to: 227-298

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/deepseek_sparse_attention/sparse_attention_backward/jax.py`
around lines 23 - 136, `max_topk` is not being forwarded into the JAX sparse
backward launch path, so `FlashAttentionDSABackwardSm100` is built with its
default value and KV loading can be wrong. Update `_launch_with_topk_length` and
`_launch_without_topk_length` to accept and pass `max_topk` when constructing
`FlashAttentionDSABackwardSm100`, and thread the computed `topk_shape[1]`
through `_sparse_attention_backward_impl` via `static_args`/`call_cutedsl` so
the kernel cache key includes it. Use the existing `topk_shape` and the
`_launch_*` helpers as the entry points for the fix.

Comment on lines +228 to +234
m, _, batch = a_tensor.shape
n, _, _b_batch = b_tensor.shape
c_tensor = None
if c_major == "m":
c_tensor = torch.empty_strided((m, n, l), (1, m, m * n), dtype=c_dtype, device=a_tensor.device)
c_tensor = torch.empty_strided((m, n, batch), (1, m, m * n), dtype=c_dtype, device=a_tensor.device)
elif c_major == "n":
c_tensor = torch.empty_strided((m, n, l), (n, 1, m * n), dtype=c_dtype, device=a_tensor.device)
c_tensor = torch.empty_strided((m, n, batch), (n, 1, m * n), dtype=c_dtype, device=a_tensor.device)

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Unused/unchecked _b_batch before allocating c_tensor.

b_tensor.shape is unpacked into _b_batch but it's never validated against batch (from a_tensor.shape) before c_tensor is sized using batch. If A/B batch dims disagree, an incorrectly-shaped c_tensor is allocated here and the mismatch is only caught later inside check_support() (via require_gemm_shapes), after the wasted allocation. Consider validating upfront or just discarding the unused value with _.

🔧 Proposed fix
     m, _, batch = a_tensor.shape
-    n, _, _b_batch = b_tensor.shape
+    n, _, b_batch = b_tensor.shape
+    if b_batch != batch:
+        raise ValueError(f"a_tensor and b_tensor batch dims must match, got {batch} and {b_batch}")
     c_tensor = None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
m, _, batch = a_tensor.shape
n, _, _b_batch = b_tensor.shape
c_tensor = None
if c_major == "m":
c_tensor = torch.empty_strided((m, n, l), (1, m, m * n), dtype=c_dtype, device=a_tensor.device)
c_tensor = torch.empty_strided((m, n, batch), (1, m, m * n), dtype=c_dtype, device=a_tensor.device)
elif c_major == "n":
c_tensor = torch.empty_strided((m, n, l), (n, 1, m * n), dtype=c_dtype, device=a_tensor.device)
c_tensor = torch.empty_strided((m, n, batch), (n, 1, m * n), dtype=c_dtype, device=a_tensor.device)
m, _, batch = a_tensor.shape
n, _, b_batch = b_tensor.shape
if b_batch != batch:
raise ValueError(f"a_tensor and b_tensor batch dims must match, got {batch} and {b_batch}")
c_tensor = None
if c_major == "m":
c_tensor = torch.empty_strided((m, n, batch), (1, m, m * n), dtype=c_dtype, device=a_tensor.device)
elif c_major == "n":
c_tensor = torch.empty_strided((m, n, batch), (n, 1, m * n), dtype=c_dtype, device=a_tensor.device)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/gemm_amax/api.py` around lines 228 - 234, The batch dimension
from b_tensor is currently unpacked as _b_batch in api.py’s c_tensor allocation
path but never checked against a_tensor’s batch before using batch to size
c_tensor. In the gemm_amax allocation logic, validate that both tensors share
the same batch dimension before calling torch.empty_strided, or discard the
unused value with _ if the batch is guaranteed to be checked elsewhere; keep the
fix near the m, n, batch unpacking and c_tensor setup so the mismatch is caught
before allocation.

d_m, d_n, d_l = self._tensor_shape(self.d_desc, name="sample_d")

self._value_error_if((b_k, b_l) != (k, l), f"B shape mismatch: expected (*, {k}, {l}), got {(n, b_k, b_l)}")
m, n, k, l = require_gemm_shapes(

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Ambiguous loop variable l.

Ruff (E741) flags l on this changed line. Since require_gemm_shapes already names this dimension batch, renaming improves clarity and clears the lint. Note l is used again at Line 174/187, so update those references too.

🧰 Tools
🪛 Ruff (0.15.20)

[error] 91-91: Ambiguous variable name: l

(E741)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/gemm_srelu/api.py` at line 91, Rename the ambiguous
loop/dimension variable `l` in `require_gemm_shapes` usage within `api.py` to
`batch` (or another clearer name) to satisfy Ruff E741 and match the existing
shape naming. Update all related references in the same flow, including the
later uses in the GEMM/SReLU API logic at the spots where `l` is read again, so
the renamed variable stays consistent across the function.

Source: Linters/SAST tools

self._value_error_if(
self.ab12_desc.stride_order != self.c_desc.stride_order,
f"AB12 and C tensor stride orders must match, got {self.ab12_desc.stride_order} and {self.c_desc.stride_order}",
m, n, k, l = require_gemm_shapes(

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Ambiguous loop variable l.

Ruff (E741) flags this changed line. Rename to batch (consistent with require_gemm_shapes) to clear the lint; l is unused afterward here so the change is localized.

🧰 Tools
🪛 Ruff (0.15.20)

[error] 123-123: Ambiguous variable name: l

(E741)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/gemm_swiglu/api.py` at line 123, Rename the ambiguous loop
variable in require_gemm_shapes assignment within gemm_swiglu/api.py from l to
batch to satisfy Ruff E741 and keep the naming consistent with
require_gemm_shapes; this change is localized because the variable is not used
afterward, so only update the unpacking in the relevant API function.

Source: Linters/SAST tools

@Anerudhan
Anerudhan requested review from Anerudhan and hwanseoc July 7, 2026 21:04
@mgoldfarb-nvidia
mgoldfarb-nvidia marked this pull request as draft July 9, 2026 21:57
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.

1 participant