Repository navigation
mtp: leaf fill cannot buy acceptance in this verifier — P6's last plan item, settled with a test - #1305
Conversation
…n item, settled with a test #805 (plan P6) lists "spare verify rows filled with the drafts' 2nd/3rd candidates" as the one protocol item never built. It should not be built as specified: the loop's acceptance rule makes it worthless, and the measurement is now a test rather than an argument. The rule is `argmax(logits[i-1]) == draft[i]` (npu/mtp/draft.h:317). A draft is accepted iff it IS the base's own argmax at that position, and the argmax that grades depth i is produced by row i-1 of the same batch. So: 1. a candidate at the SAME position that is not the argmax is refused with certainty, whatever carries it; 2. a correct candidate DEEPER in the chain is never scored while its parent is refused, and an alternative at a depth cannot even be placed in the same batch -- it would be graded by its parent's row, which does not exist yet, so it needs another batch, i.e. exactly the pass the loop already pays to re-draft. Measured (tests/mtp_draft_test.cpp [16], toy base given a real two-level ranking so "2nd candidate" is a thing it can answer for): rank-2 candidate at every depth: accepted 0/92 the same head with the argmax moved onto the runner-up (the control): accepted 20/20 <- the zero above is about the argmax, not the harness right at depths 2..k, wrong at depth 1: accepted 0, reached_at[1] == 0 the same head at k=8 (M=16 rows): accepted 0 of 184 rows <- batch width was never the constraint A real tree verifier (per-row positions + attention mask) would be a different design and would also need a GDN state per branch, which this plan forbids ("no per-token state banks"). docs/npu.md now states the determination next to the break-even paragraph, and the stale "batch it harder, #805's leaf fill" comment in tools/mtp_acceptance.cpp says what is true instead. Verified: `./mtp_draft_test` -> all checks passed (the 40-odd existing checks, [16]'s 8, and the control); `ctest -R 'mtp_draft|mtp_depth_pool|npu_leaderboard'` 3/3; `tools/copyright.py --check` rc 0 with the tree staged; `g++-16 -std=c++26 -fsyntax-only` on tools/mtp_acceptance.cpp rc 0. refs #805.
|
Preparing review... |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at 919dff8475e65c0ac3a3f91f293e3b883e1d0a2e. The determination is correct for this verifier, and the test measures it with a control that makes the zero mean something.
The rule, read in the code: npu/mtp/draft.h:313-319: draft 0 is checked against the last step's logits, draft i>0 against row i-1 of the batch, if (argmax(lg, V) != d[i]) break;. It is a linear chain that stops at the first mismatch. The loop is greedy by construction (draft.h:44: emitted tokens identical to plain greedy decode), so a non-argmax candidate is rejected with certainty, and nothing below a refused parent is ever graded. Both consequences in the body follow directly.
Measured here: g++-16 -std=c++26 -O2 -fopenmp tests/mtp_draft_test.cpp npu/mtp/mtp_head.cpp → all checks passed, rc 0. Section [16] prints exactly the body's numbers:
PASS leaf fill (1): the base's rank-2 candidate at the SAME position is refused every time
PASS leaf fill (1) control: with the argmax on the runner-up, the same head is accepted every time
INFO leaf fill (1) control: same head, argmax moved -- accepted 20/20
INFO leaf fill (1): rank-2 candidate at every depth, accepted 0/92
PASS leaf fill (2): a correct depth-2 candidate is never scored while depth 1 is refused
PASS leaf fill (2): widening the batch (k=8, M=16 rows) buys exactly nothing when the parent is refused
INFO leaf fill (2): correct deeper candidates, accepted 0 at k=4, 0 at k=8 (of 92 / 184 rows)
copyright.py --check rc 0. 3 files, +139/−3; the tools/mtp_acceptance.cpp change is comment-only. (My local -fsyntax-only of it stops at nlohmann/json.hpp, a submodule this worktree doesn't have checked out. main's copy stops at the same line, so that's the environment, not the PR.)
Nits, not blocking
tools/mtp_acceptance.cpp:489-492: the new sentence ends…cost is the base or the head. (P6's "leaf fill" … [16].), but the next, untouched line still begins// the head. Measured here…, so the comment reads "…the base or the head. (…) the head. Measured here". Drop the leftoverthe head.on that line.- The doc says "this verifier", which is right. One word, "greedy", in the bold lead would stop a reader from carrying the conclusion to a sampling verifier (speculative sampling can accept non-argmax drafts).
refs #805 is right: this closes the leaf-fill item as "not this design" and leaves the tree-verifier question named.
#805 (plan P6) lists one protocol item that was never built: "spare verify rows filled with the drafts' 2nd/3rd candidates". This PR settles it — it should not be built as specified, and the evidence is a test rather than an argument.
Why it cannot work here
The loop's acceptance rule is
argmax(logits[i-1]) == draft[i](npu/mtp/draft.h:317). A draft is accepted iff it is the base's own argmax at that position, and the argmax that grades depth i is produced by row i-1 of the same batch. Two consequences:Measured —
tests/mtp_draft_test.cpp[16], toy base given a real two-level rankingThe control is the point: the identical head against the identical base is accepted every time once the argmax moves, so
0/92is a measurement of the acceptance rule, not a broken harness. And widening the batch to the full M=16 changes nothing, because the batch's rows are the chain, not candidate tests.A real tree verifier (per-row positions + attention mask) would be a different design, and with a recurrent GDN state carried row by row it would also need a state per branch — the "no per-token state banks" constraint this same plan imposes. The two levers that did move this loop are already recorded: the hidden variant (post-final-norm) and the drafter's stream cursor.
Contents
tests/mtp_draft_test.cpp— section [16] (8 checks) plus the two toy heads and therank_two/swap_top_twoknobs it needs.docs/npu.md— the determination next to the break-even paragraph, with the counts and the code line.tools/mtp_acceptance.cpp— the stale "batch it harder, npu plan P6: the shared drafter protocol - tau-gated chain, 4-row verify with leaf fill, deferred GDN commit; acceptance per target in the leaderboard #805's leaf fill" comment now says what is true.Verified
refs #805.