From 3f3419018d90fbb7680bd67f516d451e6b89ebb2 Mon Sep 17 00:00:00 2001 From: Gatefixer <312823363+lance-gatefixer[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 01:47:39 +0000 Subject: [PATCH 1/4] fix(index): make k-means training seedable --- java/lance-jni/src/utils.rs | 1 + java/lance-jni/src/vector_trainer.rs | 1 + rust/lance-index/src/vector/ivf/builder.rs | 7 ++ rust/lance-index/src/vector/kmeans.rs | 44 ++++++++++++- rust/lance-index/src/vector/pq/builder.rs | 12 +++- rust/lance/src/index/vector.rs | 2 + rust/lance/src/index/vector/ivf.rs | 74 +++++++++++----------- rust/lance/src/index/vector/ivf/v2.rs | 4 ++ 8 files changed, 105 insertions(+), 40 deletions(-) diff --git a/java/lance-jni/src/utils.rs b/java/lance-jni/src/utils.rs index c9d2d2005b1..b5cea544d6d 100644 --- a/java/lance-jni/src/utils.rs +++ b/java/lance-jni/src/utils.rs @@ -468,6 +468,7 @@ pub fn get_vector_index_params( kmeans_redos, codebook, sample_rate, + kmeans_seed: None, }) }, )?; diff --git a/java/lance-jni/src/vector_trainer.rs b/java/lance-jni/src/vector_trainer.rs index d988aebd468..4ce707ab565 100755 --- a/java/lance-jni/src/vector_trainer.rs +++ b/java/lance-jni/src/vector_trainer.rs @@ -78,6 +78,7 @@ fn build_pq_params_from_java( kmeans_redos, codebook: None, sample_rate, + kmeans_seed: None, }) } diff --git a/rust/lance-index/src/vector/ivf/builder.rs b/rust/lance-index/src/vector/ivf/builder.rs index b8b6e0ec4cc..432b9628b9e 100644 --- a/rust/lance-index/src/vector/ivf/builder.rs +++ b/rust/lance-index/src/vector/ivf/builder.rs @@ -39,6 +39,12 @@ pub struct IvfBuildParams { pub sample_rate: usize, + /// Optional seed for k-means centroid initialization. + /// + /// `None` initializes from OS entropy, while `Some(seed)` makes centroid + /// selection reproducible for the same training data and parameters. + pub kmeans_seed: Option, + /// Optional per-step sample rate for streaming IVF kmeans training. /// /// When set, IVF training loads at most `num_partitions * streaming_sample_rate` @@ -94,6 +100,7 @@ impl Default for IvfBuildParams { centroids: None, retrain: false, sample_rate: 256, // See faiss + kmeans_seed: None, streaming_sample_rate: None, streaming_coreset_rate: None, streaming_refine_passes: 0, diff --git a/rust/lance-index/src/vector/kmeans.rs b/rust/lance-index/src/vector/kmeans.rs index 30016e92f8d..50b932f6d87 100644 --- a/rust/lance-index/src/vector/kmeans.rs +++ b/rust/lance-index/src/vector/kmeans.rs @@ -92,6 +92,12 @@ pub struct KMeansParams { /// Optional sync callback for iteration progress: (current_iteration, max_iterations). pub on_progress: Option>, + + /// Optional seed for random centroid initialization. + /// + /// `None` initializes from OS entropy, while `Some(seed)` makes centroid + /// selection reproducible for the same training data and parameters. + pub seed: Option, } impl std::fmt::Debug for KMeansParams { @@ -105,6 +111,7 @@ impl std::fmt::Debug for KMeansParams { .field("balance_factor", &self.balance_factor) .field("hierarchical_k", &self.hierarchical_k) .field("on_progress", &self.on_progress.as_ref().map(|_| "...")) + .field("seed", &self.seed) .finish() } } @@ -120,6 +127,7 @@ impl Default for KMeansParams { balance_factor: 0.0, hierarchical_k: 16, on_progress: None, + seed: None, } } } @@ -159,6 +167,12 @@ impl KMeansParams { self } + /// Set the seed used for random centroid initialization. + pub fn with_seed(mut self, seed: u64) -> Self { + self.seed = Some(seed); + self + } + /// Set the number of clusters to train in each hierarchical level. /// /// Higher would split the clusters more aggressively, which would be more accurate but slower. @@ -927,8 +941,10 @@ impl KMeans { let mut cluster_sizes = vec![0; k]; let mut adjusted_balance_factor = f32::MAX; - // TODO: use seed for Rng. - let mut rng = SmallRng::from_os_rng(); + let mut rng = match params.seed { + Some(seed) => SmallRng::seed_from_u64(seed), + None => SmallRng::from_os_rng(), + }; for redo in 1..=params.redos { let mut kmeans: Self = match ¶ms.init { KMeanInit::Random => Self::init_random::( @@ -1850,6 +1866,30 @@ mod tests { ); } + #[test] + fn test_seeded_training_is_reproducible() { + const DIM: usize = 4; + const K: usize = 8; + const NUM_ROWS: usize = 64; + + let values = Float32Array::from_iter_values( + (0..NUM_ROWS * DIM).map(|value| ((value * 37) % 101) as f32), + ); + let data = FixedSizeListArray::try_new_from_values(values, DIM as i32).unwrap(); + let train = || { + let params = KMeansParams::new(None, 10, 2, DistanceType::L2).with_seed(42); + KMeans::new_with_params(&data, K, ¶ms).unwrap() + }; + + let first = train(); + let second = train(); + assert_eq!( + first.centroids.as_primitive::().values(), + second.centroids.as_primitive::().values() + ); + assert_eq!(first.loss, second.loss); + } + #[tokio::test] async fn test_compute_membership_and_loss() { const DIM: usize = 256; diff --git a/rust/lance-index/src/vector/pq/builder.rs b/rust/lance-index/src/vector/pq/builder.rs index c267e7550e8..0886d74ef5d 100644 --- a/rust/lance-index/src/vector/pq/builder.rs +++ b/rust/lance-index/src/vector/pq/builder.rs @@ -42,6 +42,12 @@ pub struct PQBuildParams { /// Sample rate to train PQ codebook. pub sample_rate: usize, + + /// Optional seed for k-means centroid initialization. + /// + /// `None` initializes from OS entropy, while `Some(seed)` makes centroid + /// selection reproducible for the same training data and parameters. + pub kmeans_seed: Option, } impl From<&PQBuildParams> for crate::pb::vector_index_details::ProductQuantization { @@ -62,6 +68,7 @@ impl Default for PQBuildParams { kmeans_redos: 1, codebook: None, sample_rate: 256, + kmeans_seed: None, } } } @@ -148,7 +155,7 @@ impl PQBuildParams { .into_iter() .enumerate() .map(|(sub_vec_idx, sub_vec)| { - let params = KMeansParams::new( + let mut params = KMeansParams::new( self.codebook.as_ref().map(|cb| { let sub_vec_centroids = FixedSizeListArray::try_new_from_values( cb.as_fixed_size_list().values().as_primitive::().slice( @@ -164,6 +171,9 @@ impl PQBuildParams { self.kmeans_redos, distance_type, ); + if let Some(seed) = self.kmeans_seed { + params = params.with_seed(seed); + } train_kmeans::( &sub_vec, params, diff --git a/rust/lance/src/index/vector.rs b/rust/lance/src/index/vector.rs index 4a77b36a18e..7783c048976 100644 --- a/rust/lance/src/index/vector.rs +++ b/rust/lance/src/index/vector.rs @@ -1984,6 +1984,7 @@ fn derive_ivf_params(ivf_model: &IvfModel) -> IvfBuildParams { #[allow(deprecated)] retrain: false, // Don't retrain since we have centroids sample_rate: 256, // Default + kmeans_seed: None, streaming_sample_rate: None, streaming_coreset_rate: None, streaming_refine_passes: 0, @@ -2005,6 +2006,7 @@ fn derive_pq_params(pq_quantizer: &ProductQuantizer) -> PQBuildParams { kmeans_redos: 1, // Default codebook: Some(Arc::new(pq_quantizer.codebook.clone())), sample_rate: 256, // Default + kmeans_seed: None, } } diff --git a/rust/lance/src/index/vector/ivf.rs b/rust/lance/src/index/vector/ivf.rs index 18d54c5f67a..fd35c695f3f 100644 --- a/rust/lance/src/index/vector/ivf.rs +++ b/rust/lance/src/index/vector/ivf.rs @@ -3044,9 +3044,13 @@ where let _ = progress_tx.send(total); }) }; - let kmeans_params = KMeansParams::new(centroids, params.max_iters as u32, REDOS, metric_type) - .with_balance_factor(1.0) - .with_on_progress(on_progress); + let mut kmeans_params = + KMeansParams::new(centroids, params.max_iters as u32, REDOS, metric_type) + .with_balance_factor(1.0) + .with_on_progress(on_progress); + if let Some(seed) = params.kmeans_seed { + kmeans_params = kmeans_params.with_seed(seed); + } let kmeans = lance_index::vector::kmeans::train_kmeans::( data, kmeans_params, @@ -3422,6 +3426,7 @@ struct KMeansStepOptions { sample_rate: usize, max_iters: usize, on_progress: KMeansProgressCallback, + kmeans_seed: Option, } fn train_ivf_kmeans_step( @@ -3438,6 +3443,9 @@ where KMeansParams::new(centroids, options.max_iters as u32, 1, options.metric_type) .with_balance_factor(1.0) .with_on_progress(options.on_progress.clone()); + if let Some(seed) = options.kmeans_seed { + kmeans_params = kmeans_params.with_seed(seed); + } if has_centroids { // Incremental refinement already has the full centroid set. The // hierarchical trainer bootstraps a smaller tree and is only suitable @@ -3456,37 +3464,24 @@ where fn train_ivf_kmeans_step_arrow_array_no_loss( centroids: Option>, data: &FixedSizeListArray, - metric_type: MetricType, - num_partitions: usize, - sample_rate: usize, - max_iters: usize, - on_progress: Arc, + options: KMeansStepOptions, ) -> Result { - let dimension = data.value_length() as usize; let values = data.values(); - let step_options = KMeansStepOptions { - dimension, - metric_type, - num_partitions, - sample_rate, - max_iters, - on_progress, - }; - let kmeans = match (values.data_type(), metric_type) { + let kmeans = match (values.data_type(), options.metric_type) { (DataType::Float16, _) => train_ivf_kmeans_step::( centroids, values.as_primitive::(), - &step_options, + &options, )?, (DataType::Float32, _) => train_ivf_kmeans_step::( centroids, values.as_primitive::(), - &step_options, + &options, )?, (DataType::Float64, _) => train_ivf_kmeans_step::( centroids, values.as_primitive::(), - &step_options, + &options, )?, (DataType::Int8, DistanceType::L2) | (DataType::Int8, DistanceType::Dot) @@ -3495,18 +3490,18 @@ fn train_ivf_kmeans_step_arrow_array_no_loss( train_ivf_kmeans_step::( centroids, data.values().as_primitive::(), - &step_options, + &options, )? } (DataType::UInt8, DistanceType::Hamming) => train_ivf_kmeans_step::( centroids, values.as_primitive::(), - &step_options, + &options, )?, _ => Err(Error::index(format!( "KMeans: can not train data type {} with distance type: {}", values.data_type(), - metric_type + options.metric_type )))?, }; Ok(kmeans) @@ -4064,18 +4059,20 @@ fn append_local_coreset( local_k: usize, max_iters: usize, on_progress: Arc, + kmeans_seed: Option, ) -> Result<()> { let dimension = data.value_length() as usize; let sample_rate = data.len().div_ceil(local_k).max(1); - let kmeans = train_ivf_kmeans_step_arrow_array_no_loss( - None, - data, + let options = KMeansStepOptions { + dimension, metric_type, - local_k, + num_partitions: local_k, sample_rate, max_iters, on_progress, - )?; + kmeans_seed, + }; + let kmeans = train_ivf_kmeans_step_arrow_array_no_loss(None, data, options)?; let centroids = FixedSizeListArray::try_new_from_values(kmeans.centroids, dimension as i32)?; let kmeans = KMeans::with_centroids(centroids.values().clone(), dimension, metric_type, f64::MAX); @@ -4447,6 +4444,7 @@ async fn train_streaming_coreset_ivf_model( local_k, params.max_iters, on_progress.clone(), + params.kmeans_seed, )?; coreset.append(chunk_coreset); coreset.reduce_to_budget(dimension, coreset_budget); @@ -4622,15 +4620,17 @@ async fn train_streaming_ivf_model( ); } - let kmeans = train_ivf_kmeans_step_arrow_array_no_loss( - centroids.clone(), - &training_data, - mt, + let options = KMeansStepOptions { + dimension, + metric_type: mt, num_partitions, - step_sample_rate, - params.max_iters, - on_progress.clone(), - )?; + sample_rate: step_sample_rate, + max_iters: params.max_iters, + on_progress: on_progress.clone(), + kmeans_seed: params.kmeans_seed, + }; + let kmeans = + train_ivf_kmeans_step_arrow_array_no_loss(centroids.clone(), &training_data, options)?; let trained_centroids = Arc::new(FixedSizeListArray::try_new_from_values( kmeans.centroids, dimension as i32, diff --git a/rust/lance/src/index/vector/ivf/v2.rs b/rust/lance/src/index/vector/ivf/v2.rs index 33533e851f2..1f371e187fd 100644 --- a/rust/lance/src/index/vector/ivf/v2.rs +++ b/rust/lance/src/index/vector/ivf/v2.rs @@ -2591,6 +2591,7 @@ mod tests { num_bits: 4, max_iters: 2, sample_rate: 16, + kmeans_seed: Some(42), ..Default::default() } } @@ -2601,6 +2602,7 @@ mod tests { num_bits, max_iters: 2, sample_rate: 16, + kmeans_seed: Some(42), ..Default::default() } } @@ -2666,6 +2668,7 @@ mod tests { let mut ivf_params = IvfBuildParams::new(LIGHTWEIGHT_PQ_PARTITIONS); ivf_params.max_iters = 2; ivf_params.sample_rate = 16; + ivf_params.kmeans_seed = Some(42); let pq_params = lightweight_pq_params_with_bits(num_bits); let params = if use_hnsw { VectorIndexParams::with_ivf_hnsw_pq_params( @@ -5139,6 +5142,7 @@ mod tests { let mut ivf_params = IvfBuildParams::new(1); ivf_params.max_iters = 2; ivf_params.sample_rate = 16; + ivf_params.kmeans_seed = Some(42); let params = VectorIndexParams::with_ivf_hnsw_pq_params( DistanceType::Cosine, ivf_params, From cbe9bada8a32c7321fd517a13b6942b5c67be10d Mon Sep 17 00:00:00 2001 From: Gatefixer <312823363+lance-gatefixer[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 01:55:05 +0000 Subject: [PATCH 2/4] test(index): isolate seeded k-means initialization --- rust/lance-index/src/vector/kmeans.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rust/lance-index/src/vector/kmeans.rs b/rust/lance-index/src/vector/kmeans.rs index 50b932f6d87..50b8c6ebcce 100644 --- a/rust/lance-index/src/vector/kmeans.rs +++ b/rust/lance-index/src/vector/kmeans.rs @@ -1867,7 +1867,7 @@ mod tests { } #[test] - fn test_seeded_training_is_reproducible() { + fn test_seeded_initialization_is_reproducible() { const DIM: usize = 4; const K: usize = 8; const NUM_ROWS: usize = 64; @@ -1877,7 +1877,9 @@ mod tests { ); let data = FixedSizeListArray::try_new_from_values(values, DIM as i32).unwrap(); let train = || { - let params = KMeansParams::new(None, 10, 2, DistanceType::L2).with_seed(42); + // Keep this to one iteration to isolate the seeded initialization + // from floating-point ordering in later parallel reductions. + let params = KMeansParams::new(None, 1, 1, DistanceType::L2).with_seed(42); KMeans::new_with_params(&data, K, ¶ms).unwrap() }; From a1b17be292820bbb06de9d3bc084403b6f90c26b Mon Sep 17 00:00:00 2001 From: Gatefixer <312823363+lance-gatefixer[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 02:23:42 +0000 Subject: [PATCH 3/4] test(index): make IVF-PQ fixture fully deterministic --- rust/lance/src/index/vector/ivf/v2.rs | 48 +++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/rust/lance/src/index/vector/ivf/v2.rs b/rust/lance/src/index/vector/ivf/v2.rs index 1f371e187fd..ab3c84e3e20 100644 --- a/rust/lance/src/index/vector/ivf/v2.rs +++ b/rust/lance/src/index/vector/ivf/v2.rs @@ -2088,7 +2088,7 @@ mod tests { use lance_index::vector::ivf::IvfBuildParams; use lance_index::vector::kmeans::{KMeansParams, train_kmeans}; use lance_index::vector::pq::{PQBuildParams, ProductQuantizer}; - use lance_index::vector::quantizer::QuantizerMetadata; + use lance_index::vector::quantizer::{Quantizer, QuantizerMetadata}; use lance_index::vector::sq::ScalarQuantizer; use lance_index::vector::sq::builder::SQBuildParams; use lance_index::vector::{ @@ -2659,6 +2659,8 @@ mod tests { let test_dir = TempStrDir::default(); let (batch, schema) = make_seeded_vector_batch(LIGHTWEIGHT_PQ_ROWS); + let repeated_batch = batch.clone(); + let repeated_schema = schema.clone(); let vectors = batch["vector"].as_fixed_size_list().clone(); let batches = RecordBatchIterator::new(vec![Ok(batch)], schema); let mut dataset = Dataset::write(batches, test_dir.as_str(), None) @@ -2667,7 +2669,9 @@ mod tests { let mut ivf_params = IvfBuildParams::new(LIGHTWEIGHT_PQ_PARTITIONS); ivf_params.max_iters = 2; - ivf_params.sample_rate = 16; + // Scan the entire fixture so IVF training does not enter the + // OS-seeded sampling path before seeded k-means starts. + ivf_params.sample_rate = LIGHTWEIGHT_PQ_ROWS / LIGHTWEIGHT_PQ_PARTITIONS; ivf_params.kmeans_seed = Some(42); let pq_params = lightweight_pq_params_with_bits(num_bits); let params = if use_hnsw { @@ -2742,6 +2746,46 @@ mod tests { / K as f32; assert_ge!(recall, 0.5, "recall: {recall}"); + let repeated_test_dir = TempStrDir::default(); + let repeated_batches = RecordBatchIterator::new(vec![Ok(repeated_batch)], repeated_schema); + let mut repeated_dataset = + Dataset::write(repeated_batches, repeated_test_dir.as_str(), None) + .await + .unwrap(); + repeated_dataset + .create_index( + &["vector"], + IndexType::Vector, + Some(INDEX_NAME.to_owned()), + ¶ms, + true, + ) + .await + .unwrap(); + + let first_index = load_vector_index_context(&dataset, "vector", INDEX_NAME).await; + let repeated_index = + load_vector_index_context(&repeated_dataset, "vector", INDEX_NAME).await; + assert_eq!( + first_index.index.ivf_model().centroids, + repeated_index.index.ivf_model().centroids + ); + assert_eq!( + first_index.index.ivf_model().loss, + repeated_index.index.ivf_model().loss + ); + assert_eq!( + first_index.index.ivf_model().lengths, + repeated_index.index.ivf_model().lengths + ); + let Quantizer::Product(first_pq) = first_index.index.quantizer() else { + panic!("expected product quantizer"); + }; + let Quantizer::Product(repeated_pq) = repeated_index.index.quantizer() else { + panic!("expected product quantizer"); + }; + assert_eq!(first_pq.codebook, repeated_pq.codebook); + drop(dataset); let reopened = Dataset::open(test_dir.as_str()).await.unwrap(); let reopened_stats: serde_json::Value = From d692d5abd3f2fd0c88cbb4e99b747d81952ee3c7 Mon Sep 17 00:00:00 2001 From: Gatefixer <312823363+lance-gatefixer[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2026 03:00:34 +0000 Subject: [PATCH 4/4] test(index): stabilize legacy multivector PQ recall --- rust/lance/src/index/vector/ivf/v2.rs | 31 +++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/rust/lance/src/index/vector/ivf/v2.rs b/rust/lance/src/index/vector/ivf/v2.rs index ce6e68e87fa..0dde25f67ec 100644 --- a/rust/lance/src/index/vector/ivf/v2.rs +++ b/rust/lance/src/index/vector/ivf/v2.rs @@ -2113,6 +2113,7 @@ mod tests { const NUM_ROWS: usize = 512; const DIM: usize = 32; + const MULTIVEC_VECTORS_PER_ROW: usize = 3; // 8-bit PQ needs at least 256 training vectors; 320 leaves a stable margin // while 20 neighbors provide a useful recall oracle. const PQ_MATRIX_NUM_ROWS: usize = 320; @@ -2328,13 +2329,12 @@ mod tests { where T::Native: SampleUniform, { - const VECTOR_NUM_PER_ROW: usize = 3; let start_id = start_id.unwrap_or(0); let ids = Arc::new(UInt64Array::from_iter_values( start_id..start_id + num_rows as u64, )); let total_floats = match is_multivector { - true => num_rows * VECTOR_NUM_PER_ROW * DIM, + true => num_rows * MULTIVEC_VECTORS_PER_ROW * DIM, false => num_rows * DIM, }; let vectors = generate_random_array_with_range::(total_floats, range); @@ -2358,7 +2358,7 @@ mod tests { )); let array = Arc::new(ListArray::new( vector_field, - OffsetBuffer::from_lengths(std::iter::repeat_n(VECTOR_NUM_PER_ROW, num_rows)), + OffsetBuffer::from_lengths(std::iter::repeat_n(MULTIVEC_VECTORS_PER_ROW, num_rows)), Arc::new(fsl), None, )); @@ -4461,15 +4461,18 @@ mod tests { nlist: usize, distance_type: DistanceType, version: IndexFileVersion, + ivf_sample_rate: usize, ) -> VectorIndexParams { let mut ivf_params = IvfBuildParams::new(nlist); ivf_params.max_iters = 2; - ivf_params.sample_rate = PQ_MATRIX_NUM_ROWS; + ivf_params.sample_rate = ivf_sample_rate; + ivf_params.kmeans_seed = Some(42); let pq_params = PQBuildParams { num_sub_vectors: 4, num_bits: 8, max_iters: 2, sample_rate: 1, + kmeans_seed: Some(42), ..Default::default() }; let mut params = @@ -4492,7 +4495,7 @@ mod tests { let query = batch["vector"].as_fixed_size_list().value(0); let batches = RecordBatchIterator::new(vec![Ok(batch)], schema); let mut dataset = Dataset::write(batches, test_uri, None).await.unwrap(); - let params = pq_matrix_params(nlist, distance_type, version.clone()); + let params = pq_matrix_params(nlist, distance_type, version.clone(), PQ_MATRIX_NUM_ROWS); dataset .create_index( &["vector"], @@ -4880,7 +4883,7 @@ mod tests { #[case::v3(IndexFileVersion::V3)] #[tokio::test] async fn test_ivf_pq_distance_range(#[case] version: IndexFileVersion) { - let params = pq_matrix_params(1, DistanceType::L2, version); + let params = pq_matrix_params(1, DistanceType::L2, version, PQ_MATRIX_NUM_ROWS); test_distance_range(Some(params), 1).await; } @@ -4897,7 +4900,7 @@ mod tests { let mut dataset = Dataset::write(batches, test_dir.as_str(), None) .await .unwrap(); - let params = pq_matrix_params(1, DistanceType::L2, version); + let params = pq_matrix_params(1, DistanceType::L2, version, PQ_MATRIX_NUM_ROWS); dataset .create_index(&["vector"], IndexType::Vector, None, ¶ms, true) .await @@ -4907,13 +4910,23 @@ mod tests { #[tokio::test] async fn test_legacy_ivf_pq_cosine_multivec_smoke() { - let params = pq_matrix_params(1, DistanceType::Cosine, IndexFileVersion::Legacy); + let params = pq_matrix_params( + 1, + DistanceType::Cosine, + IndexFileVersion::Legacy, + NUM_ROWS * MULTIVEC_VECTORS_PER_ROW, + ); test_index_multivec_impl::(params, 1, 0.5, 0.0..1.0).await; } #[tokio::test] async fn test_ivf_pq_delete_all_rows_lifecycle() { - let params = pq_matrix_params(1, DistanceType::L2, IndexFileVersion::V3); + let params = pq_matrix_params( + 1, + DistanceType::L2, + IndexFileVersion::V3, + PQ_MATRIX_NUM_ROWS, + ); test_delete_all_rows(params).await; }