Skip to content

Tags: fastio/DiskANN

Tags

diskann-garnet-v2.0.1

Toggle diskann-garnet-v2.0.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[diskann-garnet] Fix handling of missing quant vectors during delete() (

microsoft#1130)

Deleting an ID from the index removes several different keys associated
with a vector: the internal and external ID mappings, the vector and
quant vector data, the neighbor data, and the attributes.

For each of these associated keys, we returned an error from `delete()`
whenever the key was not found, but for attributes and quant vectors,
these errors are benign since it is normal for those keys to be missing.
It is cheaper to attempt to delete a key than to check whether it exists
and then delete it if it does.

A corresponding test for delete is added, although other tests would
have also caught this had the `Store` test storage utility not
unconditionally trued true for deletes. It now better models Garnet's
behavior where deletes to non-existent keys returns false.

Co-authored-by: Jack Moffitt <jackmoffitt@microsoft.com>

diskann-garnet-v2.0.0

Toggle diskann-garnet-v2.0.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[diskann-garnet] Implement BIN and Q8 quantizers (microsoft#1050)

This implements the `BIN` and `Q8` quantizers for diskann-garnet.

This PR includes the following:
- Vectors are now stored as `Poly<[u8], AlignOfEight>` instead of
`Vec<T>`. This means that the same type can be used for both quantized
and full precision vectors.
- A new `GarnetQuantizer` trait is inroduced which allows us to
type-erase the different quantizers instead of parameterizing the
index/provider types.
- Two new FFI calls were added to diskann-garnet. `insert()` now returns
a success flag which can also signal that an associated quantizer is
ready for training. `build_quant_table()` is called by Garnet
asynchronously after `insert()` signals readiness for training to build
quantization tables, and `backfill_quant_vectors` is called when
training is complete to quantize previously inserted vectors. These new
calls allow Garnet to control the training and backfill threads.
- The accessor is now called `DynamicAccessor` because it dynamically
switches between full precision only and quantized only operation
depending on availability and readiness of the associated quantizer.
- A new `visit_used()` call was added to the FSM to facilitate walking
all stored vectors.
- The FSM is now lockable, which prevents it reusing IDs during
quantization backfill. This means once backfilling starts, new inserts
will be assigned IDs above any that need quantization.
- vectorset now supports quantization and distance metric settings, and
a concurrency bug with multi-threaded inserts was fixed which caused IDs
to be assigned incorrectly.

Disk persistence will be done in a follow along with more extensive
testing.

Because of the FFI changes, the version has been bumped to 2.0.0.

v0.53.0

Toggle v0.53.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Bump version to 0.53.0 (microsoft#1111)

# DiskANN v0.53.0 Release Notes

## Breaking Changes

An AI generated, human reviewed list of changes is summarized below.

### Paged search overhauled — channel-based API
([microsoft#1078](microsoft#1078))

`PagedSearchState` and its `'static`-bound pause/resume model have been
replaced with an async, channel-based interface. The recommended way to
drive paged search is now via a `tokio::sync::mpsc` channel, with the
searcher embedded in an otherwise-`'static` future. See the [rendered
RFC](https://github.com/microsoft/DiskANN/blob/main/rfcs/01078-paged-search.md)
for the new shape. Callers wired against `PagedSearchState` must migrate
to the channel API.

Users of paged search via `wrapped_async::DiskANNIndex` that know their
inner futures will never suspend can use the new
`wrapped_async::DiskANNIndex::paged_search_no_await`; this will
efficiently run paged searches with minimal synchronization overhead.

### `DiskANNIndex::flat_search` removed
([microsoft#1076](microsoft#1076))

`DiskANNIndex::flat_search` and the `IdIterator` trait have been removed
from the `diskann` crate. Equivalent functionality lives on the new
inherent method `DiskIndexSearcher::flat_search` in `diskann-disk`. This
unblocks the experimental directions in microsoft#1067 and microsoft#983.

```rust
// Before
diskann_index.flat_search(query, ...)?;

// After
disk_index_searcher.flat_search(query, ...).await?;
```

### `DiskIndexSearcher::flat_search` now batched
([microsoft#1097](microsoft#1097))

The new `DiskIndexSearcher::flat_search` uses the bulk `pq_distances`
path instead of one-vector-at-a-time `Accessor::build_query_computer` +
`evaluate_similarity`. Downstream behavior is equivalent but tighter
resource bounds apply.

### `centroid` removed from PQ interfaces
([microsoft#1010](microsoft#1010))

The dataset-centroid argument has been removed from `FixedChunkPQTable`
construction, `populate`, and most other PQ APIs. The shift only ever
worked for L2 distance and was silently ignored for inner-product /
cosine, so passing it was a footgun. When an L2 shift is required, fold
it into the PQ pivots instead (the library now does this internally).

```rust
// Before
let table = FixedChunkPQTable::new(.., centroid, ..);

// After — drop the centroid argument
let table = FixedChunkPQTable::new(.., ..);
```

### Flat search interface
([microsoft#983](microsoft#983))

A new `flat` module in `diskann` adds a provider-agnostic brute-force
search surface, mirroring the shape of graph search. Backends implement
a single trait, `DistancesUnordered<C>` (in `flat/strategy.rs`), which
fuses iteration and distance computation, allowing any backend
(in-memory, quantized, disk, remote) to plug into a shared algorithm.
See the [rendered
RFC](https://github.com/microsoft/DiskANN/blob/main/rfcs/00983-flat-search.md).
This is additive but is the new canonical surface — direct ad-hoc
flat-search call sites should migrate.

### `bf_tree` extracted into `diskann-bftree` crate
([microsoft#1020](microsoft#1020))

The bf_tree provider has been moved out of `diskann-providers`
(previously at
`diskann-providers/src/model/graph/provider/async_/bf_tree/`) into a new
standalone `diskann-bftree` crate. Along with the move:

- Switched from PQ to spherical quantization.
- Dropped dependencies on `DeletionCheck`, `AsDeletionCheck`, and
`RemoveDeletedIdsAndCopy`.
- Simplified generics.

Consumers must update their `Cargo.toml` to depend on `diskann-bftree`
and update import paths.

### `direct_distance_impl` and `inner_product_raw` re-exposed
([microsoft#1081](microsoft#1081))

`direct_distance_impl` (free function) and
`FixedChunkPQTable::inner_product_raw` are `pub` again after being
privatized in microsoft#1044. Restored to unblock a downstream user. Not breaking
in the typical direction — this restores previously available API
surface.

### MinMax `recompress` takes a grid-scale parameter
([microsoft#1109](microsoft#1109))

The MinMax `recompress` API now accepts a grid-scale parameter. 

## New Features

- SIMD-optimized L2-squared norm
([microsoft#1107](microsoft#1107))
- Significantly faster bitmap computation
([microsoft#1099](microsoft#1099))
- Large speedup on the bitmap construction path used by filtered search.
- LLVM IR bloat regression check in CI
([microsoft#1083](microsoft#1083))
- CI now flags regressions in generated LLVM IR size, helping catch
unintended monomorphization blow-ups.
- Recall computation fix for under-k groundtruth
([microsoft#1069](microsoft#1069))

## Merged PRs

* Revise README for DiskANN3 by @harsha-simhadri in
microsoft#1046
* [CI] Try to fix publishing step by @hildebrandmw in
microsoft#1057
* [benchmark] Remove `DispatchRule` by @hildebrandmw in
microsoft#1064
* [benchmark] Automatic Input Registration by @hildebrandmw in
microsoft#1066
* Remove centroid from most PQ interfaces by @hildebrandmw in
microsoft#1010
* [diskann/disk] Remove `flat_search` from `DiskANNIndex` by
@hildebrandmw in microsoft#1076
* macos build and miri check to nightly by @harsha-simhadri in
microsoft#1058
* [API] Make some methods public again by @hildebrandmw in
microsoft#1081
* [benchmark] Simply `Inputs` more by @hildebrandmw in
microsoft#1077
* Turn on stack protection for the diskann-garnet NuGet build by
@jackmoffitt in microsoft#1082
* Fix options for diskann-garnet nuget pipeline by @jackmoffitt in
microsoft#1091
* [CI] add LLVM IR bloat regression check by @arazumov in
microsoft#1083
* Bump openssl from 0.10.79 to 0.10.80 by @dependabot[bot] in
microsoft#1093
* [Disk CI benchmarks] Use 1ES.Pool=diskann-github by @arazumov in
microsoft#869
* Fix recall computation for fewer than k groundtruth results by
@magdalendobson in microsoft#1069
* bf_tree migration away from diskann-providers by @JordanMaples in
microsoft#1020
* [RFC/diskann] Overhaul paged search by @hildebrandmw in
microsoft#1078
* Remove unsafe code from compute_vec_l2sq by @arazumov in
microsoft#1094
* Remove direct accessor call in `diskann-garnet` by @hildebrandmw in
microsoft#1098
* Refactor `DiskIndexSearcher::flat_search` to use batching by
@hildebrandmw in microsoft#1097
* [flat index] Flat Search Interface by @arkrishn94 in
microsoft#983
* migrating multi-hop tests from diskann-providers to diskann by
@JordanMaples in microsoft#928
* Significantly speed up bitmap computation by @magdalendobson in
microsoft#1099
* `compute_vecs_l2sq`: Replace scalar L2 Squared norm with
SIMD-optimized FastL2NormSquared by @arazumov in
microsoft#1107
* [minmax] Add grid scaling to recompress API by @arkrishn94 in
microsoft#1109

**Full Changelog**:
microsoft/DiskANN@v0.52.0...v0.53.0

diskann-garnet-v1.0.27

Toggle diskann-garnet-v1.0.27's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix options for diskann-garnet nuget pipeline (microsoft#1091)

`-Z` flags must be specified individually and can't be combined. This
corrects the syntax.

Co-authored-by: Jack Moffitt <jackmoffitt@microsoft.com>

v0.52.0

Toggle v0.52.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Set version to v0.52.0 (microsoft#1056)

# DiskANN v0.52.0 Release Notes

## Breaking Changes

An AI generated, human reviewed list of changes is summarized below.

### `get_degree_stats` signature changed
([microsoft#998](microsoft#998))

`DiskANNIndex::get_degree_stats` now takes an explicit iterator of IDs
instead of requiring the data provider to implement `IntoIterator`.

```rust
// Before — provider had to impl IntoIterator
index.get_degree_stats(&mut accessor)?;

// After — caller supplies the ID iterator
index.get_degree_stats(&mut accessor, id_iter)?;
```

### PQ dimension contract tightened; entries now `&[f32]` only
([microsoft#1044](microsoft#1044))

With `AlignedBoxWithSlice` removed from the PQ path, the dimension
handling has been refactored into a three-layer contract:

| Layer | Where | Contract |
|---|---|---|
| **Boundary (inmem)** | `QueryComputer::new`,
`MultiQueryComputer::new`, `DistanceComputer::evaluate_similarity` |
`len == dim` (returns `Err` on mismatch) |
| **Boundary (disk)** | `PQScratch::set` | `len >= dim`, slices to
`[..dim]` |
| **Internal** | `TableL2/IP/Cosine::{new, populate}` | Trusted — no
re-validation |

**Other changes:**
- PQ table populate/distance methods now accept `&[f32]` instead of `<U:
Into<f32>>`. Callers must pre-decode quantized vectors via
`VectorRepr::as_f32`.
- Generic trampoline impls (`&Vec<u8>`, `&&[u8]`) on `QueryComputer` /
`DistanceComputer` have been removed.
### `calculate_chunk_offsets` relocated to `ChunkOffsets` constructors
([microsoft#976](microsoft#976))

The free functions `calculate_chunk_offsets` and
`calculate_chunk_offsets_auto` have been moved into constructors on
`ChunkOffsets` / `ChunkOffsetsView` in `diskann-quantization::views`.

```rust
// Before
let offsets = calculate_chunk_offsets(dim, num_chunks);

// After (allocating)
let offsets = ChunkOffsets::partition(dim, num_chunks)?;

// After (zero-alloc, borrows caller-owned scratch)
let view = ChunkOffsetsView::partition_into(dim, &mut scratch)?;
```

Additionally, `get_chunk_from_training_data` has been moved from public
API.

### `CachingProvider` removed
([microsoft#1052](microsoft#1052))

The entire `diskann_providers::model::graph::provider::async_::caching`
module has been deleted.

**Why:** The `CachingProvider` was an experiment in transparent caching
over `DataProvider`. In practice it required double monomorphization of
the indexing code, didn't save integration work for bulk methods like
`on_elements_unordered`/`distances_unordered`, and was complex to
maintain. An internal user who …migrated off it removed ~1,000 lines of
code, improved compile times by ~20%, and substantially reduced
complexity.

**Upgrade:** Manage caching directly in your `DataProvider`
implementation.

## New Features

### AVX-512 4-bit distance kernels
([microsoft#1045](microsoft#1045))

Native V4 (AVX-512) specializations for 4-bit packed vector distance
computations:

- **`SquaredL2`** — 16 × `u32` lanes per iteration via
`_mm512_madd_epi16`.
- **`InnerProduct`** — AVX-512 VNNI (`_mm512_dpbusd_epi32`) over `u8x64`
/ `i8x64` operands.

Previously, V4 hardware fell back to two AVX2 (V3) kernel invocations
per 512-bit chunk. The native kernels double per-instruction throughput.
No API changes — existing code benefits automatically on AVX-512 capable
hardware.

## Merged PRs
* Deprecate 32-bit targets by @suhasjs in
microsoft#1022
* Add a fast path to `Map::prepare`. by @hildebrandmw in
microsoft#1023
* Add boundary checks in gen_associated_data_from_range() by @Copilot in
microsoft#847
* [deps] Don't pull `rayon` as a dependency of `diskann`. by
@hildebrandmw in microsoft#1024
* Bump openssl from 0.10.78 to 0.10.79 by @dependabot[bot] in
microsoft#1026
* Cleaning up test work and changing the get_degree_stats signature. by
@JordanMaples in microsoft#998
* Reduce scalar-quantization benchmark monomorphization by
@suri-kumkaran in microsoft#1041
* [diskann-vector] Support truly unaligned distances. by @hildebrandmw
in microsoft#981
* rename spherical.json to graph index with spherical quantization by
@harsha-simhadri in microsoft#1042
* [PQ Cleanup] Part 2: Consolidate `calculate_chunk_offsets*` by
@arkrishn94 in microsoft#976
* PQ: tighten dim contract; right-size scratch buffer by @wuw92 in
microsoft#1044
* Add v4 distance kernels (4-bit SquaredL2 / InnerProduct) by @m3hm3t in
microsoft#1045
* Remove the Caching Provider by @hildebrandmw in
microsoft#1052

## New Contributors
* @suhasjs made their first contribution in
microsoft#1022
* @m3hm3t made their first contribution in
microsoft#1045

**Full Changelog**:
microsoft/DiskANN@v0.51.0...v0.52.0

Co-authored-by: Mark Hildebrand <mhildebrand@microsoft.com>

v0.51.0

Toggle v0.51.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[v0.51.0] Bump version to 0.51.0 (microsoft#1013)

Bump version to 0.51.0 due to propagate changes to downstream consumers

## Breaking API changes (AI Generated)

- **`ObjectPool` moved** (microsoft#975): now lives in `diskann-utils`. Update
imports from `diskann::...::ObjectPool` → `diskann_utils::ObjectPool`.

- **`AlignedSlice` removed** (microsoft#994): the `AlignedSlice` abstraction in
`diskann-vector` is gone. Code that converted between vector
representations through `AlignedSlice` should now use the `Poly` /
`CastFromSlice` polymorphic interfaces directly (see
`diskann-vector::conversion` and `diskann-quantization::alloc::poly`).
Storage that previously held `AlignedSlice` values should hold `Poly<T,
A>` instead.

- **`AsThreadPool` generic removed** (microsoft#967): functions that previously
took `pool: impl AsThreadPool` now take `pool: &RayonThreadPool`. Pass a
borrow of an existing pool; remove the generic parameter from your call
sites.

- **`sgemm()` returns `Result`** (microsoft#997): in `diskann-linalg`, the new
signature is:

  ```rust
  pub fn sgemm(
      atranspose: Transpose, btranspose: Transpose,
      m: usize, n: usize, k: usize,
      alpha: f32, a: &[f32], b: &[f32],
      beta: Option<f32>, c: &mut [f32],
  ) -> Result<(), SgemmError>
  ```

`SgemmError` has variants `InvalidMatrixDimensions { matrix_name,
expected_rows, expected_cols, actual_len }` and `DimensionOverflow {
matrix_name, rows, cols }`. Replace previous panic-on-bad-input
assumptions with explicit handling.

- **Benchmarks are stateful** (microsoft#995): the `Benchmark` impls in
`diskann-benchmark` are no longer stateless unit structs. Each benchmark
type now has a `::new()` constructor (often holding `PhantomData<T>` or
plugin state), and registration uses an instance:

  ```rust
  // before
  benchmarks.register("name", MyBench);
  // after
  benchmarks.register("name", MyBench::<T>::new());
  ```
If you wrote a custom benchmark, give it a `new()` and register an
instance. Combined with microsoft#996, search-side benchmarks now compose
`Plugins<Provider, Phase, Strategy>` and expose builder methods like
`.search(plugin)` to register search plugins on the instance.

- **`diskann-benchmark`: `async` → `graph-index`** (microsoft#1009): the
benchmark category previously named `async` was renamed to
`graph-index`. JSON config `type` values and example file names changed
accordingly:
  - `async-build` → `graph-index-build`
  - `async-dynamic-run` → `graph-index-dynamic-run`
- and the same prefix swap for `*-pq`, `*-sq`,
`*-spherical-quantization`, etc.

Update any benchmark config files, scripts, or CI that reference the old
`async-*` names.

- **`diskann-disk` buffer alignment decoupled from `block_size`**
(microsoft#984): code that assumed I/O buffer alignment equals the disk block
size should now configure alignment explicitly.

## Non-breaking

- New cache-aware block-transposed Chamfer/MaxSim distance for f32/f16
(microsoft#863).
- A/A benchmark documentation (microsoft#974); CI publish workflow improvements
(microsoft#755, microsoft#1017); openssl bump (microsoft#973); `compute_closest_centers` allocation
reduction (microsoft#980).
- **`DistanceComputer` `'static` bound relaxed** (microsoft#1007) and **redundant
`DistanceFunction` impls removed** (microsoft#1008)


**Full Changelog**:
microsoft/DiskANN@v0.50.1...v0.51.0

v0.50.1

Toggle v0.50.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Bump to 0.50.1 (microsoft#970)

Bumping to 0.50.1 to propagate changes to consumers.

Changes since previous bump: 

## What's Changed
* Add more agentic guard rails by @hildebrandmw in
microsoft#871
* Cleanup `diskann-benchmark-runner` and friends. by @hildebrandmw in
microsoft#865
* Use `--all-targets` for the no-default-features CI run. by
@hildebrandmw in microsoft#874
* Remove unused `normalizing_util.rs` from `diskann-providers` by
@Copilot in microsoft#902
* Benchmark Support for A/B Tests by @hildebrandmw in
microsoft#900
* [diskann-garnet] Bump diskann-garnet to 1.0.26 by @tiagonapoli in
microsoft#925
* Remove the `AdjacencyList` from `diskann-providers` by @hildebrandmw
in microsoft#915
* [PQ cleanup] Part 1: Move pq_scratch, quantizer_preprocess and
pq_dataset to `diskann-disk` by @arkrishn94 in
microsoft#930
* Forbid Debug in diskann-benchmark by @arrayka in
microsoft#914
* Remove DebugProvider by @JordanMaples in
microsoft#923
* [diskann-garnet] Create workflow to publish to nuget by @tiagonapoli
in microsoft#926
* Move k-means implementation from diskann-providers to diskann-disk by
@Copilot in microsoft#933
* Inline minmax distance evaluations by @arkrishn94 in
microsoft#935
* Use `rust-toolchain.toml` in CI by @hildebrandmw in
microsoft#934
* Add a globally blocking CI gate. by @hildebrandmw in
microsoft#932
* Remove `utils/math_util.rs` from `diskann-providers` by @Copilot in
microsoft#921
* Bump rand from 0.9.2 to 0.9.3 by @dependabot[bot] in
microsoft#945
* Remove OPQ and friends by @arkrishn94 in
microsoft#947
* Migrate test_flaky_consolidate from diskann_providers to diskann by
@JordanMaples in microsoft#942
* Remove GraphDataType from diskann-providers by @wuw92 in
microsoft#950
* Remove unused method extract_best_l_candidates in
NeighborPriorityQueue by @doliawu in
microsoft#951
* Add `Debug` bounds to `VectorRepr`'s distance GATs. by @hildebrandmw
in microsoft#948
* Add benchmark pipeline with Rust-native A/B validation by
@YuanyuanTian-hh in microsoft#912
* Remove unnecessary `Default` bound from `Neighbor`'s `VectorIdType` by
@doliawu in microsoft#956
* Replace `AlignedBoxWithSlice` with plain `Vec` / `Matrix` where
alignment is unused by @wuw92 in
microsoft#955
* [minmax] 8-bit benchmark by @arkrishn94 in
microsoft#959
* Add `MultiInsertStrategy` implementations for `BfTreeProvider` by
@hildebrandmw in microsoft#949
* Replace `AlignedBoxWithSlice` with `Vec` in PQScratch and disk fp
vector caches by @wuw92 in microsoft#960
* Adding unit tests for paged_search by @JordanMaples in
microsoft#962
* Remove AlignedBoxWithSlice wrapper and add alias to Poly<[T],
AlignedAllocator> by @JordanMaples in
microsoft#965
* Remove synthetic/structured data generation from diskann-providers by
@JordanMaples in microsoft#963
* added tests and some baselines for range_search by @JordanMaples in
microsoft#961

## New Contributors
* @JordanMaples made their first contribution in
microsoft#923
* @wuw92 made their first contribution in
microsoft#950
* @doliawu made their first contribution in
microsoft#951
* @YuanyuanTian-hh made their first contribution in
microsoft#912

**Full Changelog**:
microsoft/DiskANN@v0.50.0...v0.50.1

diskann-garnet-v1.0.26

Toggle diskann-garnet-v1.0.26's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
[diskann-garnet] Create workflow to publish to nuget (microsoft#926)

Create Github Actions workflow to publish to Nuget using NUGET_API_KEY
added to repository

- Publishes will be triggered by `diskann-garnet-v*` tags

---------

Co-authored-by: Tiago Napoli <tiagonapoli@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

v0.50.0

Toggle v0.50.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Bump to 0.50.0 (microsoft#870)

# Version 0.50.0

Release notes generated with the help of Copilot.

## API Breaking Changes

This release contains several large API changes that should be
mechanical to apply to existing code but that will enable forward
evolution of our integrations.

### WorkingSet/Fillset/Strategy API Changes (microsoft#859)

#### Removals
The following items have been removed:

| Removed | Replacement |
|--------|--------------|
| `Fill` | `diskann::graph::workingset::Fill<WorkingSet>` and
`diskann::graph::workingset::Map` |
| `AsElement<T>` | `diskann::graph::glue::MultiInsertStrategy::finish`
and `diskann::graph::workingset::AsWorkingSet` |
| `Accessor::Extended` | Just gone! |
| `VectorIdBoxSlice` | `diskann::graph::glue::Batch` (and `Matrix<T>`) |
| `reborrow::Lower`/`AsyncLower` | No longer needed |

#### Strategy Type Parameters (no longer `?Sized`)
The `T` parameter on `SearchStrategy`, `InsertStrategy`, and
`SetElement` is no longer `?Sized`.

```rust
// Before
impl SearchStrategy<DP, [f32]> for MyStrategy { ... }
impl SetElement<[f32]> for MyProvider { ... }

// After
impl SearchStrategy<DP, &[f32]> for MyStrategy { ... }
impl SetElement<&[f32]> for MyProvider { ... }
```
HRTB syntax is now generally needed, but makes contraints far more
uniform.

Similarly, `InplaceDeleteStrategy::DeleteElement<'a>` has been tightened
from `Send + Sync + ?Sized`
to `Copy + Send + Sync` (sized). If your delete element was an unsized
type like `[f32]`, change
it to a reference like `&'a [f32]`.

#### `Guard` moved from `SetElement` to `DataProvider`

```rust
// Before
trait SetElement<T>: DataProvider {
    type Guard: Guard<Id = Self::InternalId>;
    ...
}

// After
trait DataProvider {
    type Guard: Guard<Id = Self::InternalId> + 'static;
    ...
}

trait SetElement<T>: DataProvider {
    // Returns Self::Guard from DataProvider
    fn set_element(...) -> Result<Self::Guard, Self::SetError>;
}
```

Move your `Guard` associated type from your `SetElement` impl to your
`DataProvider` impl.

#### `PruneStrategy` gains `WorkingSet`

```rust
// Before
trait PruneStrategy<Provider> {
    type PruneAccessor<'a>: Accessor + ... + FillSet;
    ...
}

// After
trait PruneStrategy<Provider> {
    type WorkingSet: Send + Sync;
    type PruneAccessor<'a>: Accessor + ... + workingset::Fill<Self::WorkingSet>;
    fn create_working_set(&self, capacity: usize) -> Self::WorkingSet;
    ...
}
```

**What to do:**
1. Add a `WorkingSet` associated type. Use `workingset::Map<Id,
Box<[T]>>` as the default.
Users of multi-insert will want to use `workingset::Map<Id, Box<[T]>,
workingset::map::Ref<[T]>>`.
3. Add `create_working_set` returning a `Map` via
`Builder::new(Capacity::Default).build(capacity)`.
4. Replace `FillSet` bound on `PruneAccessor` with
`workingset::Fill<Self::WorkingSet>`.

#### `FillSet` to `Fill<WorkingSet>`
```rust
// Before
impl FillSet for MyAccessor {
    fn fill_set(&mut self, ids: &[Id], map: &mut HashMap<Id, Extended>) -> Result<()> { ... }
}

// After
impl Fill<MyWorkingSet> for MyAccessor {
    type Error = ...;
    type View<'a> = ... where Self: 'a, MyWorkingSet: 'a;

    fn fill<'a, Itr>(
        &'a mut self,
        working_set: &'a mut MyWorkingSet,
        itr: Itr,
    ) -> impl SendFuture<Result<Self::View<'a>, Self::Error>>
    where
        Itr: ExactSizeIterator<Item = Self::Id> + Clone + Send + Sync,
    { ... }
}
```

**Shortcut:** If your `WorkingSet` is `workingset::Map<K, V, P>` and
your accessor's
`ElementRef` converts `Into<V>`, the blanket `Fill` impl works
automatically, then you don't
need to implement `Fill` at all.

#### New `MultiInsertStrategy` (replaces multi-insert portion of
`InsertStrategy`)

If you use `multi_insert`, you now need a `MultiInsertStrategy`
implementation:

```rust
trait MultiInsertStrategy<Provider, B: Batch>: Send + Sync {
    type WorkingSet: Send + Sync + 'static;
    type Seed: AsWorkingSet<Self::WorkingSet> + Send + Sync + 'static;
    type FinishError: Into<ANNError> + std::fmt::Debug + Send + Sync;
    type InsertStrategy: for<'a> InsertStrategy<
        Provider,
        B::Element<'a>,
        PruneStrategy: PruneStrategy<Provider, WorkingSet = Self::WorkingSet>,
    >;

    fn insert_strategy(&self) -> Self::InsertStrategy;
    fn finish<Itr>(&self, provider: &Provider, context: &Provider::Context,
        batch: &Arc<B>, ids: Itr,
    ) -> impl Future<Output = Result<Self::Seed, Self::FinishError>> + Send
    where Itr: ExactSizeIterator<Item = Provider::InternalId> + Send;
}
```

**What to do:**
- `finish` is called once after the batch is inserted. Use it to
pre-populate working set
state (e.g., fetch quantized representations). Return a `Seed` that
implements
  `AsWorkingSet`.
- For the common case, use `workingset::map::Builder` as your `Seed`
type.
- `multi_insert` now takes `Arc<B>` where `B: Batch` instead of
`VectorIdBoxSlice`.
  `Matrix<T>` implements `Batch` out of the box.

Using `diskann::graph::workingset::Map` and its associated
infrastructure should largely work.
#### `multi_insert` call site

```rust
// Before
index.multi_insert(strategy, context, vectors, ids).await?;
// where vectors: VectorIdBoxSlice

// After
index.multi_insert::<S, B>(strategy, context, vectors, ids).await?;
// where vectors: Arc<B>, B: Batch
// DP: for<'a> SetElement<B::Element<'a>> is required
```

The turbofish `<S, B>` is often needed because HRTB bounds make
inference conservative.

### Search Post Processing (microsoft#828)

The `Search` trait now requires `S: SearchStrategy<DP, T>` at the trait
level, and introduces two new generic parameters on the `search` method:

- `O` - the output type written to the buffer (used to be a parameter of
`SearchStrategy`)
- `PP` — the post-processor, bounded by `for<'a>
SearchPostProcess<S::SearchAccessor<'a>, T, O> + Send + Sync`
- `OB` — the output buffer, bounded by `SearchOutputBuffer<O> + Send +
?Sized`

Previously, `OB` was a trait-level generic and the post-processor was
obtained internally from the strategy. Now both are method-level
generics, giving callers control over each independently.

The output "id" type `O` has been removed from `SearchStrategy`
entirely, which greatly simplifies things.

At the `DiskANNIndex` level, two entry points are provided:

- `search()` — requires `S: DefaultPostProcessor`, creates the
strategy's default processor automatically, then delegates to
`search_with()`. This is the common path.
- `search_with()` — requires only `S: SearchStrategy`, accepts any
compatible `PP`. This is the extension point for custom post-processing.

#### DefaultPostProcessor

Strategies opt in to default post-processing by implementing
`DefaultPostProcessor`:

```rust
pub trait DefaultPostProcessor<Provider, T, O>: SearchStrategy<Provider, T> {
    type Processor: for<'a> SearchPostProcess<Self::SearchAccessor<'a>, T, O> + Send + Sync;
    fn default_post_processor(&self) -> Self::Processor;
}
```

The `default_post_processor!` macro covers the common case where the
processor is `Default`-constructible. `DefaultSearchStrategy` is a
convenience trait (with a blanket impl…) for `SearchStrategy +
DefaultPostProcessor`.

#### Start-point filtering

The in-mem code used to rely on a special `Internal` type to customize
post-processing for the inplace delete pipeline.
Now that post-processing is customizable, this workaround is no longer
needed.

Start-point filtering is composed at the type level using the existing
`Pipeline` mechanism:

- `HasDefaultProcessor::Processor = Pipeline<FilterStartPoints,
RemoveDeletedIdsAndCopy>` — filters start points during regular search
- `InplaceDeleteStrategy::SearchPostProcessor = RemoveDeletedIdsAndCopy`
— no start-point filtering during delete re-pruning

#### Range search

To respond to moving the output buffer to a method level geneirc, range
search now writes results directly through a `DistanceFiltered` output
buffer wrapper that applies radius and inner-radius filtering inline
during post-processing. This replaces the previous approach of
allocating intermediate `Vec`s and copying results through an
`IdDistance` buffer.

`SearchOutputBuffer` is now implemented for `Vec<Neighbor<I>>`,
providing an unbounded growable buffer suitable for range search and
other cases where the result count is not known in advance.

The `RangeSearchResults` type has been removed now that a
`SearchOutputBuffer` is properly used.

#### Migration guide

**If you implement `SearchStrategy`** — minimal changes required. Remove
the `O` parameter (if needed). Implement `DefaultPostProcessor` with
your current post-processor if you want your strategy to work with
`index.search()`. Use the `default_post_processor!` macro for simple
cases:

```rust
impl DefaultPostProcessor<MyProvider, [f32]> for MyStrategy {
    default_post_processor!(Pipeline<FilterStartPoints, RemoveDeletedIdsAndCopy>);
}
```

**If you call `index.search()`** — no API change for strategies that
implement `DefaultPostProcessor` (all built-in strategies do).

**If you need custom post-processing** — use `index.search_with()` and
pass your post-processor directly:

```rust
let processor = MyCustomPostProcessor::new(/* ... */);
let stats = index.search_with(params, &strategy, processor, &context, &query, &mut output).await?;
```

**If you implement `Search`** — the method signature has changed. `PP`
and `OB` are now method-level generics, and `processor` is an explicit
parameter:

```rust
fn search_with<O, PP, OB>(
    self,
    index: &DiskANNIndex<DP>,
    strategy: &S,
    processor: PP,       // new
    context: &DP::Context,
    query: T,
    output: &mut OB,
) -> impl SendFuture<ANNResult<Self::Output>>
where
    P: Search<DP, S, T>,
    S: glue::SearchStrategy<DP, T>,
    PP: for<'a> glue::SearchPostProcess<S::SearchAccessor<'a>, T, O> + Send + Sync,
    O: Send,
    OB: search_output_buffer::SearchOutputBuffer<O> + Send + ?Sized,
```

**If you use range search results** — `RangeSearchOutput` no longer
carries `ids` and `distances` fields. Results are written to the output
buffer passed to `search()`. Use `Vec<Neighbor<u32>>` (or any
`SearchOutputBuffer` impl) to collect them.

**If you implement `InplaceDeleteStrategy`**: Add the
`DeleteSearchAccessor` associated type, ensuring it is the same as the
accessor selected by the `SearchStrategy`. This is redundant
information, but needed to help constrain downstream trait bounds.

### V4 Backend Raised to Ice Lake / Zen4+ (microsoft#861)

The `V4` SIMD backend in `diskann-wide` now requires additional AVX-512
extensions beyond the
baseline `x86-64-v4` feature set:

| New requirement | Description |
|-----------------|-------------|
| `avx512vnni` | Vector Neural Network Instructions |
| `avx512bitalg` | Bit algorithm instructions (VPOPCNT on bytes/words) |
| `avx512vpopcntdq` | POPCNT on dword/qword elements |
| `avx512vbmi` | Vector Byte Manipulation Instructions (cross-lane byte
permutes) |

This effectively raises the minimum CPU for the `V4` tier from Cascade
Lake to **Ice Lake Server
(Intel)** or **Zen4+ (AMD)**.

**Impact:** On CPUs with baseline AVX-512 but without these extensions
(e.g., Skylake-X,
Cascade Lake), runtime dispatch will select the `V3` backend instead. No
code
changes are required.

## What's Changed
* Remove dead code from diskann-tools by @Copilot in
microsoft#829
* CI maintenance by @hildebrandmw in
microsoft#838
* Add baseline tests for insert/multi-insert. by @hildebrandmw in
microsoft#837
* Remove unused file by @ChenSunriseJiaBao in
microsoft#850
* Add compatibility tests for spherical flatbuffer serialization by
@hildebrandmw in microsoft#839
* Overhaul Post-Processing by @hildebrandmw in
microsoft#828
* Bump the `V4` backend to IceLake/Zen4+ by @hildebrandmw in
microsoft#861
* Remove `ConcurrentQueue` from `diskann-providers` by @hildebrandmw in
microsoft#862
* Enhance code review instructions with additional checks by
@harsha-simhadri in microsoft#866
* Add synchronous `load_with` constructors and `run` escape hatch to
`wrapped_async` by @suri-kumkaran in
microsoft#864
* Overhaul WorkingSet/FillSet and its interaction with Multi-Insert by
@hildebrandmw in microsoft#859

## New Contributors
* @ChenSunriseJiaBao made their first contribution in
microsoft#850

**Full Changelog**:
microsoft/DiskANN@v0.49.1...v0.50.0

benchmark-data-v1

Toggle benchmark-data-v1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Remove `ConcurrentQueue` from `diskann-providers` (microsoft#862)

The concurrent queue tests have been flaky on MacOS.

It turns out that no-one is using this data structure anyways, so we can
fix the flaky tests be removing it entirely.