Skip to content

[WS1][CUDA][Qwen3-Next] TP distributed logprob parity: fixed reduction order, selected-token logprob exact across NCCL settings (RFC #428 stack 9/11) - #549

Open
fusheng-ji wants to merge 117 commits into
RL-Align:test-qwennextfrom
fusheng-ji:feat/ws1-qwen3-next-distributed-logprob-tp
Open

fusheng-ji wants to merge 117 commits into
RL-Align:test-qwennextfrom
fusheng-ji:feat/ws1-qwen3-next-distributed-logprob-tp

Conversation

@fusheng-ji

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

Copy link
Copy Markdown
Contributor

Stack: #466 (merged) → #467 → #468 → #469 → #503 → #507 → #543 → #544 → this PR → #557 → benchmarks (#551), base test-qwennext; merges after #544. This PR's own diff. RFC #428 row distributed_logprob_parity, CUDA, TP part (EP = 1); expert parallelism is the next PR (#557).

What

  • Contract (docs/operators/qwen3-next-distributed-logprob.md): every TP4 point where ranks combine values -- embedding, row-parallel GDN/attention/MoE outputs, column-parallel input gradients, the vocabulary-parallel selected logprob / LSE / entropy, sampling logits -- with its operation, order, dtype and transport (NCCL all-gather for bytes only; RL-Kernel's fixed-order collective cuda_ipc_fixed_tree for sums).
  • Parity runner (tools/validation/models/qwen3_next/qwen3_next_logprob_parity.py, 4 GPUs, real weights): SHA-256 of every point's output and gradient per rank, a TP1 reference, and a raw-NCCL negative control; qwen3_next_logprob_parity_compare.py checks one digest per rank across NCCL settings.
  • CI guard (tests/models/qwen3_next/test_qwen3_next_tp_reductions.py): no raw torch.distributed reduction in Qwen3-Next model, engine or VIME modules; the TP reduction must use the fixed-order collective.
  • Evidence publisher with the chain publisher's redaction rules.
  • Layout as reviewed on this PR: validation code and JSON under rl_engine/validation/models/qwen3_next/, scripts under tools/validation/models/qwen3_next/, comparison benchmarks and plots in the benchmark PR ([WS1][CUDA][Qwen3-Next] Comparison benchmarks and plots for the Qwen3-Next stack: MoE, attention, logprob, EP (RFC #428 stack 11/11) #551), raw JSON as release ZIPs with summaries in reports/experiments/qwen3_next/. The earlier PRs of the stack now follow the same layout themselves.

Prior art & reuse decision

Same official-LM-head logits for every measured candidate (TP4 shards are slices of one full-vocabulary product), 1024 tokens:

Library Implementation TP4 == TP1 Invariant over 13 NCCL settings Batch invariant Max abs vs FP64 TP4 latency Verdict
RL-Kernel WS2 shared_logprob (both engines here) yes yes yes 7.8e-7 2.55 ms
vLLM 0.30.0 sampler: all-gather + log_softmax yes yes yes 8.1e-7 1.51 ms exact, but rollout only
SGLang 0.5.21 logits processor: all-gather + log_softmax (srt/layers/logits_processor.py) — — — — — same structure as vLLM (source); rollout only
Megatron-core 0.16 vocab_parallel_cross_entropy no no (2 results) yes 8.1e-7 0.57 ms NCCL all-reduce of max and sum-exp
Vime 0.3.2 default calculate_log_probs_and_entropy no no (2 results) yes 8.1e-7 0.56 ms as Megatron
Miles (radixark/miles@23d3d38) fused_log_probs_and_entropy, imported unmodified no no (2 results) yes 8.1e-7 0.33 ms NCCL all-reduce of max and sums; repeat-stable at a fixed setting; backward store-only, reproducible and batch invariant
verl (volcengine/verl@e5a627c) linear_cross_entropy, imported unmodified (fuses the LM-head GEMM; own logits, FP64 reference of its own product) no no (2 results) yes 1.2e-5 0.63 ms NCCL all-reduce; forward repeat-stable (its forward atomic_add runs only for reduction sum/mean); backward d_hidden/d_weight use tl.atomic_add but are repeat-stable (20/20), not batch invariant (probe rows 1/10 cells)
CUTLASS / FlashInfer / FlashAttention — N/A: no vocabulary-parallel logprob

The measured implementations differ from each other by up to 9.5e-7 (Megatron and VIME agree with each other). vLLM's path is exact for rollout, but the training side has no counterpart that reproduces it; Megatron/VIME, Miles and verl (all measured) reduce max and sum-exp with NCCL all-reduce, so they move with NCCL settings. Decision: both engines keep RL-Kernel's WS2 provider -- the only measured way to equal bits on both sides. Its cost is latency (4.5× Megatron at this shape); a faster tile-stats kernel can keep the same merge order and is follow-up work.

Results (4 × B200, official checkpoint)

Qwen3-Next distributed logprob parity

Top: digest of every TP4 point under each NCCL setting vs NCCL defaults; the last row is the raw-NCCL negative control. Bottom: prior-art invariance, largest pairwise difference, latency.

Check Result
17 TP4 points × 4 ranks × 13 NCCL settings (defaults; Ring, all-reduce Tree × LL / LL128 / Simple × 1 / default channels) one digest per point and rank
TP4 vs TP1 on the same logits selected logprob and LSE bitwise equal; entropy differs (documented)
negative control: real o_proj partials, NCCL all_reduce 5 different results
full-vocabulary GEMM vs gathered TP4 logits bitwise equal
end to end (#543): vLLM rollout vs VIME recompute selected logprobs 16 cases × 4 weight versions bitwise

NCCL 2.29 has no Tree all-gather, so the Tree settings restrict all-reduce only (NCCL_ALGO=allreduce:tree). Summary, tested commits and commands: reports/experiments/qwen3_next/qwen3-next-distributed-logprob-b200.md; raw reports ZIP (SHA-256 a48bf29d…2cd9), parity measured at 04db726, prior art at 33495ad. Post-review GPU rerun at b10b6616 (ZIP): all 233 mandatory operator cases and four norm gates passed; all 17 points invariant across the 13 settings; selected logprob / LSE equal TP1; the negative control again gave five digests per rank.

Status

History

Tests

python -m pytest -q tests/models/qwen3_next/test_qwen3_next_tp_reductions.py
NCCL_ALGO=<algo> NCCL_PROTO=<proto> [NCCL_MAX_NCHANNELS=1] torchrun --nproc-per-node 4 \
    tools/validation/models/qwen3_next/qwen3_next_logprob_parity.py --checkpoint <ckpt> --output <dir>/<setting>
python tools/validation/models/qwen3_next/qwen3_next_logprob_parity_compare.py --runs <dir>/* --out report.json
Check Result
Qwen3-Next CPU list in ci.yml + digest tests 439 passed (99 CUDA-only skips)
CI guard: a planted raw all_reduce / reduce_scatter_tensor is reported passed
evidence redaction test, pre-commit on all files, layout check clean
remote CI (lint, unit tests, docs, DCO) passed at the current head (0b88a839)

Scope

CUDA, TP4 × EP1 × CP1, one node of four B200. Expert parallelism is #557; ROCm is a separate claim.

Results (H100 / Hopper, 4 GPUs, official checkpoint)

Run at this PR's head 8e88cd82. 4 × NVIDIA H100 80GB, torch 2.13.0+cu130, vLLM 0.30.0, NCCL 2.29.7, checkpoint revision 9c7f2fbe. Raw data: hopper-qwen3-next/pr549.

Check Result
tests/models/qwen3_next/test_*.py + check_*.py (4 GPUs) 787 passed
adapter, TP4 attention, TP4 GDN, TP4 head, TP4 MoE gates all passed
TP4 logprob parity over 13 NCCL settings (default; Ring, all-reduce Tree × LL / LL128 / Simple × 1 / default channels), 1024 tokens passed
  • All 18 recorded TP4 points × 4 ranks give one digest per point and rank across the 13 settings (deterministic_points_invariant: true).
  • TP4 equals TP1 for the selected logprob and LSE bitwise (tp4_equals_tp1: true); entropy differs between TP4 and TP1 as documented (entropy_tp4_equals_tp1: false).
  • The control behaves as on B200: the NCCL all_reduce of the o_proj partials gives 8 distinct results over the 13 settings, the fixed-order collective gives 1.
  • The full logits match the gathered logits.

Logprob prior-art on H100 (1024 tokens, TP4 vs TP1, 10 repeats; runner at 77d59a59):

Candidate TP4 == TP1 batch bitwise
rl_kernel_ws2 yes yes
vllm_gather yes yes
megatron_ce no yes
vime_default no yes
miles_fused, verl_fused not available in env –

Report: pr549_logprob/report.json (also parity/report.json for the gate parity).

…rouped expert GEMM

RFC RL-Align#428 moe_route_combine_contract (CUDA).

- Routing: FP32 router through the pinned vLLM batch-invariant GEMM, a
  fixed-order softmax, stable descending argsort (ties go to the lower
  expert id) and FP32 renormalisation of the ten selected weights.
- Experts: per-route gate_up and down projections through vLLM 0.30.0's
  fused_moe Triton kernel with the batch-invariant tile passed explicitly
  (BLOCK 64/64/32, SPLIT_K=1); no routed-weight multiply and no sum inside
  the kernel, so every (token, route) row has one writer. Bitwise equal to
  the previous per-expert GEMM loop at TP4 and TP1 widths; fails closed if
  VLLM_TRITON_USE_TD is enabled.
- Combine: ten ordered FP32 multiply-adds and one BF16 cast; no atomics.
- Backward: per-expert pinned GEMMs into one dense gradient buffer; dx is
  summed in ascending expert order.
- TP4 block: HF load/export for all 512 experts, replicated router and
  shared-expert gate, Megatron-style copy/reduce autograd boundaries moved
  into qwen3_next_tp.py.
- Prior-art runner and plot comparing HF transformers, vLLM fused_moe
  (BI=0/1) and FlashInfer cutlass_fused_moe; TP4 real-weight gate script.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Prep for RFC RL-Align#428 full_model_chain; serves the gdn_state_bridge row.

- GDN: packed recurrent prefill/decode through the pinned vLLM 0.30.0
  provider with an explicit state boundary (qwen3_next_gdn.py), the
  depthwise causal convolution with cache handoff (qwen3_next_conv.py),
  their combined shared core (qwen3_next_provider.py) and the TP4 block
  with HF GQA-interleaved weight mapping (qwen3_next_tp_gdn.py), now on the
  shared TP boundaries in qwen3_next_tp.py.
- Attention: the deterministic CUDA attention kernel accepts head dim 256
  (ROCm stays 128) and validates backward inputs; shared_attention and
  TP4FullAttention with the KV head replicated on each rank pair, partial
  RoPE at absolute positions and explicit per-slot KV state.
- Real-weight TP4 gates for GDN (layer 0) and attention (layer 3), sharing
  scripts/qwen3_next_gate_common.py with the MoE gate.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…nce (B200, 2e950f3)

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Compares RL-Kernel's deterministic attention with torch SDPA, vLLM FlashAttention-2 (num_splits auto and 1), vLLM's Triton unified attention and FlashInfer's ragged prefill: batch, chunked-prefill and decode invariance, backward repeatability, accuracy against FP64 and latency.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…nce (B200, 9e6c4f2/6517077)

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
RFC RL-Align#428 full_model_chain (CUDA).

- Engine adapters: the native vLLM rollout (exact-prefill scheduler, paged
  KV, traced response rows), the VIME/Megatron replay and training actor,
  engine weight import/export and the logprob adapter, all on the shared
  TP4 providers.
- Stage runners (preflight, layers, l2, train1, train2, train) with the
  validated acceptance contract (version 4) and scripts/qwen3_next_run.py,
  a launcher that works inside any four-GPU allocation: each stage gets a
  clean clone, an immutable run.json identified by a fresh execution_id,
  and refuses a predecessor that did not pass or pins another identity.
  Scheduler-independent helpers live in scripts/qwen3_next_runtime.py.
- Sub-node digests: both engines observe the normed mixer input, the mixer
  output, the normed MLP input, the selected expert ids and the MoE output
  of every layer, plus the embedding; the first divergence names the layer
  and sub-node.
- scripts/qwen3_next_chain_report.py writes a ChainGateReport-compatible
  report (schema qwen3-next-chain-v1, compatible with ws1-c10-c11-v5) with
  first_drift_family/layer/cell; scripts/qwen3_next_publish_evidence.py
  publishes a chain's records without site paths, hosts or users.
- Native rollout caps raised to 64 sequences, 16384 batched tokens and a
  4 GiB KV budget so the batch-invariance matrix runs under the same
  certified profile; the exact-prefill scheduler still fixes every chunk.
- The operator-case registry is regenerated from collection by
  scripts/qwen3_next_operator_cases.py.
- Packaging: transformers may be 5.13.1-5.17.0; a qwen3-next extra pins the
  audited runtime.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
RFC RL-Align#428 batch_invariance_ci (CUDA).

- Stage bi (after a passed l2, same certified profile): 97 pinned cells in
  rl_engine/testing/qwen3_next_bi_cases.py / qwen3_next_bi_manifest.json.
  concurrency (1-64 requests, target first or last), interference
  (companion content, lengths, arrival during prefill/decode, generation
  lengths), chunk boundaries (prompts 63-1024 around 64/128/528/1024, chunk
  64/128, first chunk 1/63/65, compared with the replay recompute) and
  replay THD packing (1-7 neighbours, target first or last). A quick CI
  subset runs one cell per family plus 64 concurrent requests.
- Native rollout: version 2 requests run one traced target next to
  untraced companions; the exact-prefill scheduler admits each request at
  its arrival step, pins the target's planned chunk at every step and
  fails on any preemption; the trace records only the target.
- Replay: packed_layout() packs variable-length neighbours with the traced
  target in one THD batch.
- bi-matrix.json names each failing cell's first divergent layer, sub-node
  and layer family; the chain report gains a batch_invariance section, and
  the evidence publisher takes the bi run with --also.
- gtest: bitwise judgments compare raw bits (signed zero, NaN/Inf and
  dtype now count), not allclose with zero tolerance.

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

Batch-invariance chunk cells use prompts such as 129 tokens; the state-shape check only admitted the four L2 lengths, which their own validators already pin.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Editable sources, base interpreters and build directories in package metadata now keep only their last path component; cgroup paths keep only their file name. The leftover check matches absolute paths only, so package paths such as vllm/.../cpu/data/ are not flagged, and any mention of the batch scheduler blocks publication.

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

- Megatron-core 0.16 MoELayer with Transformer Engine grouped GEMM and fused
  permute, configured as VIME runs Qwen3-Next (FP32 softmax router, top-10,
  all-to-all dispatcher), including its backward.
- SGLang 0.5.21 Triton fused_moe in default and deterministic-inference mode.
  sgl_kernel cannot load next to torch 2.13, so its two kernels on this path
  are replaced by SGLang's own Triton moe_sum_reduce and JIT
  moe_align_block_size; any other sgl_kernel call fails loudly.
- --only and --merge combine reports from different Python environments.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…ured (B200, c58816b)

Every library in the reuse table is now measured: Megatron-core 0.16 + TE 2.16 as VIME configures Qwen3-Next, and SGLang 0.5.21 in default and deterministic-inference mode. The TP4 gate evidence is unchanged (2e950f3, identical MoE code).

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

Add three engines to the attention prior-art runner:
- FlashAttention-4 (CuTe DSL) varlen as vendored by vLLM, forward and backward.
- Transformer Engine DotProductAttention on THD-packed sequences, in
  inference mode (cuDNN fused) and in training mode (on SM100 TE has no
  fused head-dim-256 backward and falls back to its unfused path).
- Megatron-core's local DotProductAttention, one launch per sequence.

The backward check now compares the target's dq/dk/dv alone against the
same sequence packed first and last, so engines with a packed backward are
checked for batch invariance rather than repeatability. A prefill/decode
crash is recorded per candidate without skipping its other checks.
--only and --merge combine reports from different Python environments.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
The decode and forward+backward latency sections are keyed by candidate
directly, the prefill section by size first; merge them at any depth.

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>
…p as E402-exempt

The gate scripts insert the repo root into sys.path before importing
rl_engine and tools, which flake8 reports as E402. Mark those imports
with noqa, as the other tools/ scripts do.

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

The gate scripts insert the repo root into sys.path before importing
rl_engine and tools, which flake8 reports as E402. Mark those imports
with noqa, as the other tools/ scripts do.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Annotate the latency samples and accept the default-argument lambdas used as
timed calls. No behaviour change.

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

Annotate the latency samples and accept the default-argument lambdas used as
timed calls. No behaviour change.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Merge the migrated TP4 mixers (and through them the refactored
test-qwennext) and move the full-model chain into the new layout:

- model-level TP4 pieces (embedding, engine weights, logprob, sub-node
  observations) into rl_engine/models/qwen3_next/;
- vLLM adapters and the native rollout into
  rl_engine/integrations/engines/rollout/vllm/;
- VIME providers into rl_engine/integrations/orchestrators/vime/providers/
  and the native VIME lifecycle (recompute, training, workers, profile)
  into rl_engine/integrations/orchestrators/vime/experiments/qwen3_next/;
- the provider identity into rl_engine/integrations/common/;
- chain report, L2/case fixtures and manifests into
  rl_engine/validation/models/;
- stage runners and the launcher into tools/validation/models/, CI scripts
  into ci/scripts/, tests into tests/models/qwen3_next/.

Imports point at canonical modules; the provider-source digest list names
the canonical RMSNorm backend instead of its compatibility alias. Recorded
contract identities (e.g. sample_counter_owner) are unchanged.

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

Merge the migrated full-model chain and move the batch-invariance work into
the new layout: the case generator and manifest into
rl_engine/validation/models/, the stage runner into tools/validation/models/,
its tests into tests/models/qwen3_next/. The raw-bit comparison for bitwise
judgments now lives in the canonical
rl_engine/validation/operators/forward_invariance.py (the old module is a
compatibility alias). The evidence README gives the commands in the current
layout and says where the runners were at the measured commit; no measured
file changed (SHA256SUMS verified).

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

The published chain evidence is a byte-exact copy covered by SHA256SUMS; a
pytest JUnit XML without a final newline would otherwise be rewritten by
end-of-file-fixer and break the checksum. Same treatment as reports/archive/.

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

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

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · ci/scripts/run_qwen3_next_chain_gate.sh (L10-L10): This gate relies on pytest's exit code alone.

Not changed. test_qwen3_next_chain_report.py has no skip or importorskip, runs on CPU tensors, and imports torch and the contracts at module level, so an import failure is a collection error (exit 2) that stops the script under set -e; the provider workflow needs its skip check because its CUDA and vLLM modules can skip, this module cannot.

@fusheng-ji

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · ci/scripts/run_qwen3_next_chain_gate.sh (L22-L22): The report gate only asserts passed, which build_report can return true for a structurally valid report with zero cells (empty matrices/case_ids yields first is None, vacuous all(...) over empty detections, passed=True).

Same point as 544-S1, 549-S7, 557-S3; not changed. Under set -euo pipefail, a missing key raises KeyError and the script exits non-zero before sys.exit(0), and build_report cannot produce an empty report (it raises on empty matrices, and every cell adds invariance entries).

@fusheng-ji

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · rl_engine/backends/cuda/norm/rmsnorm.py (L373-L376): No validation that weight.shape == (x.shape[-1],) (nor that x/weight/gate are CUDA and share a dtype).

Same point as 468-S4, 503-S9, 543-S5, 549-S1; not changed. RMSNormCuda.forward uses the same asserts, and the binding repeats every check with TORCH_CHECK (weight [H], device and dtype; gate device, shape and dtype), so under python -O a bad tensor still raises before any pointer is read.

@fusheng-ji

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · rl_engine/backends/cuda/norm/rmsnorm.py (L389-L391): The ungated parameter_vjp_contributions_fp32 accepts positional x, weight, grad_output, eps=... and applies the mask, while this gated version is keyword-only (*, x, weight, gate, grad_output, eps).

Not changed: the ungated hook is also keyword-only and takes no mask, and the only caller, the C4 harness, passes keywords.

@fusheng-ji

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · rl_engine/backends/cuda/norm/rmsnorm.py (L365-L368): _require_cuda_gated reuses the module-wide _RMSNORM_API_VERSION that guards the already-shipped ungated RMSNorm surface.

Not changed. The plain and gated bindings ship in the same .so with one rmsnorm_api_version attribute, so any bump covers both. The gated symbols exist only in builds of this code, _require_cuda_gated checks both symbols and the version, and a signature change would also raise a pybind TypeError.

@fusheng-ji

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · rl_engine/backends/cuda/norm/rmsnorm.py (L280-L285): The forward saves only x, weight, gate, rstd, but backward calls rstd.reshape(x.shape[:-1]) and dgate uses rstd.reshape(x.shape[:-1]).unsqueeze(-1).

Same point as 503-S7, 549-S2, 557-I4383087967; not changed. RMSNormGatedCuda.forward and the binding both require a 2-D x, so backward always sees a [T] rstd; the op flattens N-D input first, and the harness passes 2-D x. test_cuda_gated_parameter_vjp_contributions_match_the_fold covers N-D x for the hook.

@fusheng-ji

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · rl_engine/backends/cuda/norm/rmsnorm.py (L389-L390): parameter_vjp_contributions_fp32 returns unreduced per-row contributions (rows), not the folded dweight, unlike the plain RMSNormCudaOp.parameter_vjp_contributions_fp32 which does the same.

Same point as 469-S4, 507-S1, 544-S4; not changed. Both hooks return per-row contributions on purpose: the C4 harness keys them per logical token, checks the row count and folds them itself in fixed fp32 order. It always passes 2-D x; an N-D row count would raise, not pass. The plain hook is covered for (2, 3, 2048) and the gated one for N-D x.

@fusheng-ji

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · rl_engine/backends/cuda/norm/rmsnorm.py (L404-L408): _gate_activation_fp32 computes act(gate) with torch.nn.functional.silu/torch.sigmoid in fp32, while the CUDA kernel's gate_activation is 1/(1+expf(-z)) (and z*sigma for silu).

Same point as 507-S3, 507-S2; not changed. The hook and the backward share _gate_activation_fp32, so their dweight rows agree bit for bit (checked by the fold test); the kernel's activation only feeds y and dx, and it uses expf, not __expf.

@fusheng-ji

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · pyproject.toml (L21-L21): The base dependency was loosened to transformers>=5.13.1,<=5.17.0, while the new qwen3-next extra pins transformers==5.17.0.

Same point as 549-S3, 543-S3; not changed. pip merges the base and extra specifiers, so .[qwen3-next] installs exactly transformers 5.17.0, and the preflight enforces the audited runtime.

@fusheng-ji

Copy link
Copy Markdown
Contributor Author

OpenCodeReview · pyproject.toml (L37-L40): torch==2.13.0 / vllm==0.30.0 / triton==3.7.1 are hard-pinned, but the pre-existing vllm = ["vllm>=0.6.0"] extra still allows an unbounded vllm version.

Same point as 543-S4; not changed. >=0.6.0 and ==0.30.0 intersect at 0.30.0, so requesting both extras resolves to the audited version.

a203719 parametrised test_cuda_gated_parameter_vjp_contributions_match_the_fold
over shape and added four norm tests, but the registry still pinned the old
ids, so the preflight operator gate reported them as not found. Regenerated
with qwen3_next_operator_cases.py --write.

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

create_actor(training=False) built the full actor through
setup_model_and_optimizer, which wraps the model in DDP and allocates
optimizer state. The recompute/logprob path never steps, and on 80 GB GPUs
that state does not fit. With training=False the model is now built through
get_model(wrap_with_ddp=False) and loaded without an optimizer, and
(args, model, None, None) is returned. The training=True path is unchanged.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
VIME's load_checkpoint requires skip_load_to_model_and_opt; both
create_actor calls omitted it, so the full-model l2 recompute failed with a
TypeError before any model work. The stub in the actor test now requires it.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…t.toml, gate the remaining new tests

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

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>
fusheng-ji added a commit to fusheng-ji/RL-Kernel that referenced this pull request Nov 13, 2026
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…issing TP group

run_qwen3_next_chain_gate.sh read the report with a bare open() and
indexed JSON-only keys, so a truncated file, a non-object report or one
without first_drift_family raised a traceback and exited 1, the code
reserved for a failed gate. Read it in a with-block, require an object
and the payload-only first_drift_family, and send every such failure
through invalid() (exit 2).

The TP reduce dereferenced collective_for_group(...), which returns None
for a missing group; raise a clear RuntimeError as the EP exchange does.

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>
@fusheng-ji

Copy link
Copy Markdown
Contributor Author

One OpenCodeReview summary item:

  1. run_qwen3_next_chain_gate.sh L26, unreadable report path: fixed in 4e2c33af, which reaches this PR through the merge 30c0e5a7; a missing or unreadable file now exits 2 with invalid chain report: cannot read .... No other change in this PR.

fusheng-ji added a commit to fusheng-ji/RL-Kernel that referenced this pull request Nov 14, 2026
Provider job, chain preflight (failed at rollout-smoke), logprob prior art over
13 NCCL settings, EP matrix at 2c699e3, RL-Align#557 gates at f935bf3, RL-Align#549 parity
at 30c0e5a, RL-Align#543/RL-Align#544 preflight at their own heads. Redacted.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Comment thread .github/workflows/ci.yml
tests/models/qwen3_next/test_qwen3_next_failure.py \
tests/models/qwen3_next/test_qwen3_next_imported_binary.py \
tests/models/qwen3_next/test_qwen3_next_installed_provider_identity.py \
tests/models/qwen3_next/test_qwen3_next_l2.py \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · high
This 28-module qwen3_next block was appended to the "Run Mocked Hardware Discovery Tests" step, which does not build the rl_engine._C extension and is CPU-only. Several of the newly listed modules (e.g. test_qwen3_next_imported_binary.py, test_qwen3_next_installed_provider_identity.py, test_qwen3_next_storage.py, test_qwen3_next_training_checkpoint.py, test_qwen3_next_l2.py) exercise compiled-extension, vLLM-import or on-disk binary/provider evidence paths. Any module that imports those at module scope without pytest.importorskip/skip markers will fail collection here, turning the required CI job red instead of skipping. Please confirm each newly added module is import-safe on a runner without the extension/vLLM (or split these into their own step with the appropriate skip guard).

@Flink-ddd Flink-ddd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thank for you push it. also cc @inaniloquentee

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants