use std::fmt;
pub trait CompressedKv: fmt::Debug {
fn decompress(&self) -> Vec<f32>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn compressed_size_bytes(&self) -> usize;
fn original_size_bytes(&self) -> usize {
self.len() * std::mem::size_of::<f32>()
}
fn compression_ratio(&self) -> f64 {
let cs = self.compressed_size_bytes();
if cs == 0 {
return 0.0;
}
self.original_size_bytes() as f64 / cs as f64
}
}
pub trait KvCompressor: fmt::Debug {
type Compressed: CompressedKv;
fn compress(&self, values: &[f32]) -> Self::Compressed;
fn name(&self) -> &str;
}
#[derive(Debug, Clone)]
pub struct KiviConfig {
pub bits: u8,
}
impl KiviConfig {
pub fn new(bits: u8) -> Self {
assert!(
bits == 2 || bits == 4,
"KIVI supports 2-bit or 4-bit quantization"
);
Self { bits }
}
}
#[derive(Debug, Clone)]
pub struct KiviCompressor {
config: KiviConfig,
}
impl KiviCompressor {
pub fn new(config: KiviConfig) -> Self {
Self { config }
}
fn max_code(&self) -> u8 {
(1u8 << self.config.bits) - 1
}
}
#[derive(Debug, Clone)]
pub struct KiviCompressed {
codes: Vec<u8>,
scale: f32,
zero_point: f32,
bits: u8,
}
impl CompressedKv for KiviCompressed {
fn decompress(&self) -> Vec<f32> {
self.codes
.iter()
.map(|&c| self.scale * c as f32 + self.zero_point)
.collect()
}
fn len(&self) -> usize {
self.codes.len()
}
fn compressed_size_bytes(&self) -> usize {
let packed_bits = self.codes.len() * self.bits as usize;
let packed_bytes = packed_bits.div_ceil(8);
packed_bytes + 4 + 4 }
}
impl KvCompressor for KiviCompressor {
type Compressed = KiviCompressed;
fn compress(&self, values: &[f32]) -> KiviCompressed {
if values.is_empty() {
return KiviCompressed {
codes: Vec::new(),
scale: 0.0,
zero_point: 0.0,
bits: self.config.bits,
};
}
let min = values.iter().cloned().fold(f32::INFINITY, f32::min);
let max = values.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
let max_code = self.max_code();
let range = max - min;
let scale = if range == 0.0 {
0.0
} else {
range / max_code as f32
};
let codes: Vec<u8> = values
.iter()
.map(|&v| {
if scale == 0.0 {
0
} else {
let code = ((v - min) / scale).round() as u8;
code.min(max_code)
}
})
.collect();
KiviCompressed {
codes,
scale,
zero_point: min,
bits: self.config.bits,
}
}
fn name(&self) -> &str {
match self.config.bits {
2 => "KIVI-2bit",
4 => "KIVI-4bit",
_ => "KIVI",
}
}
}
#[derive(Debug, Clone)]
pub struct RkvConfig {
pub budget_fraction: f32,
pub redundancy_weight: f32,
}
impl RkvConfig {
pub fn new(budget_fraction: f32) -> Self {
assert!(
budget_fraction > 0.0 && budget_fraction <= 1.0,
"budget_fraction must be in (0.0, 1.0]"
);
Self {
budget_fraction,
redundancy_weight: 0.5,
}
}
pub fn with_redundancy_weight(mut self, w: f32) -> Self {
self.redundancy_weight = w;
self
}
}
#[derive(Debug, Clone)]
pub struct RkvCompressor {
config: RkvConfig,
}
impl RkvCompressor {
pub fn new(config: RkvConfig) -> Self {
Self { config }
}
pub fn select_keep(&self, importance: &[f32], redundancy: Option<&[f32]>) -> Vec<usize> {
let n = importance.len();
if n == 0 {
return Vec::new();
}
let budget = ((n as f32 * self.config.budget_fraction).ceil() as usize)
.max(1)
.min(n);
let mut scored: Vec<(usize, f32)> = importance
.iter()
.enumerate()
.map(|(i, &imp)| {
let red = redundancy.map_or(0.0, |r| r.get(i).copied().unwrap_or(0.0));
(i, imp - self.config.redundancy_weight * red)
})
.collect();
scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
scored.truncate(budget);
let mut keep: Vec<usize> = scored.iter().map(|&(i, _)| i).collect();
keep.sort_unstable();
keep
}
pub fn select_evict(&self, importance: &[f32], redundancy: Option<&[f32]>) -> Vec<usize> {
let keep = self.select_keep(importance, redundancy);
let keep_set: std::collections::HashSet<usize> = keep.into_iter().collect();
(0..importance.len())
.filter(|i| !keep_set.contains(i))
.collect()
}
}
#[derive(Debug, Clone)]
pub struct RkvCompressed {
retained_values: Vec<f32>,
retained_indices: Vec<usize>,
original_len: usize,
}
impl CompressedKv for RkvCompressed {
fn decompress(&self) -> Vec<f32> {
let mut out = vec![0.0f32; self.original_len];
for (&idx, &val) in self.retained_indices.iter().zip(&self.retained_values) {
out[idx] = val;
}
out
}
fn len(&self) -> usize {
self.original_len
}
fn compressed_size_bytes(&self) -> usize {
self.retained_values.len() * 4
+ self.retained_indices.len() * std::mem::size_of::<usize>()
+ std::mem::size_of::<usize>()
}
}
impl KvCompressor for RkvCompressor {
type Compressed = RkvCompressed;
fn compress(&self, values: &[f32]) -> RkvCompressed {
let importance: Vec<f32> = values.iter().map(|v| v.abs()).collect();
let keep = self.select_keep(&importance, None);
let retained_values: Vec<f32> = keep.iter().map(|&i| values[i]).collect();
let retained_indices = keep;
RkvCompressed {
retained_values,
retained_indices,
original_len: values.len(),
}
}
fn name(&self) -> &str {
"R-KV"
}
}
#[derive(Debug, Clone)]
pub struct LowRankConfig {
pub rank: usize,
}
impl LowRankConfig {
pub fn new(rank: usize) -> Self {
assert!(rank > 0, "rank must be > 0");
Self { rank }
}
}
#[derive(Debug, Clone)]
pub struct LowRankCompressor {
config: LowRankConfig,
}
impl LowRankCompressor {
pub fn new(config: LowRankConfig) -> Self {
Self { config }
}
}
#[derive(Debug, Clone)]
pub struct LowRankCompressed {
coefficients: Vec<f32>,
basis: Vec<f32>,
mean: f32,
original_len: usize,
rank: usize,
}
impl CompressedKv for LowRankCompressed {
fn decompress(&self) -> Vec<f32> {
if self.basis.is_empty() || self.coefficients.is_empty() {
return vec![self.mean; self.original_len];
}
self.coefficients
.iter()
.map(|&c| self.mean + c * self.basis[0])
.collect()
}
fn len(&self) -> usize {
self.original_len
}
fn compressed_size_bytes(&self) -> usize {
self.coefficients.len() * 4
+ self.basis.len() * 4
+ 4 + 8 }
}
impl LowRankCompressed {
pub fn rank(&self) -> usize {
self.rank
}
}
impl KvCompressor for LowRankCompressor {
type Compressed = LowRankCompressed;
fn compress(&self, values: &[f32]) -> LowRankCompressed {
if values.is_empty() {
return LowRankCompressed {
coefficients: Vec::new(),
basis: Vec::new(),
mean: 0.0,
original_len: 0,
rank: self.config.rank,
};
}
let n = values.len();
let mean = values.iter().sum::<f32>() / n as f32;
let centered: Vec<f32> = values.iter().map(|&v| v - mean).collect();
let variance = centered.iter().map(|x| x * x).sum::<f32>() / n as f32;
let std_dev = variance.sqrt();
if std_dev < 1e-10 {
return LowRankCompressed {
coefficients: vec![0.0; n],
basis: vec![1.0],
mean,
original_len: n,
rank: self.config.rank,
};
}
let coefficients: Vec<f32> = centered.iter().map(|&c| c / std_dev).collect();
let basis = vec![std_dev];
LowRankCompressed {
coefficients,
basis,
mean,
original_len: n,
rank: self.config.rank,
}
}
fn name(&self) -> &str {
"LowRank"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kivi_4bit_roundtrip() {
let compressor = KiviCompressor::new(KiviConfig::new(4));
let values = vec![0.0, 0.25, 0.5, 0.75, 1.0];
let compressed = compressor.compress(&values);
let restored = compressed.decompress();
assert_eq!(restored.len(), values.len());
for (orig, rest) in values.iter().zip(&restored) {
assert!((orig - rest).abs() < 0.07, "orig={orig}, restored={rest}");
}
}
#[test]
fn kivi_2bit_roundtrip() {
let compressor = KiviCompressor::new(KiviConfig::new(2));
let values = vec![-1.0, 0.0, 0.5, 1.0];
let compressed = compressor.compress(&values);
let restored = compressed.decompress();
assert_eq!(restored.len(), 4);
for (orig, rest) in values.iter().zip(&restored) {
assert!((orig - rest).abs() < 0.5, "orig={orig}, restored={rest}");
}
}
#[test]
fn kivi_compression_ratio() {
let compressor = KiviCompressor::new(KiviConfig::new(4));
let values: Vec<f32> = (0..256).map(|i| i as f32 / 255.0).collect();
let compressed = compressor.compress(&values);
let ratio = compressed.compression_ratio();
assert!(ratio > 3.0, "ratio={ratio}, expected > 3.0");
}
#[test]
fn kivi_empty() {
let compressor = KiviCompressor::new(KiviConfig::new(4));
let compressed = compressor.compress(&[]);
assert_eq!(compressed.len(), 0);
assert!(compressed.is_empty());
assert!(compressed.decompress().is_empty());
}
#[test]
fn kivi_constant_values() {
let compressor = KiviCompressor::new(KiviConfig::new(4));
let values = vec![0.5; 10];
let compressed = compressor.compress(&values);
let restored = compressed.decompress();
for &v in &restored {
assert!((v - 0.5).abs() < 1e-6);
}
}
#[test]
#[should_panic(expected = "KIVI supports 2-bit or 4-bit")]
fn kivi_invalid_bits() {
KiviConfig::new(8);
}
#[test]
fn rkv_select_keep_basic() {
let compressor = RkvCompressor::new(RkvConfig::new(0.5));
let importance = vec![1.0, 5.0, 2.0, 8.0, 3.0];
let keep = compressor.select_keep(&importance, None);
assert_eq!(keep.len(), 3);
assert!(keep.contains(&1));
assert!(keep.contains(&3));
assert!(keep.windows(2).all(|w| w[0] < w[1]));
}
#[test]
fn rkv_redundancy_penalty() {
let compressor = RkvCompressor::new(RkvConfig::new(0.5).with_redundancy_weight(1.0));
let importance = vec![1.0, 10.0, 5.0, 3.0];
let redundancy = vec![0.0, 9.0, 0.0, 0.0];
let keep = compressor.select_keep(&importance, Some(&redundancy));
assert_eq!(keep.len(), 2);
assert!(keep.contains(&2));
assert!(keep.contains(&3));
}
#[test]
fn rkv_full_budget_keeps_all() {
let compressor = RkvCompressor::new(RkvConfig::new(1.0));
let importance = vec![1.0, 2.0, 3.0];
let keep = compressor.select_keep(&importance, None);
assert_eq!(keep, vec![0, 1, 2]);
}
#[test]
fn rkv_select_evict() {
let compressor = RkvCompressor::new(RkvConfig::new(0.5));
let importance = vec![1.0, 5.0, 2.0, 8.0];
let evict = compressor.select_evict(&importance, None);
assert_eq!(evict.len(), 2);
}
#[test]
fn rkv_compress_roundtrip() {
let compressor = RkvCompressor::new(RkvConfig::new(0.5));
let values = vec![0.1, 5.0, 0.2, 8.0, 0.3];
let compressed = compressor.compress(&values);
let restored = compressed.decompress();
assert_eq!(restored.len(), 5);
assert!((restored[1] - 5.0).abs() < 1e-6);
assert!((restored[3] - 8.0).abs() < 1e-6);
}
#[test]
fn rkv_empty() {
let compressor = RkvCompressor::new(RkvConfig::new(0.5));
let keep = compressor.select_keep(&[], None);
assert!(keep.is_empty());
}
#[test]
#[should_panic(expected = "budget_fraction must be in")]
fn rkv_invalid_budget() {
RkvConfig::new(0.0);
}
#[test]
fn lowrank_roundtrip() {
let compressor = LowRankCompressor::new(LowRankConfig::new(1));
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let compressed = compressor.compress(&values);
let restored = compressed.decompress();
assert_eq!(restored.len(), 5);
for (orig, rest) in values.iter().zip(&restored) {
assert!((orig - rest).abs() < 1e-4, "orig={orig}, restored={rest}");
}
}
#[test]
fn lowrank_constant_values() {
let compressor = LowRankCompressor::new(LowRankConfig::new(1));
let values = vec![3.0; 8];
let compressed = compressor.compress(&values);
let restored = compressed.decompress();
for &v in &restored {
assert!((v - 3.0).abs() < 1e-6);
}
}
#[test]
fn lowrank_empty() {
let compressor = LowRankCompressor::new(LowRankConfig::new(1));
let compressed = compressor.compress(&[]);
assert_eq!(compressed.len(), 0);
assert!(compressed.decompress().is_empty());
}
#[test]
fn lowrank_compression_metadata() {
let compressor = LowRankCompressor::new(LowRankConfig::new(2));
let values: Vec<f32> = (0..100).map(|i| i as f32).collect();
let compressed = compressor.compress(&values);
assert_eq!(compressed.len(), 100);
assert_eq!(compressed.rank(), 2);
assert!(compressed.compressed_size_bytes() > 0);
}
#[test]
#[should_panic(expected = "rank must be > 0")]
fn lowrank_invalid_rank() {
LowRankConfig::new(0);
}
#[test]
fn compressor_name() {
assert_eq!(KiviCompressor::new(KiviConfig::new(4)).name(), "KIVI-4bit");
assert_eq!(KiviCompressor::new(KiviConfig::new(2)).name(), "KIVI-2bit");
assert_eq!(RkvCompressor::new(RkvConfig::new(0.5)).name(), "R-KV");
assert_eq!(
LowRankCompressor::new(LowRankConfig::new(1)).name(),
"LowRank"
);
}
}