1use arrow::{
5 array::AsArray,
6 datatypes::{Float16Type, Float32Type, Float64Type},
7};
8use arrow_array::{Array, ArrayRef, BooleanArray, FixedSizeListArray};
9use arrow_schema::{DataType, Field};
10use lance_arrow::FixedSizeListArrayExt;
11use lance_core::{Error, Result};
12use lance_io::encodings::plain::bytes_to_array;
13use lance_linalg::distance::DistanceType;
14use prost::bytes;
15use std::sync::LazyLock;
16use std::{ops::Range, sync::Arc};
17
18use super::pb;
19use crate::pb::Tensor;
20use crate::vector::flat::storage::FlatFloatStorage;
21use crate::vector::hnsw::HNSW;
22use crate::vector::hnsw::builder::{HnswBuildParams, HnswQueryParams};
23use crate::vector::v3::subindex::IvfSubIndex;
24
25enum SimpleIndexStatus {
26 Auto,
27 Enabled,
28 Disabled,
29}
30
31static USE_HNSW_SPEEDUP_INDEXING: LazyLock<SimpleIndexStatus> = LazyLock::new(|| {
32 if let Ok(v) = std::env::var("LANCE_USE_HNSW_SPEEDUP_INDEXING") {
33 if v == "enabled" {
34 SimpleIndexStatus::Enabled
35 } else if v == "disabled" {
36 SimpleIndexStatus::Disabled
37 } else {
38 SimpleIndexStatus::Auto
39 }
40 } else {
41 SimpleIndexStatus::Auto
42 }
43});
44
45#[derive(Debug)]
46pub struct SimpleIndex {
47 store: FlatFloatStorage,
48 index: HNSW,
49}
50
51impl SimpleIndex {
52 pub fn try_new(store: FlatFloatStorage) -> Result<Self> {
53 let hnsw = HNSW::index_vectors(
54 &store,
55 HnswBuildParams::default().ef_construction(15).num_edges(12),
56 )?;
57 Ok(Self { store, index: hnsw })
58 }
59
60 pub fn may_train_index(
67 centroids: ArrayRef,
68 dimension: usize,
69 distance_type: DistanceType,
70 ) -> Result<Option<Self>> {
71 match *USE_HNSW_SPEEDUP_INDEXING {
72 SimpleIndexStatus::Auto => {
73 if centroids.len() < 1_000_000 {
74 return Ok(None);
75 }
76 }
77 SimpleIndexStatus::Disabled => return Ok(None),
78 _ => {}
79 }
80
81 match centroids.data_type() {
82 DataType::Float32 => {
83 let fsl =
84 FixedSizeListArray::try_new_from_values(centroids.clone(), dimension as i32)?;
85 let store = FlatFloatStorage::new(fsl, distance_type);
86 Self::try_new(store).map(Some)
87 }
88 _ => Ok(None),
89 }
90 }
91
92 pub(crate) fn search(&self, query: ArrayRef) -> Result<(u32, f32)> {
93 let res = self.index.search_basic(
94 query,
95 1,
96 &HnswQueryParams {
97 ef: 15,
98 lower_bound: None,
99 upper_bound: None,
100 dist_q_c: 0.0,
101 },
102 None,
103 &self.store,
104 )?;
105 Ok((res[0].id, res[0].dist.0))
106 }
107}
108
109#[inline]
110#[allow(dead_code)]
111pub(crate) fn prefetch_arrow_array(array: &dyn Array) -> Result<()> {
112 match array.data_type() {
113 DataType::FixedSizeList(_, _) => {
114 let array = array.as_fixed_size_list();
115 return prefetch_arrow_array(array.values());
116 }
117 DataType::Float16 => {
118 let array = array.as_primitive::<Float16Type>();
119 do_prefetch(array.values().as_ptr_range())
120 }
121 DataType::Float32 => {
122 let array = array.as_primitive::<Float32Type>();
123 do_prefetch(array.values().as_ptr_range())
124 }
125 DataType::Float64 => {
126 let array = array.as_primitive::<Float64Type>();
127 do_prefetch(array.values().as_ptr_range())
128 }
129 _ => {
130 return Err(Error::invalid_input(format!(
131 "Unsupported data type for prefetch: {}",
132 array.data_type()
133 )));
134 }
135 }
136
137 Ok(())
138}
139
140#[inline]
141pub(crate) fn do_prefetch<T>(ptrs: Range<*const T>) {
142 unsafe {
145 let (ptr, end_ptr) = (ptrs.start as *const i8, ptrs.end as *const i8);
146 let mut current_ptr = ptr;
147 while current_ptr < end_ptr {
148 const CACHE_LINE_SIZE: usize = 64;
149 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
150 {
151 use core::arch::x86_64::{_MM_HINT_T0, _mm_prefetch};
152 _mm_prefetch(current_ptr, _MM_HINT_T0);
153 }
154 current_ptr = current_ptr.add(CACHE_LINE_SIZE);
155 }
156 }
157}
158
159impl From<pb::tensor::DataType> for DataType {
160 fn from(dt: pb::tensor::DataType) -> Self {
161 match dt {
162 pb::tensor::DataType::Uint8 => Self::UInt8,
163 pb::tensor::DataType::Uint16 => Self::UInt16,
164 pb::tensor::DataType::Uint32 => Self::UInt32,
165 pb::tensor::DataType::Uint64 => Self::UInt64,
166 pb::tensor::DataType::Float16 => Self::Float16,
167 pb::tensor::DataType::Float32 => Self::Float32,
168 pb::tensor::DataType::Float64 => Self::Float64,
169 pb::tensor::DataType::Bfloat16 => unimplemented!(),
170 }
171 }
172}
173
174impl TryFrom<&DataType> for pb::tensor::DataType {
175 type Error = Error;
176
177 fn try_from(dt: &DataType) -> Result<Self> {
178 match dt {
179 DataType::UInt8 => Ok(Self::Uint8),
180 DataType::UInt16 => Ok(Self::Uint16),
181 DataType::UInt32 => Ok(Self::Uint32),
182 DataType::UInt64 => Ok(Self::Uint64),
183 DataType::Float16 => Ok(Self::Float16),
184 DataType::Float32 => Ok(Self::Float32),
185 DataType::Float64 => Ok(Self::Float64),
186 _ => Err(Error::index(format!(
187 "pb tensor type not supported: {:?}",
188 dt
189 ))),
190 }
191 }
192}
193
194impl TryFrom<DataType> for pb::tensor::DataType {
195 type Error = Error;
196
197 fn try_from(dt: DataType) -> Result<Self> {
198 (&dt).try_into()
199 }
200}
201
202impl TryFrom<&FixedSizeListArray> for pb::Tensor {
203 type Error = Error;
204
205 fn try_from(array: &FixedSizeListArray) -> Result<Self> {
206 let mut tensor = Self::default();
207 tensor.data_type = pb::tensor::DataType::try_from(array.value_type())? as i32;
208 tensor.shape = vec![array.len() as u32, array.value_length() as u32];
209 let flat_array = array.values();
210 tensor.data = flat_array.into_data().buffers()[0].to_vec();
211 Ok(tensor)
212 }
213}
214
215impl TryFrom<&pb::Tensor> for FixedSizeListArray {
216 type Error = Error;
217
218 fn try_from(tensor: &Tensor) -> Result<Self> {
219 if tensor.shape.len() != 2 {
220 return Err(Error::index(format!(
221 "only accept 2-D tensor shape, got: {:?}",
222 tensor.shape
223 )));
224 }
225 let dim = tensor.shape[1] as usize;
226 let num_rows = tensor.shape[0] as usize;
227
228 let data = bytes::Bytes::from(tensor.data.clone());
229 let flat_array = bytes_to_array(
230 &DataType::from(pb::tensor::DataType::try_from(tensor.data_type).unwrap()),
231 data,
232 dim * num_rows,
233 0,
234 )?;
235
236 if flat_array.len() != dim * num_rows {
237 return Err(Error::index(format!(
238 "Tensor shape {:?} does not match to data len: {}",
239 tensor.shape,
240 flat_array.len()
241 )));
242 }
243
244 let field = Field::new("item", flat_array.data_type().clone(), true);
245 Ok(Self::try_new(
246 Arc::new(field),
247 dim as i32,
248 flat_array,
249 None,
250 )?)
251 }
252}
253
254pub fn is_finite(fsl: &FixedSizeListArray) -> BooleanArray {
260 let is_finite = fsl
261 .iter()
262 .map(|v| match v {
263 Some(v) => match v.data_type() {
264 DataType::Float16 => {
265 let v = v.as_primitive::<Float16Type>();
266 v.null_count() == 0 && v.values().iter().all(|v| v.is_finite())
267 }
268 DataType::Float32 => {
269 let v = v.as_primitive::<Float32Type>();
270 v.null_count() == 0 && v.values().iter().all(|v| v.is_finite())
271 }
272 DataType::Float64 => {
273 let v = v.as_primitive::<Float64Type>();
274 v.null_count() == 0 && v.values().iter().all(|v| v.is_finite())
275 }
276 _ => v.null_count() == 0,
277 },
278 None => false,
279 })
280 .collect::<Vec<_>>();
281 BooleanArray::from(is_finite)
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 use arrow_array::{Float16Array, Float32Array, Float64Array};
289 use half::f16;
290 use lance_arrow::FixedSizeListArrayExt;
291 use num_traits::identities::Zero;
292
293 #[test]
294 fn test_fsl_to_tensor() {
295 let fsl =
296 FixedSizeListArray::try_new_from_values(Float16Array::from(vec![f16::zero(); 20]), 5)
297 .unwrap();
298 let tensor = pb::Tensor::try_from(&fsl).unwrap();
299 assert_eq!(tensor.data_type, pb::tensor::DataType::Float16 as i32);
300 assert_eq!(tensor.shape, vec![4, 5]);
301 assert_eq!(tensor.data.len(), 20 * 2);
302
303 let fsl =
304 FixedSizeListArray::try_new_from_values(Float32Array::from(vec![0.0; 20]), 5).unwrap();
305 let tensor = pb::Tensor::try_from(&fsl).unwrap();
306 assert_eq!(tensor.data_type, pb::tensor::DataType::Float32 as i32);
307 assert_eq!(tensor.shape, vec![4, 5]);
308 assert_eq!(tensor.data.len(), 20 * 4);
309
310 let fsl =
311 FixedSizeListArray::try_new_from_values(Float64Array::from(vec![0.0; 20]), 5).unwrap();
312 let tensor = pb::Tensor::try_from(&fsl).unwrap();
313 assert_eq!(tensor.data_type, pb::tensor::DataType::Float64 as i32);
314 assert_eq!(tensor.shape, vec![4, 5]);
315 assert_eq!(tensor.data.len(), 20 * 8);
316 }
317}