Skip to content

[WS1][CUDA][MiniMax-H3] full-model qualification: thresholds, ensemble rule and reference manifest (RFC #420 stack 10b/10) - #587

Draft
fusheng-ji wants to merge 237 commits into
RL-Align:test-h3from
fusheng-ji:feat/h3-full-model-qualification
Draft

fusheng-ji wants to merge 237 commits into
RL-Align:test-h3from
fusheng-ji:feat/h3-full-model-qualification

Conversation

@fusheng-ji

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

Copy link
Copy Markdown
Contributor

RFC #420 stack 10b/10 (base test-h3, merge bottom-up): … → #494 → 10a (#586) → this PR (10b) → 10c (#588). This PR merges after 10a. This PR's own diff (8.8k lines, 6.9k of them three JSON data files).

Replaces #539 (closed, superseded by this stack). The split is described in 10a. This layer holds the full-model qualification; the generation gate is 10c.

Evidence. Measurement outputs (logs, JSON, figures) are not part of this diff. They are on the fork's pr_assets branch and every link is fixed to commit 967000e9. Runs on the heads of this stack: B200 pending/b200-split-539, H100 pending/h100-split-539 (each with tests.json, redaction.json and SHA256SUMS). Evidence of the earlier #539 runs: pr539/.

This stack

layer PR own diff content
10a #586 3.6k block 0 replay and diagnostics
10b #587 8.8k full-model qualification
10c #588 1.5k generation gate

What

RFC #420 closeout row ws1_full_h3_transformer, numerical part: the real 50 transformer blocks, both text-refiner layers and the input/conditioning/output components, forward and backward, against a frozen reference.

  • tools.validation.models.h3_full_model runs the full model. Each candidate block binds its own parameters to the block-0 graph of 10a, and the row/interim/reference labels are kept. Every parameter and input gradient is checked.
  • A reference pass (independent FP32 and BF16 provider runs) freezes per-tensor error bounds with a committed formula: 764 tensor bounds in examples/models/minimax_h3/full-model-thresholds.json, a provider-ensemble rule in full-model-ensemble-rule.json, and a 1,635-file SHA-256 reference inventory (reference-manifest.json).
  • The candidate gate (candidate.sbatch, h3_candidate_prepare.py, h3_full_compare.py) builds its own H3 extension, verifies every reference and checkpoint hash, then runs the block-0 golden tests (no skips), full-model forward/backward with every candidate node required to run (CUDA fallback rejected) and first drift ordered by real forward/backward execution. Only a passing numerical comparison lets the generation gate (10c) start.
  • h3_full_provider_spread.py and h3_full_ensemble_rule.py produce the provider spread and the acceptance rule; --provider-variant and --provider-nodes on the full-model runner swap in provider configurations or chosen provider nodes.
  • examples/models/minimax_h3/README.md documents the kit; reference.sbatch regenerates the reference. The scheduler scripts keep only site-neutral resource requests.

Prior art & reuse decision

No operator is written here. The reference is the diffusers pipeline itself (FP32 and BF16), run in four distinct provider configurations (default, flash attention, memory-efficient attention, cuBLASLt) so that the acceptance rule is calibrated on provider-to-provider spread, not on a single run of one configuration.

Results

B200

Gates of candidate.sbatch at c249a735 on one B200 (torch 2.9.1+cu130) against the regenerated reference, from a clean checkout. Evidence: README, acceptance.

Gate Result
Block-0 golden tests 9 passed
Full-model forward/backward (50 blocks, both refiners, boundaries), 764 tensors, provider-ensemble rule passed, 0 failing; worst tensor at 0.55 of its bound (block 39 adaln_proj.linear.weight)
Final outputs vs the FP32 reference, relative_l2 video 0.0063 (0.35 of its bound), audio 0.0218 (0.39)
The original single-configuration bounds (kept as a record) 54 of 764 fail: 52 on max_abs only, 2 also on relative_l2

H100 (Hopper)

One packed FL2VA sample, forward plus complete backward, 638 parameter gradients, on 4 × NVIDIA H100 (sm90), torch 2.13.0+cu130; files under pr539/h100-full-model/ on pr_assets (commit 23cc7e28). All references and bounds were re-measured on H100: FP32 reference plus four BF16 providers.

Check Result
Block-0 golden gates 9 passed, 0 skipped
Ensemble rule calibrated on H100 k = 4.0 (the B200 rule uses 2.6); leave-one-out worst ratios 0.99 / 0.99 / 2.77 / 3.94
Candidate vs ensemble rule passes, 0 failures, worst ratio 0.577
Candidate vs the single default-provider bound (1.5x provider error + floor) fails; 347 tensors exceed it, first drift at nodes.block.33
Output error vs FP32 video 0.0063, audio 0.0198 (default provider 0.0064 / 0.0214)

The single-provider bound is not the acceptance criterion, for these reasons:

  • Outputs and input gradients match or beat the default provider. Only the parameter gradients of the attention weights in the 50 main blocks are higher (about 1.65x; relative L2 around 0.5 vs 0.3).
  • The baseline is set in the backward of the last block: the gradient error is 0.021 for both candidate and provider at block 49, and jumps to 0.39 (provider) / 0.78 (candidate) at block 48, then keeps that ratio in every earlier block.
  • Block 49's attention is ill-conditioned (maximum logit about 3189, mean softmax entropy 0.57; other blocks 14 to 536 and 1.0 to 1.5). A bf16-sized perturbation of its inputs moves dk by 0.16 (median) to 0.65 (95th percentile); other blocks move by 0.003 to 0.005.
  • With identical inputs the candidate attention is more accurate than SDPA at block 49 (out/dq/dk/dv error 0.0014/0.0010/0.0016/0.0016 vs flash 0.0015/0.0092/0.0043/0.0018). The other BF16 providers spread widely at the same place (block-48 gradient error: cuBLASLt 0.39, efficient 0.45, flash 0.68); the candidate is at the high end of that spread, inside the ensemble bound.
  • Keeping q/k in fp32 only inside block 49 was evaluated and is not expected to help (the error in q/k is inherited from earlier blocks), so no kernel or numerical-contract change was made.

gradient error per block

Status

  • Numerical acceptance of the full model: passed (B200 gates at c249a735; H100 re-measured as above).
  • How the acceptance rule came about.
    • The bounds frozen from the reference run (1.5 × one provider pass + floor) failed 54 of 764 tensors for the candidate. The same bounds also fail diffusers runs that differ only in attention backend or BLAS library: flash attention 36 tensors on max_abs (1 on relative_l2), cuBLASLt 38 (3), memory-efficient attention 66 (9), while the default configuration they were calibrated on fails none.
    • A diagnostic that ran the full model with chosen block nodes replaced by their diffusers replay (attention, the six GEMMs, SwiGLU, Q/K norm, the two norms, the residuals, the AdaLN projection, all interim operators, all nodes) found no single group responsible.
    • The acceptance rule is therefore calibrated on four distinct provider configurations only, with no candidate data: relative_l2 ≤ k × the largest provider error + floor, with k = 2.6 the smallest factor at which every provider configuration, held out in turn, passes against the other three. max_abs, which is set by one element, is reported but not gated.
    • The rule was designed after the candidate had failed the earlier bounds. That is why the comparison against the old bounds is kept in the evidence next to the new decision.
  • The reference was regenerated. The first reference run's artifacts (about 200 GB) were deleted to save disk space, so they were regenerated with the original commands at the same source commit (2681cadb). 1,614 of the 1,635 pinned files are byte-identical to the first run's; the others are the eight report.json files, the thresholds file that embeds their hashes and twelve video.mp4 / audio.wav containers, which also differ between two runs of one job.
  • The 764 bounds and the ensemble rule are data files read by the gate (candidate.sbatch, h3_full_compare.py), so they stay in this diff (6.9k of its 8.8k lines).
  • The generation quality gate is not passed; see 10c.

Tests

Layer head 5ecb92de; same commands as 10a.

RL_KERNEL_H3_STRICT=1 python setup.py build_ext --inplace
python -m pytest tests/models/minimax_h3 tests/backends/cuda/test_h3_full_attention.py -q
Check B200 H100
tests/models/minimax_h3 + test_h3_full_attention.py (2 GPUs) 549 passed, 4 skipped (sglang and matplotlib on the path) 551 passed, 2 skipped
full tests/ (minus tests/distributed and a ROCm import-error file) 73 failed, 3519 passed; the same 73 fail on upstream/test-h3 (measured on the heads before the re-cut; same layer code)
pre-commit, mypy clean

Skips: as in 10a (the 4- and 8-GPU NCCL cases on B200; the 8-GPU cases on H100). The 73 failures are on test-h3 as well; none is introduced by this layer.

tests/models/minimax_h3/test_h3_full_model.py is the unit test of the full-model runner added in this layer.

Scope / Known limitations

  • Boundary components (input projections, conditioning, output heads) stay provider/reference.
  • Acceptance rules. The full-model numerical gate is relative_l2 only (max_abs is reported); it rests on measured provider spread and was designed after the candidate had failed the earlier bounds.
  • Reference artifacts. The first reference run's artifacts were deleted and regenerated; the pinned inventory is re-pinned to the regenerated files, 21 of 1,635 of which differ from the first run's (metadata and media containers).
  • CUDA only; not run on AMD hardware.

…+ shared H3 harness

RFC RL-Align#420 step 3, row `timestep_sinusoid_h3`.

- CUDA kernel: one thread per (t, k), the provider's exact FP32 op
  sequence (__fmul_rn, precise expf/sinf/cosf); bitwise equal to
  diffusers' get_timestep_embedding on CUDA. Analytic row-local backward.
  Non-1-D, empty, integer, non-finite or out-of-[0, 1] timesteps fail
  closed (RFC probe H10).
- PyTorch reference (provider replay + FP64 golden), registry, gtest.
- Shared H3 harness used by the following rows:
  * h3_manifest.json + prepare_h3_weights.py: pinned revision and sha256,
    only shard 1 of 14 is downloaded;
  * h3_provider.py: diffusers replayed op for op;
  * h3_chain.py: stage-wise chain replay, each stage declaring its
    promises (provider-bitwise, golden tolerance); used by
    scripts/h3_chain_replay.py and tests/h3/test_h3_conditioning_e2e.py;
  * h3_report.py + scripts/h3_evidence.py + scripts/plot_h3_evidence.py:
    per-op performance/accuracy JSON and figure;
  * benchmarks/benchmark_h3_conditioning.py.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Produced by scripts/h3_evidence.py at 0522865 on a clean tree; rendered with scripts/plot_h3_evidence.py and embedded in the operator doc.

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

RFC RL-Align#420 step 3, row `timestep_mlp_fp32` (stacked on timestep_sinusoid_h3).

- New CUDA module csrc/cuda/h3/det_linear.cu, contract h3-det-linear-v1:
  warp per output-column group, lane-strided 16-byte K chunks, ascending
  fmaf chains, xor butterfly, bias then fused SiLU. The launch shape never
  changes a column's summation order, so rows are batch/position
  invariant and runs are repeat-bitwise.
- Backward without cuBLAS or atomics: dinput via fixed 64-row N chunks
  folded in order (row-local), dweight/dbias as ascending-row FP32 folds.
- PyTorch reference (provider path + FP64 golden); BF16 anywhere is
  rejected (RFC probe H7: the time_embedder is a declared FP32 module).
- On the pinned weights both CUDA and cuBLAS are ~1e-6 from the FP64
  golden (100x inside the contract); the op is 2x faster for T >= 2.
- Chain stage (covered by the end-to-end test), report/figure hooks,
  registry, gtest, doc.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Produced by scripts/h3_evidence.py at 65ef7f6 on a clean tree; rendered with scripts/plot_h3_evidence.py and embedded in the operator doc.

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

RFC RL-Align#420 step 3, row `adaln_projection_3mod` (stacked on timestep_mlp_fp32).

- SiLU at temb's FP32 precision and exactly one cast to the projection
  dtype (bitwise equal to the provider activation); a BF16 temb is
  rejected (RFC probe H7).
- 2688 -> 96768 projection on a BF16 tensor-core path in det_linear.cu,
  contract h3-det-linear-bf16-mma-v1: warp = 16 output columns x 8 input
  rows (zero-padded), K in ascending 16-wide groups, each mma.sync
  m16n8k16 from a zero accumulator added into an FP32 running sum. Same
  instruction sequence for every T <= 8, so rows are bitwise
  batch/order/tile invariant; 99.98% of outputs correctly rounded vs
  99.84% for cuBLAS; kernel time flat at ~78 us for T = 1..8. SM80+.
- Six (3T, 5376) views in diffusers' view(-1, 6H).chunk(6) layout, row
  t*3+m; a channel-tagging test pins chunk/modality placement (probe H4).
- Backward: FP32 d_temb through the identity-VJP cast, ascending-row
  dW/db folds, no cuBLAS or atomics. The FP64 golden applies the cast
  straight-through so its own gradient is not rounded to BF16.
- Chain stage (end-to-end test), report/figure hooks, registry, gtest, doc.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Produced by scripts/h3_evidence.py at d06e120 on a clean tree; rendered with scripts/plot_h3_evidence.py and embedded in the operator doc.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
RFC RL-Align#420 step 3, row `adaln_row_gather` (stacked on adaln_projection_3mod).

- Forward: one launch gathers all six (S, H) modulation tensors by
  timestep_index * 3 + token_tag with 16-byte copies; bitwise equal to
  diffusers' six index_select calls. Out-of-range tags or timesteps fail
  closed (RFC probes H2/H3), never clamped.
- Backward: deterministic FP32 segmented sum (stable sort by row, fixed
  256-position tiles, ordered fold, one cast; no atomics) instead of
  index_select's atomic BF16 scatter-add. The PyTorch reference gets an
  equally deterministic backward.
- H3AdaLNModulationCudaOp: projection + gather as one autograd node,
  bitwise-equal forward, FP32 table gradient into the projection backward.
- Chain replay gains the gather stage and a backward mode (separate,
  fused and diffusers chains vs an FP64 golden); the end-to-end test now
  covers the whole chain forward and backward.
- Report/figure hooks, registry, gtest (reduction class: the VJP is a
  segmented sum), doc.

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

Produced by scripts/h3_evidence.py and scripts/h3_chain_replay.py --backward at fa551c2 on a clean tree; rendered with scripts/plot_h3_evidence.py and embedded in the operator doc.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
RFC RL-Align#420 step 4, row `h3_rmsnorm` (stacked on adaln_row_gather).

- CUDA kernel replaying PyTorch's own RMSNorm reduction order
  (vectorized_layer_norm_kernel: (32, 4) block per row, 4-element
  vectors, shuffle-down and cross-warp trees, rsqrtf, w * (rstd * x)):
  bitwise equal to nn.RMSNorm on all four pinned H3 norm weights and
  batch/position invariant.
- Fused AdaLN modulation n * (1 + scale[i]) + shift[i] with the rows
  gathered in-kernel from strided table views and each step rounded where
  the eager expression rounds: bitwise equal to diffusers for the block
  (adaln_indices) and norm_out (timestep_indices), without materialising
  the (S, H) gathered tensors; 2.5x faster forward at S = 131072.
- Deterministic backward: row-local dx, dweight over fixed 256-row tiles,
  dshift/dscale as sorted segment sums (diffusers: non-deterministic BF16
  atomics, ~15x larger error).
- Manifest pins the block/refiner-final/norm_out norm weights and
  norm_out.linear (all in shard 1); chain gains a per-mode history and the
  block norm1 + modulation stage (covered by the end-to-end test).
- Registry, gtest, report/figure hooks, doc.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Produced by scripts/h3_evidence.py at f36aa60 on a clean tree; rendered with scripts/plot_h3_evidence.py and embedded in the operator doc.

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

RFC RL-Align#420 step 6, row `adaln_gate_residual` (stacked on h3_rmsnorm).

- residual + gate[adaln_index] * sublayer_output in one pass, the gate row
  read from the AdaLN table view in the kernel; the two roundings sit where
  the eager expression rounds (__fmul_rn/__fadd_rn, no FMA contraction), so
  the forward is bitwise equal to diffusers in bf16/fp16/fp32. 1.7x faster
  forward at S = 131072.
- Backward: d_residual = grad; d_sublayer bitwise equal to the eager VJP
  (exact product, one rounding); d_gate a deterministic FP32 segment sum
  (diffusers: non-deterministic BF16 atomics, 13-49x larger error, varying
  run to run). The PyTorch reference gets an equally deterministic gate
  gradient.
- Chain stage residual + gate_msa * (stand-in attention output), covered
  by the end-to-end test; registry, gtest, report/figure hooks, doc.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Produced by scripts/h3_evidence.py at a26b41a on a clean tree; rendered with scripts/plot_h3_evidence.py and embedded in the operator doc.

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

RFC RL-Align#420 step 7, row `final_adaln_out` (stacked on adaln_gate_residual).

- norm_out as one autograd node: FP32 SiLU -> one BF16 cast -> the
  deterministic tensor-core GEMV on norm_out.linear (shift first), then
  the h3_rmsnorm kernel indexed by timestep_indices. Forward equal to
  diffusers on 99.96% of elements (1-ULP projection ties); rows bitwise
  batch/position invariant; 2.4x faster forward at S = 131072.
- Backward keeps the table gradient in FP32 into the projection backward:
  d_temb/dW/db 13-57x closer to FP64 than diffusers (whose error varies
  run to run), all gradients repeat-bitwise.
- Golden rounds only at the declared boundaries (SiLU cast, BF16 table),
  straight-through for the gradient; gtest inputs at realistic scale.
- Chain stage: the gated residual stream -> norm_out with the MLP stage's
  temb (end-to-end test now runs timestep -> ... -> norm_out); registry,
  report/figure hooks, doc.

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

Produced by scripts/h3_evidence.py and scripts/h3_chain_replay.py --backward at 38d575f on a clean tree; rendered with scripts/plot_h3_evidence.py and embedded in the operator doc.

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

# Conflicts:
#	csrc/ops.cpp
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>

# Conflicts:
#	scripts/plot_h3_evidence.py
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Validate pinned tensor contents at preparation and load time. Execute chain prerequisites for selected stages and require checkpoint weights for weighted evidence. Reject invalid CUDA tile and backward inputs, align the CUDA wrapper guards, skip unsupported devices, and rotate comparative timing with raw samples.

Validation: 143 H3 tests passed on B200 after a fresh CUDA build; illegal FP32 tiles fail compilation; isolated AdaLN replay and pinned-weight evidence generation passed; Black, isort, Ruff, and Flake8 passed.
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Check timestep and modality bounds on the device before computing gather addresses, including unchecked calls and overflowing int64 indices. Reject non-prefix replay stage selections and partial backward chains before loading weights.

Validation: rebuilt the CUDA extension on B200; tests/h3: 175 passed, including 12 isolated native bounds probes and 16 CLI regressions.
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
CodeRabbit skips automatic reviews targeting non-default branches by default. Allow the test-h3 base branch so pushes to the H3 stack receive incremental reviews.

Validation: configuration validated against the official CodeRabbit JSON schema.
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Include the RFC 420 target branch in CodeRabbit automatic reviews so pushes to the H3 stack are reviewed instead of silently skipped.

Validation: configuration parses and validates against the published CodeRabbit v2 schema; the additional branch pattern matches only test-h3.
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Brings in the seeded timing and invariance inputs of the evidence report.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Brings in the seeded timing and invariance inputs of the evidence report.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
The raw results (JSON, figures, audit files) now live on the fork's pr_assets branch;
the documents link to them at a fixed commit.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
The history of this branch was rewritten on 2026-10-23 (evidence directories removed from every
commit, sign-offs added). Commit hashes quoted in the reports and documents now name the
rewritten commits; the evidence archives still record the pre-rewrite hashes.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
… and diagnostics (RFC RL-Align#420, RL-Align#539 split 1/3)

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…e rule and reference manifest (RFC RL-Align#420, RL-Align#539 split 2/3)

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

coderabbitai Bot commented Oct 25, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

This was referenced Oct 25, 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>
…fication

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant