1use super::attn::{act_quant_inplace, apply_rotary, pow2_ceil, sparse_attn, window_topk_idxs};
21use super::engram::{e4m3_to_f32, f32_to_e4m3_rne};
22use super::hc::rms_norm;
23use super::moe::linear_bf16;
24use super::to_bf16_rne;
25
26mod compressor;
27mod indexer;
28
29pub use compressor::{CompressorState, CompressorWeights, compressor};
30pub use indexer::{
31 IndexerCfg, IndexerWeights, SharedRuntime, indexer, select_candidate_blocks, torch_cpu_topk_set,
32};
33
34pub const FP4_BLOCK: usize = 32;
36pub const LATENT_BLOCK: usize = 16;
38const FP4_MAX: f32 = 6.0;
39const E2M1: [f32; 8] = [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0];
40
41pub fn to_e2m1_rne(y: f32) -> f32 {
44 let a = y.abs();
45 let mut lo = 0usize;
46 for (i, &v) in E2M1.iter().enumerate() {
47 if v <= a {
48 lo = i;
49 }
50 }
51 let hi = (lo + 1).min(7);
52 let (dlo, dhi) = (a - E2M1[lo], E2M1[hi] - a);
53 let pick_hi = dhi < dlo || (dhi == dlo && hi.is_multiple_of(2) && lo % 2 == 1);
54 let v = if pick_hi { E2M1[hi] } else { E2M1[lo] };
55 v.copysign(y)
56}
57
58pub fn fp4_quant_e8m0_inplace(x: &mut [f32], block: usize) {
62 for blk in x.chunks_mut(block) {
63 let amax = blk
64 .iter()
65 .fold(0f32, |a, v| a.max(v.abs()))
66 .max(6.0 * 2f32.powi(-126));
67 let s = pow2_ceil(amax / FP4_MAX);
68 for v in blk.iter_mut() {
69 *v = to_bf16_rne(to_e2m1_rne((*v / s).clamp(-FP4_MAX, FP4_MAX)) * s);
70 }
71 }
72}
73
74pub fn fp4_quant_e4m3_inplace(x: &mut [f32], block: usize) {
78 for blk in x.chunks_mut(block) {
79 let amax = blk
80 .iter()
81 .fold(0f32, |a, v| a.max(v.abs()))
82 .max(6.0 * 2f32.powi(-9));
83 let s = e4m3_to_f32(f32_to_e4m3_rne(amax / FP4_MAX));
84 for v in blk.iter_mut() {
85 *v = to_bf16_rne(to_e2m1_rne((*v / s).clamp(-FP4_MAX, FP4_MAX)) * s);
86 }
87 }
88}
89
90pub fn yarn_freqs_cis(
93 dim: usize,
94 seqlen: usize,
95 original_seq_len: usize,
96 base: f32,
97 factor: f32,
98 beta_fast: f32,
99 beta_slow: f32,
100) -> Vec<(f32, f32)> {
101 let half = dim / 2;
102 let mut freqs: Vec<f32> = (0..half)
103 .map(|k| 1.0 / base.powf((2 * k) as f32 / dim as f32))
104 .collect();
105 let corrected = |rot: f64| -> f64 {
106 dim as f64 * (original_seq_len as f64 / (rot * 2.0 * std::f64::consts::PI)).ln()
107 / (2.0 * (base as f64).ln())
108 };
109 let low = corrected(beta_fast as f64).floor().max(0.0) as f32;
110 let high = (corrected(beta_slow as f64).ceil()).min((dim - 1) as f64) as f32;
111 for (i, f) in freqs.iter_mut().enumerate() {
112 let ramp = ((i as f32 - low) / (high - low).max(1e-3)).clamp(0.0, 1.0);
113 let smooth = 1.0 - ramp;
114 *f = *f / factor * (1.0 - smooth) + *f * smooth;
115 }
116 let mut out = Vec::with_capacity(seqlen * half);
117 for p in 0..seqlen {
118 for &f in &freqs {
119 let a = p as f32 * f;
120 out.push((a.cos(), a.sin()));
121 }
122 }
123 out
124}
125
126pub fn linear_f32(x: &[f32], w: &[f32], rows: usize, in_dim: usize, out_dim: usize) -> Vec<f32> {
129 let mut y = vec![0f32; rows * out_dim];
130 for i in 0..rows {
131 for o in 0..out_dim {
132 y[i * out_dim + o] = x[i * in_dim..(i + 1) * in_dim]
133 .iter()
134 .zip(&w[o * in_dim..(o + 1) * in_dim])
135 .map(|(a, b)| a * b)
136 .sum();
137 }
138 }
139 y
140}
141
142pub struct CompAttnCfg {
143 pub dim: usize,
144 pub n_heads: usize,
145 pub head_dim: usize,
146 pub rope_dim: usize,
147 pub q_rank: usize,
148 pub o_rank: usize,
149 pub groups: usize,
150 pub window: usize,
151 pub eps: f32,
152 pub ratio: usize,
153 pub is_kv_source: bool,
154 pub is_index_source: bool,
155 pub is_candidate_source: bool,
156 pub uses_candidates: bool,
157}
158
159pub struct LayerAttnState {
160 pub window: Vec<f32>,
161 pub compressor: Option<CompressorState>,
162 pub compress_kv_cache: Option<Vec<f32>>,
163 pub k_cache: Option<Vec<f32>>,
164}
165
166impl LayerAttnState {
167 pub fn new(c: &CompAttnCfg, max_seq: usize, index_hd: usize) -> Self {
168 let groups = if c.ratio > 0 { max_seq / c.ratio } else { 0 };
169 LayerAttnState {
170 window: vec![0f32; c.window * c.head_dim],
171 compressor: if c.is_kv_source {
172 Some(CompressorState::new(c.ratio.max(1), c.head_dim))
173 } else {
174 None
175 },
176 compress_kv_cache: if c.is_kv_source {
177 Some(vec![0f32; groups * c.head_dim])
178 } else {
179 None
180 },
181 k_cache: if c.is_kv_source {
182 Some(vec![0f32; groups * index_hd])
183 } else {
184 None
185 },
186 }
187 }
188}
189
190pub struct CompAttnRun {
191 pub q: Vec<f32>,
192 pub kv_rows: Vec<f32>,
193 pub idx: Vec<i32>,
194 pub topk: usize,
195 pub o: Vec<f32>,
196 pub out: Vec<f32>,
197}
198
199#[allow(clippy::too_many_arguments)]
202pub fn attention_any(
203 x: &[f32],
204 seqlen: usize,
205 start_pos: usize,
206 w: &super::attn::AttnWeights,
207 comp: Option<&CompressorWeights>,
208 idxw: Option<&IndexerWeights>,
209 icfg: &IndexerCfg,
210 c: &CompAttnCfg,
211 fc: &[(f32, f32)],
212 st: &mut LayerAttnState,
213 shared: &mut SharedRuntime,
214) -> CompAttnRun {
215 let (hd, rd, nh) = (c.head_dim, c.rope_dim, c.n_heads);
216 let pos: Vec<usize> = (0..seqlen).map(|t| start_pos + t).collect();
217 let head_pos: Vec<usize> = pos
218 .iter()
219 .flat_map(|&p| std::iter::repeat_n(p, nh))
220 .collect();
221
222 let qr = rms_norm(
223 &linear_bf16(x, w.wq_a, seqlen, c.dim, c.q_rank),
224 w.q_norm,
225 seqlen,
226 c.q_rank,
227 c.eps,
228 );
229 let mut q = linear_bf16(&qr, w.wq_b, seqlen, c.q_rank, nh * hd);
230 apply_rotary(&mut q, hd, rd, &head_pos, fc, false);
231
232 let mut kv = rms_norm(
233 &linear_bf16(x, w.wkv, seqlen, c.dim, hd),
234 w.kv_norm,
235 seqlen,
236 hd,
237 c.eps,
238 );
239 apply_rotary(&mut kv, hd, rd, &pos, fc, false);
240 act_quant_inplace(&mut kv);
241 let win = c.window;
242 let mut kv_rows = if start_pos == 0 {
243 if seqlen <= win {
244 st.window[..seqlen * hd].copy_from_slice(&kv);
245 } else {
246 let cutoff = seqlen % win;
247 let tail = &kv[(seqlen - win) * hd..];
248 st.window[cutoff * hd..win * hd].copy_from_slice(&tail[..(win - cutoff) * hd]);
249 st.window[..cutoff * hd].copy_from_slice(&tail[(win - cutoff) * hd..]);
250 }
251 kv.clone()
252 } else {
253 let slot = start_pos % win;
254 st.window[slot * hd..(slot + 1) * hd].copy_from_slice(&kv);
255 st.window.clone()
256 };
257 let (mut idx, mut topk) = window_topk_idxs(win, seqlen, start_pos);
258
259 if c.ratio > 0 {
260 let ratio = c.ratio;
261 let offset = kv_rows.len() / hd;
262 let compress_len = (start_pos + seqlen) / ratio;
263 let latent = if c.is_kv_source {
264 let r = compressor(
265 x,
266 seqlen,
267 start_pos,
268 ratio,
269 c.dim,
270 hd,
271 comp.expect("compressor weights"),
272 st.compressor.as_mut().expect("compressor state"),
273 c.eps,
274 );
275 shared.compress_kv = st.compress_kv_cache.as_ref().expect("cache").clone();
276 r
277 } else {
278 None
279 };
280 let (cidx, ctopk) = if !c.is_index_source {
283 (shared.topk_idxs.clone(), shared.topk)
284 } else if compress_len == 0 {
285 (Vec::new(), 0)
286 } else {
287 let r = indexer(
288 x,
289 &qr,
290 latent.as_deref(),
291 seqlen,
292 start_pos,
293 offset,
294 ratio,
295 icfg,
296 idxw.expect("indexer weights"),
297 fc,
298 st.k_cache.as_mut(),
299 shared,
300 c.is_candidate_source,
301 c.uses_candidates,
302 );
303 shared.topk_idxs = r.0.clone();
304 shared.topk = r.1;
305 r
306 };
307 if let Some(mut lat) = latent {
308 let groups = lat.len() / hd;
309 let lpos: Vec<usize> = if start_pos == 0 {
310 (0..groups).map(|g| g * ratio).collect()
311 } else {
312 vec![start_pos + 1 - ratio]
313 };
314 apply_rotary(&mut lat, hd, rd, &lpos, fc, false);
315 fp4_quant_e4m3_inplace(&mut lat, LATENT_BLOCK);
316 let cache = st.compress_kv_cache.as_mut().expect("cache");
317 let at = start_pos / ratio;
318 cache[at * hd..(at + groups) * hd].copy_from_slice(&lat);
319 shared.compress_kv = cache.clone();
320 }
321 kv_rows.extend_from_slice(&shared.compress_kv[..compress_len * hd]);
323 let mut merged = Vec::with_capacity(seqlen * (topk + ctopk));
324 for t in 0..seqlen {
325 merged.extend_from_slice(&idx[t * topk..(t + 1) * topk]);
326 merged.extend_from_slice(&cidx[t * ctopk..(t + 1) * ctopk]);
327 }
328 idx = merged;
329 topk += ctopk;
330 }
331
332 let scale = (hd as f32).powf(-0.5);
333 let o_sa = sparse_attn(&q, &kv_rows, w.sink, &idx, seqlen, nh, hd, topk, scale);
334 let mut o = o_sa.clone();
335 apply_rotary(&mut o, hd, rd, &head_pos, fc, true);
336 let gw = nh * hd / c.groups;
337 let mut og = vec![0f32; seqlen * c.groups * c.o_rank];
338 for t in 0..seqlen {
339 for g in 0..c.groups {
340 let ov = &o[t * nh * hd + g * gw..t * nh * hd + (g + 1) * gw];
341 for r in 0..c.o_rank {
342 let wr = &w.wo_a[(g * c.o_rank + r) * gw..(g * c.o_rank + r + 1) * gw];
343 og[(t * c.groups + g) * c.o_rank + r] =
344 to_bf16_rne(ov.iter().zip(wr).map(|(a, b)| a * b).sum());
345 }
346 }
347 }
348 let out = linear_bf16(&og, w.wo_b, seqlen, c.groups * c.o_rank, c.dim);
349 CompAttnRun {
350 q,
351 kv_rows,
352 idx,
353 topk,
354 o: o_sa,
355 out,
356 }
357}
358
359#[cfg(test)]
360#[path = "compress_tests.rs"]
361mod tests;