1use super::kda::KdaConfig;
6use super::latent_moe::LatentMoeConfig;
7use super::layer::{K3Graph, K3LayerSpec, MixerKind, MlpKind};
8use super::mla::MlaConfig;
9use super::ops::{fill, ident, ones};
10
11#[derive(Clone, Copy, Debug)]
14pub struct Ablation {
15 pub attnres_mix: f32,
16 pub force_expert: Option<usize>,
17 pub skip_layer: Option<usize>,
19 pub o_proj_tp: usize,
21 pub drop_o_proj_rank: Option<usize>,
23}
24
25impl Default for Ablation {
26 fn default() -> Self {
27 Self {
28 attnres_mix: 1.0,
29 force_expert: None,
30 skip_layer: None,
31 o_proj_tp: 1,
32 drop_o_proj_rank: None,
33 }
34 }
35}
36
37impl Ablation {
38 pub fn from_env() -> Self {
44 let mut a = Self::default();
45 if let Ok(v) = std::env::var("K3_ATTNRES_MIX")
46 && let Ok(m) = v.parse()
47 {
48 a.attnres_mix = m;
49 }
50 if let Ok(v) = std::env::var("K3_FORCE_EXPERT") {
51 a.force_expert = v.parse().ok();
52 }
53 a
54 }
55}
56
57#[derive(Clone, Debug)]
58pub struct KdaWeights {
59 pub q_proj: Vec<f32>,
60 pub k_proj: Vec<f32>,
61 pub v_proj: Vec<f32>,
62 pub conv: Vec<f32>,
63 pub f_a: Vec<f32>,
64 pub f_b: Vec<f32>,
65 pub dt_bias: Vec<f32>,
66 pub a_log: Vec<f32>,
67 pub b_proj: Vec<f32>,
68 pub g_proj: Vec<f32>,
69 pub o_norm: Vec<f32>,
70 pub o_proj: Vec<f32>,
71}
72
73#[derive(Clone, Debug)]
74pub struct MlaWeights {
75 pub q_a: Vec<f32>,
76 pub q_a_ln: Vec<f32>,
77 pub q_b: Vec<f32>,
78 pub kv_a: Vec<f32>,
79 pub kv_a_ln: Vec<f32>,
80 pub kv_b: Vec<f32>,
81 pub g_proj: Vec<f32>,
82 pub o_proj: Vec<f32>,
83}
84
85#[derive(Clone, Debug)]
86pub struct DenseMlp {
87 pub gate: Vec<f32>,
88 pub up: Vec<f32>,
89 pub down: Vec<f32>,
90}
91
92#[derive(Clone, Debug)]
93pub struct MoeWeights {
94 pub down: Vec<f32>,
95 pub up: Vec<f32>,
96 pub norm: Vec<f32>,
97 pub router: Vec<f32>,
98 pub bias: Vec<f32>,
99 pub experts: Vec<(Vec<f32>, Vec<f32>, Vec<f32>)>,
100 pub shared: Option<DenseMlp>,
101}
102
103#[derive(Clone, Debug)]
104pub enum MixerW {
105 Kda(KdaWeights),
106 Mla(MlaWeights),
107}
108
109#[derive(Clone, Debug)]
110pub enum MlpW {
111 Dense(DenseMlp),
112 Moe(MoeWeights),
113}
114
115#[derive(Clone, Debug)]
116pub struct K3CpuLayer {
117 pub spec: K3LayerSpec,
118 pub input_norm: Vec<f32>,
119 pub post_norm: Vec<f32>,
120 pub attn_res_proj: Vec<f32>,
121 pub attn_res_norm: Vec<f32>,
122 pub mlp_res_proj: Vec<f32>,
123 pub mlp_res_norm: Vec<f32>,
124 pub mixer: MixerW,
125 pub mlp: MlpW,
126}
127
128#[derive(Clone, Debug)]
130pub struct K3CpuModel {
131 pub graph: K3Graph,
132 pub kda: KdaConfig,
133 pub mla: MlaConfig,
134 pub moe: LatentMoeConfig,
135 pub dense_intermediate: usize,
136 pub eps: f32,
137 pub rope_theta: f32,
138 pub vocab: usize,
139 pub embed: Vec<f32>,
140 pub lm_head: Vec<f32>,
141 pub final_norm: Vec<f32>,
142 pub output_res_proj: Vec<f32>,
143 pub output_res_norm: Vec<f32>,
144 pub layers: Vec<K3CpuLayer>,
145}
146
147impl K3CpuModel {
148 pub fn synthetic_tiny() -> Self {
150 let hidden = 4;
151 let graph = twin_pattern_graph(hidden);
152 let kda = KdaConfig {
153 heads: 1,
154 head_dim: 2,
155 conv_kernel: 4,
156 gate_lower_bound: Some(-5.0),
157 use_full_rank_gate: true,
158 };
159 let mla = MlaConfig {
160 heads: 1,
161 qk_nope_head_dim: 2,
162 qk_rope_head_dim: 2,
163 v_head_dim: 2,
164 q_lora_rank: 4,
165 kv_lora_rank: 2,
166 mla_use_nope: true,
167 mla_use_output_gate: true,
168 };
169 let moe = LatentMoeConfig {
170 hidden,
171 latent: 4,
172 expert_hidden: 4,
173 n_routed: 2,
174 top_k: 1,
175 n_shared: 1,
176 situ_beta: 4.0,
177 situ_linear_beta: 25.0,
178 use_norm: true,
179 renormalize: true,
180 };
181 Self::build(graph, kda, mla, moe, 4, 8, 1e-5, 10_000.0)
182 }
183
184 pub fn synthetic_small() -> Self {
186 let hidden = 8;
187 let graph = twin_pattern_graph(hidden);
188 let kda = KdaConfig {
189 heads: 2,
190 head_dim: 4,
191 conv_kernel: 4,
192 gate_lower_bound: Some(-5.0),
193 use_full_rank_gate: true,
194 };
195 let mla = MlaConfig {
196 heads: 2,
197 qk_nope_head_dim: 4,
198 qk_rope_head_dim: 2,
199 v_head_dim: 4,
200 q_lora_rank: 8,
201 kv_lora_rank: 4,
202 mla_use_nope: true,
203 mla_use_output_gate: true,
204 };
205 let moe = LatentMoeConfig {
206 hidden,
207 latent: 8,
208 expert_hidden: 8,
209 n_routed: 2,
210 top_k: 1,
211 n_shared: 1,
212 situ_beta: 4.0,
213 situ_linear_beta: 25.0,
214 use_norm: true,
215 renormalize: true,
216 };
217 Self::build(graph, kda, mla, moe, 8, 16, 1e-5, 10_000.0)
218 }
219
220 pub fn synthetic_prod_width_dummy() -> Self {
224 let hidden = 7168;
225 let graph = prod_width_dummy_graph(hidden);
226 let kda = KdaConfig {
227 heads: 96,
228 head_dim: 2,
229 conv_kernel: 4,
230 gate_lower_bound: Some(-5.0),
231 use_full_rank_gate: true,
232 };
233 let mla = MlaConfig {
234 heads: 96,
235 qk_nope_head_dim: 2,
236 qk_rope_head_dim: 2,
237 v_head_dim: 2,
238 q_lora_rank: 8,
239 kv_lora_rank: 4,
240 mla_use_nope: true,
241 mla_use_output_gate: true,
242 };
243 let moe = LatentMoeConfig {
244 hidden,
245 latent: 64,
246 expert_hidden: 64,
247 n_routed: 8,
248 top_k: 2,
249 n_shared: 1,
250 situ_beta: 4.0,
251 situ_linear_beta: 25.0,
252 use_norm: true,
253 renormalize: true,
254 };
255 let mut model = Self::build(graph, kda, mla, moe, 64, 256, 1e-5, 10_000.0);
256 for (i, layer) in model.layers.iter_mut().enumerate() {
259 let seed = 200 + i as u32;
260 match &mut layer.mixer {
261 MixerW::Kda(w) => w.o_proj = fill(w.o_proj.len(), seed, 0.05),
262 MixerW::Mla(w) => w.o_proj = fill(w.o_proj.len(), seed + 1, 0.05),
263 }
264 }
265 model
266 }
267}
268
269fn twin_pattern_graph(hidden: usize) -> K3Graph {
270 let mixers = [
271 MixerKind::Kda,
272 MixerKind::Kda,
273 MixerKind::Kda,
274 MixerKind::Mla,
275 MixerKind::Kda,
276 MixerKind::Kda,
277 MixerKind::Kda,
278 MixerKind::Mla,
279 ];
280 let layers = mixers
281 .iter()
282 .enumerate()
283 .map(|(i, m)| K3LayerSpec {
284 index: i,
285 mixer: *m,
286 mlp: if i == 0 {
287 MlpKind::Dense
288 } else {
289 MlpKind::LatentMoe
290 },
291 })
292 .collect();
293 K3Graph {
294 layers,
295 hidden,
296 attn_res_block_size: 4,
297 situ_beta: 4.0,
298 situ_linear_beta: 25.0,
299 use_full_rank_gate: true,
300 mla_use_nope: true,
301 mla_use_output_gate: true,
302 }
303}
304
305fn prod_width_dummy_graph(hidden: usize) -> K3Graph {
306 let layers = vec![
307 K3LayerSpec {
308 index: 0,
309 mixer: MixerKind::Kda,
310 mlp: MlpKind::Dense,
311 },
312 K3LayerSpec {
313 index: 1,
314 mixer: MixerKind::Mla,
315 mlp: MlpKind::LatentMoe,
316 },
317 ];
318 K3Graph {
319 layers,
320 hidden,
321 attn_res_block_size: 12,
322 situ_beta: 4.0,
323 situ_linear_beta: 25.0,
324 use_full_rank_gate: true,
325 mla_use_nope: true,
326 mla_use_output_gate: true,
327 }
328}
329
330impl K3CpuModel {
331 #[allow(clippy::too_many_arguments)]
332 fn build(
333 graph: K3Graph,
334 kda: KdaConfig,
335 mla: MlaConfig,
336 moe: LatentMoeConfig,
337 dense_intermediate: usize,
338 vocab: usize,
339 eps: f32,
340 rope_theta: f32,
341 ) -> Self {
342 let h = graph.hidden;
343 let layers = graph
344 .layers
345 .iter()
346 .map(|spec| synth_layer(spec, &kda, &mla, &moe, dense_intermediate))
347 .collect();
348 Self {
349 graph,
350 kda,
351 mla,
352 moe,
353 dense_intermediate,
354 eps,
355 rope_theta,
356 vocab,
357 embed: fill(vocab * h, 1, 0.05),
358 lm_head: fill(vocab * h, 2, 0.05),
359 final_norm: ones(h),
360 output_res_proj: fill(h, 3, 0.05),
361 output_res_norm: ones(h),
362 layers,
363 }
364 }
365}
366
367fn synth_layer(
368 spec: &K3LayerSpec,
369 kda: &KdaConfig,
370 mla: &MlaConfig,
371 moe: &LatentMoeConfig,
372 dense_int: usize,
373) -> K3CpuLayer {
374 let h = moe.hidden;
375 let seed = 10 + spec.index as u32 * 17;
376 let mixer = match spec.mixer {
377 MixerKind::Kda => MixerW::Kda(synth_kda(kda, h, seed)),
378 MixerKind::Mla => MixerW::Mla(synth_mla(mla, h, seed)),
379 };
380 let mlp = match spec.mlp {
381 MlpKind::Dense => MlpW::Dense(synth_dense(h, dense_int, seed + 1)),
382 MlpKind::LatentMoe => MlpW::Moe(synth_moe(moe, seed + 2)),
383 };
384 K3CpuLayer {
385 spec: *spec,
386 input_norm: ones(h),
387 post_norm: ones(h),
388 attn_res_proj: fill(h, seed + 3, 0.05),
389 attn_res_norm: ones(h),
390 mlp_res_proj: fill(h, seed + 4, 0.05),
391 mlp_res_norm: ones(h),
392 mixer,
393 mlp,
394 }
395}
396
397fn synth_kda(kda: &KdaConfig, hidden: usize, seed: u32) -> KdaWeights {
398 let q = kda.qkv_dim();
399 KdaWeights {
400 q_proj: ident(q, hidden),
401 k_proj: ident(q, hidden),
402 v_proj: ident(q, hidden),
403 conv: fill(kda.conv_elems(), seed, 0.05),
404 f_a: fill(kda.head_dim * hidden, seed + 1, 0.05),
405 f_b: fill(q * kda.head_dim, seed + 2, 0.05),
406 dt_bias: vec![0.0; q],
407 a_log: vec![-1.0; kda.heads],
408 b_proj: fill(kda.heads * hidden, seed + 3, 0.05),
409 g_proj: fill(q * hidden, seed + 4, 0.05),
410 o_norm: ones(kda.head_dim),
411 o_proj: ident(hidden, q),
412 }
413}
414
415fn synth_mla(mla: &MlaConfig, hidden: usize, seed: u32) -> MlaWeights {
416 let qk = mla.heads * mla.qk_head_dim();
417 let dv = mla.heads * mla.v_head_dim;
418 let kv_in = mla.kv_lora_rank + mla.qk_rope_head_dim;
419 let kv_b_out = mla.heads * (mla.qk_nope_head_dim + mla.v_head_dim);
420 MlaWeights {
421 q_a: ident(mla.q_lora_rank, hidden),
422 q_a_ln: ones(mla.q_lora_rank),
423 q_b: fill(qk * mla.q_lora_rank, seed, 0.05),
424 kv_a: fill(kv_in * hidden, seed + 1, 0.05),
425 kv_a_ln: ones(mla.kv_lora_rank),
426 kv_b: fill(kv_b_out * mla.kv_lora_rank, seed + 2, 0.05),
427 g_proj: fill(dv * hidden, seed + 3, 0.05),
428 o_proj: ident(hidden, dv),
429 }
430}
431
432fn synth_dense(h: usize, inter: usize, seed: u32) -> DenseMlp {
433 DenseMlp {
434 gate: fill(inter * h, seed, 0.05),
435 up: fill(inter * h, seed + 1, 0.05),
436 down: ident(h, inter),
437 }
438}
439
440fn synth_moe(moe: &LatentMoeConfig, seed: u32) -> MoeWeights {
441 let mut experts = Vec::with_capacity(moe.n_routed);
442 for e in 0..moe.n_routed {
443 let mut w3 = ident(moe.expert_hidden, moe.latent);
444 if e > 0 {
446 w3[0] = 1.0 + e as f32;
447 }
448 experts.push((
449 ident(moe.expert_hidden, moe.latent),
450 ident(moe.latent, moe.expert_hidden),
451 w3,
452 ));
453 }
454 let shared = (moe.n_shared > 0).then(|| synth_dense(moe.hidden, moe.expert_hidden, seed + 9));
455 MoeWeights {
456 down: ident(moe.latent, moe.hidden),
457 up: ident(moe.hidden, moe.latent),
458 norm: ones(moe.latent),
459 router: fill(moe.n_routed * moe.hidden, seed, 0.2),
460 bias: vec![0.0; moe.n_routed],
461 experts,
462 shared,
463 }
464}