1use std::{borrow::Cow, sync::Arc};
5
6use super::index::FlatMetadata;
7use crate::frag_reuse::FragReuseIndex;
8use crate::vector::quantizer::QuantizerStorage;
9use crate::vector::storage::{DistCalculator, VectorStore};
10use crate::vector::utils::do_prefetch;
11use arrow::array::AsArray;
12use arrow::compute::concat_batches;
13use arrow::datatypes::{Float16Type, Float64Type, UInt8Type};
14use arrow_array::ArrowPrimitiveType;
15use arrow_array::{
16 Array, ArrayRef, FixedSizeListArray, RecordBatch, UInt64Array,
17 types::{Float32Type, UInt64Type},
18};
19use arrow_schema::{DataType, SchemaRef};
20use lance_core::deepsize::DeepSizeOf;
21use lance_core::{Error, ROW_ID, Result};
22use lance_file::previous::reader::FileReader as PreviousFileReader;
23use lance_linalg::distance::hamming::hamming;
24use lance_linalg::distance::{Cosine, DistanceType, Dot, L2};
25
26pub const FLAT_COLUMN: &str = "flat";
27
28#[derive(Debug, Clone)]
30pub struct FlatFloatStorage {
31 metadata: FlatMetadata,
32 batch: RecordBatch,
33 distance_type: DistanceType,
34
35 pub(super) row_ids: Arc<UInt64Array>,
37 vectors: Arc<FixedSizeListArray>,
38}
39
40impl DeepSizeOf for FlatFloatStorage {
41 fn deep_size_of_children(&self, _: &mut lance_core::deepsize::Context) -> usize {
42 self.batch.get_array_memory_size()
43 }
44}
45
46#[async_trait::async_trait]
47impl QuantizerStorage for FlatFloatStorage {
48 type Metadata = FlatMetadata;
49
50 fn try_from_batch(
51 batch: RecordBatch,
52 metadata: &Self::Metadata,
53 distance_type: DistanceType,
54 frag_reuse_index: Option<Arc<FragReuseIndex>>,
55 ) -> Result<Self> {
56 let batch = if let Some(frag_reuse_index_ref) = frag_reuse_index.as_ref() {
57 frag_reuse_index_ref.remap_row_ids_record_batch(batch, 0)?
58 } else {
59 batch
60 };
61
62 let row_ids = Arc::new(
63 batch
64 .column_by_name(ROW_ID)
65 .ok_or(Error::schema(format!("column {} not found", ROW_ID)))?
66 .as_primitive::<UInt64Type>()
67 .clone(),
68 );
69 let vectors = Arc::new(
70 batch
71 .column_by_name(FLAT_COLUMN)
72 .ok_or(Error::schema("column flat not found".to_string()))?
73 .as_fixed_size_list()
74 .clone(),
75 );
76 Ok(Self {
77 metadata: metadata.clone(),
78 batch,
79 distance_type,
80 row_ids,
81 vectors,
82 })
83 }
84
85 fn metadata(&self) -> &Self::Metadata {
86 &self.metadata
87 }
88
89 async fn load_partition(
90 _: &PreviousFileReader,
91 _: std::ops::Range<usize>,
92 _: DistanceType,
93 _: &Self::Metadata,
94 _: Option<Arc<FragReuseIndex>>,
95 ) -> Result<Self> {
96 unimplemented!("Flat will be used in new index builder which doesn't require this")
97 }
98}
99
100impl FlatFloatStorage {
101 pub fn new(vectors: FixedSizeListArray, distance_type: DistanceType) -> Self {
103 let row_ids = Arc::new(UInt64Array::from_iter_values(0..vectors.len() as u64));
104 let vectors = Arc::new(vectors);
105
106 let batch = RecordBatch::try_from_iter_with_nullable(vec![
107 (ROW_ID, row_ids.clone() as ArrayRef, true),
108 (FLAT_COLUMN, vectors.clone() as ArrayRef, true),
109 ])
110 .unwrap();
111
112 Self {
113 metadata: FlatMetadata {
114 dim: vectors.value_length() as usize,
115 },
116 batch,
117 distance_type,
118 row_ids,
119 vectors,
120 }
121 }
122
123 pub fn vector(&self, id: u32) -> ArrayRef {
124 self.vectors.value(id as usize)
125 }
126}
127
128impl VectorStore for FlatFloatStorage {
129 type DistanceCalculator<'a> = FlatFloatDistanceCalc<'a>;
130
131 fn to_batches(&self) -> Result<impl Iterator<Item = RecordBatch>> {
132 Ok([self.batch.clone()].into_iter())
133 }
134
135 fn append_batch(&self, batch: RecordBatch, _vector_column: &str) -> Result<Self> {
136 let new_batch = concat_batches(&batch.schema(), vec![&self.batch, &batch].into_iter())?;
138 let mut storage = self.clone();
139 storage.row_ids = Arc::new(
140 new_batch
141 .column_by_name(ROW_ID)
142 .ok_or(Error::schema(format!("column {} not found", ROW_ID)))?
143 .as_primitive::<UInt64Type>()
144 .clone(),
145 );
146 storage.vectors = Arc::new(
147 new_batch
148 .column_by_name(FLAT_COLUMN)
149 .ok_or(Error::schema("column flat not found".to_string()))?
150 .as_fixed_size_list()
151 .clone(),
152 );
153 storage.batch = new_batch;
154 Ok(storage)
155 }
156
157 fn schema(&self) -> &SchemaRef {
158 self.batch.schema_ref()
159 }
160
161 fn as_any(&self) -> &dyn std::any::Any {
162 self
163 }
164
165 fn len(&self) -> usize {
166 self.vectors.len()
167 }
168
169 fn distance_type(&self) -> DistanceType {
170 self.distance_type
171 }
172
173 fn row_id(&self, id: u32) -> u64 {
174 self.row_ids.values()[id as usize]
175 }
176
177 fn row_ids(&self) -> impl Iterator<Item = &u64> {
178 self.row_ids.values().iter()
179 }
180
181 fn dist_calculator(&self, query: ArrayRef, _dist_q_c: f32) -> Self::DistanceCalculator<'_> {
182 Self::DistanceCalculator::new(self.vectors.as_ref(), query, self.distance_type)
183 }
184
185 fn dist_calculator_from_id(&self, id: u32) -> Self::DistanceCalculator<'_> {
186 Self::DistanceCalculator::new_from_id(self.vectors.as_ref(), id, self.distance_type)
187 }
188}
189
190#[derive(Debug, Clone)]
192pub struct FlatBinStorage {
193 metadata: FlatMetadata,
194 batch: RecordBatch,
195 distance_type: DistanceType,
196
197 pub(super) row_ids: Arc<UInt64Array>,
199 vectors: Arc<FixedSizeListArray>,
200}
201
202impl DeepSizeOf for FlatBinStorage {
203 fn deep_size_of_children(&self, _: &mut lance_core::deepsize::Context) -> usize {
204 self.batch.get_array_memory_size()
205 }
206}
207
208#[async_trait::async_trait]
209impl QuantizerStorage for FlatBinStorage {
210 type Metadata = FlatMetadata;
211
212 fn try_from_batch(
213 batch: RecordBatch,
214 metadata: &Self::Metadata,
215 distance_type: DistanceType,
216 frag_reuse_index: Option<Arc<FragReuseIndex>>,
217 ) -> Result<Self> {
218 let batch = if let Some(frag_reuse_index_ref) = frag_reuse_index.as_ref() {
219 frag_reuse_index_ref.remap_row_ids_record_batch(batch, 0)?
220 } else {
221 batch
222 };
223
224 let row_ids = Arc::new(
225 batch
226 .column_by_name(ROW_ID)
227 .ok_or(Error::schema(format!("column {} not found", ROW_ID)))?
228 .as_primitive::<UInt64Type>()
229 .clone(),
230 );
231 let vectors = Arc::new(
232 batch
233 .column_by_name(FLAT_COLUMN)
234 .ok_or(Error::schema("column flat not found".to_string()))?
235 .as_fixed_size_list()
236 .clone(),
237 );
238 Ok(Self {
239 metadata: metadata.clone(),
240 batch,
241 distance_type,
242 row_ids,
243 vectors,
244 })
245 }
246
247 fn metadata(&self) -> &Self::Metadata {
248 &self.metadata
249 }
250
251 async fn load_partition(
252 _: &PreviousFileReader,
253 _: std::ops::Range<usize>,
254 _: DistanceType,
255 _: &Self::Metadata,
256 _: Option<Arc<FragReuseIndex>>,
257 ) -> Result<Self> {
258 unimplemented!("Flat will be used in new index builder which doesn't require this")
259 }
260}
261
262impl FlatBinStorage {
263 pub fn new(vectors: FixedSizeListArray, distance_type: DistanceType) -> Self {
265 let row_ids = Arc::new(UInt64Array::from_iter_values(0..vectors.len() as u64));
266 let vectors = Arc::new(vectors);
267
268 let batch = RecordBatch::try_from_iter_with_nullable(vec![
269 (ROW_ID, row_ids.clone() as ArrayRef, true),
270 (FLAT_COLUMN, vectors.clone() as ArrayRef, true),
271 ])
272 .unwrap();
273
274 Self {
275 metadata: FlatMetadata {
276 dim: vectors.value_length() as usize,
277 },
278 batch,
279 distance_type,
280 row_ids,
281 vectors,
282 }
283 }
284
285 pub fn vector(&self, id: u32) -> ArrayRef {
286 self.vectors.value(id as usize)
287 }
288}
289
290impl VectorStore for FlatBinStorage {
291 type DistanceCalculator<'a> = FlatDistanceCal<'a, UInt8Type>;
292
293 fn to_batches(&self) -> Result<impl Iterator<Item = RecordBatch>> {
294 Ok([self.batch.clone()].into_iter())
295 }
296
297 fn append_batch(&self, batch: RecordBatch, _vector_column: &str) -> Result<Self> {
298 let new_batch = concat_batches(&batch.schema(), vec![&self.batch, &batch].into_iter())?;
300 let mut storage = self.clone();
301 storage.row_ids = Arc::new(
302 new_batch
303 .column_by_name(ROW_ID)
304 .ok_or(Error::schema(format!("column {} not found", ROW_ID)))?
305 .as_primitive::<UInt64Type>()
306 .clone(),
307 );
308 storage.vectors = Arc::new(
309 new_batch
310 .column_by_name(FLAT_COLUMN)
311 .ok_or(Error::schema("column flat not found".to_string()))?
312 .as_fixed_size_list()
313 .clone(),
314 );
315 storage.batch = new_batch;
316 Ok(storage)
317 }
318
319 fn schema(&self) -> &SchemaRef {
320 self.batch.schema_ref()
321 }
322
323 fn as_any(&self) -> &dyn std::any::Any {
324 self
325 }
326
327 fn len(&self) -> usize {
328 self.vectors.len()
329 }
330
331 fn distance_type(&self) -> DistanceType {
332 self.distance_type
333 }
334
335 fn row_id(&self, id: u32) -> u64 {
336 self.row_ids.values()[id as usize]
337 }
338
339 fn row_ids(&self) -> impl Iterator<Item = &u64> {
340 self.row_ids.values().iter()
341 }
342
343 fn dist_calculator(&self, query: ArrayRef, _dist_q_c: f32) -> Self::DistanceCalculator<'_> {
344 Self::DistanceCalculator::new_binary(self.vectors.as_ref(), query, self.distance_type)
345 }
346
347 fn dist_calculator_from_id(&self, id: u32) -> Self::DistanceCalculator<'_> {
348 Self::DistanceCalculator::new_binary_from_id(self.vectors.as_ref(), id, self.distance_type)
349 }
350}
351
352pub struct FlatDistanceCal<'a, T: ArrowPrimitiveType> {
353 vectors: &'a [T::Native],
354 query: Cow<'a, [T::Native]>,
355 dimension: usize,
356 #[allow(clippy::type_complexity)]
357 distance_fn: fn(&[T::Native], &[T::Native]) -> f32,
358}
359
360impl<'a, T> FlatDistanceCal<'a, T>
361where
362 T: ArrowPrimitiveType,
363 T::Native: L2 + Cosine + Dot,
364{
365 fn new(vectors: &'a FixedSizeListArray, query: ArrayRef, distance_type: DistanceType) -> Self {
366 let flat_array = vectors.values().as_primitive::<T>();
368 let dimension = vectors.value_length() as usize;
369 Self {
370 vectors: flat_array.values(),
371 query: Cow::Owned(query.as_primitive::<T>().values().to_vec()),
372 dimension,
373 distance_fn: distance_type.func(),
374 }
375 }
376
377 fn new_from_id(vectors: &'a FixedSizeListArray, id: u32, distance_type: DistanceType) -> Self {
378 let flat_array = vectors.values().as_primitive::<T>();
379 let dimension = vectors.value_length() as usize;
380 let vectors = flat_array.values();
381 let id = id as usize;
382 Self {
383 vectors,
384 query: Cow::Borrowed(&vectors[dimension * id..dimension * (id + 1)]),
385 dimension,
386 distance_fn: distance_type.func(),
387 }
388 }
389}
390
391impl<'a> FlatDistanceCal<'a, UInt8Type> {
392 fn new_binary(
393 vectors: &'a FixedSizeListArray,
394 query: ArrayRef,
395 _distance_type: DistanceType,
396 ) -> Self {
397 let flat_array = vectors.values().as_primitive::<UInt8Type>();
400 let dimension = vectors.value_length() as usize;
401 Self {
402 vectors: flat_array.values(),
403 query: Cow::Owned(query.as_primitive::<UInt8Type>().values().to_vec()),
404 dimension,
405 distance_fn: hamming,
406 }
407 }
408
409 fn new_binary_from_id(
410 vectors: &'a FixedSizeListArray,
411 id: u32,
412 _distance_type: DistanceType,
413 ) -> Self {
414 let flat_array = vectors.values().as_primitive::<UInt8Type>();
415 let dimension = vectors.value_length() as usize;
416 let vectors = flat_array.values();
417 let id = id as usize;
418 Self {
419 vectors,
420 query: Cow::Borrowed(&vectors[dimension * id..dimension * (id + 1)]),
421 dimension,
422 distance_fn: hamming,
423 }
424 }
425}
426
427impl<T: ArrowPrimitiveType> FlatDistanceCal<'_, T> {
428 #[inline]
429 fn get_vector(&self, id: u32) -> &[T::Native] {
430 &self.vectors[self.dimension * id as usize..self.dimension * (id + 1) as usize]
431 }
432}
433
434impl<T: ArrowPrimitiveType> DistCalculator for FlatDistanceCal<'_, T> {
435 #[inline]
436 fn distance(&self, id: u32) -> f32 {
437 let query = self.query.as_ref();
438 let vector = self.get_vector(id);
439 (self.distance_fn)(query, vector)
440 }
441
442 fn distance_all(&self, _k_hint: usize) -> Vec<f32> {
443 let query = self.query.as_ref();
444 self.vectors
445 .chunks_exact(self.dimension)
446 .map(|vector| (self.distance_fn)(query, vector))
447 .collect()
448 }
449
450 #[inline]
451 fn prefetch(&self, id: u32) {
452 let vector = self.get_vector(id);
453 do_prefetch(vector.as_ptr_range())
454 }
455}
456
457pub enum FlatFloatDistanceCalc<'a> {
458 Float16(FlatDistanceCal<'a, Float16Type>),
459 Float32(FlatDistanceCal<'a, Float32Type>),
460 Float64(FlatDistanceCal<'a, Float64Type>),
461}
462
463impl<'a> FlatFloatDistanceCalc<'a> {
464 fn new(vectors: &'a FixedSizeListArray, query: ArrayRef, distance_type: DistanceType) -> Self {
465 match vectors.value_type() {
466 DataType::Float16 => Self::Float16(FlatDistanceCal::<Float16Type>::new(
467 vectors,
468 query,
469 distance_type,
470 )),
471 DataType::Float32 => Self::Float32(FlatDistanceCal::<Float32Type>::new(
472 vectors,
473 query,
474 distance_type,
475 )),
476 DataType::Float64 => Self::Float64(FlatDistanceCal::<Float64Type>::new(
477 vectors,
478 query,
479 distance_type,
480 )),
481 dt => panic!("flat float storage does not support data type {dt}"),
482 }
483 }
484
485 fn new_from_id(vectors: &'a FixedSizeListArray, id: u32, distance_type: DistanceType) -> Self {
486 match vectors.value_type() {
487 DataType::Float16 => Self::Float16(FlatDistanceCal::<Float16Type>::new_from_id(
488 vectors,
489 id,
490 distance_type,
491 )),
492 DataType::Float32 => Self::Float32(FlatDistanceCal::<Float32Type>::new_from_id(
493 vectors,
494 id,
495 distance_type,
496 )),
497 DataType::Float64 => Self::Float64(FlatDistanceCal::<Float64Type>::new_from_id(
498 vectors,
499 id,
500 distance_type,
501 )),
502 dt => panic!("flat float storage does not support data type {dt}"),
503 }
504 }
505}
506
507impl DistCalculator for FlatFloatDistanceCalc<'_> {
508 fn distance(&self, id: u32) -> f32 {
509 match self {
510 Self::Float16(calc) => calc.distance(id),
511 Self::Float32(calc) => calc.distance(id),
512 Self::Float64(calc) => calc.distance(id),
513 }
514 }
515
516 fn distance_all(&self, k_hint: usize) -> Vec<f32> {
517 match self {
518 Self::Float16(calc) => calc.distance_all(k_hint),
519 Self::Float32(calc) => calc.distance_all(k_hint),
520 Self::Float64(calc) => calc.distance_all(k_hint),
521 }
522 }
523
524 fn prefetch(&self, id: u32) {
525 match self {
526 Self::Float16(calc) => calc.prefetch(id),
527 Self::Float32(calc) => calc.prefetch(id),
528 Self::Float64(calc) => calc.prefetch(id),
529 }
530 }
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536
537 use arrow_array::{Float16Array, Float64Array};
538 use half::f16;
539 use lance_arrow::FixedSizeListArrayExt;
540
541 fn make_f16_storage() -> FlatFloatStorage {
542 let values = Float16Array::from(vec![
543 f16::from_f32(1.0),
544 f16::from_f32(2.0),
545 f16::from_f32(4.0),
546 f16::from_f32(6.0),
547 ]);
548 let vectors = FixedSizeListArray::try_new_from_values(values, 2).unwrap();
549 FlatFloatStorage::new(vectors, DistanceType::L2)
550 }
551
552 fn make_f64_storage() -> FlatFloatStorage {
553 let values = Float64Array::from(vec![1.0, 2.0, 4.0, 6.0]);
554 let vectors = FixedSizeListArray::try_new_from_values(values, 2).unwrap();
555 FlatFloatStorage::new(vectors, DistanceType::L2)
556 }
557
558 #[test]
559 fn test_flat_float_storage_distance_f16() {
560 let storage = make_f16_storage();
561 let query: ArrayRef = Arc::new(Float16Array::from(vec![
562 f16::from_f32(1.0),
563 f16::from_f32(2.0),
564 ]));
565
566 let calc = storage.dist_calculator(query, 0.0);
567 let distances = calc.distance_all(2);
568
569 assert_eq!(distances.len(), 2);
570 assert_eq!(distances[0], 0.0);
571 assert!((distances[1] - 25.0).abs() < 1e-4);
572 }
573
574 #[test]
575 fn test_flat_float_storage_distance_f64() {
576 let storage = make_f64_storage();
577 let query: ArrayRef = Arc::new(Float64Array::from(vec![1.0, 2.0]));
578
579 let calc = storage.dist_calculator(query, 0.0);
580 let distances = calc.distance_all(2);
581
582 assert_eq!(distances.len(), 2);
583 assert_eq!(distances[0], 0.0);
584 assert!((distances[1] - 25.0).abs() < 1e-6);
585 }
586}