pub struct FrameCodec {
head_dim: usize,
k0: Vec<f32>,
v0: Vec<f32>,
frames_k: Vec<Vec<f32>>,
frames_v: Vec<Vec<f32>>,
}
impl FrameCodec {
pub fn new(head_dim: usize) -> Self {
Self {
head_dim,
k0: Vec::new(),
v0: Vec::new(),
frames_k: Vec::new(),
frames_v: Vec::new(),
}
}
pub fn encode_step(&mut self, k: &[f32], v: &[f32]) {
assert_eq!(k.len(), self.head_dim, "k length must equal head_dim");
assert_eq!(v.len(), self.head_dim, "v length must equal head_dim");
if self.k0.is_empty() {
self.k0 = k.to_vec();
self.v0 = v.to_vec();
return;
}
let k_prev = self.decode_key(self.token_count() - 1);
let v_prev = self.decode_val(self.token_count() - 1);
let fk = rank1_frame_transition(k, &k_prev, self.head_dim);
let fv = rank1_frame_transition(v, &v_prev, self.head_dim);
self.frames_k.push(fk);
self.frames_v.push(fv);
}
pub fn token_count(&self) -> usize {
if self.k0.is_empty() {
0
} else {
1 + self.frames_k.len()
}
}
pub fn decode_key(&self, t: usize) -> Vec<f32> {
self.decode_vector(&self.k0, &self.frames_k, t)
}
pub fn decode_val(&self, t: usize) -> Vec<f32> {
self.decode_vector(&self.v0, &self.frames_v, t)
}
pub fn attend(&self, query: &[f32]) -> Vec<f32> {
let n = self.token_count();
if n == 0 {
return vec![0.0; self.head_dim];
}
let scale = 1.0 / (self.head_dim as f32).sqrt();
let mut scores = Vec::with_capacity(n);
let mut keys = Vec::with_capacity(n);
let mut vals = Vec::with_capacity(n);
keys.push(self.k0.clone());
vals.push(self.v0.clone());
for t in 1..n {
keys.push(self.decode_key(t));
vals.push(self.decode_val(t));
}
for k in &keys {
let dot: f32 = query.iter().zip(k.iter()).map(|(q, ki)| q * ki).sum();
scores.push(dot * scale);
}
let max_s = scores.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
let mut exp_scores: Vec<f32> = scores.iter().map(|s| (s - max_s).exp()).collect();
let sum_exp: f32 = exp_scores.iter().sum();
for e in &mut exp_scores {
*e /= sum_exp;
}
let mut out = vec![0.0f32; self.head_dim];
for (weight, v) in exp_scores.iter().zip(vals.iter()) {
for (o, vi) in out.iter_mut().zip(v.iter()) {
*o += weight * vi;
}
}
out
}
pub fn bytes_used(&self) -> usize {
let anchor = 2 * self.head_dim * 4;
let frames =
(self.frames_k.len() + self.frames_v.len()) * self.head_dim * self.head_dim * 4;
anchor + frames
}
pub fn bytes_absolute(&self) -> usize {
self.token_count() * 2 * self.head_dim * 4
}
fn decode_vector(&self, anchor: &[f32], frames: &[Vec<f32>], t: usize) -> Vec<f32> {
assert!(
t < self.token_count(),
"t={t} out of range (n={})",
self.token_count()
);
if t == 0 {
return anchor.to_vec();
}
let d = self.head_dim;
let mut current = anchor.to_vec();
for frame in &frames[..t] {
current = matvec(frame, ¤t, d);
}
current
}
}
fn rank1_frame_transition(curr: &[f32], prev: &[f32], d: usize) -> Vec<f32> {
let norm_sq: f32 = prev.iter().map(|x| x * x).sum();
let mut frame = identity_matrix(d);
if norm_sq < 1e-12 {
for i in 0..d {
frame[i * d + i] += curr[i]; }
return frame;
}
let delta: Vec<f32> = curr.iter().zip(prev.iter()).map(|(c, p)| c - p).collect();
for i in 0..d {
for j in 0..d {
frame[i * d + j] += delta[i] * prev[j] / norm_sq;
}
}
frame
}
fn identity_matrix(d: usize) -> Vec<f32> {
let mut m = vec![0.0f32; d * d];
for i in 0..d {
m[i * d + i] = 1.0;
}
m
}
fn matvec(m: &[f32], x: &[f32], d: usize) -> Vec<f32> {
let mut y = vec![0.0f32; d];
for i in 0..d {
for j in 0..d {
y[i] += m[i * d + j] * x[j];
}
}
y
}
#[cfg(test)]
mod tests {
use super::*;
fn approx_eq_vec(a: &[f32], b: &[f32], tol: f32) -> bool {
a.len() == b.len() && a.iter().zip(b).all(|(x, y)| (x - y).abs() < tol)
}
#[test]
fn test_encode_decode_roundtrip_anchor() {
let mut codec = FrameCodec::new(4);
let k0 = vec![1.0, 0.0, 0.0, 0.0];
let v0 = vec![0.0, 1.0, 0.0, 0.0];
codec.encode_step(&k0, &v0);
assert!(approx_eq_vec(&codec.decode_key(0), &k0, 1e-6));
assert!(approx_eq_vec(&codec.decode_val(0), &v0, 1e-6));
}
#[test]
fn test_encode_decode_roundtrip_second_token() {
let mut codec = FrameCodec::new(4);
let k0 = vec![1.0, 0.0, 0.0, 0.0];
let v0 = vec![0.0, 1.0, 0.0, 0.0];
let k1 = vec![0.5, 0.5, 0.0, 0.0];
let v1 = vec![0.0, 0.5, 0.5, 0.0];
codec.encode_step(&k0, &v0);
codec.encode_step(&k1, &v1);
assert!(
approx_eq_vec(&codec.decode_key(1), &k1, 1e-5),
"decode_key(1) = {:?}, expected {:?}",
codec.decode_key(1),
k1
);
assert!(approx_eq_vec(&codec.decode_val(1), &v1, 1e-5));
}
#[test]
fn test_encode_decode_multiple_tokens() {
let d = 4;
let mut codec = FrameCodec::new(d);
let kvs: Vec<(Vec<f32>, Vec<f32>)> = (0..5)
.map(|i| {
let k = (0..d).map(|j| ((i + j) as f32) * 0.1).collect();
let v = (0..d).map(|j| ((i * 2 + j) as f32) * 0.1).collect();
(k, v)
})
.collect();
for (k, v) in &kvs {
codec.encode_step(k, v);
}
for (t, (k, v)) in kvs.iter().enumerate() {
assert!(
approx_eq_vec(&codec.decode_key(t), k, 1e-4),
"key mismatch at t={t}"
);
assert!(
approx_eq_vec(&codec.decode_val(t), v, 1e-4),
"val mismatch at t={t}"
);
}
}
#[test]
fn test_identity_frame_for_constant_sequence() {
let d = 3;
let mut codec = FrameCodec::new(d);
let k = vec![1.0, 0.0, 0.0];
let v = vec![0.0, 1.0, 0.0];
for _ in 0..4 {
codec.encode_step(&k, &v);
}
for t in 0..4 {
assert!(approx_eq_vec(&codec.decode_key(t), &k, 1e-5), "t={t}");
}
}
#[test]
fn test_attend_matches_direct_single_token() {
let d = 4;
let k0 = vec![1.0, 0.0, 0.0, 0.0];
let v0 = vec![0.0, 0.0, 1.0, 0.0];
let q = vec![1.0, 0.0, 0.0, 0.0];
let mut codec = FrameCodec::new(d);
codec.encode_step(&k0, &v0);
let out = codec.attend(&q);
assert!(
approx_eq_vec(&out, &v0, 1e-5),
"single-token attend = {out:?}"
);
}
#[test]
fn test_attend_matches_direct_multi_token() {
let d = 4;
let kvs = vec![
(vec![1.0f32, 0.0, 0.0, 0.0], vec![1.0f32, 0.0, 0.0, 0.0]),
(vec![0.0f32, 1.0, 0.0, 0.0], vec![0.0f32, 1.0, 0.0, 0.0]),
(vec![0.0f32, 0.0, 1.0, 0.0], vec![0.0f32, 0.0, 1.0, 0.0]),
];
let q = vec![1.0f32, 0.0, 0.0, 0.0];
let mut codec = FrameCodec::new(d);
for (k, v) in &kvs {
codec.encode_step(k, v);
}
let scale = 1.0 / (d as f32).sqrt();
let scores_raw: Vec<f32> = kvs
.iter()
.map(|(k, _)| k.iter().zip(&q).map(|(ki, qi)| ki * qi).sum::<f32>() * scale)
.collect();
let max_s = scores_raw.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
let exp: Vec<f32> = scores_raw.iter().map(|s| (s - max_s).exp()).collect();
let sum_exp: f32 = exp.iter().sum();
let weights: Vec<f32> = exp.iter().map(|e| e / sum_exp).collect();
let mut expected = vec![0.0f32; d];
for (w, (_, v)) in weights.iter().zip(kvs.iter()) {
for (o, vi) in expected.iter_mut().zip(v.iter()) {
*o += w * vi;
}
}
let out = codec.attend(&q);
assert!(
approx_eq_vec(&out, &expected, 1e-4),
"attend mismatch: got {out:?}, expected {expected:?}"
);
}
#[test]
fn test_attend_empty_returns_zero() {
let codec = FrameCodec::new(4);
let out = codec.attend(&[1.0, 0.0, 0.0, 0.0]);
assert_eq!(out, vec![0.0; 4]);
}
#[test]
fn test_token_count() {
let mut codec = FrameCodec::new(4);
assert_eq!(codec.token_count(), 0);
codec.encode_step(&[1.0, 0.0, 0.0, 0.0], &[0.0, 1.0, 0.0, 0.0]);
assert_eq!(codec.token_count(), 1);
codec.encode_step(&[0.5, 0.5, 0.0, 0.0], &[0.0, 0.5, 0.5, 0.0]);
assert_eq!(codec.token_count(), 2);
}
}