ipfrs_semantic/quantization/
scalar.rs1use ipfrs_core::{Error, Result};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ScalarQuantizerConfig {
9 pub bits: u8,
11 pub signed: bool,
13 pub min_values: Vec<f32>,
15 pub max_values: Vec<f32>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ScalarQuantizer {
25 config: ScalarQuantizerConfig,
27 dimension: usize,
29 trained: bool,
31}
32
33impl ScalarQuantizer {
34 pub fn new(dimension: usize, signed: bool) -> Self {
40 Self {
41 config: ScalarQuantizerConfig {
42 bits: 8,
43 signed,
44 min_values: vec![f32::MAX; dimension],
45 max_values: vec![f32::MIN; dimension],
46 },
47 dimension,
48 trained: false,
49 }
50 }
51
52 pub fn uint8(dimension: usize) -> Self {
54 Self::new(dimension, false)
55 }
56
57 pub fn int8(dimension: usize) -> Self {
59 Self::new(dimension, true)
60 }
61
62 pub fn train(&mut self, vectors: &[Vec<f32>]) -> Result<()> {
70 if vectors.is_empty() {
71 return Err(Error::InvalidInput(
72 "Cannot train on empty vector set".to_string(),
73 ));
74 }
75
76 for (i, vec) in vectors.iter().enumerate() {
78 if vec.len() != self.dimension {
79 return Err(Error::InvalidInput(format!(
80 "Vector {} has dimension {}, expected {}",
81 i,
82 vec.len(),
83 self.dimension
84 )));
85 }
86 }
87
88 self.config.min_values = vec![f32::MAX; self.dimension];
90 self.config.max_values = vec![f32::MIN; self.dimension];
91
92 for vec in vectors {
94 for (i, &val) in vec.iter().enumerate() {
95 if val < self.config.min_values[i] {
96 self.config.min_values[i] = val;
97 }
98 if val > self.config.max_values[i] {
99 self.config.max_values[i] = val;
100 }
101 }
102 }
103
104 for i in 0..self.dimension {
106 let range = self.config.max_values[i] - self.config.min_values[i];
107 if range < 1e-6 {
108 self.config.min_values[i] -= 0.5;
110 self.config.max_values[i] += 0.5;
111 } else {
112 let margin = range * 0.01;
113 self.config.min_values[i] -= margin;
114 self.config.max_values[i] += margin;
115 }
116 }
117
118 self.trained = true;
119 Ok(())
120 }
121
122 pub fn train_incremental(&mut self, vector: &[f32]) -> Result<()> {
124 if vector.len() != self.dimension {
125 return Err(Error::InvalidInput(format!(
126 "Vector has dimension {}, expected {}",
127 vector.len(),
128 self.dimension
129 )));
130 }
131
132 for (i, &val) in vector.iter().enumerate() {
133 if val < self.config.min_values[i] {
134 self.config.min_values[i] = val;
135 }
136 if val > self.config.max_values[i] {
137 self.config.max_values[i] = val;
138 }
139 }
140
141 self.trained = true;
142 Ok(())
143 }
144
145 pub fn is_trained(&self) -> bool {
147 self.trained
148 }
149
150 pub fn quantize(&self, vector: &[f32]) -> Result<QuantizedVector> {
152 if !self.trained {
153 return Err(Error::InvalidInput(
154 "Quantizer must be trained before use".to_string(),
155 ));
156 }
157
158 if vector.len() != self.dimension {
159 return Err(Error::InvalidInput(format!(
160 "Vector has dimension {}, expected {}",
161 vector.len(),
162 self.dimension
163 )));
164 }
165
166 let mut quantized = Vec::with_capacity(self.dimension);
167
168 for (i, &val) in vector.iter().enumerate() {
169 let min = self.config.min_values[i];
170 let max = self.config.max_values[i];
171 let range = max - min;
172
173 let normalized = if range > 1e-6 {
175 ((val - min) / range).clamp(0.0, 1.0)
176 } else {
177 0.5
178 };
179
180 let q = if self.config.signed {
182 ((normalized * 255.0 - 128.0).round() as i8) as u8
184 } else {
185 (normalized * 255.0).round() as u8
187 };
188
189 quantized.push(q);
190 }
191
192 Ok(QuantizedVector {
193 data: quantized,
194 signed: self.config.signed,
195 })
196 }
197
198 pub fn dequantize(&self, quantized: &QuantizedVector) -> Result<Vec<f32>> {
200 if !self.trained {
201 return Err(Error::InvalidInput(
202 "Quantizer must be trained before use".to_string(),
203 ));
204 }
205
206 if quantized.data.len() != self.dimension {
207 return Err(Error::InvalidInput(format!(
208 "Quantized vector has dimension {}, expected {}",
209 quantized.data.len(),
210 self.dimension
211 )));
212 }
213
214 let mut result = Vec::with_capacity(self.dimension);
215
216 for (i, &q) in quantized.data.iter().enumerate() {
217 let min = self.config.min_values[i];
218 let max = self.config.max_values[i];
219 let range = max - min;
220
221 let normalized = if self.config.signed {
223 ((q as i8) as f32 + 128.0) / 255.0
225 } else {
226 q as f32 / 255.0
228 };
229
230 let val = min + normalized * range;
231 result.push(val);
232 }
233
234 Ok(result)
235 }
236
237 pub fn distance_l2_quantized(&self, a: &QuantizedVector, b: &QuantizedVector) -> Result<f32> {
241 if a.data.len() != b.data.len() {
242 return Err(Error::InvalidInput(
243 "Vectors must have same dimension".to_string(),
244 ));
245 }
246
247 let mut sum_sq: i64 = 0;
248
249 for (qa, qb) in a.data.iter().zip(b.data.iter()) {
250 let diff = if a.signed {
251 (*qa as i8 as i64) - (*qb as i8 as i64)
252 } else {
253 (*qa as i64) - (*qb as i64)
254 };
255 sum_sq += diff * diff;
256 }
257
258 Ok((sum_sq as f32).sqrt() / 255.0)
261 }
262
263 pub fn dot_product_quantized(&self, a: &QuantizedVector, b: &QuantizedVector) -> Result<f32> {
265 if a.data.len() != b.data.len() {
266 return Err(Error::InvalidInput(
267 "Vectors must have same dimension".to_string(),
268 ));
269 }
270
271 let mut sum: i64 = 0;
272
273 for (qa, qb) in a.data.iter().zip(b.data.iter()) {
274 if a.signed {
275 sum += (*qa as i8 as i64) * (*qb as i8 as i64);
276 } else {
277 sum += (*qa as i64) * (*qb as i64);
278 }
279 }
280
281 Ok(sum as f32 / (255.0 * 255.0))
283 }
284
285 pub fn dimension(&self) -> usize {
287 self.dimension
288 }
289
290 pub fn compression_ratio(&self) -> f32 {
292 4.0 }
294
295 pub fn memory_estimate(&self, num_vectors: usize) -> usize {
297 num_vectors * self.dimension + 2 * self.dimension * 4
300 }
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct QuantizedVector {
306 pub data: Vec<u8>,
308 pub signed: bool,
310}
311
312impl QuantizedVector {
313 pub fn new(data: Vec<u8>, signed: bool) -> Self {
315 Self { data, signed }
316 }
317
318 pub fn dimension(&self) -> usize {
320 self.data.len()
321 }
322
323 pub fn as_bytes(&self) -> &[u8] {
325 &self.data
326 }
327
328 pub fn size_bytes(&self) -> usize {
330 self.data.len()
331 }
332}