Skip to content

test(MODEL-MM-QWEN4-EXP): the CUDA router tie-break is stable, and the geometry that proves it had never been run - #2595

Merged
mudler merged 7 commits into
mainfrom
row/MOEDIV-q4exp-tiebreak-stability
Sep 2, 2026
Merged

test(MODEL-MM-QWEN4-EXP): the CUDA router tie-break is stable, and the geometry that proves it had never been run#2595
mudler merged 7 commits into
mainfrom
row/MOEDIV-q4exp-tiebreak-stability

Conversation

@localai-org-maint-bot

@localai-org-maint-bot localai-org-maint-bot commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

test(MODEL-MM-QWEN4-EXP): the CUDA router tie-break is stable, and the geometry that proves it had never been run

Wave MOEDIV (#2552) measured that 32.9% of this model's routing boundaries are
exact bf16 ties, and concluded that the CPU/CUDA expert flips are tie-break
order rather than a defect. It verified that conclusion BY SOURCE READ of both
kernels. A source read establishes the INTENDED tie-break; it does not establish
that a parallel argmax realises it. This wave tests the part a source read
cannot reach.

The answer

The CUDA top-k realises the lowest-index tie-break it declares. On
bit-identical logits whose top-k boundary is an EXACT bf16 tie, at E = 512
k = 10 -- the geometry qwen4_exp routes -- the dispatched kernel returns the
closed-form correct selection, agrees byte-for-byte with the serial GPU oracle
and with the CPU reference, repeats identically over 32 launches, and gives the
same answer for a row run alone and as each of 257 rows of a batch.

Measured on thor:gpu0 (job 5f50e5f7-c730-463f-93cc-3293835ed007, pod
rc-worker-n8smh, NVIDIA Thor, driver 595.78, cc 11.0, nvcc cuda_13.0.r13.0,
sm_110): CMAKE rc=0, BUILD rc=0 objects=586, 41 *.cu.o, libcudart.so.13
and libcublasLt.so.13 in the test binary's own ldd, and
test_moe_router_tie_stability reading 4 cases | 4652 assertions | 0 failed |
rc=0
.

So MOEDIV's reading stands, the remaining CPU-vs-CUDA token divergence stays
attributed to arithmetic, and the session goal's three disagreeing ids do not
move -- because nothing moved them. No product code changed. No fix was made,
because none was needed.

What the wave found anyway

The tree could not have answered this question before. MoeRouterWarpValuesPerThread
returns 0 for E = 512, so qwen4_exp routes through MoeRouterTopKKernel<Tin,false>
at 2 experts per thread, and the standing adversarial sweep in
tests/vt/test_ops_moe_grouped.cpp stops at E = 256 -- where a thread holds ONE
expert and the per-thread strided compare can never see a tie at all.

Flipping that compare to highest-index-wins (cuda_moe.cu, ONE site, asserted
n == 1, MUT BUILD rc=0 objects=1) is caught by 104 of the new assertions
and by ZERO assertions of the standing sweep, whose counts are byte-identical
before and after: test_ops_moe_grouped read 1907 | 1906 passed | 1 failed
both times, that one failure being the pre-existing #962 Marlin NVFP4 block-size
disagreement on this arch, and test_ops_moe read 33451 | 33451 both times.
Restoring returned 4652/4652.

geometry pattern par == r.expect failures under the mutation
E = 512 3, 5 12, 12
E = 1024 3, 5 12, 12
E = 256 any 0

Zero at E = 256 is the finding: the compare the mutation breaks does not exist
below E > kBlock.

Two negatives kept rather than dropped

The mutant stayed perfectly deterministic. All 279 repeat comparisons and
2313 batch-position comparisons passed with the wrong tie-break compiled in. A
repeatability check alone would have called it clean, so determinism is recorded
as necessary and never sufficient.

A corroborating orin:gpu0 lease registered no CUDA backend, skipped every
device case, and read 488/488 GREEN with the broken tie-break in the binary.
The assertion count is what distinguishes it from Thor -- 488 against 4652 --
but the assertion count ALONE separates only two of three states, because a
CPU-only build reads 488 as well. The CASE count is the other half:

state cases assertions rc
CPU-only build -- the device cases are #ifdef'd out 1 488 0
CUDA build, no device -- they skip and return 4 488 0
CUDA build with a device 4 4652 0

Every table here therefore carries both counts and never a status line, and the
spec's Gates section now teaches both.

How it is asserted

Every row has a closed-form correct answer -- h strict winners, then a tied set
S sharing one bf16 bit pattern, then a low floor -- so the top-k is the h winners
followed by the k-h LOWEST INDICES OF S. That is upstream's own rule at the
parity pin 5559679229, not a local convention:
csrc/libtorch_stable/moe/topk_softmax_kernels.cu:536-537 ("We want lower
indices to "win" in every thread so we break ties this way"), :515-517
("only updated if > (not >=)"), :707-708 (case 512: LAUNCH_TOPK(512, ...),
so E = 512 is a registered topkGating width upstream), :186,222,225
(cub::ArgMax in the fallback) and :465. The gate therefore measures the
semantics and cannot pass by both arms being wrong the same way.

Indices are compared with == and the selection as an ordered vector; there is
no tolerance anywhere in the file, because a discrete selection has bimodal
error. The counted property is tied_seen -- rows whose boundary carries the
same bf16 bit pattern on both sides, read off the LOGITS rather than any kernel
-- CHECKed equal to the row count, so a builder that stopped producing ties
cannot read green.

Red-first on the CPU half, on the dev box, CPU-only build at this branch head:
flipping the greedy argmax's strict > to >= at ONE site --
src/vt/cpu/cpu_ops.cpp:2985, grep -c = 1 -- turns every selection
assertion in the CPU case red. Baseline 1 case | 488 | 0 failed | rc 0; mutant
1 case | 488 | 380 passed | 108 failed | rc 1, all 108 of them
CHECK( ids == r.expect ) and none of them the counted property; restored
488/488. 108 is the whole population and not a subset: h < k is REQUIREd,
so each of the 54 rows selects at least one member of the tied set, and >=
mis-selects on every row in both dtypes -- 54 x 2 = 108.

(An earlier revision of this body said 90. That figure came from a
five-pattern draft of the file -- 5 x 3 x 3 x 2 -- and the shipped file has
six patterns. It was re-measured rather than carried forward, because
squash_merge_commit_message = PR_BODY makes this text the commit message and a
wrong measured number in it cannot be repaired afterwards.)

Owed

Two gaps the fresh review found are RECORDED here rather than fixed, each
with an issue and a ## Owed entry in the row's spec, because neither belongs
in a test-only change.

  • #2603 -- the permissive
    HasCuda() skip lets a device-less CUDA build read green.
    That is the
    orin:gpu0 row above, and it is a tree-wide shape:
    grep -rl 'no CUDA backend registered; skipping' tests/ finds 14 files, 13 of
    them not this one. The tree already carries the idiom that refuses instead
    (tests/parity/test_qwen27n_fp8_tower_paged_engine.cpp:149 reads
    VT_REQUIRE_27N_FP8_GATE and turns absence into a FAILURE), so adopting it is
    a row of its own.
  • #2604 -- half the device
    sweep is a duplicate above E = 256.
    MoeRouterWarpValuesPerThread returns 0
    for every E outside {32,64,128,256} and LaunchRouterWarp
    (cuda_moe.cu:564) returns false on vpt == 0, so at E = 512 and E = 1024
    -- qwen4_exp's own geometry -- both VT_MOE_ROUTER_WARP arms dispatch the
    same block kernel. The counts stay honest, because every assertion really
    executes; but 36 of the 72 rows at E > 256 are byte-identical repeats, the
    12 per cell above is 2 arms x 3 h x 2 dtypes of which 6 are a second
    count of one disagreement, and COVERAGE at E > 256 is half what the doubled
    figures imply. Only E = 256 compares two structures.

The gate against vLLM itself stays owed for the reason the row's
spec already records under ## Owed: it needs an artifact vLLM can load, and
vLLM's GGUF support is an out-of-tree plugin while every safetensors arm of this
model exceeds the largest fleet box. This change says our two arms agree with
each other AND with upstream's stated rule; it does not run upstream's kernel on
upstream's weights.

Evidence: docs/bench-evidence/qwen4exp-moe-tiebreak-stability-20260902.md.
Spec: .agents/specs/qwen4-exp-flash-next.md "## Wave TIEBREAK".

Closes #2586

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]

…EALISES the tie-break it declares

Wave MOEDIV (#2552) measured that 32.9% of this model's routing boundaries are
exact bf16 ties and concluded, from a source read of both arms, that the flips
are tie-break order rather than a defect. A source read establishes the INTENDED
tie-break. It does not establish that a parallel argmax realises it.

The standing adversarial sweep in tests/vt/test_ops_moe_grouped.cpp sweeps
E in {32,64,128,256}, and MoeRouterWarpValuesPerThread admits none of E=512, so
the block kernel at the geometry qwen4_exp actually routes has never been
executed by a test. Its tie patterns also keep the tied set inside a contiguous
prefix, which at kBlock=256 sits in warp 0 and never asks the cross-warp
comparison to break a tie.

This section states the hypothesis so it can fail, the closed-form expectation
that gates the semantics rather than arm-vs-arm agreement, the determinism
arms, the counted property that stops a case reading green while measuring
nothing, and the five upstream anchors at the parity pin that show vLLM DEFINES
this tie-break in both of its kernels and registers E=512 as a topkGating width.

FOLLOWING_AGENTS_PROTOCOL

Refs: #2586
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…the router at the geometry qwen4_exp routes

Wave MOEDIV (#2552) concluded from a source read that the CPU and CUDA routers
break an exact tie the same way. This adds the executable form of that claim,
at E=512 k=10 -- the geometry the model routes and the one no case in this tree
has ever run, because MoeRouterWarpValuesPerThread excludes E=512 from the warp
kernel and the standing adversarial sweep in test_ops_moe_grouped.cpp stops at
E=256.

Each row has a CLOSED-FORM correct answer: h strict winners, then a tied set S
whose members carry one bf16 bit pattern, then a low floor, so the top-k is the
h winners followed by the k-h LOWEST INDICES OF S. That is upstream's own rule
(topk_softmax_kernels.cu:536-537, :515-517, :465 @ vLLM 5559679229), so this
gates the semantics and cannot pass by both arms being wrong the same way.
Indices are compared with == and the selection as a sorted set; there is no
tolerance in the file, because a discrete selection has bimodal error.

Six tie PATTERNS decide which level of the reduction has to break the tie.
Pattern 5 is the one that needs E > 256: it puts BOTH slots of the same threads
in S, so the per-thread strided scan alone decides between expert t and t+256.
At E <= 256 a thread holds one expert and that compare cannot be reached, which
is why no existing case discriminates it.

Counted property: tied_seen counts rows whose boundary carries the SAME bf16 bit
pattern on both sides, read off the LOGITS rather than any kernel, and is CHECKed
against the row count -- a builder that stopped producing ties cannot read green.

CPU arm on this box: assertions 488 | 488 passed | 0 failed, rc=0.
Red-first: flipping the CPU kernel's strict `>` to `>=` (cpu_ops.cpp
MoeRouterTopKKernel, one site, N=1) turned 90 of the selection assertions red
while the counted property stayed intact; restored byte-for-byte and green again.
The CUDA arms need nvcc and a device and are measured in a lease.

FOLLOWING_AGENTS_PROTOCOL

Refs: #2586
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…try that proves it had never been run

Thor measured wave TIEBREAK's question and the answer is no defect: at E=512
k=10 on bit-identical logits with an exact bf16 tie at the boundary, the
dispatched CUDA kernel returns the closed-form correct selection, agrees
byte-for-byte with the serial GPU oracle and the CPU reference, repeats over 32
launches and is invariant across 257 batch positions. 4 cases, 4652 assertions,
0 failed, rc=0. MOEDIV's conclusion stands and the session goal's three
disagreeing token ids do not move, because nothing moved them.

The wave still found something. Flipping the block kernel's per-thread strided
scan to highest-index-wins is caught by 104 of the new assertions and by ZERO
assertions of the standing sweep, whose counts are byte-identical before and
after. The failures land only at E in {512, 1024} and only on the two patterns
that put two tied experts on one thread; E=256 contributes zero, because that
compare does not exist below E > kBlock.

Two negatives are recorded rather than dropped. The mutant stayed perfectly
deterministic, so repeatability alone would have called it clean. And the
corroborating orin lease registered no CUDA backend, skipped every device case,
and read 488/488 GREEN with the broken tie-break compiled in -- distinguishable
from Thor's real result only by the assertion COUNT.

FOLLOWING_AGENTS_PROTOCOL

Refs: #2586
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…ms it could falsify

Sixteen commits, none of them touching the router. The wave's two load-bearing
statements are re-read against the merged tree rather than assumed: the standing
sweep in tests/vt/test_ops_moe_grouped.cpp still stops at E=256, and
MoeRouterWarpValuesPerThread still returns 0 for E=512, so the block kernel is
still the path qwen4_exp routes and the coverage gap the mutation quantified is
still the gap.

FOLLOWING_AGENTS_PROTOCOL

Refs: #2586
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…s this tree cannot run

The section carried `ctest -R 'vt_ops'` and `build/tests/vt_tests -ts='*moe*'`.
This tree builds ONE binary per suite, has no `vt_tests` aggregate and no
`vt_ops` ctest label, so both commands would have selected nothing and reported
success for it. `ctest --test-dir build -R moe` on a tree with only one suite
built exits 8 with `Unable to find executable` on 32 of 33 entries, which is the
same class of answer: a gate that names something absent is not a gate.

Replaced with the build-then-run pair that was actually used, and with the
instruction the Outcome paid for: read the ASSERTION COUNT beside every rc. On a
CPU-only build the three device cases skip and the suite reads 488 at rc 0
instead of a device run's 4652, and the orin lease in the Outcome read exactly
that number GREEN with a broken tie-break compiled in.

Local rcs at this head, one binary at a time:
  test_moe_router_tie_stability rc=0  488 | 488 passed | 0 failed
  test_ops_moe                  rc=0   69 |  69 passed | 0 failed
  test_ops_moe_grouped          rc=0    3 |   3 passed | 0 failed
  test_moe_router_warp_map      rc=0 4597 | 4597 passed | 0 failed

FOLLOWING_AGENTS_PROTOCOL

Refs: #2586
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…d head, because the merge touched a file the binary links

The wave's numbers were taken at 64617b1. The merge with origin/main then
brought in src/vt/cuda/cuda_exl3.cu, which links into the same binary, and this
row has already had a merge falsify prose inside its own pull request. So the
counts were taken again rather than reasoned about.

Second thor:gpu0 lease at d52bc83, job 186b67e9-4e48-43fb-9424-65735964083c,
BUILD rc=0 objects=586, 41 *.cu.o: baseline 4652/0 rc=0, mutant 4652/104 rc=1,
restored 4652/0 rc=0, and the standing suites unmoved at 1907/1906/1 and
33451/0 under both. The mutant's failures land 12/12 at E=512 patterns 3 and 5,
12/12 at E=1024, and ZERO at E=256, exactly as before.

Only this commit lands after that run and it edits two documents, so the binary
those numbers came from is the binary the branch ships.

FOLLOWING_AGENTS_PROTOCOL

Refs: #2586
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…d it is measured here

The pull-request body said flipping the CPU router's strict `>` to `>=` turned
"90 selection assertions" red. That number does not reproduce and it never
could: the file has SIX tie patterns, and 90 is 5 x 3 x 3 x 2, a stale figure
from a five-pattern draft. `squash_merge_commit_message = PR_BODY` here, so the
body becomes the commit message on `main`, and a wrong measured number in it is
an unrepairable falsehood in the history.

Re-measured on the authoring host, CPU-only build at this branch head, one site
(`src/vt/cpu/cpu_ops.cpp:2985`, `grep -c` = 1): baseline `1 case | 488 | 0
failed | rc 0`, mutant `1 case | 488 | 380 passed | 108 failed | rc 1`, restored
`488/488`. All 108 failures are `CHECK( ids == r.expect )` and none is a counted
property, so the row builder is proved still to have built ties. 108 is the
whole selection population rather than a subset: `h < k` is `REQUIRE`d, so each
of the 54 rows selects at least one tied member, and `>=` mis-selects on every
row in both dtypes.

Three further repairs, none of them a redesign of the wave.

The `## Outcome` said a sweep that stops at 256 is "blind to it", which invites
a broader reading than the mutations support. The fresh review inverted the
other two reduction levels: the warp `__shfl_down_sync` argmax reds 313
assertions of the standing sweep and the cross-warp pass reds 199. Only the
per-thread strided compare reds zero there. The sentence now names that one
defect class and says the sweep is adequate for the levels that exist at
E <= 256.

The Gates instruction taught the assertion count alone, and the assertion count
separates only two of three states: a CPU-only build reads `1 case | 488`, a
device-less CUDA build reads `4 cases | 488`, and a real device run reads
`4 cases | 4652`. Both 488s are rc 0. The case count is the other half of the
discriminator and the section now carries both.

`test_moe_router_tie_stability.cpp:43` put upstream's `:465` sentence in
quotation marks as `[0,1,...,k-1]`; upstream writes `[0,1,2,...,k-1]`.

Two gaps the review found are RECORDED and not fixed, each with an issue and a
`## Owed` entry, because neither belongs in a test-only change. The permissive
`HasCuda()` skip that lets a device-less CUDA build read green is a tree-wide
shape across 14 files (#2603). Half the device sweep is a duplicate above
E = 256, because `MoeRouterWarpValuesPerThread` returns 0 there and both
`VT_MOE_ROUTER_WARP` arms dispatch the same block kernel, so coverage at the
geometry `qwen4_exp` routes is half what the doubled counts imply (#2604). The
counts stay honest; only their reading changes.

No product code changed.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
@mudler
mudler merged commit c9518a7 into main Sep 2, 2026
7 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants