extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use crate::math::round;
use crate::v3::attention::KvStore;
use crate::v3::config::V3Config;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KvPrecision {
F32,
Int8,
}
pub fn fake_quant_vec(x: &mut [f32], bits: u32) {
let (scale, inv, qmax) = quant_scale(x, bits);
for v in x.iter_mut() {
*v = round(*v * inv).clamp(-qmax - 1.0, qmax) * scale;
}
}
fn quant_scale(x: &[f32], bits: u32) -> (f32, f32, f32) {
let qmax = ((1u32 << (bits - 1)) - 1) as f32;
let mut absmax = 0.0f32;
for v in x.iter() {
let a = if *v < 0.0 { -*v } else { *v };
if a > absmax {
absmax = a;
}
}
let scale = if absmax > 0.0 { absmax / qmax } else { 1.0 };
(scale, 1.0 / scale, qmax)
}
fn quant_into(src: &[f32], dst: &mut [i8]) -> f32 {
let (scale, inv, qmax) = quant_scale(src, 8);
for (d, v) in dst.iter_mut().zip(src) {
*d = round(*v * inv).clamp(-qmax - 1.0, qmax) as i8;
}
scale
}
pub fn quantize_rows(x: &[f32], head_dim: usize) -> (Vec<i8>, Vec<f32>) {
let rows = x.len() / head_dim;
let mut ints = vec![0i8; x.len()];
let mut scales = vec![1.0f32; rows];
for (r, scale) in scales.iter_mut().enumerate() {
let o = r * head_dim;
*scale = quant_into(&x[o..o + head_dim], &mut ints[o..o + head_dim]);
}
(ints, scales)
}
struct LayerCache {
k: Vec<f32>,
v: Vec<f32>,
kq: Vec<i8>,
vq: Vec<i8>,
k_scale: Vec<f32>,
v_scale: Vec<f32>,
slots: usize,
cap: usize,
q_tail: Vec<f32>,
k_tail: Vec<f32>,
v_tail: Vec<f32>,
}
pub struct V3Cache {
cfg: V3Config,
precision: KvPrecision,
layers: Vec<LayerCache>,
tokens: Vec<u32>,
engram_tail: Vec<Vec<f32>>,
pos: usize,
}
impl V3Cache {
pub fn new(cfg: &V3Config, hint: usize) -> Self {
Self::with_precision(cfg, hint, KvPrecision::F32)
}
pub fn with_precision(cfg: &V3Config, hint: usize, precision: KvPrecision) -> Self {
let hint = hint.clamp(1, cfg.max_seq_len);
let taps_tail = cfg.qkv_conv_taps.saturating_sub(1);
let layers = (0..cfg.num_layers)
.map(|li| {
let cap = match cfg.attention_span(li) {
Some(w) => w.min(cfg.max_seq_len),
None => cfg.max_seq_len,
};
let slots = hint.min(cap);
let int8 = precision == KvPrecision::Int8;
let heads = slots * cfg.num_kv_heads;
LayerCache {
k: if int8 {
Vec::new()
} else {
vec![0.0; slots * cfg.k_dim()]
},
v: if int8 {
Vec::new()
} else {
vec![0.0; slots * cfg.v_dim()]
},
kq: if int8 {
vec![0; slots * cfg.k_dim()]
} else {
Vec::new()
},
vq: if int8 {
vec![0; slots * cfg.v_dim()]
} else {
Vec::new()
},
k_scale: if int8 { vec![1.0; heads] } else { Vec::new() },
v_scale: if int8 { vec![1.0; heads] } else { Vec::new() },
slots,
cap,
q_tail: vec![0.0; taps_tail * cfg.q_dim()],
k_tail: vec![0.0; taps_tail * cfg.k_dim()],
v_tail: vec![0.0; taps_tail * cfg.v_dim()],
}
})
.collect();
let reach = cfg.engram.conv_taps.saturating_sub(1) * cfg.engram.conv_dilation;
let engram_tail = (0..cfg.engram.sites.len())
.map(|_| vec![0.0; reach * cfg.d_model])
.collect();
Self {
cfg: cfg.clone(),
precision,
layers,
tokens: Vec::with_capacity(hint),
engram_tail,
pos: 0,
}
}
pub fn precision(&self) -> KvPrecision {
self.precision
}
pub fn len(&self) -> usize {
self.pos
}
pub fn is_empty(&self) -> bool {
self.pos == 0
}
pub fn bytes(&self) -> usize {
let per_layer: usize = self
.layers
.iter()
.map(|l| {
(l.k.len() + l.v.len() + l.k_scale.len() + l.v_scale.len()) * 4
+ l.kq.len()
+ l.vq.len()
+ (l.q_tail.len() + l.k_tail.len() + l.v_tail.len()) * 4
})
.sum();
let engram: usize = self.engram_tail.iter().map(|t| t.len() * 4).sum();
per_layer + engram + self.tokens.len() * 4
}
pub fn token_history(&self) -> &[u32] {
&self.tokens
}
pub fn push_token(&mut self, tok: u32) {
let keep = self.cfg.engram.orders.iter().copied().max().unwrap_or(1);
self.tokens.push(tok);
if self.tokens.len() > keep {
let drop = self.tokens.len() - keep;
self.tokens.drain(..drop);
}
}
fn ensure(&mut self, li: usize, pos: usize) {
let (k_dim, v_dim) = (self.cfg.k_dim(), self.cfg.v_dim());
let heads = self.cfg.num_kv_heads;
let layer = &mut self.layers[li];
if pos < layer.slots || layer.slots >= layer.cap {
return;
}
let want = (layer.slots * 2).max(pos + 1).min(layer.cap);
if layer.kq.is_empty() && layer.vq.is_empty() {
layer.k.resize(want * k_dim, 0.0);
layer.v.resize(want * v_dim, 0.0);
} else {
layer.kq.resize(want * k_dim, 0);
layer.vq.resize(want * v_dim, 0);
layer.k_scale.resize(want * heads, 1.0);
layer.v_scale.resize(want * heads, 1.0);
}
layer.slots = want;
}
pub fn span(&self, li: usize, pos: usize) -> (usize, usize) {
let slots = self.layers[li].slots;
let lo = match self.cfg.attention_span(li) {
Some(w) if pos + 1 > w => pos + 1 - w,
_ => 0,
};
let lo = lo.max((pos + 1).saturating_sub(slots));
(lo, pos)
}
pub fn is_saturated(&self) -> bool {
(0..self.cfg.num_layers)
.any(|li| self.cfg.attention_span(li).is_none() && self.pos > self.layers[li].slots)
}
pub fn slots(&self, li: usize) -> usize {
self.layers[li].slots
}
pub fn write_kv(&mut self, li: usize, pos: usize, k: &[f32], v: &[f32]) {
self.ensure(li, pos);
let (k_dim, v_dim) = (self.cfg.k_dim(), self.cfg.v_dim());
let precision = self.precision;
let num_kv_heads = self.cfg.num_kv_heads;
let (qk, vh) = (self.cfg.qk_head_dim, self.cfg.v_head_dim);
let layer = &mut self.layers[li];
let slot = pos % layer.slots;
match precision {
KvPrecision::F32 => {
layer.k[slot * k_dim..(slot + 1) * k_dim].copy_from_slice(k);
layer.v[slot * v_dim..(slot + 1) * v_dim].copy_from_slice(v);
}
KvPrecision::Int8 => {
let base = slot * num_kv_heads;
for h in 0..num_kv_heads {
let sk = quant_into(
&k[h * qk..(h + 1) * qk],
&mut layer.kq[slot * k_dim + h * qk..slot * k_dim + (h + 1) * qk],
);
layer.k_scale[base + h] = sk;
let sv = quant_into(
&v[h * vh..(h + 1) * vh],
&mut layer.vq[slot * v_dim + h * vh..slot * v_dim + (h + 1) * vh],
);
layer.v_scale[base + h] = sv;
}
}
}
}
pub fn kv(&self, li: usize) -> KvStore<'_> {
let l = &self.layers[li];
match self.precision {
KvPrecision::F32 => KvStore::F32 { k: &l.k, v: &l.v },
KvPrecision::Int8 => KvStore::Int8 {
k: &l.kq,
k_scale: &l.k_scale,
v: &l.vq,
v_scale: &l.v_scale,
},
}
}
pub fn conv_step(&mut self, li: usize, which: Qkv, taps: &[f32], raw: &mut [f32]) {
let n_taps = self.cfg.qkv_conv_taps;
if n_taps == 0 {
return;
}
let dim = raw.len();
let pos = self.pos;
let layer = &mut self.layers[li];
let tail = match which {
Qkv::Q => &mut layer.q_tail,
Qkv::K => &mut layer.k_tail,
Qkv::V => &mut layer.v_tail,
};
debug_assert_eq!(tail.len(), (n_taps - 1) * dim);
let mut out = vec![0.0f32; dim];
for c in 0..dim {
let mut acc = taps[c] * raw[c];
for j in 1..n_taps {
if j > pos {
break;
}
let slot = n_taps - 1 - j;
acc += taps[j * dim + c] * tail[slot * dim + c];
}
out[c] = acc;
}
if n_taps > 1 {
tail.copy_within(dim.., 0);
let last = (n_taps - 2) * dim;
tail[last..last + dim].copy_from_slice(raw);
}
raw.copy_from_slice(&out);
}
pub fn engram_conv_step(&mut self, site: usize, taps: &[f32], raw: &mut [f32]) {
let e = &self.cfg.engram;
let (n_taps, dil) = (e.conv_taps, e.conv_dilation);
let max_order = e.orders.iter().copied().max().unwrap_or(1);
let d = self.cfg.d_model;
let pos = self.pos;
let reach = (n_taps - 1) * dil;
let tail = &mut self.engram_tail[site];
let mut out = vec![0.0f32; d];
for c in 0..d {
let mut acc = 0.0f32;
for j in 0..n_taps {
let back = j * dil;
if back > pos || pos < j * max_order {
continue;
}
let val = if j == 0 {
raw[c]
} else {
tail[(reach - back) * d + c]
};
acc += taps[j * d + c] * val;
}
out[c] = acc;
}
if reach > 0 {
tail.copy_within(d.., 0);
let last = (reach - 1) * d;
tail[last..last + d].copy_from_slice(raw);
}
raw.copy_from_slice(&out);
}
pub fn seed_conv_tail(&mut self, li: usize, which: Qkv, raw: &[f32]) {
let layer = &mut self.layers[li];
let tail = match which {
Qkv::Q => &mut layer.q_tail,
Qkv::K => &mut layer.k_tail,
Qkv::V => &mut layer.v_tail,
};
debug_assert_eq!(raw.len(), tail.len());
tail.copy_from_slice(raw);
}
pub fn seed_engram_tail(&mut self, site: usize, raw: &[f32]) {
let tail = &mut self.engram_tail[site];
debug_assert_eq!(raw.len(), tail.len());
tail.copy_from_slice(raw);
}
pub fn seed_tokens(&mut self, tokens: &[u32]) {
let keep = self.cfg.engram.orders.iter().copied().max().unwrap_or(1);
self.tokens.clear();
let start = tokens.len().saturating_sub(keep);
self.tokens.extend_from_slice(&tokens[start..]);
}
pub fn set_pos(&mut self, pos: usize) {
self.pos = pos;
}
pub fn advance(&mut self) {
self.pos += 1;
}
pub fn pos(&self) -> usize {
self.pos
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Qkv {
Q,
K,
V,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::v3::config::V3Engram;
fn cfg() -> V3Config {
V3Config {
vocab_size: 8192,
out_vocab: 8192,
d_model: 8,
num_heads: 2,
num_kv_heads: 1,
num_layers: 4,
qk_head_dim: 4,
v_head_dim: 4,
max_seq_len: 64,
hada_n: 8,
mhc_lanes: 2,
rope_theta: 10000.0,
sliding_window: 4,
global_layers: vec![3],
qkv_conv_taps: 3,
engram: V3Engram {
orders: vec![2, 3],
heads: 1,
slots: 16,
sub_dim: 4,
sites: vec![1],
conv_taps: 4,
conv_dilation: 3,
seed_heads: 0,
},
}
}
#[test]
fn an_int8_ring_wraps_and_grows_like_the_f32_one() {
let c = cfg();
let mut cache = V3Cache::with_precision(&c, 2, KvPrecision::Int8);
let heads = c.num_kv_heads;
let write = |cache: &mut V3Cache, pos: usize| {
let m = (pos + 1) as f32;
let k: Vec<f32> = (0..c.k_dim()).map(|i| m * (i as f32 + 1.0)).collect();
let v: Vec<f32> = (0..c.v_dim()).map(|i| -m * (i as f32 + 1.0)).collect();
cache.write_kv(0, pos, &k, &v);
};
for pos in 0..10 {
write(&mut cache, pos);
}
assert_eq!(cache.slots(0), 4, "local layer is capped at its window");
let KvStore::Int8 {
k,
k_scale,
v_scale,
..
} = cache.kv(0)
else {
panic!("an int8 cache must hand out an int8 store");
};
assert_eq!(k.len(), 4 * c.k_dim());
assert_eq!(k_scale.len(), 4 * heads);
assert_eq!(v_scale.len(), 4 * heads);
let slot = 9 % 4;
let want = {
let m = 10.0f32;
let mut x: Vec<f32> = (0..c.k_dim()).map(|i| m * (i as f32 + 1.0)).collect();
for ch in x.chunks_mut(c.qk_head_dim) {
fake_quant_vec(ch, 8);
}
x
};
for h in 0..heads {
let sc = k_scale[slot * heads + h];
for i in 0..c.qk_head_dim {
let idx = slot * c.k_dim() + h * c.qk_head_dim + i;
let got = k[idx] as f32 * sc;
let expect = want[h * c.qk_head_dim + i];
assert!(
(got - expect).abs() <= 1e-4 * expect.abs().max(1.0),
"slot {slot} head {h} element {i}: {got} against {expect}"
);
}
}
}
#[test]
fn layers_grow_to_their_cap_and_no_further() {
let c = cfg();
let mut cache = V3Cache::new(&c, 2);
assert_eq!(
cache.slots(0),
2,
"local layer starts at the hint, not its window"
);
assert_eq!(cache.slots(3), 2, "global layer starts at the hint");
let k = vec![1.0f32; c.k_dim()];
let v = vec![1.0f32; c.v_dim()];
cache.write_kv(0, 3, &k, &v);
assert_eq!(cache.slots(0), 4);
cache.write_kv(0, 99, &k, &v);
assert_eq!(
cache.slots(0),
4,
"a local layer must never exceed its window"
);
cache.write_kv(3, 9, &k, &v);
assert!(cache.slots(3) >= 10, "global layer should have grown");
}
fn shipped() -> V3Config {
V3Config {
vocab_size: 8192,
out_vocab: 8192,
d_model: 768,
num_heads: 12,
num_kv_heads: 2,
num_layers: 20,
qk_head_dim: 48,
v_head_dim: 64,
max_seq_len: 8192,
hada_n: 1024,
mhc_lanes: 4,
rope_theta: 100_000.0,
sliding_window: 1024,
global_layers: vec![4, 9, 14, 19],
qkv_conv_taps: 3,
engram: V3Engram {
orders: vec![2, 3],
heads: 3,
slots: 18432,
sub_dim: 128,
sites: vec![3, 7, 11, 15, 19],
conv_taps: 4,
conv_dilation: 3,
seed_heads: 0,
},
}
}
#[test]
fn a_global_layer_never_asks_for_more_than_it_stores() {
let mut c = shipped();
c.max_seq_len = 8;
c.sliding_window = 4;
c.num_layers = 2;
c.global_layers = vec![1];
let mut cache = V3Cache::new(&c, 8);
let k = vec![0.0f32; c.k_dim()];
let v = vec![0.0f32; c.v_dim()];
for pos in 0..12 {
cache.write_kv(1, pos, &k, &v);
let (lo, hi) = cache.span(1, pos);
let slots = cache.slots(1);
assert!(
hi - lo < slots,
"span {lo}..={hi} exceeds {slots} slots at position {pos}"
);
}
for _ in 0..12 {
cache.advance();
}
assert!(
cache.is_saturated(),
"running past the context must be visible"
);
}
#[test]
fn a_short_session_pays_for_what_it_uses() {
let c = shipped();
let k = vec![0.0f32; c.k_dim()];
let v = vec![0.0f32; c.v_dim()];
let mut short = V3Cache::new(&c, 512);
for li in 0..c.num_layers {
short.write_kv(li, 511, &k, &v);
}
let short_mb = short.bytes() as f64 / 1024.0 / 1024.0;
let reserved = (16 * 1024 + 4 * 8192) * (c.k_dim() + c.v_dim()) * 4;
let reserved_mb = reserved as f64 / 1024.0 / 1024.0;
assert!(
short_mb < reserved_mb / 4.0,
"a 512-token session should cost far less than reserving the \
windows: {short_mb:.1} MB vs {reserved_mb:.1} MB"
);
assert!(
short_mb < 9.5,
"expected under 9.5 MB at f32, got {short_mb:.1} MB"
);
assert!(
short_mb > 8.0,
"unexpectedly small — has the KV representation changed? {short_mb:.1} MB"
);
}
#[test]
fn a_long_session_caps_the_local_layers() {
let c = shipped();
let k = vec![0.0f32; c.k_dim()];
let v = vec![0.0f32; c.v_dim()];
let mut cache = V3Cache::new(&c, 64);
for li in 0..c.num_layers {
cache.write_kv(li, 4095, &k, &v);
}
for li in 0..c.num_layers {
if c.is_global(li) {
assert!(
cache.slots(li) >= 4096,
"global layer {li} must hold all of it"
);
} else {
assert_eq!(cache.slots(li), 1024, "local layer {li} caps at its window");
}
}
}
#[test]
fn spans_bound_local_layers_only() {
let c = cfg();
let mut cache = V3Cache::new(&c, 8);
let k = vec![0.0f32; c.k_dim()];
let v = vec![0.0f32; c.v_dim()];
for pos in 0..=10 {
cache.write_kv(0, pos, &k, &v);
cache.write_kv(3, pos, &k, &v);
}
assert_eq!(cache.span(0, 2), (0, 2));
assert_eq!(cache.span(0, 10), (7, 10));
assert_eq!(cache.span(3, 10), (0, 10));
}
#[test]
fn conv_step_matches_the_sequence_conv() {
use crate::v3::attention::causal_depthwise_conv;
let c = cfg();
let dim = c.q_dim();
let taps: Vec<f32> = (0..3 * dim).map(|i| (i as f32 * 0.37).sin()).collect();
let seq = 6;
let raw: Vec<f32> = (0..seq * dim).map(|i| (i as f32 * 0.11).cos()).collect();
let mut want = raw.clone();
causal_depthwise_conv(&mut want, &taps, seq, dim, 3);
let mut cache = V3Cache::new(&c, seq);
for t in 0..seq {
let mut cur = raw[t * dim..(t + 1) * dim].to_vec();
cache.conv_step(0, Qkv::Q, &taps, &mut cur);
for (i, &g) in cur.iter().enumerate() {
let e = want[t * dim + i];
assert!(
(g - e).abs() < 1e-5,
"position {t} dim {i}: step {g} vs sequence {e}"
);
}
cache.advance();
}
}
#[test]
fn engram_conv_step_matches_the_sequence_conv() {
use crate::v3::engram::{value_conv, EngramDims};
let c = cfg();
let d = c.d_model;
let dims = EngramDims {
num_tables: 2,
slots: 16,
sub_dim: 4,
d_model: d,
conv_taps: 4,
conv_dilation: 3,
max_order: 3,
};
let taps: Vec<f32> = (0..4 * d).map(|i| (i as f32 * 0.23).cos()).collect();
let seq = 12;
let raw: Vec<f32> = (0..seq * d).map(|i| (i as f32 * 0.19).sin()).collect();
let mut want = raw.clone();
value_conv(&mut want, &taps, seq, &dims);
let mut cache = V3Cache::new(&c, seq);
for t in 0..seq {
let mut cur = raw[t * d..(t + 1) * d].to_vec();
cache.engram_conv_step(0, &taps, &mut cur);
for (i, &g) in cur.iter().enumerate() {
let e = want[t * d + i];
assert!(
(g - e).abs() < 1e-5,
"position {t} dim {i}: step {g} vs sequence {e}"
);
}
cache.advance();
}
}
}