Skip to content

[nvfp4 training][rl] Add the four-over-six grouped GEMM and dispatcher wiring - #4853

Open
wolfcomos wants to merge 3 commits into
pytorch:mainfrom
wolfcomos:4over6/ao3-grouped-dispatcher
Open

[nvfp4 training][rl] Add the four-over-six grouped GEMM and dispatcher wiring#4853
wolfcomos wants to merge 3 commits into
pytorch:mainfrom
wolfcomos:4over6/ao3-grouped-dispatcher

Conversation

@wolfcomos

@wolfcomos wolfcomos commented Aug 31, 2026

Copy link
Copy Markdown

Stacked on #4851 / #4852 — this PR's diff includes their commits; the change to review here is the top commit. Integrates the four-over-six recipe with MoE training for routed-expert layers, reusing existing pieces rather than adding kernels: the #4852 quantizer (its CuTe DSL fast path dispatches automatically) and the existing F.scaled_grouped_mm for the GEMMs.

  • Per-tensor activations: each token group's amax expands to a per-row vector so the whole packed tensor quantizes in one call — bitwise identical to quantizing each group separately — and one scaled_grouped_mm with per-group second-level scales runs the forward.
  • Expert weights quantize in one flattened call for both block shapes (the quantizer accepts a per-row amax with 16x16 blocks under a tile-uniform contract): one kernel launch regardless of expert count.
  • Row-scaled activations: one scaled_grouped_mm carrying the constant per-tensor factor in every group's slot, its bf16 output upcast and scaled by the raw per-row amaxes. The grouped GEMM emits bf16 only, which costs one output rounding vs per-group dense GEMMs; tests emulate that rounding in a loop oracle and compare bitwise (with a reduction-order SQNR fallback), and separately bound the rounding cost.
  • Backward: high_precision or dequantized grouped GEMMs (four-over-six has no quantized backward); dequantized differentiates the quantized forward itself from the saved 4-bit codes and scales.

Wiring mirrors the Float8/MXFP8 recipes exactly: NVFP4FourOverSixTrainingOpConfig(TrainingOpBaseConfig) — a pytree-constant dataclass with explicit __eq__/__hash__ — plus an isinstance branch in _quantize_then_scaled_grouped_mm. The dispatcher branch owns the offs[-1] tail slice / zero-extend for padded token dispatchers that over-allocate A past the logical rows. quantize_() model conversion for the grouped config is future work; framework integrations drive the grouped GEMM dispatcher (see the torchtitan converters). Adds an NVFP4 four-over-six section to the moe_training README.

Tests: 40 grouped tests (grouped-vs-dense forward parity, row-scaled fused-vs-oracle, backward bitwise references including empty groups and ragged padding, dispatcher round-trip with over-allocated tails, fullgraph compile in both scale granularities) plus the dense suite with the relaxed quantizer combinations, all green on GB200.

Reference
NVFP4 RL training recipe roadmap references from Ziang Li from humans&: https://humansand.ai/blog/nvfp4-rl, radixark/miles#615, NVIDIA/TransformerEngine#2972

Stack: #4851#4852 ← this PR.

wolfcomos and others added 3 commits August 30, 2026 23:55
Four-over-six is an adaptive NVFP4 block-scaling recipe: every
quantization block is encoded twice — the standard map-to-6 encoding
and a 1.5x-scale map-to-4 candidate whose denser FP4 grid lowers error
for blocks with mass below the amax — and the lower-error candidate is
stored. It is part of the NVFP4 RL training recipe roadmap (miles).

This PR adds the recipe surface and a correctness-first reference
implementation; optimized kernels land separately:

- four_over_six_quantize: pure-PyTorch quantizer with per-tensor or
  row-scaled global scales and 1x16/16x16 blocks, every rounding step
  pinned so faster implementations can be validated bitwise against it.
- nvfp4_dequantize: the standard NVFP4 decode (recipe-agnostic), a
  pure-PyTorch correctness helper — an optimized kernel is future work.
