Skip to content

[flat index] Flat Search Interface#983

Merged
arkrishn94 merged 34 commits into
mainfrom
u/adkrishnan/flat-index
May 22, 2026
Merged

[flat index] Flat Search Interface#983
arkrishn94 merged 34 commits into
mainfrom
u/adkrishnan/flat-index

Conversation

@arkrishn94
Copy link
Copy Markdown
Contributor

@arkrishn94 arkrishn94 commented Apr 28, 2026

This PR introduces a trait interface and a light index to support brute-force search for providers that can be used as/are a flat-index. There is an associated RFC that walks through the interface and associated implementation in diskann as a new flat module.

Rendered RFC link.

Motivation

The repo has no first-class surface for brute-force search. This PR adds a small trait hierarchy that gives flat search the same provider-agnostic shape that graph search has, so any backend (in-memory, quantized, disk, remote) can plug in once and reuse a shared algorithm.

Traits (flat/strategy.rs)

DistancesUnordered<C> — the single trait a backend must implement. Fuses iteration and scoring into one method: the implementation drives a full scan, scoring each element with a precomputed query computer C, and invokes a callback with (id, distance) pairs. Key associated types:

  • ElementRef<'a> -- the reference shape C scores against.
  • Id -- the id type yielded to the callback (decoupled from HasId so visitors can yield any id shape).
  • C : for<'a> PreprocessedDistanceFunction<Self::ElementRef<'a>, f32> -- the precomputer query computer.
pub trait DistancesUnordered<C>: Send + Sync
where
    C: for<'a> PreprocessedDistanceFunction<Self::ElementRef<'a>, f32>,
{
    type ElementRef<'a>;
    type Id;
    type Error: ToRanked + Debug + Send + Sync + 'static;

    fn distances_unordered<F>(
        &mut self,
        computer: &C,
        f: F,
    ) -> impl SendFuture<Result<(), Self::Error>>
    where
        F: Send + FnMut(Self::Id, f32);
}

SearchStrategy<P, T> — factory that creates a DistancesUnordered visitor from a provider + context, and builds the per-query computer. Mirrors the graph-side strategy pattern. Two fallible methods:

  • create_visitor — borrows provider + context, returns a Visitor
  • build_query_computer — preprocesses the query T into a QueryComputer
pub trait SearchStrategy<P, T>: Send + Sync
where
    P: DataProvider,
{
    type ElementRef<'a>;
    type Id;
    type QueryComputer: for<'a> PreprocessedDistanceFunction<Self::ElementRef<'a>, f32>;
    type QueryComputerError: StandardError;
    type Visitor<'a>: for<'b> DistancesUnordered<
            Self::QueryComputer,
            ElementRef<'b> = Self::ElementRef<'b>,
            Id = Self::Id,
        >;
    type Error: StandardError;

    fn create_visitor<'a>(&'a self, provider: &'a P, context: &'a P::Context)
        -> Result<Self::Visitor<'a>, Self::Error>;

    fn build_query_computer(&self, query: T)
        -> Result<Self::QueryComputer, Self::QueryComputerError>;
}

Index (flat/index.rs)

FlatIndex<P> — thin 'static wrapper around a DataProvider. Currently we have implemented the naive kNN search algorithm for the flat index. knn_search asks the strategy for a visitor, builds the query computer, drives distances_unordered through a priority queue, and writes results via SearchPostProcess.

Test infrastructure (flat/test/)

A self-contained test provider with dimension-validated Strategy, transient-error injection, and a KnnOracleRun harness that compares knn_search results against a brute-force reference with baseline caching for regression detection.

Future work

  • Post-processingknn_search currently uses the graph-side SearchPostProcess trait to write results into the output buffer. Simplify the DataProvider contract for graph search #1067 will introduce a flat-specific post-processing step that decouples flat search from the graph module's output machinery.
  • Vector ids - The vector id over which DistancesUnordered acts is an associated type, instead of the InternalId of the provider. This is due to overly restrictive trait bounds on VectorId trait. We plan on relaxing this allowing for more generic id types.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 28, 2026

Codecov Report

❌ Patch coverage is 89.75694% with 59 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.45%. Comparing base (fd37402) to head (2f27864).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
diskann/src/flat/test/provider.rs 73.14% 47 Missing ⚠️
diskann/src/flat/test/cases/flat_knn_search.rs 94.11% 5 Missing ⚠️
diskann/src/flat/strategy.rs 96.89% 4 Missing ⚠️
diskann/src/flat/index.rs 97.47% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #983      +/-   ##
==========================================
- Coverage   89.45%   89.45%   -0.01%     
==========================================
  Files         458      463       +5     
  Lines       85398    85974     +576     
==========================================
+ Hits        76395    76909     +514     
- Misses       9003     9065      +62     
Flag Coverage Δ
miri 89.45% <89.75%> (-0.01%) ⬇️
unittests 89.08% <89.75%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
diskann/src/flat/test/harness.rs 100.00% <100.00%> (ø)
diskann/src/flat/index.rs 97.47% <97.47%> (ø)
diskann/src/flat/strategy.rs 96.89% <96.89%> (ø)
diskann/src/flat/test/cases/flat_knn_search.rs 94.11% <94.11%> (ø)
diskann/src/flat/test/provider.rs 73.14% <73.14%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@arkrishn94 arkrishn94 marked this pull request as ready for review April 28, 2026 16:02
@arkrishn94 arkrishn94 requested review from a team and Copilot April 28, 2026 16:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an RFC plus an initial “flat” (sequential scan) search surface in diskann, analogous to the existing graph/random-access search pipeline built around DataProvider/Accessor.

Changes:

  • Added an RFC describing the flat iterator/strategy/index abstraction and trade-offs.
  • Added a new diskann::flat module with FlatIterator, FlatSearchStrategy, FlatIndex::knn_search, and FlatPostProcess (+ CopyFlatIds).
  • Exported the new flat module from the crate root.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
rfcs/00983-flat-search.md RFC describing the design for sequential (“flat”) index search APIs.
diskann/src/lib.rs Exposes the new flat module publicly.
diskann/src/flat/mod.rs New module root + re-exports for the flat search surface.
diskann/src/flat/iterator.rs Defines the async lending iterator primitive FlatIterator.
diskann/src/flat/strategy.rs Defines FlatSearchStrategy to create per-query iterators and query computers.
diskann/src/flat/index.rs Implements FlatIndex and the brute-force knn_search scan algorithm.
diskann/src/flat/post_process.rs Defines FlatPostProcess and a basic CopyFlatIds post-processor.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread diskann/src/flat/index.rs Outdated
Comment thread diskann/src/flat/post_process.rs Outdated
Comment thread rfcs/00983-flat-search.md
Comment thread diskann/src/flat/strategy.rs Outdated
Comment thread diskann/src/flat/index.rs Outdated
Comment thread diskann/src/flat/index.rs
Comment thread rfcs/00983-flat-search.md
Comment thread rfcs/00983-flat-search.md Outdated
Comment thread rfcs/00983-flat-search.md Outdated
@arkrishn94 arkrishn94 changed the title [RFC] Flat Index Search [flat index] Flat Search Interface Apr 29, 2026
Comment thread diskann/src/flat/index.rs
Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/lib.rs
Copy link
Copy Markdown
Contributor

@hildebrandmw hildebrandmw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Aditya. Left a few general comments with some ideas on how we might improve our code sharing. In general, I'm not a fan of prefixing everything with Flat. We already have the flat module so flat::SearchStrategy reads fine to me as opposed to flat::FlatSearchStrategy, which is a little redundant.

Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/flat/post_process.rs Outdated
Comment thread diskann/src/flat/strategy.rs Outdated
Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/flat/index.rs Outdated
Comment thread diskann/src/flat/index.rs Outdated
Comment thread diskann/src/flat/index.rs Outdated
Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/flat/mod.rs
Comment thread rfcs/00983-flat-search.md
Comment thread rfcs/00983-flat-search.md Outdated
Comment thread diskann/src/flat/mod.rs Outdated
Comment thread diskann/src/flat/mod.rs Outdated
Comment thread diskann/src/flat/post_process.rs Outdated
@arrayka arrayka linked an issue May 5, 2026 that may be closed by this pull request
Comment thread diskann/src/flat/mod.rs Outdated
Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/flat/index.rs Outdated
Comment thread diskann/src/graph/test/provider.rs Outdated
Comment thread diskann/src/flat/test/harness.rs Outdated
Comment thread diskann/src/flat/index.rs Outdated
Comment thread diskann/src/flat/test/provider.rs Outdated
Comment thread diskann/src/flat/test/provider.rs Outdated
Alex Razumov (from Dev Box) and others added 5 commits May 11, 2026 17:09
Copy link
Copy Markdown
Contributor

@hildebrandmw hildebrandmw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Aditya,

Here's my take: The new HasElementRef and DistancesUnordered (the one in provider, not flat) add a hefty boilerplate burden to users of the graph index. Especially in light of something like #1067 which pushes in the other direction and while that PR is still experimental, I'm getting more convinced is the right API for the graph index. This puts us in kind of an awkward place. If we get this in, users consuming a new version will have churn for these little misc traits that will then need to be undone by #1067 (or some spiritual successor) which is not great.

If you want to start programming against this API, what if you introduce your own temporary TemporaryBuildQueryComputer (I believe this is the biggest reason why this PR needs to add HasElementRef and DistancesUnrodered) and we hold off on post processing for a short period of time to bottom out on #1067. If the latter doesn't pan out, then we can continue with these changes and accept it. Otherwise, the flat index implementations (which presumably will be less numerous than the current graph implementations) can be brought inline with the new trait organization and we'll cause less overall churn for our users.

I also remain unconvinced that the Iterator is any simpler than just implementing the new DistancesUnordered trait directly.

Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/flat/iterator.rs Outdated
Comment thread diskann/src/flat/test/provider.rs Outdated
Comment thread diskann/src/flat/test/provider.rs Outdated
Comment thread diskann/src/flat/strategy.rs Outdated
Comment thread diskann/src/flat/strategy.rs Outdated
Comment thread diskann/src/flat/strategy.rs Outdated
Comment thread diskann/src/provider.rs Outdated
@arkrishn94
Copy link
Copy Markdown
Contributor Author

arkrishn94 commented May 15, 2026

If you want to start programming against this API, what if you introduce your own temporary TemporaryBuildQueryComputer (I believe this is the biggest reason why this PR needs to add HasElementRef and DistancesUnrodered) and we hold off on post processing for a short period of time to bottom out on #1067. If the latter doesn't pan out, then we can continue with these changes and accept it. Otherwise, the flat index implementations (which presumably will be less numerous than the current graph implementations) can be brought inline with the new trait organization and we'll cause less overall churn for our users.

Thanks for the feedback and thorough review @hildebrandmw. To make sure I understand, are you suggesting we temporarily create a disjoint trait structure for the flat index (avoiding supporting post-processing) and then once #1067 settles down, we can re-evaluate how much shared surface we can have for these two indexes (both on the building/construction side and the post-processing)?

@hildebrandmw
Copy link
Copy Markdown
Contributor

If you want to start programming against this API, what if you introduce your own temporary TemporaryBuildQueryComputer (I believe this is the biggest reason why this PR needs to add HasElementRef and DistancesUnrodered) and we hold off on post processing for a short period of time to bottom out on #1067. If the latter doesn't pan out, then we can continue with these changes and accept it. Otherwise, the flat index implementations (which presumably will be less numerous than the current graph implementations) can be brought inline with the new trait organization and we'll cause less overall churn for our users.

Thanks for the feedback and thorough review @hildebrandmw. To make sure I understand, are you suggesting we temporarily create a disjoint trait structure for the flat index (avoiding supporting post-processing) and then once #1067 settles down, we can re-evaluate how much shared surface we can have for these two indexes (both on the building/construction side and the post-processing)?

Yeah, that's what I'm proposing. Something to minimize code churn, or at least defer it temporarily until we know that we aren't going to introduce these intermediate traits (HasElementRef + provider::DistancesUnordered) just to remove them again immediately.

hildebrandmw added a commit that referenced this pull request May 16, 2026
Remove `flat_search` from `DiskANNIndex` and the `IdIterator` trait from
`diskann`. Since the only caller was from `diskann-disk`, add a new
`flat_search` inherent method to `DiskIndexSearcher`.

The flat search method is not compatible with the experimental direction
in #1067 and with #983 on the horizon, this is safe to move for now.
Copy link
Copy Markdown
Contributor

@hildebrandmw hildebrandmw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your patience Aditya!

@arkrishn94 arkrishn94 enabled auto-merge (squash) May 22, 2026 16:29
@arkrishn94 arkrishn94 merged commit e3139b4 into main May 22, 2026
24 checks passed
@arkrishn94 arkrishn94 deleted the u/adkrishnan/flat-index branch May 22, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create RFC and initial implementation for flat-index search support

6 participants