Skip to main content

lance_index/vector/flat/
storage.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4use std::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::UInt8Type;
14use arrow_array::ArrowPrimitiveType;
15use arrow_array::{
16    Array, ArrayRef, FixedSizeListArray, RecordBatch, UInt64Array,
17    types::{Float32Type, UInt64Type},
18};
19use arrow_schema::SchemaRef;
20use deepsize::DeepSizeOf;
21use lance_core::{Error, ROW_ID, Result};
22use lance_file::previous::reader::FileReader as PreviousFileReader;
23use lance_linalg::distance::DistanceType;
24use lance_linalg::distance::hamming::hamming;
25
26pub const FLAT_COLUMN: &str = "flat";
27
28/// All data are stored in memory
29#[derive(Debug, Clone)]
30pub struct FlatFloatStorage {
31    metadata: FlatMetadata,
32    batch: RecordBatch,
33    distance_type: DistanceType,
34
35    // helper fields
36    pub(super) row_ids: Arc<UInt64Array>,
37    vectors: Arc<FixedSizeListArray>,
38}
39
40impl DeepSizeOf for FlatFloatStorage {
41    fn deep_size_of_children(&self, _: &mut 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    // used for only testing
102    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> = FlatDistanceCal<'a, Float32Type>;
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        // TODO: use chunked storage
137        let new_batch = concat_batches(&batch.schema(), vec![&self.batch, &batch].into_iter())?;
138        let mut storage = self.clone();
139        storage.batch = new_batch;
140        Ok(storage)
141    }
142
143    fn schema(&self) -> &SchemaRef {
144        self.batch.schema_ref()
145    }
146
147    fn as_any(&self) -> &dyn std::any::Any {
148        self
149    }
150
151    fn len(&self) -> usize {
152        self.vectors.len()
153    }
154
155    fn distance_type(&self) -> DistanceType {
156        self.distance_type
157    }
158
159    fn row_id(&self, id: u32) -> u64 {
160        self.row_ids.values()[id as usize]
161    }
162
163    fn row_ids(&self) -> impl Iterator<Item = &u64> {
164        self.row_ids.values().iter()
165    }
166
167    fn dist_calculator(&self, query: ArrayRef, _dist_q_c: f32) -> Self::DistanceCalculator<'_> {
168        Self::DistanceCalculator::new(self.vectors.as_ref(), query, self.distance_type)
169    }
170
171    fn dist_calculator_from_id(&self, id: u32) -> Self::DistanceCalculator<'_> {
172        Self::DistanceCalculator::new(
173            self.vectors.as_ref(),
174            self.vectors.value(id as usize),
175            self.distance_type,
176        )
177    }
178}
179
180/// All data are stored in memory
181#[derive(Debug, Clone)]
182pub struct FlatBinStorage {
183    metadata: FlatMetadata,
184    batch: RecordBatch,
185    distance_type: DistanceType,
186
187    // helper fields
188    pub(super) row_ids: Arc<UInt64Array>,
189    vectors: Arc<FixedSizeListArray>,
190}
191
192impl DeepSizeOf for FlatBinStorage {
193    fn deep_size_of_children(&self, _: &mut deepsize::Context) -> usize {
194        self.batch.get_array_memory_size()
195    }
196}
197
198#[async_trait::async_trait]
199impl QuantizerStorage for FlatBinStorage {
200    type Metadata = FlatMetadata;
201
202    fn try_from_batch(
203        batch: RecordBatch,
204        metadata: &Self::Metadata,
205        distance_type: DistanceType,
206        frag_reuse_index: Option<Arc<FragReuseIndex>>,
207    ) -> Result<Self> {
208        let batch = if let Some(frag_reuse_index_ref) = frag_reuse_index.as_ref() {
209            frag_reuse_index_ref.remap_row_ids_record_batch(batch, 0)?
210        } else {
211            batch
212        };
213
214        let row_ids = Arc::new(
215            batch
216                .column_by_name(ROW_ID)
217                .ok_or(Error::schema(format!("column {} not found", ROW_ID)))?
218                .as_primitive::<UInt64Type>()
219                .clone(),
220        );
221        let vectors = Arc::new(
222            batch
223                .column_by_name(FLAT_COLUMN)
224                .ok_or(Error::schema("column flat not found".to_string()))?
225                .as_fixed_size_list()
226                .clone(),
227        );
228        Ok(Self {
229            metadata: metadata.clone(),
230            batch,
231            distance_type,
232            row_ids,
233            vectors,
234        })
235    }
236
237    fn metadata(&self) -> &Self::Metadata {
238        &self.metadata
239    }
240
241    async fn load_partition(
242        _: &PreviousFileReader,
243        _: std::ops::Range<usize>,
244        _: DistanceType,
245        _: &Self::Metadata,
246        _: Option<Arc<FragReuseIndex>>,
247    ) -> Result<Self> {
248        unimplemented!("Flat will be used in new index builder which doesn't require this")
249    }
250}
251
252impl FlatBinStorage {
253    // used for only testing
254    pub fn new(vectors: FixedSizeListArray, distance_type: DistanceType) -> Self {
255        let row_ids = Arc::new(UInt64Array::from_iter_values(0..vectors.len() as u64));
256        let vectors = Arc::new(vectors);
257
258        let batch = RecordBatch::try_from_iter_with_nullable(vec![
259            (ROW_ID, row_ids.clone() as ArrayRef, true),
260            (FLAT_COLUMN, vectors.clone() as ArrayRef, true),
261        ])
262        .unwrap();
263
264        Self {
265            metadata: FlatMetadata {
266                dim: vectors.value_length() as usize,
267            },
268            batch,
269            distance_type,
270            row_ids,
271            vectors,
272        }
273    }
274
275    pub fn vector(&self, id: u32) -> ArrayRef {
276        self.vectors.value(id as usize)
277    }
278}
279
280impl VectorStore for FlatBinStorage {
281    type DistanceCalculator<'a> = FlatDistanceCal<'a, UInt8Type>;
282
283    fn to_batches(&self) -> Result<impl Iterator<Item = RecordBatch>> {
284        Ok([self.batch.clone()].into_iter())
285    }
286
287    fn append_batch(&self, batch: RecordBatch, _vector_column: &str) -> Result<Self> {
288        // TODO: use chunked storage
289        let new_batch = concat_batches(&batch.schema(), vec![&self.batch, &batch].into_iter())?;
290        let mut storage = self.clone();
291        storage.batch = new_batch;
292        Ok(storage)
293    }
294
295    fn schema(&self) -> &SchemaRef {
296        self.batch.schema_ref()
297    }
298
299    fn as_any(&self) -> &dyn std::any::Any {
300        self
301    }
302
303    fn len(&self) -> usize {
304        self.vectors.len()
305    }
306
307    fn distance_type(&self) -> DistanceType {
308        self.distance_type
309    }
310
311    fn row_id(&self, id: u32) -> u64 {
312        self.row_ids.values()[id as usize]
313    }
314
315    fn row_ids(&self) -> impl Iterator<Item = &u64> {
316        self.row_ids.values().iter()
317    }
318
319    fn dist_calculator(&self, query: ArrayRef, _dist_q_c: f32) -> Self::DistanceCalculator<'_> {
320        Self::DistanceCalculator::new(self.vectors.as_ref(), query, self.distance_type)
321    }
322
323    fn dist_calculator_from_id(&self, id: u32) -> Self::DistanceCalculator<'_> {
324        Self::DistanceCalculator::new(
325            self.vectors.as_ref(),
326            self.vectors.value(id as usize),
327            self.distance_type,
328        )
329    }
330}
331
332pub struct FlatDistanceCal<'a, T: ArrowPrimitiveType> {
333    vectors: &'a [T::Native],
334    query: Vec<T::Native>,
335    dimension: usize,
336    #[allow(clippy::type_complexity)]
337    distance_fn: fn(&[T::Native], &[T::Native]) -> f32,
338}
339
340impl<'a> FlatDistanceCal<'a, Float32Type> {
341    fn new(vectors: &'a FixedSizeListArray, query: ArrayRef, distance_type: DistanceType) -> Self {
342        // Gained significant performance improvement by using strong typed primitive slice.
343        // TODO: to support other data types other than `f32`, make FlatDistanceCal a generic struct.
344        let flat_array = vectors.values().as_primitive::<Float32Type>();
345        let dimension = vectors.value_length() as usize;
346        Self {
347            vectors: flat_array.values(),
348            query: query.as_primitive::<Float32Type>().values().to_vec(),
349            dimension,
350            distance_fn: distance_type.func(),
351        }
352    }
353}
354
355impl<'a> FlatDistanceCal<'a, UInt8Type> {
356    fn new(vectors: &'a FixedSizeListArray, query: ArrayRef, _distance_type: DistanceType) -> Self {
357        // Gained significant performance improvement by using strong typed primitive slice.
358        // TODO: to support other data types other than `f32`, make FlatDistanceCal a generic struct.
359        let flat_array = vectors.values().as_primitive::<UInt8Type>();
360        let dimension = vectors.value_length() as usize;
361        Self {
362            vectors: flat_array.values(),
363            query: query.as_primitive::<UInt8Type>().values().to_vec(),
364            dimension,
365            distance_fn: hamming,
366        }
367    }
368}
369
370impl<T: ArrowPrimitiveType> FlatDistanceCal<'_, T> {
371    #[inline]
372    fn get_vector(&self, id: u32) -> &[T::Native] {
373        &self.vectors[self.dimension * id as usize..self.dimension * (id + 1) as usize]
374    }
375}
376
377impl<T: ArrowPrimitiveType> DistCalculator for FlatDistanceCal<'_, T> {
378    #[inline]
379    fn distance(&self, id: u32) -> f32 {
380        let vector = self.get_vector(id);
381        (self.distance_fn)(&self.query, vector)
382    }
383
384    fn distance_all(&self, _k_hint: usize) -> Vec<f32> {
385        let query = &self.query;
386        self.vectors
387            .chunks_exact(self.dimension)
388            .map(|vector| (self.distance_fn)(query, vector))
389            .collect()
390    }
391
392    #[inline]
393    fn prefetch(&self, id: u32) {
394        let vector = self.get_vector(id);
395        do_prefetch(vector.as_ptr_range())
396    }
397}