atlas_core/kimi_k3/
layer.rs1use crate::config::{LayerType, ModelConfig};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum MixerKind {
19 Kda,
20 Mla,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum MlpKind {
25 Dense,
26 LatentMoe,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct K3LayerSpec {
31 pub index: usize,
32 pub mixer: MixerKind,
33 pub mlp: MlpKind,
34}
35
36#[derive(Debug, Clone)]
37pub struct K3Graph {
38 pub layers: Vec<K3LayerSpec>,
39 pub hidden: usize,
40 pub attn_res_block_size: usize,
41 pub situ_beta: f32,
42 pub situ_linear_beta: f32,
43 pub use_full_rank_gate: bool,
44 pub mla_use_nope: bool,
45 pub mla_use_output_gate: bool,
46}
47
48impl K3Graph {
49 pub fn from_config(c: &ModelConfig) -> Self {
50 let dense_end = c.mlp_only_layers.len();
51 let layers = c
52 .layer_types
53 .iter()
54 .enumerate()
55 .map(|(i, t)| K3LayerSpec {
56 index: i,
57 mixer: match t {
58 LayerType::LinearAttention => MixerKind::Kda,
59 _ => MixerKind::Mla,
60 },
61 mlp: if i < dense_end {
62 MlpKind::Dense
63 } else {
64 MlpKind::LatentMoe
65 },
66 })
67 .collect();
68 Self {
69 layers,
70 hidden: c.hidden_size,
71 attn_res_block_size: c.attn_res_block_size,
72 situ_beta: c.activation_situ_beta,
73 situ_linear_beta: c.activation_situ_linear_beta,
74 use_full_rank_gate: c.use_full_rank_gate,
75 mla_use_nope: c.mla_use_nope,
76 mla_use_output_gate: c.mla_use_output_gate,
77 }
78 }
79
80 pub fn kda_count(&self) -> usize {
81 self.layers
82 .iter()
83 .filter(|l| l.mixer == MixerKind::Kda)
84 .count()
85 }
86
87 pub fn mla_count(&self) -> usize {
88 self.layers
89 .iter()
90 .filter(|l| l.mixer == MixerKind::Mla)
91 .count()
92 }
93
94 pub fn last_is_mla(&self) -> bool {
95 self.layers
96 .last()
97 .is_some_and(|l| l.mixer == MixerKind::Mla)
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::config::parse_config;
105
106 #[test]
107 fn twin_0_40b_graph_is_six_kda_last_mla() {
108 const TWIN: &str = include_str!("../../../../docs/k3/fixtures/Kimi-K3-0.40B-config.json");
109 let c = parse_config(TWIN).expect("0.40B twin");
110 let g = K3Graph::from_config(&c);
111 assert_eq!(g.layers.len(), 8);
112 assert_eq!(g.kda_count(), 6);
113 assert_eq!(g.mla_count(), 2);
114 assert!(g.last_is_mla());
115 for i in [0, 1, 2, 4, 5, 6] {
117 assert_eq!(g.layers[i].mixer, MixerKind::Kda, "layer {i}");
118 }
119 assert_eq!(g.layers[3].mixer, MixerKind::Mla);
120 assert_eq!(g.layers[7].mixer, MixerKind::Mla);
121 assert_eq!(g.layers[0].mlp, MlpKind::Dense);
122 assert!(g.layers[1..].iter().all(|l| l.mlp == MlpKind::LatentMoe));
123 assert_eq!(g.attn_res_block_size, 4);
124 assert_eq!(g.hidden, 1024);
125 assert!(g.mla_use_nope);
126 assert!(g.mla_use_output_gate);
127 assert!(g.use_full_rank_gate);
128 assert_eq!(g.situ_beta, 4.0);
129 assert_eq!(g.situ_linear_beta, 25.0);
130 }
131
132 #[test]
133 fn official_graph_census() {
134 const OFFICIAL: &str =
135 include_str!("../../../../docs/k3/fixtures/moonshotai-Kimi-K3-config.json");
136 let c = parse_config(OFFICIAL).expect("official");
137 let g = K3Graph::from_config(&c);
138 assert_eq!(g.layers.len(), 93);
139 assert_eq!(g.kda_count(), 69);
140 assert_eq!(g.mla_count(), 24);
141 assert!(g.last_is_mla());
142 assert_eq!(g.layers[91].mixer, MixerKind::Mla);
144 assert_eq!(g.layers[92].mixer, MixerKind::Mla);
145 assert_eq!(g.attn_res_block_size, 12);
146 assert_eq!(g.layers[0].mlp, MlpKind::Dense);
147 }
148}