1use crate::config::ModelConfig;
4use anyhow::{Context, Result, bail, ensure};
5
6pub fn expected_replicated_shape(name: &str, config: &ModelConfig) -> Result<Vec<usize>> {
10 let prefix = config.weight_prefix.trim_end_matches('.');
11 let canonical = if prefix.is_empty() || name == "lm_head.weight" {
12 name
13 } else {
14 name.strip_prefix(prefix)
15 .and_then(|rest| rest.strip_prefix('.'))
16 .context("K3 weight does not match configured text prefix")?
17 };
18 let h = config.hidden_size;
19 let shape = match canonical {
20 "model.embed_tokens.weight" | "lm_head.weight" => vec![config.vocab_size, h],
21 "model.norm.weight" | "model.output_attn_res_norm.weight" => vec![h],
22 "model.output_attn_res_proj.weight" => vec![1, h],
23 _ => layer_shape(canonical, config)?,
24 };
25 shape.iter().try_fold(1usize, |elements, &dimension| {
26 ensure!(dimension > 0, "{name}: zero replicated dimension");
27 elements
28 .checked_mul(dimension)
29 .context("K3 replicated shape overflow")
30 })?;
31 Ok(shape)
32}
33
34fn layer_shape(name: &str, c: &ModelConfig) -> Result<Vec<usize>> {
35 let (layer, suffix) = name
36 .strip_prefix("model.layers.")
37 .and_then(|rest| rest.split_once('.'))
38 .context("unknown replicated K3 tensor")?;
39 let layer = layer.parse::<usize>().context("invalid K3 layer index")?;
40 ensure!(
41 layer < c.layer_types.len(),
42 "K3 replicated layer outside configuration"
43 );
44 let h = c.hidden_size;
45 let lat = c.moe_latent_size;
46 let shared = if c.shared_expert_intermediate_size > 0 {
47 c.shared_expert_intermediate_size
48 } else {
49 c.moe_intermediate_size
50 };
51 Ok(match suffix {
52 "input_layernorm.weight"
53 | "post_attention_layernorm.weight"
54 | "mlp_res_norm.weight"
55 | "self_attention_res_norm.weight" => vec![h],
56 "mlp_res_proj.weight" | "self_attention_res_proj.weight" => vec![1, h],
57 "self_attn.f_a_proj.weight" => vec![c.linear_key_head_dim, h],
58 "self_attn.o_norm.weight" => vec![c.linear_key_head_dim],
59 "self_attn.kv_a_layernorm.weight" => vec![c.kv_lora_rank],
60 "self_attn.kv_a_proj_with_mqa.weight" => vec![
61 c.kv_lora_rank
62 .checked_add(c.qk_rope_head_dim)
63 .context("K3 KV width overflow")?,
64 h,
65 ],
66 "self_attn.q_a_layernorm.weight" => vec![c.q_lora_rank],
67 "self_attn.q_a_proj.weight" => vec![c.q_lora_rank, h],
68 "block_sparse_moe.gate.weight" => vec![c.num_experts, h],
69 "block_sparse_moe.gate.e_score_correction_bias" => vec![c.num_experts],
70 "block_sparse_moe.routed_expert_down_proj.weight" => vec![lat, h],
71 "block_sparse_moe.routed_expert_up_proj.weight" => vec![h, lat],
72 "block_sparse_moe.routed_expert_norm.weight" => vec![lat],
73 "block_sparse_moe.shared_experts.down_proj.weight" => vec![h, shared],
74 "block_sparse_moe.shared_experts.gate_proj.weight"
75 | "block_sparse_moe.shared_experts.up_proj.weight" => vec![shared, h],
76 _ => bail!("{name}: unknown replicated K3 projection"),
77 })
78}
79
80pub fn validate_replicated_shape(name: &str, shape: &[usize], config: &ModelConfig) -> Result<()> {
81 let expected = expected_replicated_shape(name, config)?;
82 let flat_residual = name.ends_with("res_proj.weight")
85 && expected == [1, config.hidden_size]
86 && shape == [config.hidden_size];
87 ensure!(
88 shape == expected || flat_residual,
89 "{name}: replicated shape {shape:?} != {expected:?}"
90 );
91 Ok(())
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use crate::config::{LayerType, parse_config};
98
99 fn config() -> ModelConfig {
100 let mut c = parse_config(include_str!(
101 "../../../../docs/k3/fixtures/Kimi-K3-0.40B-config.json"
102 ))
103 .unwrap();
104 c.weight_prefix.clear();
105 c.hidden_size = 8;
106 c.vocab_size = 32;
107 c.layer_types = vec![LayerType::LinearAttention, LayerType::FullAttention];
108 c.moe_latent_size = 4;
109 c.moe_intermediate_size = 16;
110 c.shared_expert_intermediate_size = 12;
111 c.num_experts = 3;
112 c
113 }
114
115 #[test]
116 fn embedding_and_norm_reject_tiny_or_transposed_storage() {
117 let c = config();
118 assert!(validate_replicated_shape("model.embed_tokens.weight", &[32, 8], &c).is_ok());
119 for shape in [vec![1], vec![8, 32], vec![256]] {
120 assert!(validate_replicated_shape("model.embed_tokens.weight", &shape, &c).is_err());
121 }
122 assert!(validate_replicated_shape("model.norm.weight", &[8], &c).is_ok());
123 assert!(validate_replicated_shape("model.norm.weight", &[1], &c).is_err());
124 }
125
126 #[test]
127 fn router_and_shared_projections_keep_full_width_at_tp2() {
128 let mut c = config();
129 c.tp_world_size = 2;
130 c.tp_rank = 1;
131 let p = "model.layers.1.block_sparse_moe.";
132 for (suffix, shape) in [
133 ("gate.weight", vec![3, 8]),
134 ("gate.e_score_correction_bias", vec![3]),
135 ("routed_expert_down_proj.weight", vec![4, 8]),
136 ("routed_expert_up_proj.weight", vec![8, 4]),
137 ("routed_expert_norm.weight", vec![4]),
138 ("shared_experts.down_proj.weight", vec![8, 12]),
139 ("shared_experts.gate_proj.weight", vec![12, 8]),
140 ("shared_experts.up_proj.weight", vec![12, 8]),
141 ] {
142 let name = format!("{p}{suffix}");
143 assert_eq!(expected_replicated_shape(&name, &c).unwrap(), shape);
144 assert!(validate_replicated_shape(&name, &[1], &c).is_err());
145 }
146 }
147
148 #[test]
149 fn unknown_out_of_range_and_zero_dimensions_refuse() {
150 let mut c = config();
151 for name in [
152 "model.layers.9.input_layernorm.weight",
153 "model.layers.0.unknown.weight",
154 ] {
155 assert!(expected_replicated_shape(name, &c).is_err());
156 }
157 c.hidden_size = 0;
158 assert!(expected_replicated_shape("model.norm.weight", &c).is_err());
159 }
160
161 #[test]
162 fn prefixed_weights_and_residual_query_shapes_are_explicit() {
163 let mut c = config();
164 c.weight_prefix = "language_model".into();
165 assert_eq!(
166 expected_replicated_shape("language_model.model.norm.weight", &c).unwrap(),
167 [8]
168 );
169 assert!(expected_replicated_shape("other.model.norm.weight", &c).is_err());
170 assert_eq!(
171 expected_replicated_shape("lm_head.weight", &c).unwrap(),
172 [32, 8]
173 );
174 let name = "language_model.model.output_attn_res_proj.weight";
175 assert!(validate_replicated_shape(name, &[1, 8], &c).is_ok());
176 assert!(validate_replicated_shape(name, &[8], &c).is_ok());
177 assert!(validate_replicated_shape(name, &[8, 1], &c).is_err());
178 }
179
180 #[test]
181 fn every_replicated_required_name_has_a_geometry_matching_cpu_bind() {
182 use super::super::{
183 K3Graph, kda_from, mla_from, moe_from,
184 tp::{TpAxis, tensor_plan},
185 };
186 let c = config();
187 let graph = K3Graph::from_config(&c);
188 let inventory = super::super::cpu_bind::inventory(
189 &c,
190 &graph,
191 &kda_from(&c),
192 &mla_from(&c),
193 &moe_from(&c),
194 );
195 for (name, elements) in inventory {
196 let mixer = name
197 .split("model.layers.")
198 .nth(1)
199 .and_then(|tail| tail.split('.').next())
200 .and_then(|index| index.parse::<usize>().ok())
201 .map(|index| graph.layers[index].mixer)
202 .unwrap_or(super::super::MixerKind::Kda);
203 if tensor_plan(&name, mixer, &c).0 == TpAxis::Replicated {
204 let shape = expected_replicated_shape(&name, &c).unwrap();
205 assert_eq!(shape.iter().product::<usize>(), elements, "{name}");
206 }
207 }
208 }
209}