1use crate::attention::{self};
26use crate::pool::Pool;
27use crate::qtensor::QTensor;
28
29pub struct G4eGlobals {
30 pub ple_embed: QTensor,
33 pub ple_model_proj: QTensor,
35 pub ple_norm: Vec<f32>,
37 pub ple_vocab: usize,
38 pub ple_dim: usize,
39 pub num_layers: usize,
40 pub hidden: usize,
41 pub rms_eps: f64,
42 pub inv_freq_local: Vec<f32>,
47 pub inv_freq_global: Vec<f32>,
48 pub window: usize,
50 pub nh: usize,
51 pub nkv: usize,
52}
53
54pub struct G4eLayer {
55 pub input_norm: Vec<f32>,
57 pub attn_out_norm: Vec<f32>,
59 pub pre_ffn_norm: Vec<f32>,
61 pub post_ffw_norm: Vec<f32>,
63 pub wq: QTensor,
64 pub wk: Option<QTensor>,
67 pub wv: Option<QTensor>,
68 pub wo: QTensor,
69 pub q_norm: Vec<f32>,
70 pub k_norm: Option<Vec<f32>>,
71 pub kv_share_src: Option<usize>,
72 pub sliding: bool,
73 pub hd: usize,
76 pub gate: QTensor,
77 pub up: QTensor,
78 pub down: QTensor,
79 pub ple_gate: QTensor,
81 pub ple_proj: QTensor,
82 pub post_ple_norm: Vec<f32>,
83 pub layer_scalar: Option<f32>,
85}
86
87fn rms(x: &[f32], w: &[f32], eps: f64) -> Vec<f32> {
89 let ms: f64 = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
90 let inv = (ms + eps).powf(-0.5);
91 x.iter()
92 .zip(w)
93 .map(|(&v, &g)| (v as f64 * inv * g as f64) as f32)
94 .collect()
95}
96
97fn rms_plain(x: &mut [f32], eps: f64) {
99 let ms: f64 = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
100 let inv = (ms + eps).powf(-0.5) as f32;
101 for v in x.iter_mut() {
102 *v *= inv;
103 }
104}
105
106fn gelu_tanh(x: f32) -> f32 {
108 let x = x as f64;
109 (0.5 * x * (1.0 + (0.7978845608028654 * (x + 0.044715 * x * x * x)).tanh())) as f32
110}
111
112fn matvec(w: &QTensor, x: &[f32], pool: Option<&Pool>) -> Vec<f32> {
113 let mut o = vec![0.0f32; w.rows()];
114 w.matvec(x, &mut o, pool);
115 o
116}
117
118fn matvec_row(t: &QTensor, row: usize) -> Vec<f32> {
120 let mut out = vec![0.0f32; t.cols()];
121 t.row_f32(row, &mut out);
122 out
123}
124
125impl G4eGlobals {
126 pub fn extend_embedding(&self, id: u32, e: &[f32], pool: Option<&Pool>) -> Vec<f32> {
134 let (l, d) = (self.num_layers, self.ple_dim);
135 let mut proj = matvec(&self.ple_model_proj, e, pool);
136 let scale = (self.hidden as f32).powf(-0.5);
137 for v in proj.iter_mut() {
138 *v *= scale;
139 }
140 let ple_scale = (d as f32).sqrt();
141 let ple_row: Option<Vec<f32>> = if (id as usize) < self.ple_vocab {
142 Some(matvec_row(&self.ple_embed, id as usize))
143 } else {
144 None
145 };
146 let half = std::f32::consts::FRAC_1_SQRT_2;
147 let mut out = Vec::with_capacity(self.hidden + l * d);
148 out.extend_from_slice(e);
149 for li in 0..l {
150 let pn = rms(&proj[li * d..(li + 1) * d], &self.ple_norm, self.rms_eps);
151 for j in 0..d {
152 let ple = ple_row
153 .as_ref()
154 .map(|r| r[li * d + j] * ple_scale)
155 .unwrap_or(0.0);
156 out.push((pn[j] + ple) * half);
157 }
158 }
159 out
160 }
161}
162
163pub fn g4e_forward(
168 g: &G4eGlobals,
169 layers: &[G4eLayer],
170 ext: &[f32],
171 position: usize,
172 caches: &mut [crate::kv_cache::LayerKvCache],
173 num_heads: usize,
174 num_kv_heads: usize,
175 pool: Option<&Pool>,
176) -> Vec<f32> {
177 let hs = g.hidden;
178 let (e, ple) = ext.split_at(hs);
179 let eps = g.rms_eps;
180 let nh = g.nh.min(num_heads).max(num_kv_heads);
181 let nkv = g.nkv.min(num_heads);
182 let hpk = (nh / nkv).max(1);
183 let mut h = e.to_vec();
184 let dump_dir = std::env::var_os("CMF_LAYER_DUMP").map(std::path::PathBuf::from);
187 if let Some(dir) = &dump_dir {
188 let _ = std::fs::create_dir_all(dir);
189 let mut b = Vec::with_capacity(h.len() * 4);
190 for v in &h {
191 b.extend_from_slice(&v.to_le_bytes());
192 }
193 let _ = std::fs::write(dir.join(format!("p{position:06}_l_in.f32")), b);
194 }
195 for (li, lw) in layers.iter().enumerate() {
196 let hd = lw.hd;
197 let inv_freq: &[f32] = if lw.sliding {
198 &g.inv_freq_local
199 } else {
200 &g.inv_freq_global
201 };
202
203 let xn = rms(&h, &lw.input_norm, eps);
205 let mut q = matvec(&lw.wq, &xn, pool);
206 for hi in 0..nh {
207 let head = &mut q[hi * hd..(hi + 1) * hd];
208 let n = rms(head, &lw.q_norm, eps);
209 head.copy_from_slice(&n);
210 attention::rope_rotate_scaled(head, position, inv_freq, 1.0);
211 }
212 let src = lw.kv_share_src.unwrap_or(li);
213 let attn_dump: Option<String> = std::env::var("CMF_ATTN_DUMP").ok().and_then(|dir| {
214 let want: Vec<usize> = std::env::var("CMF_ATTN_DUMP_LAYERS")
215 .ok()
216 .map(|s| s.split(',').filter_map(|x| x.parse::<usize>().ok()).collect())
217 .unwrap_or_default();
218 (want.contains(&li)).then_some(dir)
219 });
220 let put = |name: &str, v: &[f32]| {
221 if let Some(dir) = &attn_dump {
222 let _ = std::fs::create_dir_all(dir);
223 let mut b = Vec::with_capacity(v.len() * 4);
224 for x in v {
225 b.extend_from_slice(&x.to_le_bytes());
226 }
227 let _ = std::fs::write(
228 std::path::Path::new(dir).join(format!("a{position:06}_l{li:02}_{name}.f32")),
229 b,
230 );
231 }
232 };
233 put("q", &q);
234 put("xn", &xn);
235 if lw.kv_share_src.is_none() {
236 let wk = lw.wk.as_ref().expect("non-shared layer has k_proj");
237 let wv = lw.wv.as_ref().expect("non-shared layer has v_proj");
238 let kn = lw.k_norm.as_ref().expect("non-shared layer has k_norm");
239 let mut k = matvec(wk, &xn, pool);
240 for gi in 0..nkv {
241 let head = &mut k[gi * hd..(gi + 1) * hd];
242 let n = rms(head, kn, eps);
243 head.copy_from_slice(&n);
244 attention::rope_rotate_scaled(head, position, inv_freq, 1.0);
245 }
246 let mut v = matvec(wv, &xn, pool);
247 for gi in 0..nkv {
248 rms_plain(&mut v[gi * hd..(gi + 1) * hd], eps);
249 }
250 put("k", &k);
251 put("v", &v);
252 caches[li].append(&k, &v, &[]);
253 }
254 let window = if lw.sliding { Some(g.window) } else { None };
255 let (mut ao, mut imp) =
256 attention::attend_all_heads(&q, &caches[src], nh, hpk, hd, 1.0, window, 0.0);
257 attention::recycle_buf(&mut imp);
258 put("ao", &ao);
259 let a = matvec(&lw.wo, &ao, pool);
260 attention::recycle_buf(&mut ao);
261
262 let an = rms(&a, &lw.attn_out_norm, eps);
264 for (hv, av) in h.iter_mut().zip(&an) {
265 *hv += av;
266 }
267
268 let xn2 = rms(&h, &lw.pre_ffn_norm, eps);
270 let gate_v = matvec(&lw.gate, &xn2, pool);
271 let up_v = matvec(&lw.up, &xn2, pool);
272 let act: Vec<f32> = gate_v
273 .iter()
274 .zip(&up_v)
275 .map(|(&gv, &uv)| gelu_tanh(gv) * uv)
276 .collect();
277 let ffn = matvec(&lw.down, &act, pool);
278 let ffn_n = rms(&ffn, &lw.post_ffw_norm, eps);
279 for (hv, fv) in h.iter_mut().zip(&ffn_n) {
280 *hv += fv;
281 }
282
283 let d = g.ple_dim;
285 let pli = &ple[li * d..(li + 1) * d];
286 let mut gated = matvec(&lw.ple_gate, &h, pool);
287 for gv in gated.iter_mut() {
288 *gv = gelu_tanh(*gv);
289 }
290 let mixed: Vec<f32> = gated
291 .iter()
292 .zip(pli)
293 .map(|(&gv, &pv)| gv * pv)
294 .collect();
295 let proj = matvec(&lw.ple_proj, &mixed, pool);
296 let pn = rms(&proj, &lw.post_ple_norm, eps);
297 for (hv, pv) in h.iter_mut().zip(&pn) {
298 *hv += pv;
299 }
300
301 if let Some(s) = lw.layer_scalar {
303 for hv in h.iter_mut() {
304 *hv *= s;
305 }
306 }
307 if let Some(dir) = &dump_dir {
308 let mut b = Vec::with_capacity(h.len() * 4);
309 for v in &h {
310 b.extend_from_slice(&v.to_le_bytes());
311 }
312 let _ = std::fs::write(dir.join(format!("p{position:06}_l{li:02}.f32")), b);
313 }
314 }
315 h
316}