Skip to content

fix(format): ModelConstraints dropped the gated-DeltaNet shape, so 18 of every 24 Qwen3.5 layers were counted as if their tensors did not exist - #3350

Open
noahgift wants to merge 4 commits into
mainfrom
PMAT-3346-gdn-shape-keys
Open

noahgift wants to merge 4 commits into
mainfrom
PMAT-3346-gdn-shape-keys

Conversation

@noahgift

Copy link
Copy Markdown
Contributor

#3346 asked which was wrong, the range or the descriptor. The answer is neither — it was the type that reads the descriptor. Stacked on #3348.

contracts/model-families/qwen3_5.yaml declares inner_size, state_size, conv_kernel, group_count and full_attention_interval. ModelConstraints carried none of them, so every consumer counted a hybrid model with dense/GQA accounting and silently missed the conv, the gates, the state norm and the mixer projections of 18 of every 24 layers.

Ground truth first, and it now matches exactly

The measurement is against a real file, not against the formula's own assumptions: ~/models/Qwen3.5-0.8B-Q4_K_M.gguf (sha256 bd258782…dc517), GGUF header parsed directly — 320 tensors, 752,393,024 parameters.

measured GGUF tensor sum   752,393,024
model_parameter_count      752,393,024      delta 0
  gated-DeltaNet layer      21,555,360  ==  measured blk.0
  full-attention layer      18,352,640  ==  measured

Two shapes in that file contradict dense accounting and are now modelled, both read off the tensors rather than assumed:

  • attn_q is [1024, 4096] = 2·n_h·d_k — the q projection emits the output gate, and attn_output [2048,1024] proves only q is doubled.
  • the file is tied: it has no output.weight.

The mutation: drop the attn_gate term and qwen35_0_8b_config_derived_count_equals_the_measured_gguf_inventory goes RED, short by exactly d·inner_size = 2,097,152.

QE2E-INV-001 is still NOT asserted, and the range was not widened

P(9B) moves 8,208,519,168 → 8,344,907,136, still 0.655 B below [9.0B, 9.2B]. The residual is not arithmetic — the 9b descriptor contradicts itself: it keeps inner_size: 2048 (the value the 0.8B uses at hidden_dim 1024) while quadrupling hidden_dim, and its group_count: 8 fails group_count · state_size == inner_size (8·128 ≠ 2048), a consistency the measured 0.8B satisfies at 16·128.

Only a real Qwen3.5-9B file settles that, and none is on this box, so the obligation stays unproved and the number is pinned by a test instead of asserted. DeltaNetShape::heads_span_the_mixer() now catches the descriptor's self-inconsistency directly.

Two disclosures

  • Outside scope, unavoidable: five one-line deltanet: None, fixture literals in apr-cli tests/oracles. Zero logic change. The enum-payload alternative breaks the same crate, because apr-cli matches AttentionType::HybridGatedDeltaNet in two files.
  • qk_norm is left alone deliberately. The descriptor says the family has none, the real file has attn_q_norm/attn_k_norm on its attention layers, and tensor_expectation.rs asserts the opposite. Both are true of different layer kinds — the repo should settle whether qk_norm is per-family or per-layer-kind. It is worth 4,096 parameters at 9B, so it does not touch the verdict.

The roadmap write pmat work add produced was reverted: #3297 moved entries to fragments, and a monolithic write is exactly the contention that PR removed.

Gate: aprender-core --lib model_arithmetic 28 passed, aprender-contracts --lib 1526 passed, clippy -D warnings clean, fmt clean.

Refs #3346, #3347, #3091, #3114

no-close: #3346 stays open — which side of the 9b descriptor is wrong is undecided until a real Qwen3.5-9B GGUF is measured.

ont-delta: shape ModelConstraints gains inner_size, state_size, conv_kernel, group_count and full_attention_interval, and model_parameter_count gains the gated-DeltaNet layer term; no obligation is added or removed, and QE2E-INV-001 is not discharged.

🤖 Generated with Claude Code

noahgift and others added 4 commits September 16, 2026 09:15
`pv proof-status` / `pv coverage` report `bindings=N/6` for
qwen35-e2e-verification-v1 by counting entries in contracts/binding.yaml that
map an EQUATION to an implementation function, over equations.len(). It was
0/6, so QE2E-BND-005 (`coverage(qwen35_contracts) = 1.0`) was false. Three
falsification tests landed earlier and did not move it, because tests are not
what it counts.

Five equations now compute their formula from the architecture config as data
(ModelSizeConfig + ModelConstraints, parsed from contracts/model-families/),
in aprender::format::model_arithmetic beside model_family.rs which owns those
types. No constant for any model is baked in; all integer arithmetic saturates
so the functions stay total under property tests; no unwrap().

  model_parameter_count  P = V*d + L*(d_attn+d_ffn+d_norm) + d_final + V*d
  flops_per_token        F = 2*P + the O(S*d*L) attention term
  memory_breakdown       M = M_weights + M_kv + M_activations
  throughput_model       tok/s = min(bw/bytes_per_token, compute/F)
  contract_composition   compose(embedding, L x block, final_norm, unembed)

23 tests: a hand-computed worked example per equation plus six properties the
contract states (P additive in layers, F linear in P, throughput monotone in
bandwidth for QE2E-MON-004, Q4K<Q5K<Q6K<F16<F32 at whole super-blocks for
QE2E-ORD-003, M_kv linear in S, composition chains and ends at [seq_len, V]).

