frink_models/parallel_dense_ffn.rs
1//! **A DENSE FFN SUMMED WITH THE ROUTED EXPERTS** -- the Grok-2 and
2//! Arctic layer shape, served through the shared-expert slot, with the
3//! two things that differ between the two graphs as one table.
4//!
5//! # What it is
6//!
7//! `src/models/grok.cpp:66-68` creates `ffn_gate` / `ffn_up` /
8//! `ffn_down` as `TENSOR_NOT_REQUIRED` beside the routed experts, and
9//! `:171-184` does this when they are present:
10//!
11//! ```text
12//! if (model.layers[il].ffn_up) {
13//! ffn_out = build_ffn(cur, ffn_up, ffn_gate, ffn_down, LLM_FFN_GELU, LLM_FFN_PAR, il);
14//! cur = ggml_scale(ctx0, ggml_add(ctx0, ffn_out, moe_out), std::sqrt(2) / 2);
15//! } else {
16//! cur = moe_out;
17//! }
18//! ```
19//!
20//! `src/models/arctic.cpp:38-42` creates the same three REQUIRED, sized
21//! `{n_embd, n_embd}`, and `:118-154` runs `ffn_out = build_ffn(
22//! ffn_norm(ffn_inp), ...SILU, PAR)`, `moe_out = build_moe_ffn(
23//! ffn_norm_exps(inpSA), ...)` and sums them with no scale.
24//!
25//! # Reach -- MEASURED
26//!
27//! Over all 155 `src/models/*.cpp` (2026-09-12): every graph that calls
28//! `build_moe_ffn` AND reads a dense `layers[il].ffn_up` was listed;
29//! all but two use the dense triple on their LEADING dense layers
30//! (`if (il < n_layer_dense_lead)` or `if (ffn_gate_inp == nullptr)`)
31//! or as `_shexp`. The two that SUM a dense FFN with the routed output
32//! on one layer are `grok.cpp:171-184` and `arctic.cpp:118-154`. So
33//! [`PARALLEL_DENSE_FFN_ARCHITECTURES`] has two rows, and the two free
34//! parameters are the columns: whether the triple is required
35//! (`arctic`) or optional (`grok`: Grok-1 has none and takes the
36//! `else`), and the scale on the sum (`sqrt(2)/2` for `grok`, none for
37//! `arctic`). What does NOT differ and so is not a column: the dense
38//! branch reads the normed FFN input `cur` in both, and its activation
39//! is the architecture's dense activation (GELU for `grok`, SiLU for
40//! `arctic`), which is what `ModelConfig::layer_ffn_acts(il).dense`
41//! already answers for the shared-expert slot. Arctic's OTHER
42//! difference -- the routed branch reading `ffn_norm_exps(inpSA)` --
43//! is `crate::router_input::RouterInput::NormedLayerInput`, one graph
44//! of 155, and not this module's business.
45//!
46//! # Why the shared-expert slot
47//!
48//! `MoeWeights::shared_experts` is already "a dense FFN that fires on
49//! every token and is added to the routed sum", computed with the
50//! architecture's dense activation on the normed FFN input, in the
51//! row body and the batched body alike; every fused Metal MoE launch
52//! refuses a layer that has one. What the slot lacked was the tensor
53//! NAMES (it loads `_shexp`) and the scale on the sum, which is why
54//! this module used to be a refusal: reading Grok-2's triple into it
55//! would have dropped the `sqrt(2)/2`. The loader fills the slot from
56//! the dense names for these two architectures and records the row's
57//! scale on the layer (`MoeWeights::parallel_sum_scale`), which the
58//! FFN bodies apply to the WHOLE branch output -- `ffn_out + moe_out`
59//! -- before the post-FFN norm, where `grok.cpp:180` applies it. A
60//! Grok-1 layer has no triple, loads no shared expert and carries no
61//! scale, which is the `else` branch.
62
63use std::f32::consts::FRAC_1_SQRT_2;
64
65use frink_gguf::TensorSource;
66
67use crate::LoadError;
68
69/// Whether the dense triple must be present on a routed layer.
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum DensePresence {
72 /// `create_tensor(..., 0)`: absent is a load error upstream.
73 Required,
74 /// `TENSOR_NOT_REQUIRED`: absent means "no dense branch, no scale".
75 Optional,
76}
77
78/// One architecture whose routed layers sum a dense FFN with the
79/// experts.
80#[derive(Debug, Clone, Copy)]
81pub struct ParallelDenseFfn {
82 pub arch: &'static str,
83 pub presence: DensePresence,
84 /// The factor on `ffn_out + moe_out`, or `None` for a plain sum.
85 pub sum_scale: Option<f32>,
86 pub lines: &'static str,
87}
88
89/// The two graphs, with the lines.
90pub const PARALLEL_DENSE_FFN_ARCHITECTURES: &[ParallelDenseFfn] = &[
91 ParallelDenseFfn {
92 arch: "grok",
93 presence: DensePresence::Optional,
94 sum_scale: Some(FRAC_1_SQRT_2),
95 lines: "src/models/grok.cpp:66-68,171-184",
96 },
97 ParallelDenseFfn {
98 arch: "arctic",
99 presence: DensePresence::Required,
100 sum_scale: None,
101 lines: "src/models/arctic.cpp:38-42,118-154",
102 },
103];
104
105/// The SAME scale on the SAME sum, under the `_shexp` names: a graph
106/// whose routed layers carry a real shared expert and scale
107/// `moe_out + shexp_out` by a constant. `cohere2moe.cpp:248-260`
108/// (`ggml_scale(ctx0, ggml_add(cur, ffn_shexp), 0.5f)`, the HF
109/// "average" combination strategy, the only one its converter admits,
110/// `conversion/command_r.py:103-105`) is the one graph of 155 that does
111/// (measured: `grep -n 'ggml_scale' src/models/*.cpp` beside a
112/// `ffn_shexp`), and only on a layer that HAS the shared expert
113/// (`:248` `if (layer.ffn_up_shexp)`). The loader records it as
114/// `MoeWeights::parallel_sum_scale`, the field the two dense rows
115/// above already fill, so the FFN bodies apply one scale at one site
116/// whichever names the dense branch was loaded from.
117pub const SHARED_EXPERT_SUM_SCALE: &[(&str, f32, &str)] =
118 &[("cohere2moe", 0.5, "src/models/cohere2moe.cpp:248-260")];
119
120/// The scale on `moe_out + shexp_out` for `arch`, when its routed
121/// layer carries a shared expert; `None` for a plain sum.
122pub fn shared_expert_sum_scale(arch: &str, has_shared_expert: bool) -> Option<f32> {
123 if !has_shared_expert {
124 return None;
125 }
126 SHARED_EXPERT_SUM_SCALE
127 .iter()
128 .find(|(a, _, _)| *a == arch)
129 .map(|(_, scale, _)| *scale)
130}
131
132/// The row for an architecture, or `None` for one whose routed layers
133/// have no dense branch.
134pub fn parallel_dense_ffn(arch: &str) -> Option<&'static ParallelDenseFfn> {
135 PARALLEL_DENSE_FFN_ARCHITECTURES
136 .iter()
137 .find(|row| row.arch == arch)
138}
139
140/// Whether routed layer `l` of `arch` carries the dense triple, by the
141/// row's presence rule: `Some(row)` when it does and the loader should
142/// fill the shared-expert slot from `ffn_{gate,up,down}` and record the
143/// scale, `None` when the layer runs the experts alone.
144///
145/// Refuses a REQUIRED triple that is missing (llama.cpp's loader would
146/// fail on the same tensor), and an incomplete triple in either case,
147/// because a graph with `ffn_up` and no `ffn_down` exists nowhere.
148pub fn parallel_dense_for_layer(
149 arch: &str,
150 file: &impl TensorSource,
151 l: usize,
152) -> Result<Option<&'static ParallelDenseFfn>, LoadError> {
153 let Some(row) = parallel_dense_ffn(arch) else {
154 return Ok(None);
155 };
156 let names = ["ffn_gate", "ffn_up", "ffn_down"].map(|t| format!("blk.{l}.{t}.weight"));
157 let present = names
158 .iter()
159 .filter(|n| file.find_tensor(n).is_some())
160 .count();
161 match (present, row.presence) {
162 (3, _) => Ok(Some(row)),
163 (0, DensePresence::Optional) => Ok(None),
164 (0, DensePresence::Required) => Err(LoadError::Gguf(
165 frink_gguf::GgufError::TensorNotFound(format!(
166 "{} (the dense half of `{arch}`'s parallel dense + MoE layer, REQUIRED by {})",
167 names[1], row.lines
168 )),
169 )),
170 _ => Err(LoadError::UnsupportedFeature(
171 arch.to_string(),
172 format!(
173 "layer {l} carries {present} of the dense `ffn_gate` / `ffn_up` / `ffn_down` \
174 triple; {} reads all three or none",
175 row.lines
176 ),
177 )),
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use crate::test_source::StubSource;
185
186 const TRIPLE: [&str; 3] = [
187 "blk.0.ffn_gate.weight",
188 "blk.0.ffn_up.weight",
189 "blk.0.ffn_down.weight",
190 ];
191
192 /// Grok-2's triple is served with the scale; Grok-1's absence is
193 /// the `else` branch; Arctic's absence is a missing REQUIRED tensor.
194 #[test]
195 fn presence_follows_each_rows_rule() {
196 let grok2 = parallel_dense_for_layer("grok", &StubSource::with_tensors(&TRIPLE), 0)
197 .unwrap()
198 .expect("Grok-2 has the branch");
199 assert_eq!(grok2.sum_scale, Some(FRAC_1_SQRT_2));
200 assert!(
201 parallel_dense_for_layer("grok", &StubSource::with_tensors(&[]), 0)
202 .unwrap()
203 .is_none()
204 );
205
206 let arctic = parallel_dense_for_layer("arctic", &StubSource::with_tensors(&TRIPLE), 0)
207 .unwrap()
208 .expect("Arctic always has the branch");
209 assert_eq!(arctic.sum_scale, None);
210 let err = parallel_dense_for_layer("arctic", &StubSource::with_tensors(&[]), 0)
211 .expect_err("REQUIRED");
212 assert!(err.to_string().contains("arctic.cpp:38-42"), "{err}");
213
214 // An incomplete triple is refused on both rows.
215 for arch in ["grok", "arctic"] {
216 let err = parallel_dense_for_layer(arch, &StubSource::with_tensors(&TRIPLE[..2]), 0)
217 .err()
218 .unwrap_or_else(|| panic!("{arch}: 2 of 3 refused"));
219 assert!(err.to_string().contains("2 of the dense"), "{err}");
220 }
221 }
222
223 /// Every other architecture with a dense `ffn_up` is a dense model
224 /// or a leading-dense MoE, and neither is this table's business.
225 #[test]
226 fn a_dense_ffn_on_any_other_architecture_is_not_this_tables_business() {
227 for arch in ["llama", "deepseek", "dbrx", "qwen3moe", "smallthinker"] {
228 assert!(
229 parallel_dense_for_layer(arch, &StubSource::with_tensors(&TRIPLE), 0)
230 .unwrap()
231 .is_none(),
232 "{arch}"
233 );
234 }
235 }
236
237 /// Every row is an audited generic-path architecture, or the seam
238 /// is unevidenced.
239 #[test]
240 fn every_row_is_an_audited_generic_row() {
241 for row in PARALLEL_DENSE_FFN_ARCHITECTURES {
242 let profile = crate::capability::resolve_profile(row.arch).unwrap_or_else(|| {
243 panic!(
244 "`{}` ({}) is not a registered architecture",
245 row.arch, row.lines
246 )
247 });
248 assert!(matches!(
249 profile.path,
250 crate::capability::ArchPath::GenericGqa { .. }
251 ));
252 assert!(
253 crate::capability::AUDITED_GENERIC_GQA.contains(&row.arch),
254 "{}",
255 row.arch
256 );
257 }
258 }
259}