Remove k_value from Knn#1151
Conversation
There was a problem hiding this comment.
Pull request overview
This PR removes the unused k_value field from diskann::graph::search::Knn, shifting k ownership to the output buffer (and other callers that actually need k). To preserve k where it’s still required (e.g., diversity search, benchmark sizing/validation), it introduces dedicated k-carrying parameter structures in the appropriate layers.
Changes:
- Remove
k_valueand related validation/errors fromdiskann::graph::search::Knnand update affected graph tests/call sites shown here. - Add
original_k_valueto diversity search params and validatekfields asNonZeroUsize. - Introduce
diskann-benchmark-core::search::graph::KnnWrapperto keepkfor validation and sizing while using the updateddiskann::graph::search::Knn.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| diskann/src/graph/test/cases/multihop.rs | Update multihop tests to construct Knn without k. |
| diskann/src/graph/test/cases/inline.rs | Update inline-filter tests to construct Knn without k. |
| diskann/src/graph/test/cases/grid_search.rs | Update grid search test to size outputs using a local k_value instead of params.k_value(). |
| diskann/src/graph/test/cases/grid_insert.rs | Update grid insert test to size outputs using a local k_value instead of params.k_value(). |
| diskann/src/graph/search/knn_search.rs | Remove k_value from Knn, simplify constructor/validation accordingly. |
| diskann/src/graph/search/diverse_search.rs | Route diversity queue construction through new diversity params’ original_k_value. |
| diskann/src/graph/misc.rs | Add validated DiverseSearchParams fields (NonZeroUsize) and introduce DiverseSearchError. |
| diskann-disk/src/search/provider/disk_provider.rs | Update disk provider to construct Knn with the new (l, beam_width) signature. |
| diskann-benchmark/src/backend/index/search/knn.rs | Switch benchmark runs from diskann::Knn to KnnWrapper. |
| diskann-benchmark/src/backend/index/result.rs | Update result reporting to read l from KnnWrapper.knn. |
| diskann-benchmark-core/src/search/graph/multihop.rs | Update benchmark-core multihop search to accept KnnWrapper. |
| diskann-benchmark-core/src/search/graph/mod.rs | Re-export KnnWrapper. |
| diskann-benchmark-core/src/search/graph/knn.rs | Add KnnWrapper and update benchmark KNN search to use it. |
| diskann-benchmark-core/src/search/graph/inline.rs | Update benchmark-core inline search to accept KnnWrapper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Returns an error if `l_value` is zero, | ||
| /// or if `beam_width` is `Some(0)`. | ||
| pub fn new(l_value: usize, beam_width: Option<usize>) -> Result<Self, KnnSearchError> { | ||
| let l_value = NonZeroUsize::new(l_value).ok_or(KnnSearchError::LZero)?; |
| let strategy = self.search_strategy(vector_filter); | ||
| let timer = Instant::now(); | ||
| let k = k_value; | ||
| let l = search_list_size as usize; | ||
| let stats = if is_flat_search { |
| pub fn new( | ||
| diverse_attribute_id: usize, | ||
| diverse_results_k: usize, | ||
| original_k_value: usize, | ||
| attribute_provider: std::sync::Arc<P>, | ||
| ) -> Self { | ||
| Self { | ||
| ) -> Result<Self, DiverseSearchError> { |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1151 +/- ##
==========================================
- Coverage 90.54% 89.46% -1.09%
==========================================
Files 487 487
Lines 92370 92103 -267
==========================================
- Hits 83640 82397 -1243
- Misses 8730 9706 +976
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
hildebrandmw
left a comment
There was a problem hiding this comment.
Thanks Magdalen - I support this movement. Just a few requests on my end.
| pub struct KnnWrapper { | ||
| k_value: NonZeroUsize, | ||
| pub knn: graph::search::Knn, | ||
| } |
There was a problem hiding this comment.
Can we change this to
pub struct KnnParams {
num_neighbors: NonZeroUsize,
knn: graph::search::Knn,
}
impl KnnParams {
pub fn new(num_neighbors: NonZeroUsize, knn: graph::search::Knn) -> Result<Self, Error>;
pub fn num_neighbors(&self) -> NonZeroUsize;
pub fn knn(&self) -> &graph::search::Knn; Reasons:
- A
pubfield forknnundoes the validation check in the constructor. - Taking
graph::search::Knndirectly allows us to use a configurable beam width and simplifies the error. num_neighborsis IMO slightly clearer.
There was a problem hiding this comment.
Alternatively, the name Knn could work too since it's in a separate module, but in this case I think KnnParams is a little less overloaded. Maybe we could call it Params and access it as knn::Params? Any one of these works, but I'm not a fan of KnnWrapper.
| pub fn new( | ||
| diverse_attribute_id: usize, | ||
| diverse_results_k: usize, | ||
| original_k_value: usize, |
There was a problem hiding this comment.
Bikeshed: maybe total_k_value or total_nearest_neighbors instead of original_k_value?
| } | ||
|
|
||
| #[cfg(feature = "experimental_diversity_search")] | ||
| use imports::*; |
There was a problem hiding this comment.
This is an ... odd way of structuring this. This would be a lot cleaner if the parameters were grouped with the implementation of experimental_diversity_search rather than spread around. Can we do that shuffle in this PR?
| diskann::ANNErrorKind::IndexError, | ||
| "search list size must be at least as large as the number of results requested", | ||
| )); | ||
| } |
There was a problem hiding this comment.
If we're going to throw an error here - does it make sense to move this to be the first check that happens? That way, all code-paths get to benefit and we can bail earlier if something looks wrong?
| pub(super) use crate::{ANNError, ANNErrorKind}; | ||
| pub(super) use std::num::NonZeroUsize; | ||
| pub(super) use thiserror::Error; | ||
| } |
There was a problem hiding this comment.
A request: About 90% of the awkwardness with this import is because DiverseSearchParams doesn't belong here - it belongs in the diverse_search module. Do you mind moving it there? Alternatively, I can open a mini-PR to do that.
| pub struct KnnWrapper { | ||
| k_value: NonZeroUsize, | ||
| pub knn: graph::search::Knn, | ||
| } |
There was a problem hiding this comment.
Alternatively, the name Knn could work too since it's in a separate module, but in this case I think KnnParams is a little less overloaded. Maybe we could call it Params and access it as knn::Params? Any one of these works, but I'm not a fan of KnnWrapper.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
diskann-garnet/src/lib.rs:714
- Unresolved merge conflict markers (<<<<<<< / ======= / >>>>>>>) and inconsistent variable names leave this function uncompilable. Resolve by keeping a single
knn_paramsbinding using the updatedKnn::new(l_value, beam_width)signature.
<<<<<<< HEAD
let params = match search::Knn::new(search_exploration_factor as usize, None) {
Ok(params) => params,
=======
let knn_params = match search::Knn::new(
| <<<<<<< HEAD | ||
| let params = match search::Knn::new(search_exploration_factor as usize, None) { | ||
| ======= | ||
| let knn_params = match search::Knn::new( | ||
| output_distances_len, | ||
| search_exploration_factor as usize, | ||
| None, | ||
| ) { | ||
| >>>>>>> 6caca35ddcfbacb2de38e57b7b53b7a7a95e1cf0 | ||
| Ok(params) => params, | ||
| Err(_) => return -1, | ||
| }; |
| let attribute_provider = self.diverse_params.attribute_provider.clone(); | ||
| let diverse_queue = DiverseNeighborQueue::new( | ||
| self.inner.l_value().get(), | ||
| self.inner.k_value(), | ||
| self.diverse_params.diverse_results_k, | ||
| self.diverse_params.total_k_value, | ||
| self.diverse_params.diverse_results_k.get(), |
| // #[cfg(feature = "experimental_diversity_search")] | ||
| // pub use misc::DiverseSearchParams; |
| if search_list_size < return_list_size { | ||
| return Err(ANNError::message( | ||
| diskann::ANNErrorKind::IndexError, | ||
| "search list size must be at least as large as the number of results requested", | ||
| )); | ||
| } |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
diskann/src/graph/search/diverse_search.rs:85
- Add a parameter check to reject
diverse_results_k > total_k_valueso diverse search cannot be constructed with an impossible target result count.
let diverse_results_k =
NonZeroUsize::new(diverse_results_k).ok_or(DiverseSearchError::DiverseKZero)?;
let total_k_value =
NonZeroUsize::new(total_k_value).ok_or(DiverseSearchError::TotalKZero)?;
| let knn_params = match search::Knn::new( | ||
| output_distances_len, | ||
| search_exploration_factor as usize, | ||
| None, | ||
| ) { |
| let knn_params = match search::Knn::new( | ||
| output_distances_len, | ||
| search_exploration_factor as usize, | ||
| None, | ||
| ) { |
| pub enum DiverseSearchError { | ||
| #[error("total k_value cannot be zero")] | ||
| TotalKZero, | ||
| #[error("diverse k_value cannot be zero")] | ||
| DiverseKZero, | ||
| } |
| if search_list_size < return_list_size { | ||
| return Err(ANNError::message( | ||
| diskann::ANNErrorKind::IndexError, | ||
| "search list size must be at least as large as the number of results requested", | ||
| )); | ||
| } |
When preparing the inline filter search PR I noticed that the
k_valueinKnnis actually not used anymore, since the output buffer is treated as the source of truth fork. This PR removes that field. In order to do this, since diversity search made use of it, we add a field for it specifically in diversity search, and construct a wrapper struct forKnnin diskann-benchmark-core for validation of parameters and use in size hinting. This moves thekparameter out of places it is not used and directly into the places where it is used.