Skip to content

[WS1][CUDA][MiniMax-H3] projection rows: audio in/out, text context and QKV on a fixed-order GEMM (RFC #420 stack 2/5) - #568

Open
fusheng-ji wants to merge 9 commits into
RL-Align:test-h3from
fusheng-ji:feat/h3-projection-rows
Open

fusheng-ji wants to merge 9 commits into
RL-Align:test-h3from
fusheng-ji:feat/h3-projection-rows

Conversation

@fusheng-ji

@fusheng-ji fusheng-ji commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

RFC #420 H3 stack 2/5 (base test-h3, merge in this order): #586 -> #587 -> #588 -> foundation -> projection rows -> WS2 parallel -> token refiner -> full transformer. This PR merges after its parent; until then GitHub also shows the parent's commits. This PR's own diff.

Evidence location: the measurement outputs of this PR (JSON, figures) are not part of its diff. They are on the fork's pr_assets branch under pending/h3-projection-rows/ (links in this description are fixed to that commit). Paths such as reports/experiments/... below name where a runner writes its output.

What

Four linear rows on one fixed-order Triton GEMM, contract h3-projection-gemm-v1:

  • one program owns one output tile and walks the whole reduction axis in ascending BLOCK_K steps into FP32 registers;
    no split-K, stream-K, atomics or autotuning; the tile shape does not depend on M, N or the grid;
  • BF16 operands: tl.dot, tile 128x128x64, 8 warps, 3 stages; FP32 operands: input_precision="ieee" (no TF32), tile 64x64x32, 4 warps, 3 stages;
  • bias added once in FP32, result rounded once to the output dtype;
  • therefore a token's output bytes do not depend on how many tokens share the call or its position, and a tensor-parallel output shard equals the same columns of the full product;
  • the backward uses the same kernel (dX: reduction over output features; dW: reduction over tokens ascending; db: ascending token sum).
Row Shape dtype
audio_input_projection_fp32 32 -> 5376 FP32
audio_output_projection_fp32 5376 -> 32 FP32
text_context_projection 5120 -> 5376 BF16
h3_qkv_gemm 5376 -> 3 x 7168 (Q, K, V; fused [Q;K;V] form equals the separate ones bit for bit) BF16

Prior art & reuse decision

Existing implementation Why not bound
cuBLAS / cuBLASLt F.linear fastest, but the reduction order follows M (split-K), so it is not batch-invariant and cannot be made fail-closed; kept as the provider baseline
CUTLASS GEMM could pin the tile and disable split-K, but needs a C++ extension for the same contract; Triton is equivalent here, CUTLASS left as a performance follow-up
FlashAttention / FlashInfer attention only
vLLM-Omni QKVParallelLinear fused weights through cuBLAS; no bitwise guarantee between fused and separate; this row implements and tests forward_fused
SGLang linears no determinism guarantee; used as cross-check only
h3-det-linear-v1 (this repo) strict, but 50-276x slower than cuBLAS at thousands of tokens; kept for the conditioning layers
verl / Miles / Vime consistency by framework switches over cuBLAS; no H3 projection operators

Decision: a new fixed-order Triton GEMM.

Results (H100 / Hopper)

Measured on NVIDIA H100 (93 GiB, sm90), driver 580, CUDA 13.0, torch 2.13.0+cu130, pinned MiniMax-H3@42ed227 weights, at head 3cf51bf9. Relative error is against an FP64 F.linear; time is the median forward time.

Row Tokens RL-Kernel cuBLAS provider Ratio RL-Kernel error Provider error
audio_input_projection_fp32 64 0.100 ms 0.024 ms 4.25x 1.0e-07 1.0e-07
audio_input_projection_fp32 1024 0.100 ms 0.041 ms 2.44x 1.0e-07 1.0e-07
audio_input_projection_fp32 4096 0.195 ms 0.090 ms 2.18x 1.0e-07 1.0e-07
audio_output_projection_fp32 64 0.553 ms 0.045 ms 12.33x 1.3e-06 4.5e-07
audio_output_projection_fp32 1024 0.561 ms 0.057 ms 9.85x 1.3e-06 4.4e-07
audio_output_projection_fp32 4096 0.551 ms 0.156 ms 3.53x 1.3e-06 7.6e-07
text_context_projection 64 0.107 ms 0.049 ms 2.18x 1.7e-03 1.7e-03
text_context_projection 1024 0.177 ms 0.096 ms 1.84x 1.7e-03 1.7e-03
text_context_projection 4096 0.470 ms 0.325 ms 1.45x 1.7e-03 1.7e-03
h3_qkv_gemm 64 0.246 ms 0.144 ms 1.72x 1.7e-03 1.7e-03
h3_qkv_gemm 1024 0.513 ms 0.358 ms 1.43x 1.7e-03 1.7e-03
h3_qkv_gemm 4096 1.704 ms 1.284 ms 1.33x 1.7e-03 1.7e-03

projection rows accuracy and performance

Numbers: bench.json. The kernel is 1.3x-4.3x slower than cuBLAS on the BF16 rows and
3.5x-12x slower on audio_output_projection_fp32 (K = 5376, FP32 ieee, 64x64x32 tile), whose error is also about 3x the provider's (1.3e-6 vs 4.4e-7; accuracy floor relaxed to 4e-6, see the doc).
That row is the first candidate for a per-row tile sweep. This is the price of the fixed reduction order; strict mode forbids split-K.

Results (B200 / Blackwell)

Measured on NVIDIA B200 (179 GiB, sm100), driver 580.126, CUDA 13.0, torch 2.13.0+cu130, pinned MiniMax-H3@42ed227 weights, clean tree at 3cf51bf9.

Tests (test_h3_projection_rows.py and test_h3_one_block.py): 67 passed (67 collected); tests.json.

Forward time and accuracy against FP64 for each row against the provider's linear (one B200, 20 repeats; bench.json):

row tokens candidate ms provider ms ratio candidate rel. error provider rel. error
audio_input_projection_fp32 64 0.042 0.023 1.85x 1.03e-07 1.03e-07
audio_input_projection_fp32 1024 0.069 0.050 1.38x 1.03e-07 1.03e-07
audio_input_projection_fp32 4096 0.153 0.076 2.01x 1.03e-07 1.03e-07
audio_output_projection_fp32 64 0.536 0.064 8.33x 1.33e-06 4.27e-07
audio_output_projection_fp32 1024 0.536 0.075 7.14x 1.32e-06 4.41e-07
audio_output_projection_fp32 4096 0.539 0.149 3.61x 1.32e-06 1.32e-06
text_context_projection 64 0.065 0.024 2.71x 1.66e-03 1.66e-03
text_context_projection 1024 0.118 0.056 2.10x 1.66e-03 1.66e-03
text_context_projection 4096 0.258 0.154 1.68x 1.66e-03 1.66e-03
h3_qkv_gemm 64 0.189 0.058 3.23x 1.65e-03 1.65e-03
h3_qkv_gemm 1024 0.332 0.186 1.78x 1.66e-03 1.66e-03
h3_qkv_gemm 4096 0.974 0.624 1.56x 1.66e-03 1.66e-03

projection rows on B200: performance and accuracy

The fixed-order kernels are slower than the provider's GEMM (ratio above 1 in every row, up to 8.33x) and match it in accuracy, except audio_output_projection_fp32, where the candidate's error is 1.3e-06 against 4.3e-07 for the provider at 64 tokens (both far below the BF16 level of the other rows).

Status

Forward and backward are bitwise repeatable and invariant to batch size, position, permutation and padding. The audio_output row has no legal output shard (see Scope).

Tests

python -m pytest tests/models/minimax_h3/test_h3_projection_rows.py tests/models/minimax_h3/test_h3_one_block.py -q   # 61 passed, 0 skipped

Covers real checkpoint tensors (shard 1: audio, context embedder; block 0: Q/K/V), FP64 accuracy, repeat and backward determinism, fused vs separate QKV, TP output shards, and fail-closed rejections (wrong widths, mixed devices, CPU tensors, wrong dtype).

B200: same files, 67 passed.

Scope / Known limitations

Strict mode is fail-closed: no TF32, no fast-math, no split-K; FP32 accumulation with one final rounding; unsupported shapes, dtypes and devices raise instead of falling back. audio_output_projection_fp32 has a 32-wide output equal to one shard unit, so it has no legal output shard; a test asserts that narrower weights are rejected.
Not part of this PR: video projections (#562), O / gate_up / down GEMMs (#554, #535), full attention (#553), ROCm, SGLang adapter, pipeline parallel.

@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 132 files, which is 32 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

Check out review usage here.

Run configuration
Commits

Reviewing files that changed from the base of the PR and between d22d4b4 and 09fcd9c.

Files selected for processing: 132
  • .github/workflows/ci.yml
  • benchmarks/models/benchmark_h3_conditioning.py
  • build_tools/extensions.py
  • csrc/bindings/ops.cpp
  • csrc/cuda/h3/adaln_row_gather.cu
  • csrc/cuda/h3/det_linear.cu
  • csrc/cuda/h3/gate_residual.cu
  • csrc/cuda/h3/rmsnorm_modulate.cu
  • csrc/cuda/h3/timestep_sinusoid.cu
  • docs/.nav.yml
  • docs/operators/README.md
  • docs/operators/h3-adaln-gate-residual.md
  • docs/operators/h3-adaln-projection.md
  • docs/operators/h3-adaln-row-gather.md
  • docs/operators/h3-final-adaln-out.md
  • docs/operators/h3-one-block.md
  • docs/operators/h3-projection-rows.md
  • docs/operators/h3-rmsnorm.md
  • docs/operators/h3-sp-norm-adaln.md
  • docs/operators/h3-timestep-mlp.md
  • docs/operators/h3-timestep-sinusoid.md
  • docs/operators/h3-tp-adaln-3mod.md
  • examples/models/minimax_h3/README.md
  • examples/models/minimax_h3/candidate.sbatch
  • examples/models/minimax_h3/diagnose-block.sbatch
  • examples/models/minimax_h3/full-model-ensemble-rule.json
  • examples/models/minimax_h3/full-model-thresholds.json
  • examples/models/minimax_h3/generation-compare.sbatch
  • examples/models/minimax_h3/generation-contract.json
  • examples/models/minimax_h3/generation-inputs.json
  • examples/models/minimax_h3/generation-prompt.txt
  • examples/models/minimax_h3/generation-resume-check.sbatch
  • examples/models/minimax_h3/generation.sbatch
  • examples/models/minimax_h3/reference-manifest.json
  • examples/models/minimax_h3/reference.sbatch
  • rl_engine/_C.pyi
  • rl_engine/backends/cuda/model_specific/__init__.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/__init__.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/adaln_modulation.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/adaln_projection.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/adaln_row_gather.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/det_linear.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/final_adaln_out.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/gate_residual.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/projection.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/rmsnorm.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/sp_norm_adaln.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/timestep_mlp.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/timestep_sinusoid.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/tp_adaln_projection.py
  • rl_engine/backends/cuda/model_specific/minimax_h3/ws2_comm.py
  • rl_engine/backends/shared/triton/gemm/h3_projection.py
  • rl_engine/reference/minimax_h3/__init__.py
  • rl_engine/reference/minimax_h3/adaln_projection.py
  • rl_engine/reference/minimax_h3/adaln_row_gather.py
  • rl_engine/reference/minimax_h3/block.py
  • rl_engine/reference/minimax_h3/final_adaln_out.py
  • rl_engine/reference/minimax_h3/fixed_order.py
  • rl_engine/reference/minimax_h3/gate_residual.py
  • rl_engine/reference/minimax_h3/projection.py
  • rl_engine/reference/minimax_h3/rmsnorm.py
  • rl_engine/reference/minimax_h3/timestep_mlp.py
  • rl_engine/reference/minimax_h3/timestep_sinusoid.py
  • rl_engine/runtime/registry.py
  • rl_engine/validation/models/h3_block.py
  • rl_engine/validation/models/h3_cases.py
  • rl_engine/validation/models/h3_chain.py
  • rl_engine/validation/models/h3_full_manifest.json
  • rl_engine/validation/models/h3_full_model.py
  • rl_engine/validation/models/h3_generation_resume.py
  • rl_engine/validation/models/h3_manifest.json
  • rl_engine/validation/models/h3_provider.py
  • rl_engine/validation/models/h3_report.py
  • rl_engine/validation/models/h3_weights.py
  • rl_engine/validation/models/h3_ws2.py
  • rl_engine/validation/operators/operator_inputs.py
  • rl_engine/validation/operators/operator_specs.py
  • tests/build/test_build_platform_collectives.py
  • tests/models/minimax_h3/conftest.py
  • tests/models/minimax_h3/test_h3_adaln_gate_residual.py
  • tests/models/minimax_h3/test_h3_adaln_modulation.py
  • tests/models/minimax_h3/test_h3_adaln_projection.py
  • tests/models/minimax_h3/test_h3_adaln_row_gather.py
  • tests/models/minimax_h3/test_h3_benchmark_timing.py
  • tests/models/minimax_h3/test_h3_chain_replay_cli.py
  • tests/models/minimax_h3/test_h3_cli.py
  • tests/models/minimax_h3/test_h3_conditioning_e2e.py
  • tests/models/minimax_h3/test_h3_det_linear.py
  • tests/models/minimax_h3/test_h3_final_adaln_out.py
  • tests/models/minimax_h3/test_h3_full_model.py
  • tests/models/minimax_h3/test_h3_generation_resume.py
  • tests/models/minimax_h3/test_h3_native_validation.py
  • tests/models/minimax_h3/test_h3_one_block.py
  • tests/models/minimax_h3/test_h3_projection_rows.py
  • tests/models/minimax_h3/test_h3_report.py
  • tests/models/minimax_h3/test_h3_rmsnorm.py
  • tests/models/minimax_h3/test_h3_sp_norm_adaln.py
  • tests/models/minimax_h3/test_h3_timestep_mlp.py
  • tests/models/minimax_h3/test_h3_timestep_sinusoid.py
  • tests/models/minimax_h3/test_h3_tp_adaln.py
  • tests/models/minimax_h3/test_h3_weights.py
  • tests/models/minimax_h3/test_h3_ws2_processes.py
  • tests/models/minimax_h3/test_prepare_h3_weights.py
  • tests/runtime/test_dispatch.py
  • tests/validation/operators/test_ws1_gtest_gpu.py
  • tools/migration/README.md
  • tools/migration/layout.json
  • tools/validation/models/h3_bitwise_reuse.py
  • tools/validation/models/h3_block_diagnostic.py
  • tools/validation/models/h3_block_replay.py
  • tools/validation/models/h3_block_ulp_sweep.py
  • tools/validation/models/h3_candidate_prepare.py
  • tools/validation/models/h3_chain_replay.py
  • tools/validation/models/h3_evidence.py
  • tools/validation/models/h3_evidence_integrity.py
  • tools/validation/models/h3_first_step_diagnostic.py
  • tools/validation/models/h3_full_compare.py
  • tools/validation/models/h3_full_ensemble_rule.py
  • tools/validation/models/h3_full_generation.py
  • tools/validation/models/h3_full_model.py
  • tools/validation/models/h3_full_provider_spread.py
  • tools/validation/models/h3_generation_adopt.py
  • tools/validation/models/h3_generation_compare.py
  • tools/validation/models/h3_prior_art.py
  • tools/validation/models/h3_projection_bench.py
  • tools/validation/models/h3_projection_charts.py
  • tools/validation/models/h3_ws2_evidence.py
  • tools/validation/models/plot_h3_bitwise_reuse.py
  • tools/validation/models/plot_h3_block.py
  • tools/validation/models/plot_h3_evidence.py
  • tools/validation/models/plot_h3_prior_art.py
  • tools/weights/prepare_h3_weights.py

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@walker-ai

Copy link
Copy Markdown

I was planning to work on the audio projections, as I’m currently developing the video input/output projections. After seeing your PR, I briefly reviewed the implementation and noticed that dW and dbias change under row permutation. Could you check backward permutation BI for audio_input_projection_fp32?

I reproduced this with the unmodified operator at 74e5b83, on H20 with PyTorch 2.4.1+cu124 and Triton 3.0.0. Each ordering uses one backward call.

import torch
from rl_engine.backends.cuda.model_specific.minimax_h3.projection import H3AudioInputProjectionCudaOp

op = H3AudioInputProjectionCudaOp()
x = torch.ones(3, 32, device="cuda")
w = torch.zeros(5376, 32, device="cuda", requires_grad=True)
b = torch.zeros(5376, device="cuda", requires_grad=True)
g = torch.zeros(3, 5376, device="cuda")
g[:, 0] = torch.tensor([1.0, 2**-24, -1.0], device="cuda")

def grads(order):
    return torch.autograd.grad(op(x[order], w, b), (w, b), g[order])

dw1, db1 = grads([0, 1, 2])
dw2, db2 = grads([0, 2, 1])
print("dW[0,0]:", dw1[0, 0].item(), dw2[0, 0].item())
print("dbias[0]:", db1[0].item(), db2[0].item())
assert torch.equal(dw1, dw2) and torch.equal(db1, db2), "parameter-gradient permutation BI failed"

Output:

dW[0,0]: 0.0 5.960464477539063e-08
dbias[0]: 0.0 5.960464477539063e-08
AssertionError: parameter-gradient permutation BI failed

The exact value for both printed components is 2**-24. Both orderings satisfy the shared FP32 gradient-accuracy tolerance (atol=rtol=1e-4). However, jointly permuting x and g changes the parameter gradients, which fails the shared requirement for bitwise gradient invariance.

fusheng-ji added a commit to fusheng-ji/RL-Kernel that referenced this pull request Oct 22, 2026
…l keys

Review finding (RL-Align#568): jointly permuting x and g changed dW/dbias because the token axis
was reduced in call order. All projection ops (audio in/out, text context, Q/K/V) now take an
optional logical_keys [T,2|3] (negative first column = padding); rows are reduced in ascending
key order with padding dropped, active keys must be unique. Key format follows RL-Align#562.
Adds the reviewer's repro plus permutation/padding tests for every row and Q/K/V.

Co-authored-by: Yitao Wang <75578057+walker-ai@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
@fusheng-ji

Copy link
Copy Markdown
Contributor Author

I was planning to work on the audio projections, as I’m currently developing the video input/output projections. After seeing your PR, I briefly reviewed the implementation and noticed that dW and dbias change under row permutation. Could you check backward permutation BI for audio_input_projection_fp32?

I reproduced this with the unmodified operator at 74e5b83, on H20 with PyTorch 2.4.1+cu124 and Triton 3.0.0. Each ordering uses one backward call.

import torch
from rl_engine.backends.cuda.model_specific.minimax_h3.projection import H3AudioInputProjectionCudaOp

op = H3AudioInputProjectionCudaOp()
x = torch.ones(3, 32, device="cuda")
w = torch.zeros(5376, 32, device="cuda", requires_grad=True)
b = torch.zeros(5376, device="cuda", requires_grad=True)
g = torch.zeros(3, 5376, device="cuda")
g[:, 0] = torch.tensor([1.0, 2**-24, -1.0], device="cuda")

def grads(order):
    return torch.autograd.grad(op(x[order], w, b), (w, b), g[order])

dw1, db1 = grads([0, 1, 2])
dw2, db2 = grads([0, 2, 1])
print("dW[0,0]:", dw1[0, 0].item(), dw2[0, 0].item())
print("dbias[0]:", db1[0].item(), db2[0].item())
assert torch.equal(dw1, dw2) and torch.equal(db1, db2), "parameter-gradient permutation BI failed"

Output:

dW[0,0]: 0.0 5.960464477539063e-08
dbias[0]: 0.0 5.960464477539063e-08
AssertionError: parameter-gradient permutation BI failed

The exact value for both printed components is 2**-24. Both orderings satisfy the shared FP32 gradient-accuracy tolerance (atol=rtol=1e-4). However, jointly permuting x and g changes the parameter gradients, which fails the shared requirement for bitwise gradient invariance.

Hi @walker-ai, first of all, I'm sorry for taking the audio projection rows while you were already working on them. I should have checked with you before claiming and opening the PR.

Thank you for the detailed review and the reproducer. You were right: dW and dbias were reduced in call order, so jointly permuting x and g changed them. I've followed your suggestion and the canonical-row design from #562 in 0da0583 and d2941f0:

all projection ops (audio in/out, text context, Q/K/V) take an optional logical_keys ([T, 2|3] int64, negative first column = padding); rows are reduced in ascending key order with padding dropped, and active keys must be unique;
your reproducer is now a test (permutations of the 3 rows give identical dW/dbias), plus permutation and padding invariance tests for every row and Q/K/V;
the block graph passes (batch row, position) keys for the Q/K/V node, and rows that already arrive in canonical order skip the gather.
I've added you as co-author on both commits. If you're willing, I'd be glad to keep working on this PR together: your key validation across multiple uses of the same parameter (as in #562) is stricter than mine, and I'd like to align on it, and on how the video projections (#562) and these rows should share the key format. Please tell me how you'd like to split the remaining work.

fusheng-ji and others added 7 commits October 22, 2026 22:54
…fixed-order Triton GEMM

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
(cherry picked from commit 650e2148c6f0e5dcdef136537e0e62f745020273)
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
(cherry picked from commit d099c0b)
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
(cherry picked from commit b41f868)
…il-closed test instead of a skip

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
(cherry picked from commit 74e5b83)
…l keys

Review finding (RL-Align#568): jointly permuting x and g changed dW/dbias because the token axis
was reduced in call order. All projection ops (audio in/out, text context, Q/K/V) now take an
optional logical_keys [T,2|3] (negative first column = padding); rows are reduced in ascending
key order with padding dropped, active keys must be unique. Key format follows RL-Align#562.
Adds the reviewer's repro plus permutation/padding tests for every row and Q/K/V.

Co-authored-by: Yitao Wang <75578057+walker-ai@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
(cherry picked from commit d2941f0)
…ready ordered rows

Follows the canonical-row design of RL-Align#562: the Q/K/V node of the block graph supplies
(batch row, position) keys so its dW is reduced in logical order, and rows that already arrive in
canonical order are reduced in place (same bytes, no gather of x and g).

Co-authored-by: Yitao Wang <75578057+walker-ai@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
(cherry picked from commit 0da0583)
…block 0 node-by-node and 50-block/generation gates

Squashes the one-block branch (its row-by-row history: timestep sinusoid, RMSNorm, AdaLN rows,
sequence/tensor parallel norm rows, final AdaLN, one block, 50-block qualification) onto the current
test-h3 so the pull request carries a reviewable, signed history. RFC RL-Align#420.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…pull request

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
@fusheng-ji
fusheng-ji force-pushed the feat/h3-projection-rows branch from 0da0583 to 3cf51bf Compare October 22, 2026 21:04
fusheng-ji added a commit to fusheng-ji/RL-Kernel that referenced this pull request Oct 23, 2026
…gn#568

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
fusheng-ji added a commit to fusheng-ji/RL-Kernel that referenced this pull request Oct 23, 2026
…#567 RL-Align#568 RL-Align#570 RL-Align#571 RL-Align#574 RL-Align#575

Test summaries (tests.json), runner outputs and figures measured on NVIDIA B200 (sm100),
driver 580, CUDA 13.0, torch 2.13.0+cu130, MiniMax-H3@42ed227, clean trees at the PR heads
eab901b, 3cf51bf, 6de320d, eb7840b, af3dcae and 4f7c5d1. Figures use B200 axis labels.
No host names, paths or scheduler identifiers.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
@walker-ai

Copy link
Copy Markdown

I was planning to work on the audio projections, as I’m currently developing the video input/output projections. After seeing your PR, I briefly reviewed the implementation and noticed that dW and dbias change under row permutation. Could you check backward permutation BI for audio_input_projection_fp32?
I reproduced this with the unmodified operator at 74e5b83, on H20 with PyTorch 2.4.1+cu124 and Triton 3.0.0. Each ordering uses one backward call.

import torch
from rl_engine.backends.cuda.model_specific.minimax_h3.projection import H3AudioInputProjectionCudaOp

op = H3AudioInputProjectionCudaOp()
x = torch.ones(3, 32, device="cuda")
w = torch.zeros(5376, 32, device="cuda", requires_grad=True)
b = torch.zeros(5376, device="cuda", requires_grad=True)
g = torch.zeros(3, 5376, device="cuda")
g[:, 0] = torch.tensor([1.0, 2**-24, -1.0], device="cuda")

def grads(order):
    return torch.autograd.grad(op(x[order], w, b), (w, b), g[order])

dw1, db1 = grads([0, 1, 2])
dw2, db2 = grads([0, 2, 1])
print("dW[0,0]:", dw1[0, 0].item(), dw2[0, 0].item())
print("dbias[0]:", db1[0].item(), db2[0].item())
assert torch.equal(dw1, dw2) and torch.equal(db1, db2), "parameter-gradient permutation BI failed"

Output:

dW[0,0]: 0.0 5.960464477539063e-08
dbias[0]: 0.0 5.960464477539063e-08
AssertionError: parameter-gradient permutation BI failed

The exact value for both printed components is 2**-24. Both orderings satisfy the shared FP32 gradient-accuracy tolerance (atol=rtol=1e-4). However, jointly permuting x and g changes the parameter gradients, which fails the shared requirement for bitwise gradient invariance.

Hi @walker-ai, first of all, I'm sorry for taking the audio projection rows while you were already working on them. I should have checked with you before claiming and opening the PR.

Thank you for the detailed review and the reproducer. You were right: dW and dbias were reduced in call order, so jointly permuting x and g changed them. I've followed your suggestion and the canonical-row design from #562 in 0da0583 and d2941f0:

all projection ops (audio in/out, text context, Q/K/V) take an optional logical_keys ([T, 2|3] int64, negative first column = padding); rows are reduced in ascending key order with padding dropped, and active keys must be unique; your reproducer is now a test (permutations of the 3 rows give identical dW/dbias), plus permutation and padding invariance tests for every row and Q/K/V; the block graph passes (batch row, position) keys for the Q/K/V node, and rows that already arrive in canonical order skip the gather. I've added you as co-author on both commits. If you're willing, I'd be glad to keep working on this PR together: your key validation across multiple uses of the same parameter (as in #562) is stricter than mine, and I'd like to align on it, and on how the video projections (#562) and these rows should share the key format. Please tell me how you'd like to split the remaining work.

Thanks for the fix and the co-author credit! I'd be happy to help.

As a first step, I can adapt our BI checks to your updated FP32 audio projections, focusing on whole-batch versus microbatch execution, row ordering, and padding.

Once we have the results, we can agree on any implementation changes and how to split them. The existing canonical backward session used by #562 could be a reuse option if cross-call accumulation needs changes.

@Flink-ddd Flink-ddd added platform: cuda Specific optimizations or bugs in NVIDIA graphics cards (such as FlashInfer, TMA optimizations) new-model MiniMax-H3 WS1 labels Oct 23, 2026
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>

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

MiniMax-H3 new-model platform: cuda Specific optimizations or bugs in NVIDIA graphics cards (such as FlashInfer, TMA optimizations) WS1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants