1use std::ops::Range;
7use std::sync::Arc;
8
9use arrow_array::{Array, FixedSizeListArray, Float32Array, RecordBatch, UInt32Array};
10
11pub use builder::IvfBuildParams;
12use lance_core::Result;
13use lance_linalg::distance::{DistanceType, MetricType};
14use tracing::instrument;
15
16use crate::vector::bq::builder::RabitQuantizer;
17use crate::vector::bq::transform::RQTransformer;
18use crate::vector::ivf::transform::PartitionTransformer;
19use crate::vector::kmeans::{compute_partitions_arrow_array, kmeans_find_partitions_arrow_array};
20use crate::vector::{pq::ProductQuantizer, transform::Transformer};
21
22use super::flat::transform::FlatTransformer;
23use super::pq::transform::PQTransformer;
24use super::quantizer::Quantization;
25use super::residual::ResidualTransform;
26use super::sq::ScalarQuantizer;
27use super::sq::transform::SQTransformer;
28use super::transform::KeepFiniteVectors;
29use super::{PART_ID_COLUMN, PQ_CODE_COLUMN, SQ_CODE_COLUMN};
30use super::{quantizer::Quantizer, residual::compute_residual};
31
32pub mod builder;
33pub mod shuffler;
34pub mod storage;
35mod transform;
36
37pub fn new_ivf_transformer(
47 centroids: FixedSizeListArray,
48 metric_type: DistanceType,
49 transforms: Vec<Arc<dyn Transformer>>,
50) -> IvfTransformer {
51 IvfTransformer::new(centroids, metric_type, transforms)
52}
53
54pub fn new_ivf_transformer_with_quantizer(
55 centroids: FixedSizeListArray,
56 metric_type: MetricType,
57 vector_column: &str,
58 quantizer: Quantizer,
59 range: Option<Range<u32>>,
60) -> Result<IvfTransformer> {
61 match quantizer {
62 Quantizer::Flat(_) | Quantizer::FlatBin(_) => Ok(IvfTransformer::new_flat(
63 centroids,
64 metric_type,
65 vector_column,
66 range,
67 )),
68 Quantizer::Product(pq) => Ok(IvfTransformer::with_pq(
69 centroids,
70 metric_type,
71 vector_column,
72 pq,
73 range,
74 )),
75 Quantizer::Scalar(sq) => Ok(IvfTransformer::with_sq(
76 centroids,
77 metric_type,
78 vector_column,
79 sq,
80 range,
81 )),
82 Quantizer::Rabit(rq) => {
83 IvfTransformer::with_rq(centroids, metric_type, vector_column, rq, range)
84 }
85 }
86}
87
88#[derive(Debug)]
91pub struct IvfTransformer {
92 centroids: FixedSizeListArray,
96
97 transforms: Vec<Arc<dyn Transformer>>,
99
100 distance_type: DistanceType,
102}
103
104impl IvfTransformer {
105 pub fn new(
107 centroids: FixedSizeListArray,
108 metric_type: MetricType,
109 transforms: Vec<Arc<dyn Transformer>>,
110 ) -> Self {
111 Self {
112 centroids,
113 distance_type: metric_type,
114 transforms,
115 }
116 }
117
118 pub fn new_partition_transformer(
119 centroids: FixedSizeListArray,
120 distance_type: DistanceType,
121 vector_column: &str,
122 ) -> Self {
123 let mut transforms: Vec<Arc<dyn Transformer>> =
124 vec![Arc::new(super::transform::Flatten::new(vector_column))];
125
126 let distance_type = if distance_type == MetricType::Cosine {
127 transforms.push(Arc::new(super::transform::NormalizeTransformer::new(
128 vector_column,
129 )));
130 MetricType::L2
131 } else {
132 distance_type
133 };
134 transforms.push(Arc::new(KeepFiniteVectors::new(vector_column)));
135
136 let partition_transform = Arc::new(PartitionTransformer::new(
137 centroids.clone(),
138 distance_type,
139 vector_column,
140 ));
141 transforms.push(partition_transform);
142 Self::new(centroids, distance_type, transforms)
143 }
144
145 pub fn new_flat(
146 centroids: FixedSizeListArray,
147 distance_type: DistanceType,
148 vector_column: &str,
149 range: Option<Range<u32>>,
150 ) -> Self {
151 let mut transforms: Vec<Arc<dyn Transformer>> =
152 vec![Arc::new(super::transform::Flatten::new(vector_column))];
153
154 let dt = if distance_type == DistanceType::Cosine {
155 transforms.push(Arc::new(super::transform::NormalizeTransformer::new(
156 vector_column,
157 )));
158 MetricType::L2
159 } else {
160 distance_type
161 };
162 transforms.push(Arc::new(KeepFiniteVectors::new(vector_column)));
163
164 let ivf_transform = Arc::new(PartitionTransformer::new(
165 centroids.clone(),
166 dt,
167 vector_column,
168 ));
169 transforms.push(ivf_transform);
170
171 if let Some(range) = range {
172 transforms.push(Arc::new(transform::PartitionFilter::new(
173 PART_ID_COLUMN,
174 range,
175 )));
176 }
177
178 transforms.push(Arc::new(FlatTransformer::new(vector_column)));
179
180 Self::new(centroids, distance_type, transforms)
181 }
182
183 pub fn with_pq(
185 centroids: FixedSizeListArray,
186 distance_type: DistanceType,
187 vector_column: &str,
188 pq: ProductQuantizer,
189 range: Option<Range<u32>>,
190 ) -> Self {
191 let mut transforms: Vec<Arc<dyn Transformer>> =
192 vec![Arc::new(super::transform::Flatten::new(vector_column))];
193
194 let distance_type = if distance_type == MetricType::Cosine {
195 transforms.push(Arc::new(super::transform::NormalizeTransformer::new(
196 vector_column,
197 )));
198 MetricType::L2
199 } else {
200 distance_type
201 };
202 transforms.push(Arc::new(KeepFiniteVectors::new(vector_column)));
203
204 let partition_transform = Arc::new(PartitionTransformer::new(
205 centroids.clone(),
206 distance_type,
207 vector_column,
208 ));
209 transforms.push(partition_transform);
210
211 if let Some(range) = range {
212 transforms.push(Arc::new(transform::PartitionFilter::new(
213 PART_ID_COLUMN,
214 range,
215 )));
216 }
217
218 if ProductQuantizer::use_residual(distance_type) {
219 transforms.push(Arc::new(ResidualTransform::new(
220 centroids.clone(),
221 PART_ID_COLUMN,
222 vector_column,
223 )));
224 }
225 transforms.push(Arc::new(PQTransformer::new(
226 pq,
227 vector_column,
228 PQ_CODE_COLUMN,
229 )));
230
231 Self::new(centroids, distance_type, transforms)
232 }
233
234 fn with_sq(
235 centroids: FixedSizeListArray,
236 metric_type: MetricType,
237 vector_column: &str,
238 sq: ScalarQuantizer,
239 range: Option<Range<u32>>,
240 ) -> Self {
241 let mut transforms: Vec<Arc<dyn Transformer>> =
242 vec![Arc::new(super::transform::Flatten::new(vector_column))];
243
244 let distance_type = if metric_type == MetricType::Cosine {
245 transforms.push(Arc::new(super::transform::NormalizeTransformer::new(
246 vector_column,
247 )));
248 MetricType::L2
249 } else {
250 metric_type
251 };
252 transforms.push(Arc::new(KeepFiniteVectors::new(vector_column)));
253
254 let partition_transformer = Arc::new(PartitionTransformer::new(
255 centroids.clone(),
256 distance_type,
257 vector_column,
258 ));
259 transforms.push(partition_transformer);
260
261 if let Some(range) = range {
262 transforms.push(Arc::new(transform::PartitionFilter::new(
263 PART_ID_COLUMN,
264 range,
265 )));
266 }
267
268 transforms.push(Arc::new(SQTransformer::new(
269 sq,
270 vector_column.to_owned(),
271 SQ_CODE_COLUMN.to_owned(),
272 )));
273
274 Self::new(centroids, distance_type, transforms)
275 }
276
277 fn with_rq(
278 centroids: FixedSizeListArray,
279 distance_type: DistanceType,
280 vector_column: &str,
281 rq: RabitQuantizer,
282 range: Option<Range<u32>>,
283 ) -> Result<Self> {
284 let mut transforms: Vec<Arc<dyn Transformer>> =
285 vec![Arc::new(super::transform::Flatten::new(vector_column))];
286
287 let distance_type = if distance_type == MetricType::Cosine {
288 transforms.push(Arc::new(super::transform::NormalizeTransformer::new(
289 vector_column,
290 )));
291 MetricType::L2
292 } else {
293 distance_type
294 };
295 transforms.push(Arc::new(KeepFiniteVectors::new(vector_column)));
296
297 let partition_transform = Arc::new(
298 PartitionTransformer::new(centroids.clone(), distance_type, vector_column)
299 .with_distance(true),
300 );
301 transforms.push(partition_transform);
302
303 if let Some(range) = range {
304 transforms.push(Arc::new(transform::PartitionFilter::new(
305 PART_ID_COLUMN,
306 range,
307 )));
308 }
309
310 transforms.push(Arc::new(ResidualTransform::new(
311 centroids.clone(),
312 PART_ID_COLUMN,
313 vector_column,
314 )));
315
316 transforms.push(Arc::new(RQTransformer::new(
317 rq,
318 distance_type,
319 centroids.clone(),
320 vector_column,
321 )?));
322
323 Ok(Self::new(centroids, distance_type, transforms))
324 }
325
326 #[inline]
327 pub fn compute_residual(&self, data: &FixedSizeListArray) -> Result<FixedSizeListArray> {
328 compute_residual(&self.centroids, data, Some(self.distance_type), None)
329 }
330
331 #[inline]
332 pub fn compute_partitions(&self, data: &FixedSizeListArray) -> Result<UInt32Array> {
333 Ok(
334 compute_partitions_arrow_array(&self.centroids, data, self.distance_type)
335 .map(|(part_ids, _)| part_ids.into())?,
336 )
337 }
338
339 pub fn find_partitions(
340 &self,
341 query: &dyn Array,
342 nprobes: usize,
343 ) -> Result<(UInt32Array, Float32Array)> {
344 Ok(kmeans_find_partitions_arrow_array(
345 &self.centroids,
346 query,
347 nprobes,
348 self.distance_type,
349 )?)
350 }
351}
352
353impl Transformer for IvfTransformer {
354 #[instrument(name = "IvfTransformer::transform", level = "debug", skip_all)]
355 fn transform(&self, batch: &RecordBatch) -> Result<RecordBatch> {
356 let mut batch = batch.clone();
357 for transform in self.transforms.as_slice() {
358 batch = transform.transform(&batch)?;
359 }
360 Ok(batch)
361 }
362}