verification_ladder is deliberately NOT bound. It measures the contract corpus,
not the model, and implementing it means deciding what makes an obligation
"verified" — the decision #3347 reports as broken today (the --table L2 column
ticks on `idx < falsification_tests.len()`). It would also be self-referential:
its output is what QE2E-BND-005 asserts equals 1.0. Left unbound at 5/6 rather
than marked `pending`, which count_binding_coverage excludes from the
denominator and would have shown a clean 5/5.

QE2E-BND-005 is therefore still FALSE and still a failing obligation. Nothing
here asserts 1.0.

Mutation results, measured not assumed. Each of the five bodies was mutated
(dropped d_final; 2*P -> 3*P; KV factor 2 -> 1; min -> max; unembed emitting
[S,d] not [S,V]) and the unit tests went RED every time, 1-3 failures each,
green again on revert. The NAME-based checkers are a different story: renaming
all five definitions left `pv proof-status --verify-bindings .` at 5/6 and
`pv verify-bindings` at 58/60 — unchanged — because
crates/aprender-contracts-staging/generated/*_scaffold.rs declares `fn` of all
five names and the scan is a textual whole-tree sweep. That directory has no
Cargo.toml and is in no workspace. With the scan root narrowed to
crates/aprender-core the same rename drops the contract to 0/6, so the check
works and its default root is what blunts it.

Pmat-Ticket: PMAT-3347

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…annot reproduce

QE2E-INV-001 could not be judged because nothing in the tree held a MEASURED
Qwen3.5 tensor inventory to judge against. This adds one: the 320 tensors of
~/models/Qwen3.5-0.8B-Q4_K_M.gguf (sha256 bd258782...dc517), read straight from
the GGUF header rather than from a model card.

It already falsifies the current arithmetic. Dense/GQA accounting applied to
that file gives 644,400,128 against a measured 752,393,024 — short by
107,992,896, 14.4% of the model, because 18 of the 24 layers are Gated DeltaNet
and no term here counts their conv, gate, state or output projections.

Two shapes in the file are also not what dense accounting predicts, and both are
pinned: attn_q is [1024, 4096] = 2 * num_heads * head_dim (the q projection
emits the attention output gate alongside the query; attn_output [2048, 1024]
confirms num_heads * head_dim = 2048), and attn_q_norm/attn_k_norm are present
at head_dim. The file is also TIED — it has no output.weight.

Refs #3346

Pmat-Ticket: PMAT-3346
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… a hybrid model can be counted

contracts/model-families/qwen3_5.yaml declares inner_size, state_size,
conv_kernel, group_count and full_attention_interval under constraints:, and
ModelConstraints carried none of them. A Gated DeltaNet layer's parameters live
entirely in those dimensions, so every consumer of the descriptor counted
Qwen3.5 as if three quarters of its layers did not exist.

Carried through as ModelConstraints::deltanet: Option<DeltaNetShape> — the
runtime YAML loader (parsing.rs) and the compiled-in registry (build_parsing.rs
+ build_codegen.rs) both populate it, and FALSIFY-MF-QWEN35-010 pins that the
declared values survive the trip and that no other family acquires a shape it
never declared. qwen3_5.yaml is the only descriptor with these keys, so every
other family keeps byte-identical accounting.

model_arithmetic gains gated_deltanet_layer_params (one term per GGUF tensor:
attn_qkv, attn_gate, ssm_conv1d, ssm_alpha/beta, ssm_a, ssm_dt.bias, ssm_norm,
ssm_out) and hybrid_layers (the interleaved schedule). attention_layer_params
gained two terms the real file has and dense accounting did not model: the
gated q projection (2*n_h*d_k) and the q/k norm vectors.

Falsified against a real model, not against itself: fed the 0.8B configuration,
the equation now reproduces the 320-tensor inventory of
Qwen3.5-0.8B-Q4_K_M.gguf EXACTLY — 752,393,024, both layer kinds matching
tensor for tensor.

QE2E-INV-001 is still NOT asserted, and no range was widened to make it pass.
The 9b descriptor now yields 8,344,907,136, up from 8,208,519,168 but still
0.655B below [9.0B, 9.2B]. The remaining gap looks like descriptor drift rather
than missing arithmetic: 9b keeps inner_size 2048 — the value the 0.8B uses at
hidden_dim 1024 — while quadrupling hidden_dim, and its group_count 8 fails
8 * 128 == 2048, a consistency the measured 0.8B satisfies at 16 * 128. Only a
real Qwen3.5-9B file can settle it; none is on this box.

Refs #3346, #3347

Pmat-Ticket: PMAT-3346
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…osed

The note said the obligation was undischarged because ModelConstraints does not carry the DeltaNet shape keys. It does now, and the 0.8B count reproduces the real GGUF exactly. What actually blocks the obligation is descriptor drift at the 9b variant.

Pmat-Ticket: PMAT-3346
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahgift noahgift added this to the 0.68.0 milestone Sep 16, 2026
@noahgift
noahgift enabled auto-merge September 16, 2026 07:48
@github-actions

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=3350 head=d3cc76f1fdcac051003e0ce4b00a2327a147deef verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

@noahgift noahgift modified the milestones: 0.68.0, 0.70.0 Sep 16, 2026
@noahgift
noahgift added this pull request to the merge queue Sep 16, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Sep 16, 2026
@noahgift noahgift modified the milestones: 0.70.0, 0.71.0 Sep 17, 2026
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.

QE2E-INV-001 asserts Qwen3.5-9B ∈ [9.0B, 9.2B] but the repo's own 9b descriptor sums to 8.209B — the descriptor declares no DeltaNet tensors

1 participant