Skip to main content

clt_database/vector/operations/
convert.rs

1use crate::{
2    vector::vector_types::{Vector, VectorType},
3    Result,
4};
5
6pub fn vector_convert(v: Vector, target_type: VectorType) -> Result<Vector> {
7    if v.vector_type == target_type {
8        return Ok(v);
9    }
10    match (v.vector_type, target_type) {
11        (VectorType::Float32Dense, VectorType::Float64Dense) => Ok(Vector::from_f64(
12            v.as_f32_slice().iter().map(|&x| x as f64).collect(),
13        )),
14        (VectorType::Float64Dense, VectorType::Float32Dense) => Ok(Vector::from_f32(
15            v.as_f64_slice().iter().map(|&x| x as f32).collect(),
16        )),
17        (VectorType::Float32Dense, VectorType::Float32Sparse) => {
18            let (mut idx, mut values) = (Vec::new(), Vec::new());
19            for (i, &value) in v.as_f32_slice().iter().enumerate() {
20                if value == 0.0 {
21                    continue;
22                }
23                idx.push(i as u32);
24                values.push(value);
25            }
26            Ok(Vector::from_f32_sparse(v.dims, values, idx))
27        }
28        (VectorType::Float64Dense, VectorType::Float32Sparse) => {
29            let (mut idx, mut values) = (Vec::new(), Vec::new());
30            for (i, &value) in v.as_f64_slice().iter().enumerate() {
31                if value == 0.0 {
32                    continue;
33                }
34                idx.push(i as u32);
35                values.push(value as f32);
36            }
37            Ok(Vector::from_f32_sparse(v.dims, values, idx))
38        }
39        (VectorType::Float32Sparse, VectorType::Float32Dense) => {
40            let sparse = v.as_f32_sparse();
41            let mut data = vec![0f32; v.dims];
42            for (&i, &value) in sparse.idx.iter().zip(sparse.values.iter()) {
43                data[i as usize] = value;
44            }
45            Ok(Vector::from_f32(data))
46        }
47        (VectorType::Float32Sparse, VectorType::Float64Dense) => {
48            let sparse = v.as_f32_sparse();
49            let mut data = vec![0f64; v.dims];
50            for (&i, &value) in sparse.idx.iter().zip(sparse.values.iter()) {
51                data[i as usize] = value as f64;
52            }
53            Ok(Vector::from_f64(data))
54        }
55        // Float1Bit conversions
56        (VectorType::Float32Dense, VectorType::Float1Bit) => {
57            let dims = v.dims;
58            let byte_count = dims.div_ceil(8);
59            let mut bits = vec![0u8; byte_count];
60            for (i, &val) in v.as_f32_slice().iter().enumerate() {
61                if val > 0.0 {
62                    bits[i / 8] |= 1 << (i & 7);
63                }
64            }
65            Ok(Vector::from_1bit(dims, bits))
66        }
67        (VectorType::Float64Dense, VectorType::Float1Bit) => {
68            let dims = v.dims;
69            let byte_count = dims.div_ceil(8);
70            let mut bits = vec![0u8; byte_count];
71            for (i, &val) in v.as_f64_slice().iter().enumerate() {
72                if val > 0.0 {
73                    bits[i / 8] |= 1 << (i & 7);
74                }
75            }
76            Ok(Vector::from_1bit(dims, bits))
77        }
78        (VectorType::Float1Bit, VectorType::Float32Dense) => {
79            let data = v.as_1bit_data();
80            let floats: Vec<f32> = (0..v.dims)
81                .map(|i| {
82                    if (data[i / 8] >> (i & 7)) & 1 == 1 {
83                        1.0
84                    } else {
85                        -1.0
86                    }
87                })
88                .collect();
89            Ok(Vector::from_f32(floats))
90        }
91        (VectorType::Float1Bit, VectorType::Float64Dense) => {
92            let data = v.as_1bit_data();
93            let floats: Vec<f64> = (0..v.dims)
94                .map(|i| {
95                    if (data[i / 8] >> (i & 7)) & 1 == 1 {
96                        1.0
97                    } else {
98                        -1.0
99                    }
100                })
101                .collect();
102            Ok(Vector::from_f64(floats))
103        }
104        // Float8 conversions
105        (VectorType::Float32Dense, VectorType::Float8) => {
106            convert_floats_to_f8(v.as_f32_slice().iter().copied(), v.dims)
107        }
108        (VectorType::Float64Dense, VectorType::Float8) => {
109            convert_floats_to_f8(v.as_f64_slice().iter().map(|&x| x as f32), v.dims)
110        }
111        (VectorType::Float8, VectorType::Float32Dense) => {
112            let (quantized, alpha, shift) = v.as_f8_data();
113            let floats: Vec<f32> = quantized
114                .iter()
115                .map(|&q| alpha * q as f32 + shift)
116                .collect();
117            Ok(Vector::from_f32(floats))
118        }
119        (VectorType::Float8, VectorType::Float64Dense) => {
120            let (quantized, alpha, shift) = v.as_f8_data();
121            let floats: Vec<f64> = quantized
122                .iter()
123                .map(|&q| alpha as f64 * q as f64 + shift as f64)
124                .collect();
125            Ok(Vector::from_f64(floats))
126        }
127        // Cross-conversions via intermediate
128        (VectorType::Float1Bit, VectorType::Float8) => {
129            let f32_vec = vector_convert(v, VectorType::Float32Dense)?;
130            vector_convert(f32_vec, VectorType::Float8)
131        }
132        (VectorType::Float8, VectorType::Float1Bit) => {
133            let f32_vec = vector_convert(v, VectorType::Float32Dense)?;
134            vector_convert(f32_vec, VectorType::Float1Bit)
135        }
136        (VectorType::Float1Bit, VectorType::Float32Sparse)
137        | (VectorType::Float8, VectorType::Float32Sparse)
138        | (VectorType::Float32Sparse, VectorType::Float1Bit)
139        | (VectorType::Float32Sparse, VectorType::Float8) => {
140            let f32_vec = vector_convert(v, VectorType::Float32Dense)?;
141            vector_convert(f32_vec, target_type)
142        }
143        _ => unreachable!(
144            "unexpected conversion: {:?} -> {:?}",
145            v.vector_type, target_type
146        ),
147    }
148}
149
150fn convert_floats_to_f8(
151    values: impl Iterator<Item = f32> + Clone,
152    dims: usize,
153) -> Result<Vector<'static>> {
154    if dims == 0 {
155        return Ok(Vector::from_f8(0, Vec::new(), 0.0, 0.0));
156    }
157    let mut min_val = f32::INFINITY;
158    let mut max_val = f32::NEG_INFINITY;
159    for val in values.clone() {
160        if val < min_val {
161            min_val = val;
162        }
163        if val > max_val {
164            max_val = val;
165        }
166    }
167    let alpha = (max_val - min_val) / 255.0;
168    let shift = min_val;
169    let mut quantized = Vec::with_capacity(dims);
170    for val in values {
171        let q = if alpha == 0.0 {
172            0u8
173        } else {
174            let v = (val - shift) / alpha + 0.5;
175            (v as i32).clamp(0, 255) as u8
176        };
177        quantized.push(q);
178    }
179    Ok(Vector::from_f8(dims, quantized, alpha, shift))
180}
181
182#[cfg(clt_turso_tests)]
183mod tests {
184    use crate::vector::{
185        operations::convert::vector_convert,
186        vector_types::{tests::ArbitraryVector, Vector, VectorType},
187    };
188    use quickcheck_macros::quickcheck;
189
190    fn concat<const N: usize>(data: &[[u8; N]]) -> Vec<u8> {
191        data.iter().flatten().cloned().collect::<Vec<u8>>()
192    }
193
194    fn assert_vectors(v1: &Vector, v2: &Vector) {
195        assert_eq!(v1.vector_type, v2.vector_type);
196        assert_eq!(v1.dims, v2.dims);
197        assert_eq!(v1.bin_data(), v2.bin_data());
198    }
199
200    fn clone_vector(v: &Vector) -> Vector<'static> {
201        Vector {
202            vector_type: v.vector_type,
203            dims: v.dims,
204            owned: Some(v.bin_data().to_vec()),
205            refer: None,
206        }
207    }
208
209    #[test]
210    pub fn test_vector_convert() {
211        let vf32 = Vector {
212            vector_type: VectorType::Float32Dense,
213            dims: 3,
214            owned: Some(concat(&[
215                1.0f32.to_le_bytes(),
216                0.0f32.to_le_bytes(),
217                2.0f32.to_le_bytes(),
218            ])),
219            refer: None,
220        };
221        let vf64 = Vector {
222            vector_type: VectorType::Float64Dense,
223            dims: 3,
224            owned: Some(concat(&[
225                1.0f64.to_le_bytes(),
226                0.0f64.to_le_bytes(),
227                2.0f64.to_le_bytes(),
228            ])),
229            refer: None,
230        };
231        let vf32_sparse = Vector {
232            vector_type: VectorType::Float32Sparse,
233            dims: 3,
234            owned: Some(concat(&[
235                1.0f32.to_le_bytes(),
236                2.0f32.to_le_bytes(),
237                0u32.to_le_bytes(),
238                2u32.to_le_bytes(),
239            ])),
240            refer: None,
241        };
242
243        let vectors = [vf32, vf64, vf32_sparse];
244        for v1 in &vectors {
245            for v2 in &vectors {
246                println!("{:?} -> {:?}", v1.vector_type, v2.vector_type);
247                let v_copy = Vector {
248                    vector_type: v1.vector_type,
249                    dims: v1.dims,
250                    owned: v1.owned.clone(),
251                    refer: None,
252                };
253                assert_vectors(&vector_convert(v_copy, v2.vector_type).unwrap(), v2);
254            }
255        }
256    }
257
258    /// Test that all 5x5 type conversions succeed and produce correct type/dims.
259    #[test]
260    pub fn test_vector_convert_all_types() {
261        let source = Vector::from_f32(vec![1.0, 0.5, 2.0]);
262
263        let all_types = [
264            VectorType::Float32Dense,
265            VectorType::Float64Dense,
266            VectorType::Float32Sparse,
267            VectorType::Float1Bit,
268            VectorType::Float8,
269        ];
270
271        for &src_type in &all_types {
272            let src = vector_convert(clone_vector(&source), src_type).unwrap();
273            for &dst_type in &all_types {
274                let result = vector_convert(clone_vector(&src), dst_type);
275                assert!(
276                    result.is_ok(),
277                    "conversion {:?} -> {:?} failed: {:?}",
278                    src_type,
279                    dst_type,
280                    result.err()
281                );
282                let converted = result.unwrap();
283                assert_eq!(converted.vector_type, dst_type);
284                assert_eq!(converted.dims, 3);
285            }
286        }
287    }
288
289    /// Lossless conversions roundtrip exactly.
290    #[test]
291    pub fn test_vector_convert_lossless_roundtrip() {
292        let vf32 = Vector::from_f32(vec![1.0, 0.0, 2.0]);
293
294        // f32 -> f64 -> f32 is exact
295        let via_f64 = vector_convert(
296            vector_convert(clone_vector(&vf32), VectorType::Float64Dense).unwrap(),
297            VectorType::Float32Dense,
298        )
299        .unwrap();
300        assert_eq!(vf32.bin_data(), via_f64.bin_data());
301
302        // f32 -> sparse -> f32 is exact
303        let via_sparse = vector_convert(
304            vector_convert(clone_vector(&vf32), VectorType::Float32Sparse).unwrap(),
305            VectorType::Float32Dense,
306        )
307        .unwrap();
308        assert_eq!(vf32.bin_data(), via_sparse.bin_data());
309    }
310
311    /// Float1Bit roundtrip preserves sign information: positive → 1, non-positive → -1.
312    #[quickcheck]
313    fn prop_vector_convert_1bit_roundtrip(v: ArbitraryVector<100>) -> bool {
314        let v_f32 = vector_convert(v.into(), VectorType::Float32Dense).unwrap();
315        let orig_slice = v_f32.as_f32_slice().to_vec();
316        let v_1bit = vector_convert(v_f32, VectorType::Float1Bit).unwrap();
317        let v_back = vector_convert(v_1bit, VectorType::Float32Dense).unwrap();
318        let back_slice = v_back.as_f32_slice();
319
320        for i in 0..100 {
321            let expected = if orig_slice[i] > 0.0 { 1.0f32 } else { -1.0f32 };
322            if back_slice[i] != expected {
323                return false;
324            }
325        }
326        true
327    }
328
329    /// Float8 roundtrip approximately preserves values within one quantization step.
330    #[quickcheck]
331    fn prop_vector_convert_f8_roundtrip(v: ArbitraryVector<100>) -> bool {
332        let v_f32 = vector_convert(v.into(), VectorType::Float32Dense).unwrap();
333        let orig_slice = v_f32.as_f32_slice().to_vec();
334        let v_f8 = vector_convert(v_f32, VectorType::Float8).unwrap();
335        let v_back = vector_convert(v_f8, VectorType::Float32Dense).unwrap();
336        let back_slice = v_back.as_f32_slice();
337
338        let min_val = orig_slice.iter().cloned().fold(f32::INFINITY, f32::min);
339        let max_val = orig_slice.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
340        let alpha = (max_val - min_val) / 255.0;
341        let tolerance = alpha + 1e-6;
342
343        for i in 0..100 {
344            if (orig_slice[i] - back_slice[i]).abs() > tolerance {
345                return false;
346            }
347        }
348        true
349    }
350
351    /// All type-pair conversions succeed for arbitrary vectors.
352    #[quickcheck]
353    fn prop_vector_convert_all_pairs(v: ArbitraryVector<16>) -> bool {
354        let v: Vector = v.into();
355        let all_types = [
356            VectorType::Float32Dense,
357            VectorType::Float64Dense,
358            VectorType::Float32Sparse,
359            VectorType::Float1Bit,
360            VectorType::Float8,
361        ];
362
363        for &target_type in &all_types {
364            if vector_convert(clone_vector(&v), target_type).is_err() {
365                return false;
366            }
367        }
368        true
369    }
370
371    #[test]
372    fn test_vector_convert_empty_to_f8() {
373        let empty_f32 = Vector::from_f32(vec![]);
374        let f8 = vector_convert(empty_f32, VectorType::Float8).unwrap();
375        assert_eq!(f8.dims, 0);
376        assert_eq!(f8.vector_type, VectorType::Float8);
377        let (quantized, alpha, shift) = f8.as_f8_data();
378        assert!(quantized.is_empty());
379        assert_eq!(alpha, 0.0);
380        assert_eq!(shift, 0.0);
381    }
382
383    #[test]
384    fn test_vector_convert_empty_f8_to_f32() {
385        let empty_f8 = Vector::from_f8(0, Vec::new(), 0.0, 0.0);
386        let f32_vec = vector_convert(empty_f8, VectorType::Float32Dense).unwrap();
387        assert_eq!(f32_vec.dims, 0);
388        assert!(f32_vec.as_f32_slice().is_empty());
389    }
390}