Skip to content

test: cover the hybrid-attention VLM architecture (Qwen35) - #51

Merged
solderzzc merged 1 commit into
mainfrom
test/vlm-qwen35-unit-tests
Aug 15, 2026
Merged

test: cover the hybrid-attention VLM architecture (Qwen35)#51
solderzzc merged 1 commit into
mainfrom
test/vlm-qwen35-unit-tests

Conversation

@solderzzc

Copy link
Copy Markdown
Member

Companion to #(LFM2VL PR, opening next). Follow-up to #49/#50MLXVLM/Models/Qwen35.swift was one of the largest remaining zero-coverage files in the model layer.

What's different about it

Its language model is a hybrid: layers alternate between full self-attention and a linear-attention block (GatedDeltaNet) on a fixed period, full_attention_interval. DecoderLayer.isLinear is (layerIdx + 1) % fullAttentionInterval != 0, so a config that never turns the linear branch on leaves GatedDeltaNet entirely dead code in the test. Setting the interval to the layer count (2/2) makes layer 0 linear and layer 1 full attention — both branches exercised on purpose, not by chance.

Vision config reuses Qwen3VLConfiguration.VisionConfiguration directly (Qwen35 is literally typealiased to it), so it inherits the two silent constraints already covered in #50: num_position_embeddings must be a perfect square, hidden_size must divide evenly by num_heads.

The forward-pass test uses the model's own newCache(parameters:), not nil — that's what routes the linear layer to a MambaCache and the attention layer to a standard KV cache, so it checks construction and cache selection actually agree rather than assuming it.

MoE fields (num_experts, ...) stay at their dense defaults — the routed-MoE path is a separate shape from the attention hybrid this covers.

Verification

MLXVLM/Models/Qwen35.swift: 0% → 66.4% line coverage
Full suite: 120 tests, 21 suites, passing (112 before, +8 across this PR and its LFM2VL sibling)

🤖 Generated with Claude Code

Follow-up to #49/#50. MLXVLM/Models/Qwen35.swift was, alongside LFM2VL.swift,
one of the largest remaining zero-coverage files in the model layer.

Its language model is a hybrid: layers alternate between full self-attention
and a linear-attention block (GatedDeltaNet) on a fixed period,
full_attention_interval. DecoderLayer.isLinear is
(layerIdx + 1) % fullAttentionInterval != 0, so a config that never turns the
linear branch on would leave GatedDeltaNet entirely dead code. Setting the
interval to the layer count (2/2) makes layer 0 linear and layer 1 full
attention, exercising both branches deliberately rather than by chance.

Vision config reuses Qwen3VLConfiguration.VisionConfiguration directly (Qwen35
is literally typealiased to it), so it inherits the same two silent
constraints already covered in #50: num_position_embeddings must be a perfect
square, and hidden_size must divide evenly by num_heads.

The forward-pass test uses the model's own newCache(parameters:), not nil —
that is what routes the linear layer to a MambaCache and the attention layer
to a standard KV cache, which is model-specific behaviour worth exercising
against the same config that built the model rather than assuming it lines up.

MoE fields (num_experts, decoder_sparse_step, ...) are left at their dense
defaults; the routed-MoE path is a separate shape from the attention hybrid
this covers.

Verified with coverage: MLXVLM/Models/Qwen35.swift moves from 0% to 66.4% line
coverage. Full suite: 120 tests, 21 suites, passing (112 before, +8 across this
PR and its sibling for LFM2VL).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@solderzzc
solderzzc merged commit ff363ae into main Aug 15, 2026
6 checks passed
@solderzzc
solderzzc deleted the test/vlm-qwen35-unit-tests branch August 15, 2026 21:13
solderzzc added a commit to SharpAI/SwiftLM that referenced this pull request Aug 15, 2026
…149)

Points at c09851f, which carries both:

- SharpAI/mlx-swift-lm#51 — the hybrid full-attention/GatedDeltaNet split in
  MLXVLM/Models/Qwen35.swift, gated by a fixed period (full_attention_interval)
- SharpAI/mlx-swift-lm#52 — the hybrid attention/short-conv split in
  MLXVLM/Models/LFM2VL.swift, gated by explicit layer indices (full_attn_idxs)

Both were, alongside Gemma4 (#147) and Qwen3VL (#148), among the largest
zero-coverage files in the model layer. Coverage: Qwen35 0% → 66.4%,
LFM2VL 0% → 34.9%.

No SwiftLM-side code changes; verified the umbrella builds clean against the
bumped pointer.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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