1use ferrox_core::matmul::{rms_norm, situ_and_mul};
39use ferrox_core::tensor::Tensor;
40use ferrox_core::weight_matrix::WeightMatrix;
41
42use crate::block_residual::apply_attn_res;
43use crate::kda::{self, KdaAttnWeights, KdaState};
44use crate::latent_moe::{self, KimiLatentMoeWeights, KimiMoeConfig};
45use crate::mla::{self, MlaAttnWeights};
46
47pub enum KimiLayerAttention {
48 Kda(Box<KdaAttnWeights>),
49 Mla(Box<MlaAttnWeights>),
50}
51
52pub struct DenseMlpWeights {
56 pub gate_proj: WeightMatrix,
57 pub up_proj: WeightMatrix,
58 pub down_proj: WeightMatrix,
59}
60
61impl DenseMlpWeights {
62 pub(crate) fn forward(&self, x: &[f32], situ_beta: f32, situ_linear_beta: f32) -> Vec<f32> {
63 let gate = self.gate_proj.apply(x);
64 let up = self.up_proj.apply(x);
65 let combined = situ_and_mul(&gate, &up, situ_beta, situ_linear_beta);
66 self.down_proj.apply(&combined)
67 }
68}
69
70pub enum KimiLayerFfn {
71 Dense(Box<DenseMlpWeights>),
72 Moe(Box<KimiLatentMoeWeights>),
73}
74
75pub struct KimiDecoderLayerWeights {
76 pub input_layernorm_weight: Vec<f32>,
77 pub attn: KimiLayerAttention,
78 pub post_attention_layernorm_weight: Vec<f32>,
79 pub ffn: KimiLayerFfn,
80 pub self_attention_res_norm_weight: Vec<f32>,
84 pub self_attention_res_proj_weight: Vec<f32>,
85 pub mlp_res_norm_weight: Vec<f32>,
86 pub mlp_res_proj_weight: Vec<f32>,
87}
88
89pub struct KimiDecoderWeights {
90 pub embedding: Tensor, pub layers: Vec<KimiDecoderLayerWeights>,
92 pub output_attn_res_norm_weight: Vec<f32>,
93 pub output_attn_res_proj_weight: Vec<f32>,
94 pub final_norm_weight: Vec<f32>,
95 pub output_head: WeightMatrix, }
97
98impl KimiDecoderWeights {
99 pub fn expert_store_stats(&self) -> Option<ferrox_core::expert_store::ExpertStoreStats> {
103 self.layers.iter().find_map(|l| match &l.ffn {
104 KimiLayerFfn::Moe(moe) => match &moe.experts {
105 crate::latent_moe::KimiExpertBacking::Stored { store, .. } => Some(store.stats()),
106 crate::latent_moe::KimiExpertBacking::Resident(_) => None,
107 },
108 KimiLayerFfn::Dense(_) => None,
109 })
110 }
111}
112
113pub struct KimiDecoderConfig {
114 pub attn_res_block_size: usize,
115 pub rms_norm_eps: f32,
116 pub situ_beta: f32,
117 pub situ_linear_beta: f32,
118 pub moe: KimiMoeConfig,
119}
120
121pub enum KimiLayerState {
122 Kda(KdaState),
123 Mla {
124 k_cache: Vec<f32>,
125 v_cache: Vec<f32>,
126 },
127}
128
129pub struct KimiDecodeState {
130 layer_states: Vec<KimiLayerState>,
131}
132
133impl KimiDecodeState {
134 pub fn new(weights: &KimiDecoderWeights, kda_cfg: &crate::config::KdaConfig) -> Self {
137 let layer_states = weights
138 .layers
139 .iter()
140 .map(|l| match &l.attn {
141 KimiLayerAttention::Kda(_) => KimiLayerState::Kda(KdaState::new(kda_cfg)),
142 KimiLayerAttention::Mla(_) => KimiLayerState::Mla {
143 k_cache: Vec::new(),
144 v_cache: Vec::new(),
145 },
146 })
147 .collect();
148 KimiDecodeState { layer_states }
149 }
150}
151
152pub fn kimi_forward_token(
154 weights: &KimiDecoderWeights,
155 cfg: &KimiDecoderConfig,
156 mla_cfg: &crate::config::MlaConfig,
157 kda_cfg: &crate::config::KdaConfig,
158 token_id: usize,
159 state: &mut KimiDecodeState,
160) -> Vec<f32> {
161 let hidden_dim = weights.embedding.cols();
162 let mut hidden = weights.embedding.row(token_id).to_vec();
163 let mut block_residual: Vec<f32> = Vec::new();
167
168 for (layer_idx, layer) in weights.layers.iter().enumerate() {
169 let prefix_sum_pre_blend = hidden.clone();
170
171 let blended = if block_residual.is_empty() {
172 prefix_sum_pre_blend.clone()
173 } else {
174 apply_attn_res(
175 &prefix_sum_pre_blend,
176 &block_residual,
177 &layer.self_attention_res_norm_weight,
178 &layer.self_attention_res_proj_weight,
179 cfg.rms_norm_eps,
180 )
181 };
182
183 let mut prefix_sum: Option<Vec<f32>> = Some(prefix_sum_pre_blend.clone());
184 if layer_idx % cfg.attn_res_block_size == 0 {
185 block_residual.extend_from_slice(&prefix_sum_pre_blend);
186 prefix_sum = None;
187 }
188
189 let normed = rms_norm(&blended, &layer.input_layernorm_weight, cfg.rms_norm_eps);
190 let attn_out = match (&layer.attn, &mut state.layer_states[layer_idx]) {
191 (KimiLayerAttention::Kda(w), KimiLayerState::Kda(s)) => {
192 kda::kda_forward_token(w, kda_cfg, &normed, cfg.rms_norm_eps, s)
193 }
194 (KimiLayerAttention::Mla(w), KimiLayerState::Mla { k_cache, v_cache }) => {
195 mla::mla_forward_token(w, mla_cfg, &normed, cfg.rms_norm_eps, k_cache, v_cache)
196 }
197 _ => unreachable!("layer attention kind and decode state kind must always match"),
198 };
199
200 let mut prefix_sum = match prefix_sum {
201 Some(mut ps) => {
202 for (p, a) in ps.iter_mut().zip(attn_out.iter()) {
203 *p += a;
204 }
205 ps
206 }
207 None => attn_out,
208 };
209
210 let blended2 = apply_attn_res(
211 &prefix_sum,
212 &block_residual,
213 &layer.mlp_res_norm_weight,
214 &layer.mlp_res_proj_weight,
215 cfg.rms_norm_eps,
216 );
217 let normed2 = rms_norm(
218 &blended2,
219 &layer.post_attention_layernorm_weight,
220 cfg.rms_norm_eps,
221 );
222 let ffn_out = match &layer.ffn {
223 KimiLayerFfn::Dense(w) => w.forward(&normed2, cfg.situ_beta, cfg.situ_linear_beta),
224 KimiLayerFfn::Moe(w) => latent_moe::kimi_latent_moe_forward(w, &cfg.moe, &normed2),
225 };
226
227 for (p, f) in prefix_sum.iter_mut().zip(ffn_out.iter()) {
228 *p += f;
229 }
230 hidden = prefix_sum;
231 }
232
233 if !block_residual.is_empty() {
234 hidden = apply_attn_res(
235 &hidden,
236 &block_residual,
237 &weights.output_attn_res_norm_weight,
238 &weights.output_attn_res_proj_weight,
239 cfg.rms_norm_eps,
240 );
241 }
242
243 let final_normed = rms_norm(&hidden, &weights.final_norm_weight, cfg.rms_norm_eps);
244 assert_eq!(final_normed.len(), hidden_dim);
245 weights.output_head.apply(&final_normed)
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use crate::config::{KdaConfig, MlaConfig};
252 use crate::kda::KdaAttnWeights;
253 use crate::latent_moe::KimiExpertBacking;
254 use crate::latent_moe::KimiExpertWeights;
255 use crate::mla::MlaAttnWeights;
256 use ferrox_core::tensor::Tensor;
257
258 const HIDDEN_DIM: usize = 8;
259 const EPS: f32 = 1e-5;
260 const SITU_BETA: f32 = 4.0;
261 const SITU_LINEAR_BETA: f32 = 25.0;
262 const ATTN_RES_BLOCK_SIZE: usize = 2;
263
264 const KDA_NUM_HEADS: usize = 2;
265 const KDA_HEAD_DIM: usize = 3;
266 const KDA_PROJ: usize = KDA_NUM_HEADS * KDA_HEAD_DIM;
267 const KDA_CONV_SIZE: usize = 4;
268 const KDA_GATE_LOWER_BOUND: f32 = -5.0;
269
270 const DENSE_INTERMEDIATE: usize = 5;
271
272 const MLA_NUM_HEADS: usize = 2;
273 const MLA_QK_NOPE: usize = 3;
274 const MLA_QK_ROPE: usize = 2;
275 const MLA_KV_LORA: usize = 4;
276 const MLA_Q_LORA: usize = 6;
277 const MLA_V_HEAD_DIM: usize = 3;
278 const MLA_PROJ: usize = MLA_NUM_HEADS * MLA_V_HEAD_DIM;
279
280 const MOE_HIDDEN_DIM: usize = 4;
281 const MOE_INTERMEDIATE: usize = 3;
282 const N_EXPERTS: usize = 4;
283 const TOP_K: usize = 2;
284 const SHARED_INTERMEDIATE: usize = 3;
285 const OUTPUT_VOCAB: usize = 5;
286
287 fn wm(data: &[f32], rows: usize, cols: usize) -> WeightMatrix {
288 assert_eq!(data.len(), rows * cols);
289 WeightMatrix::F32(Tensor::new(data.to_vec(), vec![rows, cols]))
290 }
291
292 const KDA_Q_PROJ: [f32; 48] = [
294 0.0332731, -0.0251273, -0.241248, -0.645647, 0.363556, -0.144589, -0.058427, -0.264839,
295 -0.174983, -0.313658, -0.0222095, 0.0329603, -0.0773659, 0.179789, -0.428436, 0.272643,
296 -0.29953, -0.124363, -0.270533, -0.15906, -0.136326, 0.138484, 0.728104, -0.306624,
297 -0.0415019, -0.62199, -0.0334468, 0.0501355, 0.374942, 0.438693, 0.398001, 0.0660859,
298 0.146161, -0.129336, 0.701423, -0.0954704, -0.37886, -0.0410528, 0.653784, 0.193832,
299 0.451906, 0.367486, 0.0999694, -0.498877, 0.495604, -0.203314, -0.326699, -0.410713,
300 ];
301 const KDA_K_PROJ: [f32; 48] = [
302 -0.421999, -0.0844493, 0.0366557, -0.373529, -0.320309, -0.171037, -0.208623, -0.251102,
303 -0.384313, -0.242582, -0.708695, -0.0410055, -0.163735, 0.045339, 0.0596934, -0.462932,
304 0.344817, 0.314063, 0.183688, -0.215147, 0.0682644, -0.119572, 0.24693, -0.171698,
305 -0.427691, -0.311782, -0.243235, 0.168478, -0.322684, -0.0423794, 0.296734, -0.106622,
306 0.0607117, 0.900444, 0.214488, 0.390483, 0.0470901, -0.084555, -0.509912, 0.273387,
307 -0.0251134, 0.459143, 0.34484, -0.102602, -0.315908, -0.311536, -0.154464, -0.067087,
308 ];
309 const KDA_V_PROJ: [f32; 48] = [
310 0.106298, -0.296877, -0.441024, 0.346085, -0.211878, 0.208265, 0.286445, 0.254616,
311 0.527007, 0.0238059, 0.0520139, -0.0428818, 0.547271, 0.0641891, 0.406677, -0.159912,
312 0.315001, 0.090456, 0.665635, 0.0849947, 0.332725, 0.0468494, -0.340192, -0.57511,
313 -0.199206, 0.00843515, 0.307353, -0.341596, -0.188955, -0.0262392, 0.711621, -0.12957,
314 0.0589482, 0.6676, -0.0487213, 0.318632, -0.129497, 0.45287, 0.312848, 0.404807, 0.300039,
315 -0.111872, 0.00619566, 0.337312, 0.203499, -0.615727, -0.426802, 0.00709124,
316 ];
317 const KDA_Q_CONV_W: [f32; 24] = [
318 0.232949, -0.349125, -0.144423, -1.17421, 0.392014, -0.0551284, -0.188107, 0.34828,
319 0.182368, -1.15816, 0.29745, -0.163951, -0.785389, -0.0799616, 0.0853995, 0.159346,
320 0.438013, 0.16794, 0.11068, 0.222541, 0.27932, 0.584781, 0.0801223, 0.315161,
321 ];
322 const KDA_K_CONV_W: [f32; 24] = [
323 0.0811582, 0.5723, 0.275913, 0.337176, 0.30982, -0.260437, -0.368242, 0.355531, -0.940038,
324 -0.0380444, 0.559686, -0.632208, 0.0297131, -0.246906, 0.435212, -0.65012, -0.207435,
325 -0.259419, -0.219341, 0.426728, -0.0195621, -0.164165, 0.231709, 0.363266,
326 ];
327 const KDA_V_CONV_W: [f32; 24] = [
328 0.174442,
329 0.16448,
330 -0.11751,
331 -0.00693412,
332 0.22015,
333 0.1184,
334 0.59284,
335 -0.100545,
336 0.904578,
337 -0.057162,
338 -0.185486,
339 -0.0155876,
340 0.239106,
341 -0.184512,
342 0.887074,
343 0.319565,
344 0.518244,
345 0.238721,
346 -0.0932803,
347 -0.614529,
348 0.400545,
349 0.458996,
350 -0.217588,
351 0.0540083,
352 ];
353 const KDA_A_LOG: [f32; 2] = [2.57153, 1.68862];
354 const KDA_F_A_PROJ: [f32; 24] = [
355 0.293008, 0.208071, -0.0458425, 0.26919, -0.0604267, -0.414273, -0.347327, 0.435059,
356 -0.0875413, 0.260375, 0.78432, -0.0103574, 0.129559, 0.522936, 0.205997, 0.235916,
357 0.129708, -0.515258, -0.251447, 0.0114761, -0.0608938, -0.142939, 0.0391045, -0.106787,
358 ];
359 const KDA_F_B_PROJ: [f32; 18] = [
360 0.258232, 0.0799449, -0.128173, 0.374093, 0.0249372, 0.564439, 0.291732, -0.673602,
361 0.300492, -0.312056, -0.0782346, -0.560855, 0.319259, -0.194854, -0.109685, -0.295024,
362 -0.0275032, -0.287207,
363 ];
364 const KDA_DT_BIAS: [f32; 6] = [0.406797, 0.44378, -0.118682, -0.101149, 0.144153, 0.100643];
365 const KDA_B_PROJ: [f32; 16] = [
366 -0.557976, 0.101854, -0.529571, 0.437377, 0.282072, 0.0808985, 0.119817, -0.234031,
367 -0.114455, -0.182255, 0.492746, -0.213926, -0.0890158, 0.520648, -0.0288543, 0.378583,
368 ];
369 const KDA_G_PROJ: [f32; 48] = [
370 0.115836, 0.257017, -0.28702, 0.0285552, 0.0241214, -0.586101, -0.284745, -0.1296,
371 0.369078, -0.275476, 0.352622, 0.235832, -0.13984, 0.230448, 0.440173, 0.224343, 0.103446,
372 0.108157, -0.523653, -0.674568, 0.130624, 0.228334, -0.493531, 0.257466, -0.0398756,
373 -0.385554, 0.209178, -0.414277, -0.364987, 0.515843, -0.486846, -0.128941, -0.0565001,
374 0.480469, -0.0731606, 0.279074, -0.16879, -0.589812, 0.200213, -0.3698, 0.355666, 0.269551,
375 0.450168, -0.0689023, -0.234073, -0.81206, 0.250412, 0.0890739,
376 ];
377 const KDA_O_NORM_W: [f32; 3] = [0.970175, 0.902756, 1.03511];
378 const KDA_O_PROJ: [f32; 48] = [
379 -0.148986, -0.157794, 0.225606, -0.302445, -0.0587451, -0.235495, -0.541087, -0.270422,
380 0.604319, 0.371634, 0.144359, 0.409312, 0.108881, -0.352629, -0.129886, -0.258043,
381 -0.168576, -0.241786, 0.0283778, -0.097169, -0.0738384, 0.102603, 0.302548, 0.219944,
382 0.144096, -0.521365, -0.354098, -0.79218, -0.227326, -0.778447, -0.221429, -0.0319402,
383 0.377724, 0.388695, 0.560278, 0.420781, -0.440711, 0.275919, 0.189938, 0.159768, 0.341554,
384 0.594513, 0.333733, -0.0974569, -0.308295, 0.200531, 0.101979, -0.363293,
385 ];
386 const DENSE_GATE_PROJ: [f32; 40] = [
387 0.0530662,
388 -0.0224611,
389 -0.468284,
390 0.394925,
391 -0.0289055,
392 -0.138464,
393 0.302004,
394 -0.0823999,
395 0.295465,
396 0.391958,
397 0.197625,
398 0.236802,
399 -0.000834978,
400 0.539451,
401 0.230436,
402 0.0369415,
403 0.235552,
404 -0.0302823,
405 0.216731,
406 0.106894,
407 -0.0388429,
408 -0.0235654,
409 0.0683206,
410 -0.247831,
411 -0.829944,
412 -0.255354,
413 -0.652798,
414 -0.539486,
415 -0.16364,
416 0.212245,
417 -0.263695,
418 0.266948,
419 0.474532,
420 0.249119,
421 -0.0215431,
422 0.0568342,
423 -0.0727135,
424 -0.267156,
425 0.0426981,
426 -0.921182,
427 ];
428 const DENSE_UP_PROJ: [f32; 40] = [
429 -0.236304,
430 0.180069,
431 -0.2151,
432 0.222938,
433 -0.0463798,
434 -0.31709,
435 -0.00577976,
436 0.254963,
437 -0.152982,
438 0.736692,
439 0.565269,
440 -0.00259989,
441 0.22078,
442 0.0674972,
443 0.193022,
444 0.626185,
445 0.0697913,
446 0.489142,
447 -0.0709509,
448 0.135837,
449 0.0604967,
450 0.134483,
451 0.15545,
452 -0.715912,
453 0.131568,
454 0.447037,
455 0.0964134,
456 -0.568027,
457 -0.539577,
458 -0.385414,
459 -0.308715,
460 0.338456,
461 -0.589399,
462 -0.22471,
463 -0.256634,
464 -0.172552,
465 0.229815,
466 -0.232763,
467 -0.15492,
468 0.112469,
469 ];
470 const DENSE_DOWN_PROJ: [f32; 40] = [
471 0.364035, -0.722506, -0.192076, -0.513796, 0.358074, 0.187812, -0.210479, -0.190417,
472 0.0378089, 0.398016, 0.393775, 0.119131, 0.0944593, 0.160586, -0.302192, -0.0174533,
473 -0.0131564, 0.17226, -0.0443057, -0.354608, 0.221662, 0.333016, -0.0158993, -0.0357403,
474 -0.251197, 0.0164112, 0.210909, 0.292464, 0.201202, -0.121079, 0.407385, 0.275425,
475 -0.537043, -0.0175377, -0.428206, 0.253178, 0.404171, -0.231798, 0.168481, -0.222966,
476 ];
477 const MLA_Q_A_PROJ: [f32; 48] = [
478 0.451391, -0.126339, 0.514525, 0.104224, -0.482352, -0.12592, -0.140897, 0.570421,
479 0.177444, 0.0637828, -0.219946, -0.449056, -0.0832418, -0.0932411, 0.141219, -0.688717,
480 0.311497, -0.0927841, 0.0286042, 0.608332, -0.340392, 0.292946, 0.390898, -0.0359569,
481 -0.157069, -0.194805, 0.595659, -0.129717, 0.435375, 0.707992, -0.267707, -0.446994,
482 0.257943, 0.208935, 0.0124412, 0.0539018, 0.40146, -0.144905, -0.115103, -0.432397,
483 -0.205624, -0.596541, 0.485434, 0.194575, -0.132356, -0.194361, -0.361851, -0.109006,
484 ];
485 const MLA_Q_A_NORM_W: [f32; 6] = [0.969939, 0.841643, 1.00562, 1.08525, 0.926626, 0.990575];
486 const MLA_Q_B_PROJ: [f32; 60] = [
487 0.1374,
488 -0.135647,
489 0.0887308,
490 -0.0979333,
491 -0.259915,
492 -0.0779051,
493 0.115564,
494 0.228723,
495 -0.230383,
496 0.083073,
497 0.192752,
498 -0.0369039,
499 -0.286092,
500 -0.0686448,
501 -0.307404,
502 -0.0636238,
503 0.211306,
504 -0.146429,
505 -0.0687791,
506 0.250252,
507 -0.156639,
508 0.337944,
509 0.178598,
510 -0.616342,
511 -0.0385348,
512 0.382634,
513 0.671192,
514 -0.248981,
515 0.121872,
516 0.275898,
517 0.188452,
518 0.828256,
519 -0.438505,
520 0.621337,
521 0.408834,
522 -0.303079,
523 0.0797437,
524 -0.240968,
525 0.0335185,
526 0.602176,
527 0.461823,
528 -0.23303,
529 0.0937693,
530 -0.0384911,
531 0.173248,
532 -0.515262,
533 0.0811063,
534 0.543123,
535 -0.148281,
536 -0.599126,
537 0.302461,
538 -0.417772,
539 0.064963,
540 -0.000217146,
541 0.188863,
542 0.752821,
543 -0.293606,
544 0.169847,
545 0.739423,
546 0.435163,
547 ];
548 const MLA_KV_A_PROJ: [f32; 48] = [
549 -0.421765, -0.232617, 0.203753, 0.166609, -0.045513, -0.0101532, 0.0955488, -0.165861,
550 -0.0260061, -0.0220384, -0.0330121, -0.158364, -0.0111785, -0.345142, 0.755459, -0.0558389,
551 -0.277054, -0.26961, 0.240628, 0.0947741, -0.167247, -0.23363, -0.157332, 0.345719,
552 0.156487, 0.0865676, -0.192661, -0.214795, -0.567426, -0.0837593, -0.324576, 0.310762,
553 -0.314536, -0.68736, -0.0154466, 0.315627, -0.150268, -0.0287802, 0.356274, -0.246495,
554 -0.0825744, 0.204116, 0.559475, 0.640863, 0.456519, 0.173669, 0.185071, -0.115482,
555 ];
556 const MLA_KV_A_NORM_W: [f32; 4] = [1.10413, 1.0107, 0.871313, 1.16755];
557 const MLA_KV_B_PROJ: [f32; 48] = [
558 0.0866448,
559 -0.423795,
560 0.114522,
561 -0.441771,
562 -0.186988,
563 0.0366587,
564 0.114137,
565 -0.0609123,
566 0.199646,
567 0.722449,
568 0.704719,
569 -0.460906,
570 0.0629041,
571 0.485761,
572 0.328924,
573 0.0319879,
574 -0.298518,
575 0.123369,
576 -0.136789,
577 -0.256657,
578 0.131407,
579 0.435331,
580 0.0737087,
581 -0.130659,
582 0.130558,
583 0.113592,
584 -0.0490255,
585 -0.292942,
586 -0.0925113,
587 -0.412935,
588 -0.222763,
589 -0.189368,
590 0.202786,
591 -0.128307,
592 -0.215928,
593 0.0669919,
594 0.384514,
595 0.637332,
596 -0.0592113,
597 0.0464926,
598 -0.281259,
599 0.107265,
600 0.494055,
601 -0.00978826,
602 -0.398057,
603 0.593258,
604 0.0966843,
605 0.013154,
606 ];
607 const MLA_O_PROJ: [f32; 48] = [
608 -0.384195,
609 0.195792,
610 0.334816,
611 -0.101664,
612 0.103024,
613 -0.142412,
614 0.130369,
615 -0.307274,
616 -0.367605,
617 0.559024,
618 -0.128535,
619 0.0330137,
620 0.241127,
621 0.0644755,
622 -0.0776874,
623 -0.231983,
624 -0.909896,
625 -0.268289,
626 0.205435,
627 -0.268665,
628 -0.663843,
629 0.00206543,
630 -0.454986,
631 0.226469,
632 -0.539808,
633 0.34166,
634 7.24295e-05,
635 0.339034,
636 -0.244381,
637 0.168123,
638 -0.316009,
639 0.00653252,
640 0.107088,
641 0.229998,
642 0.279935,
643 0.318718,
644 -0.262585,
645 0.528549,
646 0.356373,
647 0.217004,
648 0.154669,
649 -0.193219,
650 -0.0690429,
651 0.304651,
652 0.554123,
653 0.158962,
654 0.525434,
655 -0.20413,
656 ];
657 const MLA_G_PROJ: [f32; 48] = [
658 0.378873, -0.362289, -0.172782, -0.305609, -0.15234, 0.13899, -0.358599, -0.786074,
659 -0.168818, 0.168034, -0.27184, -0.103517, 0.34245, 0.0531821, 0.254431, -0.164725,
660 -0.0816316, -0.0860996, -0.0865057, 0.0241667, 0.259761, 0.230357, -0.726831, 0.379549,
661 0.171985, -0.378427, -0.199484, 0.138928, -0.238634, -0.55968, -0.405272, -0.184117,
662 -0.184866, 0.0497178, 0.103438, 0.380376, 0.364498, -0.175124, 0.293164, 0.200305,
663 -0.0883711, -0.155664, -0.011865, 0.304442, -0.0586883, -0.269584, -0.215651, 0.0801283,
664 ];
665 const MOE_ROUTER_WEIGHT: [f32; 32] = [
666 -0.14075, 0.348724, 0.162851, 0.220414, -0.334635, -0.0441166, 0.291079, -0.16397,
667 0.104033, 0.186523, -0.0732253, -0.18498, 0.0548991, 0.0572118, -0.212319, -0.114626,
668 0.0438156, -0.291582, -0.364393, -0.456047, -0.153299, -0.175468, 0.0600189, -0.433395,
669 -0.241496, -0.195144, 0.468446, -0.0704137, 0.0423066, 0.0625186, 0.125581, -0.384111,
670 ];
671 const MOE_BIAS: [f32; 4] = [-0.287968, -0.506168, -0.0823805, -1.16076];
672 const MOE_DOWN_PROJ: [f32; 32] = [
673 0.357856, -0.419139, 0.0951717, 0.486966, 0.138318, -0.321581, 0.023647, -0.287115,
674 -0.218501, 0.466282, -0.674924, 0.358341, -0.651141, -0.190237, 0.154624, -0.131574,
675 -0.0769183, 0.0423567, 0.768159, 0.659497, -0.106854, -0.338984, -0.0425253, -0.326632,
676 -0.157781, -0.0021155, 0.259854, 0.132918, 0.305795, 0.418168, 0.0538999, 0.630383,
677 ];
678 const MOE_UP_PROJ: [f32; 32] = [
679 0.0537259, -0.222431, 0.0517936, -0.328772, -0.218991, 0.0109666, -0.66681, 0.53133,
680 -0.24925, -0.271987, -0.57234, 0.0120699, -0.288328, 0.131623, -0.132867, -0.105812,
681 -0.284854, -0.456721, -0.150868, -0.102689, -0.245856, -0.0891634, 0.389166, 0.518893,
682 -0.0414535, 0.157239, -0.0155488, 0.207036, 0.00431687, -0.261777, -0.541271, 0.211408,
683 ];
684 const MOE_ROUTED_NORM_W: [f32; 4] = [1.14805, 0.951891, 0.971543, 0.908736];
685 const MOE_SHARED_W1: [f32; 24] = [
686 -0.377193,
687 0.27978,
688 -0.0102447,
689 0.229219,
690 -0.380455,
691 0.552381,
692 -0.0980754,
693 0.56176,
694 -0.297,
695 -0.142547,
696 -0.204375,
697 -0.338748,
698 0.388348,
699 0.489957,
700 0.356719,
701 0.0574536,
702 0.110144,
703 -0.00856218,
704 0.426843,
705 0.260044,
706 0.148431,
707 -0.184063,
708 -0.342297,
709 -0.0762749,
710 ];
711 const MOE_SHARED_W2: [f32; 24] = [
712 -0.43575, 0.0521214, -0.10101, 0.072215, 0.337606, 0.0963424, -0.107468, -0.49745,
713 0.364127, 0.217258, 0.359295, -0.0454641, -0.252116, 0.0389652, 0.178223, 0.0836014,
714 0.124572, -0.252946, -0.227852, 0.0663797, 0.110113, 0.458224, 0.520268, -0.0199504,
715 ];
716 const MOE_SHARED_W3: [f32; 24] = [
717 0.368003,
718 -0.483325,
719 -0.379792,
720 0.100306,
721 0.106976,
722 -0.0896776,
723 -0.316544,
724 0.322652,
725 -0.517715,
726 0.249498,
727 0.0612062,
728 -0.601994,
729 -0.193968,
730 0.558544,
731 -0.388051,
732 -0.277637,
733 0.122792,
734 -0.185875,
735 -0.00133169,
736 0.0236871,
737 -0.296105,
738 0.0705485,
739 -0.302294,
740 -0.246982,
741 ];
742 const MOE_E0_W1: [f32; 12] = [
743 0.0440567, 0.174659, -0.355788, -0.234582, 0.238399, 0.600469, -0.130771, 0.811262,
744 0.130333, -0.0412095, -0.714084, 0.158573,
745 ];
746 const MOE_E0_W2: [f32; 12] = [
747 -0.103864, -0.115033, 0.275029, -0.0808199, -0.348501, 0.30532, 0.310035, -0.0757431,
748 -0.132877, 0.711211, 0.226243, 0.163931,
749 ];
750 const MOE_E0_W3: [f32; 12] = [
751 -0.114776, 0.141509, 0.504018, -0.115836, 0.43992, 0.131565, -0.469145, 0.0490231,
752 0.151157, -0.0375621, -0.17218, 0.231705,
753 ];
754 const MOE_E1_W1: [f32; 12] = [
755 0.0488396,
756 -0.341746,
757 0.0418606,
758 0.221871,
759 -0.722022,
760 -0.00220993,
761 -0.0916067,
762 -0.398679,
763 -0.423551,
764 -0.645019,
765 -0.27434,
766 0.0208012,
767 ];
768 const MOE_E1_W2: [f32; 12] = [
769 -0.167076, 0.231248, -0.759916, 0.352397, -0.0142662, -0.24585, -0.0941462, 0.0286856,
770 0.103257, -0.272298, 0.177667, -0.226165,
771 ];
772 const MOE_E1_W3: [f32; 12] = [
773 0.189908, -0.207244, -0.0267815, 0.0907185, 0.191764, 0.105395, 0.0162554, -0.452242,
774 0.430473, -0.291964, -0.261698, 0.0512003,
775 ];
776 const MOE_E2_W1: [f32; 12] = [
777 -0.0189576, 0.0668481, 0.214585, 0.143547, -0.459021, 0.191771, -0.386651, 0.519018,
778 -0.38456, -0.409715, 0.101121, -0.114501,
779 ];
780 const MOE_E2_W2: [f32; 12] = [
781 0.329286, -0.122696, 0.385045, -0.309553, 0.205889, -0.0737322, 0.0664255, 0.243589,
782 -0.101208, 0.615374, 0.791347, -0.288181,
783 ];
784 const MOE_E2_W3: [f32; 12] = [
785 -0.34824, -0.0969774, -0.0404219, -0.142054, -0.140162, 0.168913, 0.329251, 0.568559,
786 0.302453, 0.2389, 0.10173, 0.26265,
787 ];
788 const MOE_E3_W1: [f32; 12] = [
789 -0.270246, -0.148207, 0.301809, 0.317732, 0.216907, 0.245138, 0.390181, -0.120865,
790 0.115518, -0.398155, -0.0685247, -0.263379,
791 ];
792 const MOE_E3_W2: [f32; 12] = [
793 -0.0315493, -0.147451, -0.471437, -0.149953, -0.0759736, -0.42393, 0.500115, 0.50571,
794 -0.126137, -0.0207527, 0.0606406, -0.138175,
795 ];
796 const MOE_E3_W3: [f32; 12] = [
797 0.0539619, -0.604005, -0.229171, 0.432303, 0.240478, 0.157986, 0.482184, -0.0937539,
798 0.255604, 0.304551, -0.0564262, -0.17754,
799 ];
800 const L0_SELF_ATTN_RES_NORM_W: [f32; 8] = [
801 1.09202, 0.89273, 1.09569, 0.903039, 0.86568, 0.985966, 0.904269, 0.901858,
802 ];
803 const L0_SELF_ATTN_RES_PROJ_W: [f32; 8] = [
804 0.181373, -0.268475, -0.278623, -0.0176931, 0.15743, -0.189069, -0.0939655, 0.0588636,
805 ];
806 const L0_MLP_RES_NORM_W: [f32; 8] = [
807 1.01577, 1.04681, 0.991541, 1.00423, 0.89548, 1.09467, 1.05601, 0.968893,
808 ];
809 const L0_MLP_RES_PROJ_W: [f32; 8] = [
810 -0.388332, -0.132232, 0.21217, -0.337705, 0.246598, -0.20951, 0.154862, -0.225175,
811 ];
812 const L0_INPUT_LAYERNORM_W: [f32; 8] = [
813 0.970917, 0.970733, 0.946098, 1.23423, 0.883455, 1.12311, 0.809911, 1.12998,
814 ];
815 const L0_POST_ATTN_LAYERNORM_W: [f32; 8] = [
816 0.986205, 1.04826, 1.00761, 1.04983, 0.890071, 0.817534, 1.17858, 0.915493,
817 ];
818 const L1_SELF_ATTN_RES_NORM_W: [f32; 8] = [
819 0.985561, 0.917139, 1.10889, 1.02053, 0.951296, 1.08218, 1.05087, 0.816958,
820 ];
821 const L1_SELF_ATTN_RES_PROJ_W: [f32; 8] = [
822 -0.13164, -0.436846, -0.109316, 0.0236738, 0.0868917, 0.038431, 0.12599, 0.680195,
823 ];
824 const L1_MLP_RES_NORM_W: [f32; 8] = [
825 1.123, 1.03367, 1.15067, 1.06599, 1.10144, 0.905083, 0.911655, 1.11872,
826 ];
827 const L1_MLP_RES_PROJ_W: [f32; 8] = [
828 -0.0917493, -0.258613, 0.62677, 0.0330113, 0.0841344, -0.332195, -0.355091, 0.152981,
829 ];
830 const L1_INPUT_LAYERNORM_W: [f32; 8] = [
831 0.878093, 0.911769, 1.15303, 1.1774, 0.961136, 1.01693, 0.935918, 1.08085,
832 ];
833 const L1_POST_ATTN_LAYERNORM_W: [f32; 8] = [
834 0.982172, 0.868114, 0.876461, 1.00153, 1.10216, 1.01644, 0.784298, 1.0521,
835 ];
836 const OUTPUT_ATTN_RES_NORM_W: [f32; 8] = [
837 0.978339, 0.909439, 1.00794, 0.900698, 1.11013, 1.13568, 1.00689, 1.06737,
838 ];
839 const OUTPUT_ATTN_RES_PROJ_W: [f32; 8] = [
840 -0.0780474, 0.195513, -0.118552, 0.46788, 0.285518, -0.16824, -0.350209, -0.0767004,
841 ];
842 const FINAL_NORM_W: [f32; 8] = [
843 0.969882, 0.931832, 1.10373, 1.16478, 0.891072, 1.0772, 0.85093, 0.91487,
844 ];
845 const EMBEDDING_ROW: [f32; 8] = [
846 0.309738, 0.0378015, -0.337758, 0.323415, 0.162627, -0.685942, 0.134525, 0.42463,
847 ];
848 const OUTPUT_HEAD: [f32; 40] = [
849 0.04209, 0.261934, -0.329344, 0.0625128, 0.272985, 0.290578, 0.0418593, 0.216545, -0.63005,
850 -0.246383, 0.0668848, -0.179091, -0.277674, 0.285747, 0.0959162, -0.0944821, -0.542151,
851 0.2749, 0.299618, -0.0826765, 0.136924, -0.193359, -0.132659, 0.306145, 0.0721566,
852 0.0321218, -0.360207, -0.152256, -0.779984, 0.286993, -0.0684097, 0.142516, -0.124933,
853 0.180322, 0.00527221, 0.0710325, 0.453557, -0.152982, 0.0701835, 0.314973,
854 ];
855 const GOLDEN_LOGITS: [f32; 5] = [0.487583, -1.04021, 0.695218, -0.214018, 1.12848];
856
857 fn expert(
858 w1: &[f32],
859 w2: &[f32],
860 w3: &[f32],
861 ffn_dim: usize,
862 in_dim: usize,
863 ) -> KimiExpertWeights {
864 KimiExpertWeights {
865 w1: wm(w1, ffn_dim, in_dim),
866 w2: wm(w2, in_dim, ffn_dim),
867 w3: wm(w3, ffn_dim, in_dim),
868 }
869 }
870
871 fn make_weights() -> KimiDecoderWeights {
872 let layer0 = KimiDecoderLayerWeights {
873 input_layernorm_weight: L0_INPUT_LAYERNORM_W.to_vec(),
874 attn: KimiLayerAttention::Kda(Box::new(KdaAttnWeights {
875 q_proj: wm(&KDA_Q_PROJ, KDA_PROJ, HIDDEN_DIM),
876 k_proj: wm(&KDA_K_PROJ, KDA_PROJ, HIDDEN_DIM),
877 v_proj: wm(&KDA_V_PROJ, KDA_PROJ, HIDDEN_DIM),
878 q_conv_weight: KDA_Q_CONV_W.to_vec(),
879 k_conv_weight: KDA_K_CONV_W.to_vec(),
880 v_conv_weight: KDA_V_CONV_W.to_vec(),
881 a_log: KDA_A_LOG.to_vec(),
882 f_a_proj: wm(&KDA_F_A_PROJ, KDA_HEAD_DIM, HIDDEN_DIM),
883 f_b_proj: wm(&KDA_F_B_PROJ, KDA_PROJ, KDA_HEAD_DIM),
884 dt_bias: KDA_DT_BIAS.to_vec(),
885 b_proj: wm(&KDA_B_PROJ, KDA_NUM_HEADS, HIDDEN_DIM),
886 g_proj: wm(&KDA_G_PROJ, KDA_PROJ, HIDDEN_DIM),
887 o_norm_weight: KDA_O_NORM_W.to_vec(),
888 o_proj: wm(&KDA_O_PROJ, HIDDEN_DIM, KDA_PROJ),
889 })),
890 post_attention_layernorm_weight: L0_POST_ATTN_LAYERNORM_W.to_vec(),
891 ffn: KimiLayerFfn::Dense(Box::new(DenseMlpWeights {
892 gate_proj: wm(&DENSE_GATE_PROJ, DENSE_INTERMEDIATE, HIDDEN_DIM),
893 up_proj: wm(&DENSE_UP_PROJ, DENSE_INTERMEDIATE, HIDDEN_DIM),
894 down_proj: wm(&DENSE_DOWN_PROJ, HIDDEN_DIM, DENSE_INTERMEDIATE),
895 })),
896 self_attention_res_norm_weight: L0_SELF_ATTN_RES_NORM_W.to_vec(),
897 self_attention_res_proj_weight: L0_SELF_ATTN_RES_PROJ_W.to_vec(),
898 mlp_res_norm_weight: L0_MLP_RES_NORM_W.to_vec(),
899 mlp_res_proj_weight: L0_MLP_RES_PROJ_W.to_vec(),
900 };
901
902 let layer1 = KimiDecoderLayerWeights {
903 input_layernorm_weight: L1_INPUT_LAYERNORM_W.to_vec(),
904 attn: KimiLayerAttention::Mla(Box::new(MlaAttnWeights {
905 q_a_proj: wm(&MLA_Q_A_PROJ, MLA_Q_LORA, HIDDEN_DIM),
906 q_a_layernorm: MLA_Q_A_NORM_W.to_vec(),
907 q_b_proj: wm(
908 &MLA_Q_B_PROJ,
909 MLA_NUM_HEADS * (MLA_QK_NOPE + MLA_QK_ROPE),
910 MLA_Q_LORA,
911 ),
912 kv_a_proj_with_mqa: wm(&MLA_KV_A_PROJ, MLA_KV_LORA + MLA_QK_ROPE, HIDDEN_DIM),
913 kv_a_layernorm: MLA_KV_A_NORM_W.to_vec(),
914 kv_b_proj: wm(
915 &MLA_KV_B_PROJ,
916 MLA_NUM_HEADS * (MLA_QK_NOPE + MLA_V_HEAD_DIM),
917 MLA_KV_LORA,
918 ),
919 o_proj: wm(&MLA_O_PROJ, HIDDEN_DIM, MLA_PROJ),
920 g_proj: Some(wm(&MLA_G_PROJ, MLA_PROJ, HIDDEN_DIM)),
921 })),
922 post_attention_layernorm_weight: L1_POST_ATTN_LAYERNORM_W.to_vec(),
923 ffn: KimiLayerFfn::Moe(Box::new(KimiLatentMoeWeights {
924 router_weight: wm(&MOE_ROUTER_WEIGHT, N_EXPERTS, HIDDEN_DIM),
925 e_score_correction_bias: MOE_BIAS.to_vec(),
926 down_proj: wm(&MOE_DOWN_PROJ, MOE_HIDDEN_DIM, HIDDEN_DIM),
927 up_proj: wm(&MOE_UP_PROJ, HIDDEN_DIM, MOE_HIDDEN_DIM),
928 routed_expert_norm_weight: Some(MOE_ROUTED_NORM_W.to_vec()),
929 experts: KimiExpertBacking::Resident(vec![
930 expert(
931 &MOE_E0_W1,
932 &MOE_E0_W2,
933 &MOE_E0_W3,
934 MOE_INTERMEDIATE,
935 MOE_HIDDEN_DIM,
936 ),
937 expert(
938 &MOE_E1_W1,
939 &MOE_E1_W2,
940 &MOE_E1_W3,
941 MOE_INTERMEDIATE,
942 MOE_HIDDEN_DIM,
943 ),
944 expert(
945 &MOE_E2_W1,
946 &MOE_E2_W2,
947 &MOE_E2_W3,
948 MOE_INTERMEDIATE,
949 MOE_HIDDEN_DIM,
950 ),
951 expert(
952 &MOE_E3_W1,
953 &MOE_E3_W2,
954 &MOE_E3_W3,
955 MOE_INTERMEDIATE,
956 MOE_HIDDEN_DIM,
957 ),
958 ]),
959 shared_expert: expert(
960 &MOE_SHARED_W1,
961 &MOE_SHARED_W2,
962 &MOE_SHARED_W3,
963 SHARED_INTERMEDIATE,
964 HIDDEN_DIM,
965 ),
966 })),
967 self_attention_res_norm_weight: L1_SELF_ATTN_RES_NORM_W.to_vec(),
968 self_attention_res_proj_weight: L1_SELF_ATTN_RES_PROJ_W.to_vec(),
969 mlp_res_norm_weight: L1_MLP_RES_NORM_W.to_vec(),
970 mlp_res_proj_weight: L1_MLP_RES_PROJ_W.to_vec(),
971 };
972
973 KimiDecoderWeights {
974 embedding: Tensor::new(EMBEDDING_ROW.to_vec(), vec![1, HIDDEN_DIM]),
975 layers: vec![layer0, layer1],
976 output_attn_res_norm_weight: OUTPUT_ATTN_RES_NORM_W.to_vec(),
977 output_attn_res_proj_weight: OUTPUT_ATTN_RES_PROJ_W.to_vec(),
978 final_norm_weight: FINAL_NORM_W.to_vec(),
979 output_head: wm(&OUTPUT_HEAD, OUTPUT_VOCAB, HIDDEN_DIM),
980 }
981 }
982
983 fn decoder_cfg() -> KimiDecoderConfig {
984 KimiDecoderConfig {
985 attn_res_block_size: ATTN_RES_BLOCK_SIZE,
986 rms_norm_eps: EPS,
987 situ_beta: SITU_BETA,
988 situ_linear_beta: SITU_LINEAR_BETA,
989 moe: KimiMoeConfig {
990 n_experts_active: TOP_K,
991 moe_renormalize: true,
992 routed_scaling_factor: 1.0,
993 situ_beta: SITU_BETA,
994 situ_linear_beta: SITU_LINEAR_BETA,
995 rms_norm_eps: EPS,
996 },
997 }
998 }
999
1000 fn mla_cfg() -> MlaConfig {
1001 MlaConfig {
1002 num_heads: MLA_NUM_HEADS,
1003 q_lora_rank: MLA_Q_LORA,
1004 kv_lora_rank: MLA_KV_LORA,
1005 qk_nope_head_dim: MLA_QK_NOPE,
1006 qk_rope_head_dim: MLA_QK_ROPE,
1007 v_head_dim: MLA_V_HEAD_DIM,
1008 use_output_gate: true,
1009 rope: None,
1010 }
1011 }
1012
1013 fn kda_cfg() -> KdaConfig {
1014 KdaConfig {
1015 num_heads: KDA_NUM_HEADS,
1016 head_dim: KDA_HEAD_DIM,
1017 short_conv_kernel_size: KDA_CONV_SIZE,
1018 gate_lower_bound: KDA_GATE_LOWER_BOUND,
1019 use_full_rank_gate: true,
1020 }
1021 }
1022
1023 #[test]
1024 fn two_mixed_layers_match_independent_python_reference() {
1025 let weights = make_weights();
1026 let cfg = decoder_cfg();
1027 let mla_cfg = mla_cfg();
1028 let kda_cfg = kda_cfg();
1029 let mut state = KimiDecodeState::new(&weights, &kda_cfg);
1030
1031 let logits = kimi_forward_token(&weights, &cfg, &mla_cfg, &kda_cfg, 0, &mut state);
1032 assert_eq!(logits.len(), GOLDEN_LOGITS.len());
1033 for (i, (a, b)) in logits.iter().zip(GOLDEN_LOGITS.iter()).enumerate() {
1034 assert!((a - b).abs() < 1e-3, "logit {i}: rust={a} python={b}");
1035 }
1036 }
1037}