feat(engine): E1 MoE block fwd_moe - router, gather_qmm/mm, shared experts (#107)#122
Conversation
…perts (#107) Family-agnostic MoE FFN primitive for DeepSeek (E2) and qwen3_5_moe (E3): softmax top-k route, SwitchGLU via gather_qmm/gather_mm, contiguous gotcha helper, always-active and sigmoid-gated shared experts. No family enablement.
Code review: PR 122 (E1 MoE block
|
| Topic | Resolution |
|---|---|
Top-k via -logits + argpartition(kth=k-1) vs mlx-lm argpartition(softmax, kth=-k)[..., -k:] |
Monotonic; same set. Order unstable either way; score combine is order-invariant. Documented. |
R2 vs mlx-lm indices.size >= 64 only |
Plan explicitly chose mlx-serve rule for multi-position verify; correct. |
Always-active shared vs mlx-serve shared_expert_gate_w == null early return |
mlx-serve handles DeepSeek shared outside moeMLP2; PR correctly models always-active inside fwd_moe. |
norm_topk_prob optional vs mlx-serve always-normalize |
Matches mlx-lm qwen knobs; right for family-agnostic E1. |
Sort path passes null lhs_indices after materializing x_rep |
Matches mlx-serve; not a missed gather lhs feature. |
| Router on softmax probs vs logits for partition | Equivalent for top-k set. |
fwd_array_contiguous public with no production call site yet |
Intentional R5; loaders in #112/E3 must call it. |
GELU-approx expert branch tied to cfg->hidden_act |
Fine for silu families in Stage E; watch if a gelu-dense / silu-expert hybrid ever appears. |
Test plan re-check
- Issue E1: MoE block (fwd_moe) - router, gather_qmm/mm, shared experts #107 checkboxes map to named GPU tests (PR body table is accurate)
-
test_fwd_moe_api+test_fwd_moe_gpugreen locally - Please confirm ASan/UBSan on the GPU suite on your machine (claimed in PR; not re-run under sanitizers here)
- Pre-existing
test_weights_gpuDeepSeek V4 GGUF failure called out as unrelated - agree, keep it out of this PR
Merge recommendation
Approve once M1/M2 are either fixed in-thread or explicitly accepted as follow-up issues filed against #108/#113 (M1 especially should not be forgotten). L1-L4 are quick polish if you are already touching the file.
Nice work - this is a clean E1 primitive with the right gotchas pinned.
Address review comment 5020153959 on #122: - M1: extract fwd_moe_combine for score-combine + shared residual so #108 can reuse without duplicating fwd_moe - M2: reject when router logits last dim != p->num_experts - L3: reject has_shared_expert_gate without has_shared - L1: characterize R2 boundary S==1 && B*S*K >= 64 sorted path - L4/L6: tighten fwd_moe_weights_t layout/presence/bias-free comment - L5: TODO for quant mode plumbing (matches fwd_linear) - L2: rename contiguous gotcha test to match hard asserts L7 overflow and L8 CPU smoke scope left as-is (refuted / non-defect).
|
Thanks - agree with approve-with-nits and the intentional non-findings table. Taking (landed in 8505f36):
Refuting:
Cycle-level TDD plan: |
Summary
Implements #107 (parent #104 E1 MoE infrastructure): a family-agnostic MoE FFN block used later by DeepSeek (E2) and qwen3_5_moe (E3).
Plan:
doc/plans/impl-107-e1-moe-block-fwd_moe.mdAPI (layered, R1)
fwd_moe_route_softmaxnorm_topk_probfwd_gather_expert_linearmlx_gather_qmm/ bf16mlx_gather_mmfwd_switch_glufwd_moefwd_array_contiguousScope decisions
S > 1 || B*S*K >= 64(mlx-serve rule)[E,out,in]+transpose; bf16[E,in,out]no internal transposegather_mmalwayssorted_indices=false(mlx 0.31.2 dense sorted bug);gather_qmmhonors sortgate_upload sanitize (E2: DeepSeek weight sanitization + weights_expected_names #112)norm_topk_probfield, or DeepSeek group gate (E1: DeepSeek-style MoE gate (sigmoid, group top-k, correction bias) #108)Issue #107 checkbox map
test_route_softmax_topk_indices_and_scores,test_route_softmax_norm_topk_prob,test_route_softmax_batch_seq_shapesmlx_gather_qmmtest_gather_qmm_single_token_matches_manual_expert, switch/moe quant pathstest_gather_qmm_lazy_slice_zeros_without_contiguousmlx_gather_mmtest_gather_mm_bf16_matches_per_expert_matmul,test_fwd_moe_all_bf16_expertstest_fwd_moe_shared_always_active_adds_dense_swiglutest_fwd_moe_shared_sigmoid_gateFiles
src/engine/forward.h/forward.c- MoE structs + implementationtests/test_fwd_moe_api.c- CPU API/NULL smoketests/test_fwd_moe_gpu.c- primary GPU suiteOut of scope (unchanged)
DeepSeek sigmoid/group gate (#108), MLA/DSA, family wiring (#113/#118), expert packing sanitize (#112), parity gates.
Test plan
make testgreen (includes newtest_fwd_moe_api)./tests/test_fwd_moe_gpugreen (15 cases)make test-gpu- MoE suite OK; pre-existingtest_weights_gpuDeepSeek V4 GGUF assert failure is unrelatedinds/scoresshape via a newfwd_moe_route_deepseek