use quant_codec_core::{
CodecId, CodecProfile, CodecProfileDigest, EvalReport, KvCacheCodec, KvSliceRequest,
KvTensorShape, QuantCodecError, VectorCodec,
};
use crate::{
codec::FibCodeV1,
kv::{
codec::{encode_kv_tensor, KvEncodedTensorV1},
layout::KvCacheLayoutV1,
shape::KvTensorShapeV1,
},
metrics,
profile::FibQuantProfileV1,
FibQuantizer,
};
pub const FIB_QUANT_CODEC_ID: &str = "fib_quant";
fn map_err(e: crate::FibQuantError) -> QuantCodecError {
QuantCodecError::ShapeMismatch {
reason: e.to_string(),
}
}
impl CodecProfile for FibQuantProfileV1 {
fn codec_id(&self) -> CodecId {
CodecId::new(FIB_QUANT_CODEC_ID).expect("valid codec id")
}
fn codec_version(&self) -> &str {
&self.codebook_version
}
fn profile_digest(&self) -> CodecProfileDigest {
match self.digest() {
Ok(hex) => {
let bytes = hex_decode(&hex).unwrap_or_else(|_| vec![0u8; 32]);
let mut arr = [0u8; 32];
let len = bytes.len().min(32);
arr[..len].copy_from_slice(&bytes[..len]);
CodecProfileDigest::from_canonical_bytes(&arr)
}
Err(_) => CodecProfileDigest::from_canonical_bytes(b"fib_quant_error"),
}
}
fn fixed_rate_bits(&self) -> Option<u16> {
Some(self.wire_index_bits as u16)
}
fn block_dim(&self) -> Option<u16> {
Some(self.block_dim as u16)
}
fn is_lossy(&self) -> bool {
true
}
}
impl VectorCodec for FibQuantizer {
type EncodedBlock = FibCodeV1;
type Error = QuantCodecError;
fn encode_block(&self, input: &[f32]) -> Result<Self::EncodedBlock, Self::Error> {
self.encode(input).map_err(map_err)
}
fn decode_block(&self, block: &Self::EncodedBlock, out: &mut [f32]) -> Result<(), Self::Error> {
let decoded = self.decode(block).map_err(map_err)?;
let len = decoded.len().min(out.len());
out[..len].copy_from_slice(&decoded[..len]);
Ok(())
}
}
pub struct FibKvCodec {
quantizer: FibQuantizer,
kv_profile: crate::kv::profile::KvCompressionProfileV1,
}
impl FibKvCodec {
pub fn new(
fib_profile: FibQuantProfileV1,
kv_profile: crate::kv::profile::KvCompressionProfileV1,
) -> Result<Self, QuantCodecError> {
let quantizer = FibQuantizer::new(fib_profile).map_err(map_err)?;
Ok(Self {
quantizer,
kv_profile,
})
}
pub fn quantizer(&self) -> &FibQuantizer {
&self.quantizer
}
}
impl VectorCodec for FibKvCodec {
type EncodedBlock = FibCodeV1;
type Error = QuantCodecError;
fn encode_block(&self, input: &[f32]) -> Result<Self::EncodedBlock, Self::Error> {
self.quantizer.encode(input).map_err(map_err)
}
fn decode_block(&self, block: &Self::EncodedBlock, out: &mut [f32]) -> Result<(), Self::Error> {
let decoded = self.quantizer.decode(block).map_err(map_err)?;
let len = decoded.len().min(out.len());
out[..len].copy_from_slice(&decoded[..len]);
Ok(())
}
}
impl KvCacheCodec for FibKvCodec {
type EncodedCache = KvEncodedTensorV1;
fn encode_kv_cache(
&self,
tensors: &[f32],
shape: KvTensorShape,
) -> Result<Self::EncodedCache, Self::Error> {
let fib_shape = convert_to_fib_shape(&shape)?;
let layout = KvCacheLayoutV1::canonical(&fib_shape).map_err(map_err)?;
let encoded = encode_kv_tensor(fib_shape, layout, self.kv_profile.clone(), tensors)
.map_err(map_err)?;
Ok(encoded)
}
fn decode_slice(
&self,
cache: &Self::EncodedCache,
request: KvSliceRequest,
out: &mut [f32],
) -> Result<(), Self::Error> {
let decoded = crate::kv::codec::decode_kv_slice(
cache,
request.layer.0,
request.head.map(|h| h.0).unwrap_or(0),
request.token_span.start as u32,
request.token_span.end as u32,
)
.map_err(map_err)?;
let len = decoded.len().min(out.len());
out[..len].copy_from_slice(&decoded[..len]);
Ok(())
}
}
fn convert_to_fib_shape(shape: &KvTensorShape) -> Result<KvTensorShapeV1, QuantCodecError> {
Ok(KvTensorShapeV1::new(
crate::kv::shape::KvRole::Key,
crate::kv::shape::KvAttentionKind::Mha,
1,
shape.layers,
shape.key_heads,
shape.key_heads, shape.seq_len as u32,
shape.head_dim,
crate::kv::shape::KvDType::F32,
crate::kv::shape::KvRopeState::NotApplicable,
))
}
pub fn eval_compress(
quantizer: &FibQuantizer,
vectors: &[&[f32]],
) -> Result<EvalReport, QuantCodecError> {
if vectors.is_empty() {
return Ok(EvalReport {
mse: None,
cosine_similarity: None,
max_abs_error: None,
bytes_exact: 0,
bytes_encoded: 0,
passed: true,
notes: vec!["empty input".to_string()],
});
}
let mut total_mse = 0.0f64;
let mut total_cos = 0.0f64;
let mut total_bytes_exact = 0u64;
let mut total_bytes_encoded = 0u64;
let mut max_abs_error = 0.0f64;
let count = vectors.len() as f64;
for v in vectors {
let code = quantizer.encode(v).map_err(map_err)?;
let decoded = quantizer.decode(&code).map_err(map_err)?;
let mse = metrics::mse(v, &decoded).map_err(map_err)?;
let cos = metrics::cosine_similarity(v, &decoded).map_err(map_err)?;
total_mse += mse;
total_cos += cos;
total_bytes_exact += (v.len() * 4) as u64;
total_bytes_encoded += code.compact_size() as u64;
for (a, b) in v.iter().zip(decoded.iter()) {
let err = (f64::from(*a) - f64::from(*b)).abs();
if err > max_abs_error {
max_abs_error = err;
}
}
}
Ok(EvalReport {
mse: Some(total_mse / count),
cosine_similarity: Some(total_cos / count),
max_abs_error: Some(max_abs_error),
bytes_exact: total_bytes_exact,
bytes_encoded: total_bytes_encoded,
passed: true,
notes: vec![format!("{} vectors", vectors.len())],
})
}
fn hex_decode(s: &str) -> Result<Vec<u8>, ()> {
if s.len() % 2 != 0 {
return Err(());
}
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| ()))
.collect()
}