use cvx_core::DeltaEntry;
pub struct DeltaEncoder {
keyframe_interval: u32,
threshold: f32,
}
impl DeltaEncoder {
pub fn new(keyframe_interval: u32, threshold: f32) -> Self {
Self {
keyframe_interval,
threshold,
}
}
pub fn encode(
&self,
sequence_index: u32,
timestamp: i64,
vector: &[f32],
previous: Option<&[f32]>,
) -> DeltaEntry {
if previous.is_none() || sequence_index % self.keyframe_interval == 0 {
return DeltaEntry::keyframe(0, timestamp);
}
let prev = previous.unwrap();
assert_eq!(
vector.len(),
prev.len(),
"vector dimensions must match: {} vs {}",
vector.len(),
prev.len()
);
let mut indices = Vec::new();
let mut values = Vec::new();
for (i, (&curr, &prev_val)) in vector.iter().zip(prev.iter()).enumerate() {
let delta = curr - prev_val;
if delta.abs() > self.threshold {
indices.push(i as u32);
values.push(delta);
}
}
DeltaEntry::delta(0, timestamp - 1, timestamp, indices, values)
}
pub fn keyframe_interval(&self) -> u32 {
self.keyframe_interval
}
pub fn threshold(&self) -> f32 {
self.threshold
}
}
pub struct DeltaDecoder;
impl DeltaDecoder {
pub fn apply(base: &[f32], delta: &DeltaEntry) -> Vec<f32> {
let mut result = base.to_vec();
for (&idx, &val) in delta.indices().iter().zip(delta.values().iter()) {
result[idx as usize] += val;
}
result
}
pub fn reconstruct(keyframe: &[f32], deltas: &[DeltaEntry]) -> Vec<f32> {
let mut current = keyframe.to_vec();
for delta in deltas {
if delta.is_keyframe() {
continue; }
current = Self::apply(¤t, delta);
}
current
}
}
pub fn compression_ratio(dim: usize, deltas: &[DeltaEntry]) -> f64 {
let full_size = deltas.len() * dim * 4; let delta_size: usize = deltas
.iter()
.map(|d| {
if d.is_keyframe() {
dim * 4 } else {
d.nnz() * (4 + 4) }
})
.sum();
if delta_size == 0 {
return 0.0;
}
full_size as f64 / delta_size as f64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_vector_is_keyframe() {
let enc = DeltaEncoder::new(10, 0.001);
let v = vec![1.0, 2.0, 3.0];
let entry = enc.encode(0, 1000, &v, None);
assert!(entry.is_keyframe());
}
#[test]
fn keyframe_every_k() {
let enc = DeltaEncoder::new(5, 0.001);
let v = vec![1.0; 10];
for i in 0..20u32 {
let entry = enc.encode(i, i as i64 * 1000, &v, Some(&v));
if i % 5 == 0 {
assert!(entry.is_keyframe(), "index {i} should be keyframe");
} else {
assert!(!entry.is_keyframe(), "index {i} should be delta");
}
}
}
#[test]
fn delta_only_stores_changed_dims() {
let enc = DeltaEncoder::new(100, 0.01);
let v1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let v2 = vec![1.0, 2.05, 3.0, 4.0, 5.1];
let entry = enc.encode(1, 2000, &v2, Some(&v1));
assert!(!entry.is_keyframe());
assert_eq!(entry.nnz(), 2);
assert_eq!(entry.indices(), &[1, 4]);
}
#[test]
fn small_changes_below_threshold_ignored() {
let enc = DeltaEncoder::new(100, 0.01);
let v1 = vec![1.0, 2.0, 3.0];
let v2 = vec![1.005, 2.005, 3.005];
let entry = enc.encode(1, 2000, &v2, Some(&v1));
assert_eq!(entry.nnz(), 0); }
#[test]
fn apply_reconstructs_correctly() {
let base = vec![1.0, 2.0, 3.0, 4.0];
let delta = DeltaEntry::delta(0, 0, 1, vec![1, 3], vec![0.5, -0.3]);
let result = DeltaDecoder::apply(&base, &delta);
assert!((result[0] - 1.0).abs() < 1e-7);
assert!((result[1] - 2.5).abs() < 1e-7);
assert!((result[2] - 3.0).abs() < 1e-7);
assert!((result[3] - 3.7).abs() < 1e-7);
}
#[test]
fn reconstruct_chain() {
let keyframe = vec![1.0, 2.0, 3.0];
let deltas = vec![
DeltaEntry::keyframe(0, 0), DeltaEntry::delta(0, 0, 1, vec![0], vec![0.1]),
DeltaEntry::delta(0, 1, 2, vec![1], vec![-0.2]),
DeltaEntry::delta(0, 2, 3, vec![0, 2], vec![0.3, 0.5]),
];
let result = DeltaDecoder::reconstruct(&keyframe, &deltas);
assert!((result[0] - 1.4).abs() < 1e-6); assert!((result[1] - 1.8).abs() < 1e-6); assert!((result[2] - 3.5).abs() < 1e-6); }
#[test]
fn encode_decode_roundtrip_precision() {
let enc = DeltaEncoder::new(100, 1e-8); let dim = 768;
let mut rng = 42u64; let mut pseudo_random = || {
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
(rng >> 33) as f32 / (u32::MAX >> 1) as f32
};
let v1: Vec<f32> = (0..dim).map(|_| pseudo_random()).collect();
let v2: Vec<f32> = v1.iter().map(|&x| x + pseudo_random() * 0.001).collect();
let entry = enc.encode(1, 1000, &v2, Some(&v1));
let reconstructed = DeltaDecoder::apply(&v1, &entry);
for (i, (&original, &recovered)) in v2.iter().zip(reconstructed.iter()).enumerate() {
assert!(
(original - recovered).abs() < 1e-7,
"dim {i}: original={original}, recovered={recovered}"
);
}
}
#[test]
fn compression_ratio_slow_drift() {
let enc = DeltaEncoder::new(10, 0.01);
let dim = 768;
let n_vectors = 100;
let mut rng = 123u64;
let mut pseudo_random = || {
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
(rng >> 33) as f32 / (u32::MAX >> 1) as f32
};
let base: Vec<f32> = (0..dim).map(|_| pseudo_random()).collect();
let mut prev = base.clone();
let mut deltas = Vec::new();
for i in 0..n_vectors {
let current: Vec<f32> = prev
.iter()
.map(|&x| {
if pseudo_random() < 0.05 {
x + pseudo_random() * 0.1
} else {
x + pseudo_random() * 0.001 }
})
.collect();
let entry = enc.encode(i as u32, i as i64 * 1000, ¤t, Some(&prev));
deltas.push(entry);
prev = current;
}
let ratio = compression_ratio(dim, &deltas);
assert!(
ratio >= 3.0,
"compression ratio {ratio:.1}x, expected >= 3x for slow drift"
);
}
}
#[cfg(test)]
mod proptests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn encode_decode_roundtrip(
base in prop::collection::vec(-10.0f32..10.0, 32..=32),
perturbation in prop::collection::vec(-0.1f32..0.1, 32..=32),
) {
let target: Vec<f32> = base.iter().zip(perturbation.iter())
.map(|(&b, &p)| b + p).collect();
let enc = DeltaEncoder::new(100, 1e-8);
let entry = enc.encode(1, 1000, &target, Some(&base));
let recovered = DeltaDecoder::apply(&base, &entry);
for (i, (&orig, &rec)) in target.iter().zip(recovered.iter()).enumerate() {
prop_assert!((orig - rec).abs() < 1e-6,
"dim {i}: orig={orig}, rec={rec}");
}
}
}
}