1use std::collections::{BTreeMap, HashMap, HashSet};
12use std::sync::Arc;
13
14use super::gguf::Gguf;
15use super::quant::{f16_to_f32, f32_to_f16, GgmlType};
16use super::spec::{
17 Act, Attention, Ffn, GateActivation, GatedDelta, Gating, HyperConnections, Indexer, Layer, Mixer, Mla, Moe, ModelSpec,
18 Rope, RopeScaling,
19};
20
21#[derive(Debug)]
23pub enum ReferenceError {
24 Io(std::io::Error),
25 NotYet(&'static str),
27}
28
29impl std::fmt::Display for ReferenceError {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 ReferenceError::Io(e) => write!(f, "{e}"),
33 ReferenceError::NotYet(what) => write!(f, "the reference does not evaluate {what} yet"),
34 }
35 }
36}
37
38impl From<std::io::Error> for ReferenceError {
39 fn from(e: std::io::Error) -> Self {
40 ReferenceError::Io(e)
41 }
42}
43
44type R<T> = Result<T, ReferenceError>;
45
46#[derive(Clone)]
48enum LayerState {
49 Attention { k: Vec<Vec<f64>>, v: Vec<Vec<f64>>, ik: Vec<Vec<f64>> },
52 Delta { conv: Vec<Vec<f64>>, memory: Vec<f64> },
55}
56
57impl LayerState {
58 fn fresh(layer: &Layer) -> LayerState {
59 match &layer.mixer {
60 Mixer::Attention(_) | Mixer::Mla(_) => LayerState::Attention { k: Vec::new(), v: Vec::new(), ik: Vec::new() },
61 Mixer::GatedDelta(g) => {
62 let channels = 2 * g.k_heads * g.head_dim + g.v_heads * g.head_dim;
63 LayerState::Delta {
64 conv: vec![vec![0.0; channels]; g.conv_taps.saturating_sub(1)],
65 memory: vec![0.0; g.v_heads * g.head_dim * g.head_dim],
66 }
67 }
68 }
69 }
70}
71
72pub trait WeightSource {
75 fn describe(&self, name: &str) -> std::io::Result<(GgmlType, Vec<u64>)>;
77 fn row_bytes(&mut self, name: &str, first: usize, count: usize) -> std::io::Result<Vec<u8>>;
79 fn has(&self, name: &str) -> bool;
80}
81
82impl WeightSource for Gguf {
83 fn describe(&self, name: &str) -> std::io::Result<(GgmlType, Vec<u64>)> {
84 let ty = self.tensor_type(name)?;
85 Ok((ty, self.dims(name).unwrap_or_default().to_vec()))
86 }
87 fn row_bytes(&mut self, name: &str, first: usize, count: usize) -> std::io::Result<Vec<u8>> {
88 Gguf::row_bytes(self, name, first, count)
89 }
90 fn has(&self, name: &str) -> bool {
91 Gguf::has(self, name)
92 }
93}
94
95#[derive(Default, Clone)]
97pub struct Tensors {
98 map: BTreeMap<String, (GgmlType, Vec<u64>, Vec<u8>)>,
99}
100
101impl Tensors {
102 pub fn f32(&mut self, name: &str, dims: &[u64], values: &[f32]) {
104 assert_eq!(dims.iter().product::<u64>() as usize, values.len(), "{name}: dims and values disagree");
105 let bytes = values.iter().flat_map(|v| v.to_le_bytes()).collect();
106 self.map.insert(name.to_string(), (GgmlType::F32, dims.to_vec(), bytes));
107 }
108
109 pub fn encoded(&mut self, name: &str, ty: GgmlType, dims: &[u64], bytes: Vec<u8>) {
111 let n = dims.iter().product::<u64>() as usize;
112 assert_eq!(ty.bytes_for(n), bytes.len(), "{name}: {} bytes for {n} {} weights", bytes.len(), ty.name());
113 self.map.insert(name.to_string(), (ty, dims.to_vec(), bytes));
114 }
115
116 pub fn names(&self) -> impl Iterator<Item = &str> {
118 self.map.keys().map(String::as_str)
119 }
120
121 pub fn values(&self, name: &str) -> Option<Vec<f32>> {
123 self.map.get(name).map(|(ty, _, bytes)| {
124 let mut out = Vec::new();
125 ty.dequantize(bytes, &mut out);
126 out
127 })
128 }
129}
130
131impl WeightSource for Tensors {
132 fn describe(&self, name: &str) -> std::io::Result<(GgmlType, Vec<u64>)> {
133 self.map
134 .get(name)
135 .map(|(ty, dims, _)| (*ty, dims.clone()))
136 .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, format!("no tensor {name}")))
137 }
138 fn row_bytes(&mut self, name: &str, first: usize, count: usize) -> std::io::Result<Vec<u8>> {
139 let (ty, dims, bytes) = self
140 .map
141 .get(name)
142 .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, format!("no tensor {name}")))?;
143 let row = ty.bytes_for(dims[0] as usize);
144 Ok(bytes[first * row..(first + count) * row].to_vec())
145 }
146 fn has(&self, name: &str) -> bool {
147 self.map.contains_key(name)
148 }
149}
150
151pub const DEFAULT_CACHE_BUDGET: usize = 16 << 30;
154
155#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
159pub enum Activations {
160 #[default]
162 Exact,
163 Int8,
166 Int8TwoPass,
169}
170
171#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
173pub enum KvStorage {
174 #[default]
176 Exact,
177 F16,
180}
181
182fn binary16(v: f64, flipped: bool) -> (f64, f64, f64) {
186 let f = v as f32;
187 let h = f32_to_f16(f);
188 let near = f16_to_f32(h);
189 if near == f || !near.is_finite() {
190 return (f64::from(near), f64::INFINITY, 0.0);
191 }
192 let up = near < f;
194 let other = match (h & 0x8000 != 0, h & 0x7fff, up) {
195 (false, _, true) | (true, _, false) => h.wrapping_add(1),
196 (false, 0, false) => 0x8001,
197 (true, 0, true) => 0x0001,
198 _ => h.wrapping_sub(1),
199 };
200 let far = f16_to_f32(other);
201 let (near, far, f) = (f64::from(near), f64::from(far), f64::from(f));
202 (if flipped { far } else { near }, (f - (near + far) / 2.0).abs(), (far - near).abs())
203}
204
205#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
207pub enum StateStorage {
208 #[default]
210 Exact,
211 Bf16,
214}
215
216impl StateStorage {
217 fn keep(self, v: f64) -> f64 {
219 match self {
220 StateStorage::Exact => v,
221 StateStorage::Bf16 => {
222 let x = v as f32;
223 if x.is_nan() {
224 return f64::from(x);
225 }
226 let bits = x.to_bits();
227 f64::from(f32::from_bits(bits.wrapping_add(0x7FFF + ((bits >> 16) & 1)) & 0xFFFF_0000))
228 }
229 }
230 }
231}
232
233pub fn quantize_activations(x: &[f64], mode: Activations) -> Vec<f64> {
235 quantize_deciding(x, mode, |_, _, q| q.round_ties_even())
236}
237
238fn quantize_deciding(x: &[f64], mode: Activations, mut round: impl FnMut(usize, u8, f32) -> f32) -> Vec<f64> {
241 if mode == Activations::Exact {
242 return x.to_vec();
243 }
244 let word = |q: f32| f32::from(q.clamp(-128.0, 127.0) as i8);
246 let mut out = Vec::with_capacity(x.len());
247 for (b, block) in x.chunks(32).enumerate() {
248 let v: Vec<f32> = block.iter().map(|&a| a as f32).collect();
249 let amax = v.iter().fold(0f32, |m, a| m.max(a.abs()));
250 match mode {
251 Activations::Int8 => {
252 let s = if amax > 0.0 { amax / 127.0 } else { 1.0 };
253 for (i, &a) in v.iter().enumerate() {
254 out.push(f64::from(word(round(b * 32 + i, 0, a / s))) * f64::from(s));
255 }
256 }
257 _ => {
258 let s = amax.max(1e-30) / 127.0;
259 let sl = s * 0.0078125;
260 for (i, &a) in v.iter().enumerate() {
261 let hi = word(round(b * 32 + i, 0, a / s));
262 let lo = word(round(b * 32 + i, 1, (-hi).mul_add(s, a) / sl));
263 out.push(f64::from(hi) * f64::from(s) + f64::from(lo) * f64::from(sl));
264 }
265 }
266 }
267 }
268 out
269}
270
271pub const NEAR_ROUNDING: f64 = 1e-3;
274
275pub const NEAR_ROUTING: f64 = 1e-3;
277
278pub const CACHE_WORD: u8 = 2;
280
281#[derive(Clone, Copy, Debug, PartialEq)]
287pub struct RoundingCall {
288 pub activation: u64,
289 pub element: usize,
290 pub word: u8,
291 pub position: usize,
292 pub margin: f64,
293 pub step: f64,
296}
297
298#[derive(Clone, Copy, Debug, PartialEq)]
300pub enum NearCall {
301 Rounding(RoundingCall),
302 Routing(RoutingCall),
303}
304
305impl NearCall {
306 pub fn position(&self) -> usize {
308 match self {
309 NearCall::Rounding(c) => c.position,
310 NearCall::Routing(c) => c.position,
311 }
312 }
313}
314
315#[derive(Clone, Copy, Debug, PartialEq)]
319pub struct RoutingCall {
320 pub layer: usize,
321 pub position: usize,
322 pub margin: f64,
323 pub score: f64,
324}
325
326pub struct Reference {
328 pub spec: ModelSpec,
329 g: Box<dyn WeightSource>,
330 states: Vec<LayerState>,
331 pos: usize,
332 threads: usize,
333 cache: HashMap<String, Arc<Vec<u8>>>,
335 cached: usize,
336 budget: usize,
337 closest: Option<RoutingCall>,
338 near: Vec<NearCall>,
340 near_roundings: HashSet<(u64, usize, u8)>,
341 at: usize,
343 flipped_roundings: HashSet<(u64, usize, u8)>,
346 flipped_routings: HashSet<(usize, usize)>,
347 activations: Activations,
348 kv: KvStorage,
349 state: StateStorage,
350 ple_tokens: Vec<u32>,
353 ple_conv: Vec<Vec<f64>>,
354}
355
356impl Reference {
357 pub fn new(spec: ModelSpec, g: impl WeightSource + 'static) -> R<Reference> {
358 for layer in &spec.layers {
359 if let Mixer::Attention(a) = &layer.mixer {
360 if a.output_gate && a.fused_qkv {
361 return Err(ReferenceError::NotYet("an output gate on a fused query-key-value projection"));
362 }
363 if a.output_gate && a.value_dim != a.head_dim {
364 return Err(ReferenceError::NotYet("an output gate over values narrower than the queries"));
365 }
366 }
367 }
368 let threads = std::thread::available_parallelism().map_or(4, |n| n.get());
369 let (ple_tokens, ple_conv) = Reference::fresh_ple(&spec);
370 Ok(Reference {
371 ple_tokens,
372 ple_conv,
373 states: spec.layers.iter().map(LayerState::fresh).collect(),
374 spec,
375 g: Box::new(g),
376 pos: 0,
377 threads,
378 cache: HashMap::new(),
379 cached: 0,
380 budget: DEFAULT_CACHE_BUDGET,
381 closest: None,
382 near: Vec::new(),
383 near_roundings: HashSet::new(),
384 at: 0,
385 flipped_roundings: HashSet::new(),
386 flipped_routings: HashSet::new(),
387 activations: Activations::Exact,
388 kv: KvStorage::Exact,
389 state: StateStorage::Exact,
390 })
391 }
392
393 pub fn with_activations(mut self, mode: Activations) -> Reference {
395 self.activations = mode;
396 self
397 }
398
399 pub fn with_kv_storage(mut self, kv: KvStorage) -> Reference {
401 self.kv = kv;
402 self
403 }
404
405 pub fn with_state_storage(mut self, state: StateStorage) -> Reference {
407 self.state = state;
408 self
409 }
410
411 pub fn with_cache_budget(mut self, bytes: usize) -> Reference {
413 self.budget = bytes;
414 self
415 }
416
417 fn tensor_bytes(&mut self, name: &str) -> R<Arc<Vec<u8>>> {
419 if let Some(b) = self.cache.get(name) {
420 return Ok(b.clone());
421 }
422 let (_, dims) = self.g.describe(name)?;
423 let row = dims.first().copied().unwrap_or(1).max(1) as usize;
424 let rows = dims.iter().product::<u64>() as usize / row;
425 let bytes = Arc::new(self.g.row_bytes(name, 0, rows)?);
426 if self.cached + bytes.len() <= self.budget {
427 self.cached += bytes.len();
428 self.cache.insert(name.to_string(), bytes.clone());
429 }
430 Ok(bytes)
431 }
432
433 pub fn position(&self) -> usize {
435 self.pos
436 }
437
438 pub fn cached(&self, l: usize) -> (&[Vec<f64>], &[Vec<f64>]) {
440 match &self.states[l] {
441 LayerState::Attention { k, v, .. } => (k, v),
442 LayerState::Delta { .. } => (&[], &[]),
443 }
444 }
445
446 pub fn memory(&self, l: usize) -> &[f64] {
448 match &self.states[l] {
449 LayerState::Delta { memory, .. } => memory,
450 LayerState::Attention { .. } => &[],
451 }
452 }
453
454 pub fn conv(&self, l: usize) -> &[Vec<f64>] {
457 match &self.states[l] {
458 LayerState::Delta { conv, .. } => conv,
459 LayerState::Attention { .. } => &[],
460 }
461 }
462
463 pub fn reset(&mut self) {
466 self.states = self.spec.layers.iter().map(LayerState::fresh).collect();
467 (self.ple_tokens, self.ple_conv) = Reference::fresh_ple(&self.spec);
468 self.pos = 0;
469 self.closest = None;
470 self.near.clear();
471 self.near_roundings.clear();
472 }
473
474 pub fn near_calls(&self) -> &[NearCall] {
476 &self.near
477 }
478
479 pub fn flip(&mut self, call: NearCall) {
482 match call {
483 NearCall::Rounding(c) => {
484 self.flipped_roundings.insert((c.activation, c.element, c.word));
485 }
486 NearCall::Routing(c) => self.flip_routing(c),
487 }
488 }
489
490 pub fn closest_routing(&self) -> Option<RoutingCall> {
492 self.closest
493 }
494
495 pub fn flip_routing(&mut self, call: RoutingCall) {
499 self.flipped_routings.insert((call.layer, call.position));
500 }
501
502 fn routed(&mut self, layer: usize, position: usize, chosen: f64, passed: f64) {
504 if !passed.is_finite() {
505 return;
506 }
507 let call = RoutingCall { layer, position, margin: chosen - passed, score: chosen };
508 if self.closest.is_none_or(|c| call.margin < c.margin) {
509 self.closest = Some(call);
510 }
511 if call.margin <= NEAR_ROUTING * call.score.abs() {
512 self.near.push(NearCall::Routing(call));
513 }
514 }
515
516 pub fn step(&mut self, tokens: &[u32]) -> R<Vec<f64>> {
518 let x = self.layers(tokens)?;
519 let last = x.last().ok_or(ReferenceError::NotYet("an empty step"))?;
520 Ok(self.head(std::slice::from_ref(last))?.remove(0))
521 }
522
523 pub fn step_every(&mut self, tokens: &[u32]) -> R<Vec<Vec<f64>>> {
526 let x = self.layers(tokens)?;
527 self.head(&x)
528 }
529
530 fn layers(&mut self, tokens: &[u32]) -> R<Vec<Vec<f64>>> {
532 let start = self.pos;
533 self.at = start + tokens.len().max(1) - 1;
534 let mut x: Vec<Vec<f64>> = Vec::with_capacity(tokens.len());
535 for &t in tokens {
536 let mut row = self.rows("token_embd.weight", t as usize, 1)?;
537 if let Some(scale) = self.spec.embedding_scale {
538 row.iter_mut().for_each(|v| *v *= scale);
539 }
540 x.push(row);
541 }
542 if let Some(hc) = self.spec.hyper {
543 let lanes = self.hyper_layers(tokens, &x, start, hc)?;
544 self.pos += tokens.len();
545 return Ok(lanes);
546 }
547 for l in 0..self.spec.layers.len() {
548 let layer = self.spec.layers[l].clone();
549 let xn = self.norm_rows(&x, &format!("blk.{l}.attn_norm.weight"))?;
550 let mut mixed = match &layer.mixer {
551 Mixer::Attention(a) => self.attention(l, a, &xn, start)?,
552 Mixer::GatedDelta(gd) => self.gated_delta(l, gd, &xn)?,
553 Mixer::Mla(m) => self.mla(l, m, &xn, start)?,
554 };
555 if layer.post_mixer_norm {
556 mixed = self.norm_rows(&mixed, &format!("blk.{l}.post_attention_norm.weight"))?;
557 }
558 add_rows(&mut x, &mixed);
559 let xn2 = self.norm_rows(&x, &format!("blk.{l}.{}.weight", self.spec.ffn_norm))?;
560 let mut ffn = self.ffn(l, &layer, &xn2)?;
561 if layer.post_ffn_norm {
562 ffn = self.norm_rows(&ffn, &format!("blk.{l}.post_ffw_norm.weight"))?;
563 }
564 add_rows(&mut x, &ffn);
565 }
566 self.pos += tokens.len();
567 Ok(x)
568 }
569
570 fn head(&mut self, x: &[Vec<f64>]) -> R<Vec<Vec<f64>>> {
572 let xf = match self.spec.hyper {
573 Some(hc) => self.mix_lanes(x, "output_hc", hc, false)?.0,
575 None => self.norm_rows(x, "output_norm.weight")?,
576 };
577 let head = if self.spec.tied_output {
578 "token_embd.weight"
579 } else {
580 "output.weight"
581 };
582 let mut logits = self.project(head, &xf)?;
583 if let Some(cap) = self.spec.final_logit_softcap {
584 logits.iter_mut().flatten().for_each(|v| *v = cap * (*v / cap).tanh());
585 }
586 Ok(logits)
587 }
588
589 pub fn generate(&mut self, prompt: &[u32], n: usize) -> R<Vec<u32>> {
591 let mut logits = self.step(prompt)?;
592 let mut out = Vec::with_capacity(n);
593 for i in 0..n {
594 let t = argmax(&logits);
595 out.push(t);
596 if i + 1 < n {
597 logits = self.step(&[t])?;
598 }
599 }
600 Ok(out)
601 }
602
603 fn tensor(&mut self, name: &str) -> R<Vec<f64>> {
605 let (ty, _) = self.g.describe(name)?;
606 let raw = self.tensor_bytes(name)?;
607 let mut out = Vec::new();
608 ty.dequantize(&raw, &mut out);
609 Ok(out.into_iter().map(f64::from).collect())
610 }
611
612 fn rows(&mut self, name: &str, first: usize, count: usize) -> R<Vec<f64>> {
614 let (ty, dims) = self.g.describe(name)?;
615 let raw = match self.cache.get(name) {
616 Some(b) => {
617 let row = ty.bytes_for(dims.first().copied().unwrap_or(1) as usize);
618 b[first * row..(first + count) * row].to_vec()
619 }
620 None => self.g.row_bytes(name, first, count)?,
621 };
622 let mut out = Vec::new();
623 ty.dequantize(&raw, &mut out);
624 Ok(out.into_iter().map(f64::from).collect())
625 }
626
627 fn norm_rows(&mut self, x: &[Vec<f64>], weight: &str) -> R<Vec<Vec<f64>>> {
628 let w = self.tensor(weight)?;
629 Ok(x.iter().map(|r| rms_norm(r, &w, self.spec.rms_eps)).collect())
630 }
631
632 fn project(&mut self, name: &str, xs: &[Vec<f64>]) -> R<Vec<Vec<f64>>> {
635 let (ty, dims) = self.g.describe(name)?;
636 let [in_f, out_f] = dims[..] else {
637 return Err(ReferenceError::NotYet("a projection that is not a matrix"));
638 };
639 let (in_f, out_f) = (in_f as usize, out_f as usize);
640 let raw = self.tensor_bytes(name)?;
641 let xs = self.read_by(ty, xs);
642 Ok(products(&[Product { raw, from: 0, ty, in_f, out_f, xs }], self.threads).pop().expect("the product"))
643 }
644
645 fn read_by(&mut self, ty: GgmlType, xs: &[Vec<f64>]) -> Vec<Vec<f64>> {
647 match ty {
648 GgmlType::F32 | GgmlType::F16 | GgmlType::Bf16 => xs.to_vec(),
649 _ => xs.iter().map(|x| self.quantize_row(x)).collect(),
650 }
651 }
652
653 fn kept(&mut self, row: Vec<f64>) -> Vec<f64> {
660 if self.kv == KvStorage::Exact {
661 return row;
662 }
663 let activation = row.iter().fold(0xcbf2_9ce4_8422_2325u64, |h, &a| {
664 (a as f32).to_bits().to_le_bytes().iter().fold(h, |h, &b| (h ^ u64::from(b)).wrapping_mul(0x0100_0000_01b3))
665 });
666 let position = self.at;
667 let level = row.iter().fold(0f64, |m, &v| m.max(f64::from((v as f32).abs()))) / 127.0;
668 let mut out = Vec::with_capacity(row.len());
669 for (element, &v) in row.iter().enumerate() {
670 let key = (activation, element, CACHE_WORD);
671 let (kept, distance, apart) = binary16(v, !self.flipped_roundings.is_empty() && self.flipped_roundings.contains(&key));
672 let margin = distance / level;
673 if margin <= NEAR_ROUNDING && self.near_roundings.insert(key) {
674 let step = apart / level;
675 self.near.push(NearCall::Rounding(RoundingCall { activation, element, word: CACHE_WORD, position, margin, step }));
676 }
677 out.push(kept);
678 }
679 out
680 }
681
682 fn quantize_row(&mut self, x: &[f64]) -> Vec<f64> {
685 if self.activations == Activations::Exact {
686 return x.to_vec();
687 }
688 let activation = x.iter().fold(0xcbf2_9ce4_8422_2325u64, |h, &a| {
689 (a as f32).to_bits().to_le_bytes().iter().fold(h, |h, &b| (h ^ u64::from(b)).wrapping_mul(0x0100_0000_01b3))
690 });
691 let position = self.at;
692 let (flipped, recorded) = (&self.flipped_roundings, &mut self.near_roundings);
693 let mut near = Vec::new();
694 let out = quantize_deciding(x, self.activations, |element, word, q| {
695 let r = q.round_ties_even();
696 let margin = f64::from(((q - q.floor()) - 0.5).abs());
697 if margin <= NEAR_ROUNDING && recorded.insert((activation, element, word)) {
698 near.push(NearCall::Rounding(RoundingCall { activation, element, word, position, margin, step: 1.0 }));
699 }
700 match !flipped.is_empty() && flipped.contains(&(activation, element, word)) {
701 true if r > q => r - 1.0,
702 true => r + 1.0,
703 false => r,
704 }
705 });
706 self.near.extend(near);
707 out
708 }
709
710 fn attention(&mut self, l: usize, a: &Attention, xn: &[Vec<f64>], start: usize) -> R<Vec<Vec<f64>>> {
711 let (hd, vd) = (a.head_dim, a.value_dim);
712 let (aq, akv, avd) = (a.heads * hd, a.kv_heads * hd, a.kv_heads * vd);
713 let fused = if a.fused_qkv {
715 let mut qkv = self.project(&format!("blk.{l}.attn_qkv.weight"), xn)?;
716 if a.bias {
717 let b = self.tensor(&format!("blk.{l}.attn_qkv.bias"))?;
718 add_bias(&mut qkv, &b);
719 }
720 Some(qkv)
721 } else {
722 None
723 };
724 let part = |rows: &Option<Vec<Vec<f64>>>, from: usize, n: usize| -> Option<Vec<Vec<f64>>> {
725 rows.as_ref().map(|r| r.iter().map(|row| row[from..from + n].to_vec()).collect())
726 };
727 let projected = match part(&fused, 0, aq) {
728 Some(q) => q,
729 None => self.project(&format!("blk.{l}.attn_q.weight"), xn)?,
730 };
731 let (mut q, gate): (Vec<Vec<f64>>, Option<Vec<Vec<f64>>>) = if a.output_gate {
733 let part = |r: &Vec<f64>, at: usize| -> Vec<f64> {
734 (0..a.heads).flat_map(|h| r[h * 2 * hd + at..h * 2 * hd + at + hd].to_vec()).collect()
735 };
736 (projected.iter().map(|r| part(r, 0)).collect(), Some(projected.iter().map(|r| part(r, hd)).collect()))
737 } else {
738 (projected, None)
739 };
740 let (mut k, mut v) = match (part(&fused, aq, akv), part(&fused, aq + akv, avd)) {
741 (Some(k), Some(v)) => (k, v),
742 _ => (
743 self.project(&format!("blk.{l}.attn_k.weight"), xn)?,
744 self.project(&format!("blk.{l}.attn_v.weight"), xn)?,
745 ),
746 };
747 if a.bias && !a.fused_qkv {
748 for (rows, which) in [(&mut q, "attn_q"), (&mut k, "attn_k"), (&mut v, "attn_v")] {
749 let b = self.tensor(&format!("blk.{l}.{which}.bias"))?;
750 add_bias(rows, &b);
751 }
752 }
753 let (qn, kn) = if a.qk_norm {
754 let qw = self.tensor(&format!("blk.{l}.attn_q_norm.weight"))?;
755 let kw = self.tensor(&format!("blk.{l}.attn_k_norm.weight"))?;
756 (Some(qw), Some(kw))
757 } else {
758 (None, None)
759 };
760 let factors = match a.rope.as_ref().map(|r| &r.scaling) {
761 Some(RopeScaling::Factors) => Some(self.tensor("rope_freqs.weight")?),
762 _ => None,
763 };
764 let sinks = if a.sinks { Some(self.tensor(&format!("blk.{l}.attn_sinks.weight"))?) } else { None };
765 let index = match self.spec.indexer.clone() {
768 Some(ix) if ix.layers.contains(&l) => {
769 let iq = self.project(&format!("blk.{l}.indexer.q_proj.weight"), xn)?;
770 let ik = self.project(&format!("blk.{l}.indexer.k_proj.weight"), xn)?;
771 let qw = self.tensor(&format!("blk.{l}.indexer.q_norm.weight"))?;
772 let kw = self.tensor(&format!("blk.{l}.indexer.k_norm.weight"))?;
773 let eps = self.spec.rms_eps;
774 let queries: Vec<Vec<Vec<f64>>> = iq
775 .iter()
776 .enumerate()
777 .map(|(i, row)| {
778 (0..ix.heads)
779 .map(|h| {
780 let mut q = rms_norm(&row[h * ix.head_dim..(h + 1) * ix.head_dim], &qw, eps);
781 if let Some(r) = &a.rope {
782 rotate(&mut q, start + i, r, factors.as_deref());
783 }
784 q
785 })
786 .collect()
787 })
788 .collect();
789 Some((ix, queries, ik, kw))
790 }
791 _ => None,
792 };
793 let ones = vec![1.0; hd];
795 let (whole, per_head) = if a.qk_norm_whole { ((qn, kn), (None, None)) } else { ((None, None), (qn, kn)) };
797 let (qn, kn) = per_head;
798 for (i, (qr, kr)) in q.iter_mut().zip(k.iter_mut()).enumerate() {
799 let pos = start + i;
800 if let (Some(qw), Some(kw)) = &whole {
801 *qr = rms_norm(qr, qw, self.spec.rms_eps);
802 *kr = rms_norm(kr, kw, self.spec.rms_eps);
803 }
804 for h in 0..a.heads {
805 let s = &mut qr[h * hd..(h + 1) * hd];
806 if let Some(w) = &qn {
807 let n = rms_norm(s, w, self.spec.rms_eps);
808 s.copy_from_slice(&n);
809 }
810 if let Some(r) = &a.rope {
811 rotate(s, pos, r, factors.as_deref());
812 }
813 if a.qk_unit_norm {
814 let n = rms_norm(s, &ones, self.spec.rms_eps);
815 s.copy_from_slice(&n);
816 }
817 if let Some(t) = a.temperature {
818 let f = t.at(pos);
819 s.iter_mut().for_each(|v| *v *= f);
820 }
821 }
822 for h in 0..a.kv_heads {
823 let s = &mut kr[h * hd..(h + 1) * hd];
824 if let Some(w) = &kn {
825 let n = rms_norm(s, w, self.spec.rms_eps);
826 s.copy_from_slice(&n);
827 }
828 if let Some(r) = &a.rope {
829 rotate(s, pos, r, factors.as_deref());
830 }
831 if a.qk_unit_norm {
832 let n = rms_norm(s, &ones, self.spec.rms_eps);
833 s.copy_from_slice(&n);
834 }
835 }
836 }
837 let k: Vec<Vec<f64>> = k.into_iter().map(|r| self.kept(r)).collect();
838 let v: Vec<Vec<f64>> = v.into_iter().map(|r| self.kept(r)).collect();
839 let eps = self.spec.rms_eps;
840 let LayerState::Attention { k: kc, v: vc, ik: ikc } = &mut self.states[l] else {
841 unreachable!("an attention layer holds an attention cache")
842 };
843 kc.extend(k);
844 vc.extend(v);
845 let (kc, vc) = (&*kc, &*vc);
846 let kept: Option<Vec<Vec<usize>>> = match index {
848 Some((ix, queries, keys, kw)) => {
849 ikc.extend(keys);
850 let ikc = &*ikc;
851 Some(
852 queries
853 .iter()
854 .enumerate()
855 .map(|(i, q)| qsa_positions(&ix, q, ikc, &kw, a.rope.as_ref(), factors.as_deref(), eps, start + i))
856 .collect(),
857 )
858 }
859 None => None,
860 };
861 let group = a.heads / a.kv_heads;
862 let mut out = vec![vec![0f64; a.heads * vd]; xn.len()];
863 for (i, o) in out.iter_mut().enumerate() {
864 let pos = start + i;
865 let first = a.window.map_or(0, |w| w.first(pos + 1) - 1);
868 let positions: Vec<usize> = match &kept {
869 Some(kept) => kept[i].clone(),
870 None => (first..=pos).collect(),
871 };
872 for h in 0..a.heads {
873 let g = h / group;
874 let qh = &q[i][h * hd..(h + 1) * hd];
875 let mut scores: Vec<f64> = positions
876 .iter()
877 .map(|&j| {
878 let kj = &kc[j][g * hd..(g + 1) * hd];
879 let s = qh.iter().zip(kj).map(|(x, y)| x * y).sum::<f64>() * a.scale;
880 match a.softcap {
881 Some(cap) => cap * (s / cap).tanh(),
882 None => s,
883 }
884 })
885 .collect();
886 match &sinks {
887 Some(sk) => {
889 let m = scores.iter().cloned().fold(sk[h], f64::max);
890 let total = scores.iter().map(|s| (s - m).exp()).sum::<f64>() + (sk[h] - m).exp();
891 scores.iter_mut().for_each(|s| *s = (*s - m).exp() / total);
892 }
893 None => softmax(&mut scores),
894 }
895 let oh = &mut o[h * vd..(h + 1) * vd];
896 for (&j, w) in positions.iter().zip(&scores) {
897 for (e, slot) in oh.iter_mut().enumerate() {
898 *slot += w * vc[j][g * vd + e];
899 }
900 }
901 }
902 }
903 if let Some(gate) = gate {
904 for (o, g) in out.iter_mut().zip(&gate) {
905 for (v, &g) in o.iter_mut().zip(g) {
906 *v *= sigmoid(g);
907 }
908 }
909 }
910 let mut projected = self.project(&format!("blk.{l}.attn_output.weight"), &out)?;
911 if a.output_bias {
912 let b = self.tensor(&format!("blk.{l}.attn_output.bias"))?;
913 add_bias(&mut projected, &b);
914 }
915 Ok(projected)
916 }
917
918 fn mla(&mut self, l: usize, m: &Mla, xn: &[Vec<f64>], start: usize) -> R<Vec<Vec<f64>>> {
926 let (h, nope, rope, vd, lora) = (m.heads, m.qk_nope, m.qk_rope, m.v_dim, m.kv_lora_rank);
927 let (qk, kw) = (nope + rope, lora + rope);
928 let q = match m.q_lora_rank {
929 None => self.project(&format!("blk.{l}.attn_q.weight"), xn)?,
930 Some(_) => {
931 let qa = self.project(&format!("blk.{l}.attn_q_a.weight"), xn)?;
932 let qa = self.norm_rows(&qa, &format!("blk.{l}.attn_q_a_norm.weight"))?;
933 self.project(&format!("blk.{l}.attn_q_b.weight"), &qa)?
934 }
935 };
936 let kva = self.project(&format!("blk.{l}.attn_kv_a_mqa.weight"), xn)?;
937 let latent: Vec<Vec<f64>> = kva.iter().map(|r| r[..lora].to_vec()).collect();
938 let latent = self.norm_rows(&latent, &format!("blk.{l}.attn_kv_a_norm.weight"))?;
939 let (key_half, value_half) = if m.kv_b_split {
942 ((format!("blk.{l}.attn_k_b.weight"), 0, lora, true), (format!("blk.{l}.attn_v_b.weight"), 0, vd, true))
943 } else {
944 let kv = format!("blk.{l}.attn_kv_b.weight");
945 ((kv.clone(), 0, nope, false), (kv, nope, vd, true))
946 };
947 let head_stride = |half: &(String, usize, usize, bool)| if m.kv_b_split { half.2 } else { nope + vd };
948 let (ks, vs) = (head_stride(&key_half), head_stride(&value_half));
949 let mut out = Vec::with_capacity(xn.len());
950 for (i, (qr, (kr, c))) in q.iter().zip(kva.iter().zip(&latent)).enumerate() {
951 let pos = start + i;
952 let mut pe = kr[lora..kw].to_vec();
953 rotate(&mut pe, pos, &m.rope, None);
954 let row = self.kept(c.iter().chain(&pe).copied().collect());
955 let LayerState::Attention { k: cache, .. } = &mut self.states[l] else {
956 unreachable!("a latent-attention layer holds an attention cache")
957 };
958 cache.push(row);
959 let qn: Vec<f64> = (0..h).flat_map(|hh| qr[hh * qk..hh * qk + nope].to_vec()).collect();
961 let mut ol = vec![0f64; h * lora];
962 for hh in 0..h {
963 let (name, first, count, rows_out) = &key_half;
964 let x = &qn[hh * nope..(hh + 1) * nope];
965 let absorbed = self.head_product(name, hh * ks + first, *count, *rows_out, x)?;
966 let mut qe = qr[hh * qk + nope..(hh + 1) * qk].to_vec();
967 rotate(&mut qe, pos, &m.rope, None);
968 let LayerState::Attention { k: cache, .. } = &self.states[l] else { unreachable!() };
969 let mut scores: Vec<f64> = cache
970 .iter()
971 .map(|row| {
972 let s = absorbed.iter().zip(&row[..lora]).map(|(a, b)| a * b).sum::<f64>()
973 + qe.iter().zip(&row[lora..]).map(|(a, b)| a * b).sum::<f64>();
974 s * m.scale
975 })
976 .collect();
977 softmax(&mut scores);
978 for (w, row) in scores.iter().zip(cache) {
979 for (slot, v) in ol[hh * lora..(hh + 1) * lora].iter_mut().zip(&row[..lora]) {
980 *slot += w * v;
981 }
982 }
983 }
984 let mut ct = Vec::with_capacity(h * vd);
986 for hh in 0..h {
987 let (name, first, count, rows_out) = &value_half;
988 ct.extend(self.head_product(name, hh * vs + first, *count, *rows_out, &ol[hh * lora..(hh + 1) * lora])?);
989 }
990 out.push(ct);
991 }
992 self.project(&format!("blk.{l}.attn_output.weight"), &out)
993 }
994
995 fn head_product(&mut self, name: &str, first: usize, count: usize, rows_out: bool, x: &[f64]) -> R<Vec<f64>> {
998 let w = self.rows(name, first, count)?;
999 let width = w.len() / count.max(1);
1000 Ok(if rows_out {
1001 w.chunks(width).map(|row| row.iter().zip(x).map(|(a, b)| a * b).sum()).collect()
1002 } else {
1003 let mut y = vec![0f64; width];
1004 for (row, xi) in w.chunks(width).zip(x) {
1005 for (slot, v) in y.iter_mut().zip(row) {
1006 *slot += xi * v;
1007 }
1008 }
1009 y
1010 })
1011 }
1012
1013 fn fresh_ple(spec: &ModelSpec) -> (Vec<u32>, Vec<Vec<f64>>) {
1021 match (&spec.ple, spec.hyper) {
1022 (Some(p), Some(hc)) => {
1023 (vec![p.eos; p.ngram - 1], vec![vec![0.0; hc.lanes * spec.d]; (p.conv_taps - 1) * p.ngram])
1024 }
1025 _ => (Vec::new(), Vec::new()),
1026 }
1027 }
1028
1029 fn hyper_layers(&mut self, tokens: &[u32], x: &[Vec<f64>], start: usize, hc: HyperConnections) -> R<Vec<Vec<f64>>> {
1033 let mut h: Vec<Vec<f64>> = x.iter().map(|r| r.repeat(hc.lanes)).collect();
1034 for l in 0..self.spec.layers.len() {
1035 let layer = self.spec.layers[l].clone();
1036 if self.spec.ple.as_ref().is_some_and(|p| p.layer == l) {
1037 let added = self.ple(l, tokens, &h, hc)?;
1038 add_rows(&mut h, &added);
1039 }
1040 let (input, inject) = self.mix_lanes(&h, &format!("blk.{l}.hc_attn"), hc, true)?;
1041 let out = match &layer.mixer {
1042 Mixer::Attention(a) => self.attention(l, a, &input, start)?,
1043 Mixer::GatedDelta(gd) => self.gated_delta(l, gd, &input)?,
1044 Mixer::Mla(m) => self.mla(l, m, &input, start)?,
1045 };
1046 inject_lanes(&mut h, &out, &inject, self.spec.d);
1047 let (input, inject) = self.mix_lanes(&h, &format!("blk.{l}.hc_ffn"), hc, true)?;
1048 let out = self.ffn(l, &layer, &input)?;
1049 inject_lanes(&mut h, &out, &inject, self.spec.d);
1050 }
1051 Ok(h)
1052 }
1053
1054 fn mix_lanes(&mut self, h: &[Vec<f64>], stem: &str, hc: HyperConnections, inject: bool) -> R<(Vec<Vec<f64>>, Vec<Vec<f64>>)> {
1058 let (d, lanes) = (self.spec.d, hc.lanes as f64);
1059 let norm = self.tensor(&format!("{stem}_norm.weight"))?;
1060 let normed: Vec<Vec<f64>> = h.iter().map(|r| lane_norm(r, &norm, d, self.spec.rms_eps)).collect();
1061 let down = self.project(&format!("{stem}_down.weight"), &normed)?;
1062 let mid: Vec<Vec<f64>> = down.iter().map(|r| r.iter().map(|v| silu(v / lanes)).collect()).collect();
1063 let up = self.project(&format!("{stem}_up.weight"), &mid)?;
1064 let inputs = normed
1065 .iter()
1066 .zip(&up)
1067 .map(|(x, w)| (0..d).map(|j| (0..hc.lanes).map(|s| sigmoid(w[s * d + j]) * x[s * d + j]).sum::<f64>() / lanes).collect())
1068 .collect();
1069 let shares = if inject {
1070 let raw = self.project(&format!("{stem}_inject.weight"), &normed)?;
1071 raw.iter().map(|r| r.iter().map(|v| 2.0 * sigmoid(v / lanes)).collect()).collect()
1072 } else {
1073 Vec::new()
1074 };
1075 Ok((inputs, shares))
1076 }
1077
1078 fn ple(&mut self, l: usize, tokens: &[u32], h: &[Vec<f64>], hc: HyperConnections) -> R<Vec<Vec<f64>>> {
1082 let p = self.spec.ple.clone().expect("a per-layer embedding");
1083 let (d, lanes) = (self.spec.d, hc.lanes);
1084 let width = lanes * d;
1085 let mut rows = Vec::with_capacity(tokens.len());
1086 for &t in tokens {
1087 self.ple_tokens.push(t);
1088 let w = &self.ple_tokens[self.ple_tokens.len() - p.ngram..];
1089 let mut emb = Vec::with_capacity((p.ngram - 1) * p.heads_per_ngram * p.head_dim);
1090 for id in ngram_rows(&p, w) {
1091 emb.extend(self.rows("per_layer_token_embd.weight", id as usize, 1)?);
1092 }
1093 rows.push(emb);
1094 }
1095 let key = self.project(&format!("blk.{l}.ple_key.weight"), &rows)?;
1096 let value = self.project(&format!("blk.{l}.ple_value.weight"), &rows)?;
1097 let eps = self.spec.rms_eps;
1098 let (nk, nq, nc) = (
1099 self.tensor(&format!("blk.{l}.ple_norm_key.weight"))?,
1100 self.tensor(&format!("blk.{l}.ple_norm_query.weight"))?,
1101 self.tensor(&format!("blk.{l}.ple_norm_conv.weight"))?,
1102 );
1103 let kernel = self.tensor(&format!("blk.{l}.ple_conv1d.weight"))?;
1104 let mut out = Vec::with_capacity(tokens.len());
1105 for (i, x) in h.iter().enumerate() {
1106 let key = lane_norm(&key[i], &nk, d, eps);
1107 let query = lane_norm(x, &nq, d, eps);
1108 let mut gated = vec![0.0; width];
1109 for s in 0..lanes {
1110 let g = (0..d).map(|j| key[s * d + j] * query[s * d + j]).sum::<f64>() / (d as f64).sqrt();
1111 let sign = if g > 0.0 { 1.0 } else if g < 0.0 { -1.0 } else { 0.0 };
1112 let gate = sigmoid(sign * g.abs().max(1e-6).sqrt());
1113 for j in 0..d {
1114 gated[s * d + j] = gate * value[i][j];
1115 }
1116 }
1117 let normed = lane_norm(&gated, &nc, d, eps);
1118 self.ple_conv.push(normed);
1120 let history = &self.ple_conv;
1121 let last = history.len() - 1;
1122 let row: Vec<f64> = (0..width)
1123 .map(|c| {
1124 let conv = (0..p.conv_taps)
1125 .map(|k| kernel[c * p.conv_taps + k] * history[last - (p.conv_taps - 1 - k) * p.ngram][c])
1126 .sum::<f64>();
1127 gated[c] + silu(conv)
1128 })
1129 .collect();
1130 self.ple_conv.remove(0);
1131 out.push(row);
1132 }
1133 Ok(out)
1134 }
1135
1136 fn gated_delta(&mut self, l: usize, gd: &GatedDelta, xn: &[Vec<f64>]) -> R<Vec<Vec<f64>>> {
1137 let (nk, nv, hd, taps) = (gd.k_heads, gd.v_heads, gd.head_dim, gd.conv_taps);
1138 let (qk, vw) = (nk * hd, nv * hd);
1139 let channels = 2 * qk + vw;
1140 let qkv = self.project(&format!("blk.{l}.attn_qkv.weight"), xn)?;
1141 let z = self.project(&format!("blk.{l}.attn_gate.weight"), xn)?;
1142 let beta = self.project(&format!("blk.{l}.ssm_beta.weight"), xn)?;
1143 let alpha = self.project(&format!("blk.{l}.ssm_alpha.weight"), xn)?;
1144 let kernel = self.tensor(&format!("blk.{l}.ssm_conv1d.weight"))?;
1145 let dt = self.tensor(&format!("blk.{l}.ssm_dt.bias"))?;
1146 let a = self.tensor(&format!("blk.{l}.ssm_a"))?;
1147 let norm = self.tensor(&format!("blk.{l}.ssm_norm.weight"))?;
1148 let eps = self.spec.rms_eps;
1149 let keep_state = self.state;
1150 let LayerState::Delta { conv, memory } = &mut self.states[l] else {
1151 unreachable!("a gated-delta layer holds a recurrent state")
1152 };
1153 let l2 = |x: &[f64]| -> Vec<f64> {
1154 let den = (x.iter().map(|v| v * v).sum::<f64>() + eps).sqrt();
1155 x.iter().map(|v| v / den).collect()
1156 };
1157 let mut reads = Vec::with_capacity(xn.len());
1158 for t in 0..xn.len() {
1159 let c: Vec<f64> = (0..channels)
1160 .map(|ch| {
1161 let older = conv.iter().enumerate().map(|(j, row)| row[ch] * kernel[ch * taps + j]).sum::<f64>();
1162 silu(older + qkv[t][ch] * kernel[ch * taps + taps - 1])
1163 })
1164 .collect();
1165 if taps > 1 {
1166 conv.remove(0);
1167 conv.push(qkv[t].clone());
1168 }
1169 let q: Vec<f64> = (0..nk).flat_map(|h| l2(&c[h * hd..(h + 1) * hd])).collect();
1170 let k: Vec<f64> = (0..nk).flat_map(|h| l2(&c[qk + h * hd..qk + (h + 1) * hd])).collect();
1171 let v = &c[2 * qk..];
1172 let mut read = vec![0.0; vw];
1173 let scale = 1.0 / (hd as f64).sqrt();
1174 for h in 0..nv {
1175 let (kq, kk) = (&q[(h % nk) * hd..(h % nk + 1) * hd], &k[(h % nk) * hd..(h % nk + 1) * hd]);
1176 let decay = (softplus(alpha[t][h] + dt[h]) * a[h]).exp();
1177 let b = sigmoid(beta[t][h]);
1178 let s = &mut memory[h * hd * hd..(h + 1) * hd * hd];
1179 for r in 0..hd {
1180 let row = &mut s[r * hd..(r + 1) * hd];
1181 let u = decay * row.iter().zip(kk).map(|(m, k)| m * k).sum::<f64>();
1182 let coef = b * (v[h * hd + r] - u);
1183 for (m, &kc) in row.iter_mut().zip(kk) {
1184 *m = decay * *m + coef * kc;
1185 }
1186 }
1187 let o: Vec<f64> =
1188 (0..hd).map(|r| s[r * hd..(r + 1) * hd].iter().zip(kq).map(|(m, q)| m * q).sum::<f64>() * scale).collect();
1189 for m in s.iter_mut() {
1190 *m = keep_state.keep(*m);
1191 }
1192 let on = rms_norm(&o, &norm, eps);
1193 for r in 0..hd {
1194 let g = z[t][h * hd + r];
1195 read[h * hd + r] = on[r]
1196 * match gd.gate {
1197 GateActivation::Silu => silu(g),
1198 GateActivation::Sigmoid => sigmoid(g),
1199 };
1200 }
1201 }
1202 reads.push(read);
1203 }
1204 self.project(&format!("blk.{l}.ssm_out.weight"), &reads)
1205 }
1206
1207 fn ffn(&mut self, l: usize, layer: &Layer, xn: &[Vec<f64>]) -> R<Vec<Vec<f64>>> {
1208 match &layer.ffn {
1209 Ffn::Dense { act, .. } => {
1210 let g = self.project(&format!("blk.{l}.ffn_gate.weight"), xn)?;
1211 let u = self.project(&format!("blk.{l}.ffn_up.weight"), xn)?;
1212 let h: Vec<Vec<f64>> = g.iter().zip(&u).map(|(g, u)| gated(act, g, u)).collect();
1213 self.project(&format!("blk.{l}.ffn_down.weight"), &h)
1214 }
1215 Ffn::Moe(m) => self.moe(l, m, xn),
1216 }
1217 }
1218
1219 fn moe(&mut self, l: usize, m: &Moe, xn: &[Vec<f64>]) -> R<Vec<Vec<f64>>> {
1223 fn descending(a: f64, b: f64) -> std::cmp::Ordering {
1226 match (a.is_nan(), b.is_nan()) {
1227 (false, false) => b.partial_cmp(&a).expect("two numbers compare"),
1228 (true, true) => std::cmp::Ordering::Equal,
1229 (true, false) => std::cmp::Ordering::Greater,
1230 (false, true) => std::cmp::Ordering::Less,
1231 }
1232 }
1233 let selection_bias = match m.gating {
1234 Gating::Sigmoid { bias: true } => Some(self.tensor(&format!("blk.{l}.exp_probs_b.bias"))?),
1235 _ => None,
1236 };
1237 let mut logits = self.project(&format!("blk.{l}.ffn_gate_inp.weight"), xn)?;
1238 let bias = |me: &mut Self, which: &str| -> R<Option<Vec<f64>>> {
1239 if m.biases {
1240 Ok(Some(me.tensor(&format!("blk.{l}.{which}.bias"))?))
1241 } else {
1242 Ok(None)
1243 }
1244 };
1245 if let Some(b) = bias(self, "ffn_gate_inp")? {
1246 add_bias(&mut logits, &b);
1247 }
1248 let (bg, bu, bd) = (bias(self, "ffn_gate_exps")?, bias(self, "ffn_up_exps")?, bias(self, "ffn_down_exps")?);
1249 let d = self.spec.d;
1250 let f = m.expert_dff;
1251 let mut routes: Vec<Vec<(usize, f64)>> = Vec::with_capacity(xn.len());
1253 for (t, lg) in logits.iter().enumerate() {
1254 let mut p = lg.clone();
1255 match m.gating {
1256 Gating::Softmax => softmax(&mut p),
1257 Gating::Sigmoid { .. } | Gating::SigmoidScaledInput => p.iter_mut().for_each(|v| *v = sigmoid(*v)),
1258 Gating::SoftmaxOfTopK => {}
1259 }
1260 let mut sel: Vec<f64> = match &selection_bias {
1262 Some(b) => p.iter().zip(b).map(|(v, b)| v + b).collect(),
1263 None => p.clone(),
1264 };
1265 if let Some((groups, used)) = m.groups {
1266 let per = p.len() / groups;
1267 let score = |g: usize| {
1268 let mut v: Vec<f64> = sel[g * per..(g + 1) * per].to_vec();
1269 v.sort_by(|a, b| descending(*a, *b));
1270 v[0] + v.get(1).copied().unwrap_or(0.0)
1271 };
1272 let mut ranked: Vec<usize> = (0..groups).collect();
1273 ranked.sort_by(|&a, &b| descending(score(a), score(b)).then(a.cmp(&b)));
1274 if used < groups {
1275 let (kept, dropped) = (score(ranked[used - 1]), score(ranked[used]));
1276 self.routed(l, self.pos + t, kept, dropped);
1277 }
1278 for &g in &ranked[used..] {
1279 sel[g * per..(g + 1) * per].iter_mut().for_each(|v| *v = f64::NEG_INFINITY);
1280 }
1281 }
1282 let mut order: Vec<usize> = (0..p.len()).collect();
1283 order.sort_by(|&a, &b| descending(sel[a], sel[b]).then(a.cmp(&b)));
1284 if let Some(&passed) = order.get(m.top_k) {
1285 self.routed(l, self.pos + t, sel[order[m.top_k - 1]], sel[passed]);
1286 if self.flipped_routings.contains(&(l, self.pos + t)) {
1287 order.swap(m.top_k - 1, m.top_k);
1288 }
1289 }
1290 let chosen = &order[..m.top_k];
1291 let mut w: Vec<f64> = chosen.iter().map(|&e| p[e]).collect();
1292 if matches!(m.gating, Gating::SoftmaxOfTopK) {
1293 softmax(&mut w);
1294 }
1295 if m.norm_top_k {
1296 let total = w.iter().sum::<f64>().max(6.103515625e-5);
1297 w.iter_mut().for_each(|v| *v /= total);
1298 }
1299 if m.weights_scale != 0.0 && m.weights_scale != 1.0 {
1300 w.iter_mut().for_each(|v| *v *= m.weights_scale);
1301 }
1302 routes.push(chosen.iter().copied().zip(w).collect());
1303 }
1304 let mut readers: BTreeMap<usize, Vec<usize>> = BTreeMap::new();
1306 for (t, route) in routes.iter().enumerate() {
1307 for &(e, _) in route {
1308 readers.entry(e).or_default().push(t);
1309 }
1310 }
1311 let scaled_input = m.gating == Gating::SigmoidScaledInput;
1313 let weight = |t: usize, e: usize| routes[t].iter().find(|(x, _)| *x == e).map(|(_, w)| *w).expect("a chosen expert");
1314 let mut ups = Vec::new();
1316 for (&e, rows) in &readers {
1317 let xs: Vec<Vec<f64>> = rows
1318 .iter()
1319 .map(|&t| if scaled_input { xn[t].iter().map(|v| v * weight(t, e)).collect() } else { xn[t].clone() })
1320 .collect();
1321 ups.push(self.expert(l, "ffn_gate_exps", e, f, &xs)?);
1322 ups.push(self.expert(l, "ffn_up_exps", e, f, &xs)?);
1323 }
1324 let gu = products(&ups, self.threads);
1325 let mut downs = Vec::new();
1326 for ((&e, _), pair) in readers.iter().zip(gu.chunks_exact(2)) {
1327 let (mut g, mut u) = (pair[0].clone(), pair[1].clone());
1328 if let (Some(bg), Some(bu)) = (&bg, &bu) {
1329 g.iter_mut().for_each(|g| g.iter_mut().zip(&bg[e * f..(e + 1) * f]).for_each(|(v, b)| *v += b));
1330 u.iter_mut().for_each(|u| u.iter_mut().zip(&bu[e * f..(e + 1) * f]).for_each(|(v, b)| *v += b));
1331 }
1332 let h: Vec<Vec<f64>> = g.iter().zip(&u).map(|(g, u)| gated(&m.act, g, u)).collect();
1333 downs.push(self.expert(l, "ffn_down_exps", e, d, &h)?);
1334 }
1335 let mut ys: HashMap<(usize, usize), Vec<f64>> = HashMap::new();
1336 for ((&e, rows), y) in readers.iter().zip(products(&downs, self.threads)) {
1337 for (&t, mut y) in rows.iter().zip(y) {
1338 if let Some(bd) = &bd {
1339 y.iter_mut().zip(&bd[e * d..(e + 1) * d]).for_each(|(v, b)| *v += b);
1340 }
1341 ys.insert((t, e), y);
1342 }
1343 }
1344 let mut out = vec![vec![0f64; d]; xn.len()];
1345 for (t, route) in routes.iter().enumerate() {
1346 for &(e, we) in route {
1347 let we = if scaled_input { 1.0 } else { we };
1348 for (slot, v) in out[t].iter_mut().zip(&ys[&(t, e)]) {
1349 *slot += we * v;
1350 }
1351 }
1352 }
1353 if m.shared_experts > 0 {
1354 let g = self.project(&format!("blk.{l}.ffn_gate_shexp.weight"), xn)?;
1355 let u = self.project(&format!("blk.{l}.ffn_up_shexp.weight"), xn)?;
1356 let h: Vec<Vec<f64>> = g.iter().zip(&u).map(|(g, u)| gated(&m.act, g, u)).collect();
1357 let mut y = self.project(&format!("blk.{l}.ffn_down_shexp.weight"), &h)?;
1358 if m.shared_gate {
1360 let g = self.tensor(&format!("blk.{l}.ffn_gate_inp_shexp.weight"))?;
1361 for (row, x) in y.iter_mut().zip(xn) {
1362 let gate = sigmoid(x.iter().zip(&g).map(|(a, b)| a * b).sum());
1363 row.iter_mut().for_each(|v| *v *= gate);
1364 }
1365 }
1366 add_rows(&mut out, &y);
1367 }
1368 Ok(out)
1369 }
1370
1371 fn expert(&mut self, l: usize, which: &str, e: usize, out_f: usize, xs: &[Vec<f64>]) -> R<Product> {
1373 let name = format!("blk.{l}.{which}.weight");
1374 let (ty, _) = self.g.describe(&name)?;
1375 let in_f = xs[0].len();
1376 let row = ty.bytes_for(in_f);
1377 let raw = self.tensor_bytes(&name)?;
1378 let xs = self.read_by(ty, xs);
1379 Ok(Product { raw, from: e * out_f * row, ty, in_f, out_f, xs })
1380 }
1381}
1382
1383const MATVEC_GRAIN: usize = 1 << 20;
1386
1387struct Product {
1389 raw: Arc<Vec<u8>>,
1390 from: usize,
1391 ty: GgmlType,
1392 in_f: usize,
1393 out_f: usize,
1394 xs: Vec<Vec<f64>>,
1395}
1396
1397fn products(ps: &[Product], threads: usize) -> Vec<Vec<Vec<f64>>> {
1402 let work = |p: &Product| p.in_f * p.xs.len().max(1);
1403 let total: usize = ps.iter().map(|p| p.out_f * work(p)).sum();
1404 let tasks = threads.min(total.div_ceil(MATVEC_GRAIN)).max(1);
1405 let share = total.div_ceil(tasks);
1407 let mut runs: Vec<Vec<(usize, usize, usize)>> = vec![Vec::new()];
1408 let mut load = 0;
1409 for (i, p) in ps.iter().enumerate() {
1410 let mut first = 0;
1411 while first < p.out_f {
1412 if load >= share && runs.len() < tasks {
1413 runs.push(Vec::new());
1414 load = 0;
1415 }
1416 let rows = if runs.len() == tasks {
1417 p.out_f - first
1418 } else {
1419 (share - load.min(share)).div_ceil(work(p)).clamp(1, p.out_f - first)
1420 };
1421 runs.last_mut().expect("a task").push((i, first, rows));
1422 load += rows * work(p);
1423 first += rows;
1424 }
1425 }
1426 let run = |task: &[(usize, usize, usize)]| -> Vec<(usize, usize, Vec<Vec<f64>>)> {
1427 task.iter()
1428 .map(|&(i, first, rows)| {
1429 let p = &ps[i];
1430 let row_bytes = p.ty.bytes_for(p.in_f);
1431 let mut w: Vec<f32> = Vec::with_capacity(p.in_f);
1432 let mut ys: Vec<Vec<f64>> = p.xs.iter().map(|_| Vec::with_capacity(rows)).collect();
1433 for r in first..first + rows {
1434 let at = p.from + r * row_bytes;
1435 w.clear();
1436 p.ty.dequantize(&p.raw[at..at + row_bytes], &mut w);
1437 for (y, x) in ys.iter_mut().zip(&p.xs) {
1438 y.push(w.iter().zip(x).map(|(&a, b)| a as f64 * b).sum());
1439 }
1440 }
1441 (i, first, ys)
1442 })
1443 .collect()
1444 };
1445 let parts: Vec<(usize, usize, Vec<Vec<f64>>)> = if runs.len() == 1 {
1446 run(&runs[0])
1447 } else {
1448 std::thread::scope(|s| {
1449 let handles: Vec<_> = runs.iter().map(|task| s.spawn(move || run(task))).collect();
1450 handles.into_iter().flat_map(|h| h.join().expect("product thread")).collect()
1451 })
1452 };
1453 let mut out: Vec<Vec<Vec<f64>>> = ps.iter().map(|p| vec![vec![0f64; p.out_f]; p.xs.len()]).collect();
1454 for (i, first, ys) in parts {
1455 for (col, y) in out[i].iter_mut().zip(ys) {
1456 col[first..first + y.len()].copy_from_slice(&y);
1457 }
1458 }
1459 out
1460}
1461
1462fn add_rows(x: &mut [Vec<f64>], y: &[Vec<f64>]) {
1463 for (a, b) in x.iter_mut().zip(y) {
1464 for (u, v) in a.iter_mut().zip(b) {
1465 *u += v;
1466 }
1467 }
1468}
1469
1470fn inject_lanes(h: &mut [Vec<f64>], out: &[Vec<f64>], shares: &[Vec<f64>], d: usize) {
1472 for ((row, o), share) in h.iter_mut().zip(out).zip(shares) {
1473 for (s, &w) in share.iter().enumerate() {
1474 for j in 0..d {
1475 row[s * d + j] += o[j] * w;
1476 }
1477 }
1478 }
1479}
1480
1481fn lane_norm(x: &[f64], w: &[f64], d: usize, eps: f64) -> Vec<f64> {
1483 x.chunks(d).zip(w.chunks(d)).flat_map(|(lane, w)| rms_norm(lane, w, eps)).collect()
1484}
1485
1486fn ngram_rows(p: &super::spec::Ple, w: &[u32]) -> Vec<u64> {
1491 let last = w.len() - 1;
1492 let back = |k: usize| -> i64 {
1493 if w[last - k..last].contains(&p.eos) {
1494 i64::from(p.eos)
1495 } else {
1496 i64::from(w[last - k])
1497 }
1498 };
1499 let mut rows = Vec::with_capacity((p.ngram - 1) * p.heads_per_ngram);
1500 for n in 2..=p.ngram {
1501 let mixed = (0..n).fold(0i64, |acc, k| {
1502 let t = if k == 0 { i64::from(w[last]) } else { back(k) };
1503 acc ^ t.wrapping_mul(p.multipliers[k] as i64)
1504 });
1505 for j in 0..p.heads_per_ngram {
1506 let head = (n - 2) * p.heads_per_ngram + j;
1507 rows.push(mixed.rem_euclid(p.head_vocab[head] as i64) as u64 + p.head_offsets[head]);
1508 }
1509 }
1510 rows
1511}
1512
1513#[allow(clippy::too_many_arguments)]
1518fn qsa_positions(
1519 ix: &Indexer,
1520 queries: &[Vec<f64>],
1521 keys: &[Vec<f64>],
1522 norm: &[f64],
1523 rope: Option<&Rope>,
1524 factors: Option<&[f64]>,
1525 eps: f64,
1526 pos: usize,
1527) -> Vec<usize> {
1528 let blocks = (pos + 1) / ix.block;
1529 let keep = ix.budget / ix.block;
1530 if blocks <= keep {
1531 return (0..=pos).collect();
1532 }
1533 let mut scored: Vec<(f64, usize)> = (0..blocks)
1534 .map(|b| {
1535 let first = b * ix.block;
1536 let mean: Vec<f64> = (0..ix.head_dim)
1537 .map(|e| keys[first..first + ix.block].iter().map(|k| k[e]).sum::<f64>() / ix.block as f64)
1538 .collect();
1539 let mut k = rms_norm(&mean, norm, eps);
1540 if let Some(r) = rope {
1541 rotate(&mut k, first, r, factors);
1542 }
1543 let score = queries.iter().map(|q| q.iter().zip(&k).map(|(a, b)| a * b).sum::<f64>().max(0.0)).sum::<f64>()
1544 / (ix.head_dim as f64).sqrt();
1545 (score, b)
1546 })
1547 .collect();
1548 scored.sort_by(|a, b| b.0.total_cmp(&a.0).then(a.1.cmp(&b.1)));
1549 let mut chosen: Vec<usize> = scored[..keep].iter().map(|&(_, b)| b).collect();
1550 chosen.sort_unstable();
1551 chosen.into_iter().flat_map(|b| b * ix.block..(b + 1) * ix.block).chain(blocks * ix.block..=pos).collect()
1552}
1553
1554fn add_bias(rows: &mut [Vec<f64>], b: &[f64]) {
1555 for r in rows {
1556 r.iter_mut().zip(b).for_each(|(v, b)| *v += b);
1557 }
1558}
1559
1560fn rms_norm(x: &[f64], w: &[f64], eps: f64) -> Vec<f64> {
1561 let ms = x.iter().map(|v| v * v).sum::<f64>() / x.len() as f64;
1562 let scale = 1.0 / (ms + eps).sqrt();
1563 x.iter().zip(w).map(|(v, w)| v * scale * w).collect()
1564}
1565
1566fn softmax(xs: &mut [f64]) {
1567 let m = xs.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
1568 let mut sum = 0.0;
1569 for v in xs.iter_mut() {
1570 *v = (*v - m).exp();
1571 sum += *v;
1572 }
1573 for v in xs.iter_mut() {
1574 *v /= sum;
1575 }
1576}
1577
1578fn sigmoid(x: f64) -> f64 {
1579 1.0 / (1.0 + (-x).exp())
1580}
1581
1582fn silu(x: f64) -> f64 {
1583 x * sigmoid(x)
1584}
1585
1586fn softplus(x: f64) -> f64 {
1588 if x > 0.0 {
1589 x + (-x).exp().ln_1p()
1590 } else {
1591 x.exp().ln_1p()
1592 }
1593}
1594
1595fn gated(act: &Act, g: &[f64], u: &[f64]) -> Vec<f64> {
1596 g.iter()
1597 .zip(u)
1598 .map(|(&g, &u)| match act {
1599 Act::Silu => g / (1.0 + (-g).exp()) * u,
1600 Act::GeluTanh => {
1601 let c = (2.0 / std::f64::consts::PI).sqrt();
1602 0.5 * g * (1.0 + (c * (g + 0.044715 * g * g * g)).tanh()) * u
1603 }
1604 Act::SwigluOai { alpha, limit } => {
1605 let gc = g.min(*limit);
1606 let uc = u.clamp(-limit, *limit);
1607 gc / (1.0 + (-alpha * gc).exp()) * (uc + 1.0)
1608 }
1609 })
1610 .collect()
1611}
1612
1613fn rotate(v: &mut [f64], pos: usize, rope: &Rope, factors: Option<&[f64]>) {
1616 let half = rope.dims / 2;
1617 for i in 0..half {
1618 let (c, s) = rope.rotation(i, pos, factors.map(|f| f[i]));
1619 let (a, b) = if rope.neox { (i, i + half) } else { (2 * i, 2 * i + 1) };
1620 let (x0, x1) = (v[a], v[b]);
1621 v[a] = x0 * c - x1 * s;
1622 v[b] = x0 * s + x1 * c;
1623 }
1624}
1625
1626pub fn argmax(logits: &[f64]) -> u32 {
1628 let mut best = 0;
1629 for (i, &v) in logits.iter().enumerate() {
1630 if v > logits[best] {
1631 best = i;
1632 }
1633 }
1634 best as u32
1635}