- four_over_six_mm / four_over_six_linear: differentiable matmul with
  quantized, high_precision, and dequantized backward modes; the
  dequantized mode differentiates the quantized-forward function itself
  (the RL train/inference-consistency mode) while saving only 4-bit
  codes and scales.
- NVFP4FourOverSixLinear: a stateless leaf module for the recipe, and
  recipe="default"|"four_over_six" on NVFP4TrainingConfig so
  quantize_() installs the right module. The existing NVFP4Linear is
  untouched: it owns RHT/SR state (sign-vector and seed buffers, TP
  machinery) that four-over-six deliberately has none of, so the
  recipes stay separate leaf modules behind one config.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The quantizer is the recipe's one hot op — it runs on every activation
and weight of every forward — so this PR adds a minimal CuTe DSL
kernel prototype for it, dispatched automatically for eligible inputs
(CUDA bf16/fp32, SM100+, contiguous, C % 64 == 0); ineligible inputs
silently fall through to the pure-PyTorch reference body. The CuTe DSL
runtime packages are assumed present wherever the gate passes — the
dispatch does no package probing.

The kernel is bitwise-identical to the reference: the rounding-critical
steps (correctly-rounded FP32 division, the FP4/E4M3 casts and their
exact decodes) are emitted as raw inline PTX so no compiler lowering
choice can change a rounding, and the width-16 error-reduction order is
pinned (clamped shuffle-down tree), insulating the kernel from
cutlass-dsl wrapper churn. Compile caching keys on every
shape/dtype/knob that changes codegen, and compilation runs under the
input's device context. One documented divergence: NaN inputs take
NaN-dropping block amaxes and encode to +6 codes, where torch.amax
propagates NaN.

The kernel is wrapped in a CUDA-only torch.library custom op with a
fake impl so torch.compile traces through the dispatch; direct op
callers get the dispatch gate's and wrapper's checks mirrored as
errors. The bench file measures the kernel against the pure-torch
reference (26-30x at training shapes). Further kernel work (fused
dequantize, grouped variants) lands in future PRs.

Numerics: all 440 swept shape/knob configurations (block x err_mode x
bound x scale granularity x dtype x data construction x shape) bitwise
identical to the pure-torch reference on GB200.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…atcher

Integrate the four-over-six recipe with MoE training: routed-expert
layers get four_over_six_grouped_mm, built by reusing the existing
pieces rather than adding kernels — the quantizer from the dense PRs
(the CuTe DSL fast path dispatches automatically) and the existing
F.scaled_grouped_mm for the GEMMs.

- Per-tensor activations: each token group's amax expands to a per-row
  vector so the whole packed tensor quantizes in one call, bitwise
  identical to quantizing each group separately; one scaled_grouped_mm
  with per-group second-level scales runs the forward.
- Expert weights quantize in one flattened call for both block shapes
  (the quantizer accepts a per-row amax with 16x16 blocks under a
  tile-uniform contract), one kernel launch regardless of expert count.
- Row-scaled activations: one scaled_grouped_mm carrying the constant
  per-tensor factor, its bf16 output upcast and scaled by the raw
  per-row amaxes (the grouped GEMM emits bf16 only, one output rounding
  vs a dense per-group loop; tests pin it against a rounding-emulated
  loop oracle).
- Backward is high-precision or dequantized grouped GEMMs (no
  quantized backward); dequantized differentiates the quantized
  forward itself from the saved 4-bit codes and scales.

Wiring mirrors the Float8/MXFP8 recipes exactly:
NVFP4FourOverSixTrainingOpConfig(TrainingOpBaseConfig) — a
pytree-constant dataclass with explicit __eq__/__hash__ — plus an
isinstance branch in _quantize_then_scaled_grouped_mm. The dispatcher
branch owns the offs[-1] tail slice / zero-extend for padded token
dispatchers that over-allocate A past the logical rows. Also adds an
NVFP4 four-over-six section to the moe_training README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pytorch-bot

pytorch-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/ao/4853

Note: Links to docs will display an error until the docs builds have been completed.

This comment was automatically generated by Dr. CI and updates every 15 minutes.

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant