1use crate::error::QuantError;
2use crate::scheme::QuantScheme;
3use crate::vector::QuantizedVector;
4use lnmp_embedding::Vector;
5
6pub fn dequantize_embedding(q: &QuantizedVector) -> Result<Vector, QuantError> {
25 if q.dim == 0 {
26 return Err(QuantError::InvalidDimension(
27 "Cannot dequantize zero-dimensional vector".to_string(),
28 ));
29 }
30
31 match q.scheme {
32 QuantScheme::QInt8 => dequantize_qint8(q),
33 QuantScheme::QInt4 => crate::qint4::dequantize_qint4(q),
34 QuantScheme::Binary => crate::binary::dequantize_binary(q),
35 QuantScheme::FP16Passthrough => crate::fp16::dequantize_fp16(q),
36 }
37}
38
39fn dequantize_qint8(q: &QuantizedVector) -> Result<Vector, QuantError> {
41 if q.data.len() != q.dim as usize {
43 return Err(QuantError::DataCorrupted(format!(
44 "Data length mismatch: expected {} bytes, got {}",
45 q.dim,
46 q.data.len()
47 )));
48 }
49
50 let mut values = Vec::with_capacity(q.dim as usize);
52
53 for &quantized_byte in &q.data {
54 let quantized = quantized_byte as i8;
55 let value = ((quantized as i32 + 128) as f32 * q.scale) + q.min_val;
57 values.push(value);
58 }
59
60 Ok(Vector::from_f32(values))
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use crate::encode::quantize_embedding;
68 use lnmp_embedding::{SimilarityMetric, Vector};
69
70 #[test]
71 fn test_dequantize_simple() {
72 let original = Vector::from_f32(vec![0.12, -0.45, 0.33]);
73 let quantized = quantize_embedding(&original, QuantScheme::QInt8).unwrap();
74 let result = dequantize_embedding(&quantized);
75
76 assert!(result.is_ok());
77 let restored = result.unwrap();
78 assert_eq!(restored.dim, 3);
79 assert!(restored.as_f32().is_ok());
81 }
82
83 #[test]
84 fn test_roundtrip_accuracy() {
85 let original =
86 Vector::from_f32(vec![0.1, -0.2, 0.3, -0.4, 0.5, -0.6, 0.7, -0.8, 0.9, -1.0]);
87 let quantized = quantize_embedding(&original, QuantScheme::QInt8).unwrap();
88 let restored = dequantize_embedding(&quantized).unwrap();
89
90 let similarity = original
92 .similarity(&restored, SimilarityMetric::Cosine)
93 .unwrap();
94
95 assert!(similarity > 0.98, "Cosine similarity: {}", similarity);
97 }
98
99 #[test]
100 fn test_roundtrip_large_vector() {
101 let values: Vec<f32> = (0..512).map(|i| (i as f32 / 512.0) - 0.5).collect();
102 let original = Vector::from_f32(values);
103
104 let quantized = quantize_embedding(&original, QuantScheme::QInt8).unwrap();
105 let restored = dequantize_embedding(&quantized).unwrap();
106
107 let similarity = original
108 .similarity(&restored, SimilarityMetric::Cosine)
109 .unwrap();
110
111 assert!(similarity > 0.99, "Cosine similarity: {}", similarity);
112 }
113
114 #[test]
115 fn test_dequantize_corrupted_data() {
116 let qv = QuantizedVector::new(10, QuantScheme::QInt8, 0.01, 0, 0.0, vec![0u8; 5]);
118 let result = dequantize_embedding(&qv);
119 assert!(result.is_err());
120 assert!(matches!(result.unwrap_err(), QuantError::DataCorrupted(_)));
121 }
122}