Skip to content

perf(cosinepair): exact query_row_top_k, halved Theta(n^2) build, zero-copy row views - #443

Merged
Mec-iS merged 1 commit into
mainfrom
perf/cosinepair-build
Aug 23, 2026
Merged

perf(cosinepair): exact query_row_top_k, halved Theta(n^2) build, zero-copy row views#443
Mec-iS merged 1 commit into
mainfrom
perf/cosinepair-build

Conversation

@Mec-iS

@Mec-iS Mec-iS commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Fixes #442

Checklist

  • My branch is up-to-date with main branch.
  • Everything works and tested on latest stable Rust.
  • Coverage and Linting have been applied

Current behaviour

New expected behaviour

  • init() evaluates each unordered pair (i, j) once (symmetric half-scan) and updates the running best of both rows: half the distance evaluations, zero per-pair allocations (zero-copy row views), row norms precomputed once in O(n*d). Distances are bit-identical to Cosine::new().distance(...) (same formula and operation order); measured ~3x faster build on a 1500x64 input (768 ms -> 250 ms, release, Apple silicon). Construction remains Theta(n^2) dot products - top_k does not make it sub-quadratic; docs now state this.
  • query_row_top_k is exact whenever approximate is false (the default) and caps the result count at top_k; strided sampling is gated behind CosinePairParameters { approximate: true } and documented as approximate. query() and distances_from() reuse the precomputed norms as well.
  • New regression tests: exactness of query_row_top_k vs a brute-force oracle, new() vs with_top_k(n-1) parity (build state and queries), per-row closest neighbour vs brute force, and documented stride sampling under approximate: true. All distance computations still route through the bespoke numbers/ArrayView1 abstractions; no unsafe, no macros.

Change logs

Changed

  • Breaking: CosinePair gained a private row_norms field (precomputed row norms). Construct through new / with_top_k / with_parameters instead of struct literals.
  • Version bumped to 0.6.11; CHANGELOG.md updated.

…, zero-copy row views (#442)

- query_row_top_k now scores every row (exact) when approximate=false;
  strided candidate sampling is gated behind CosinePairParameters.approximate
  and documented as approximate. Also fixes the bounded heap evicting its
  closest candidate instead of the farthest.
- init() evaluates each unordered pair once (symmetric half-scan), reuses
  precomputed row norms, and scores through zero-copy row views instead of
  two heap-allocated Vecs per pair. Distances are bit-identical to
  Cosine::new().distance(); measured ~3x faster build on 1500x64.
- query() and distances_from() reuse the precomputed norms as well.
- Regression tests: exactness vs brute force, new() vs with_top_k(n-1)
  parity, per-row closest neighbour, approximate stride sampling.
- Bump version to 0.6.11.
@Mec-iS
Mec-iS merged commit 6e285b3 into main Aug 23, 2026
13 checks passed
@Mec-iS
Mec-iS deleted the perf/cosinepair-build branch August 23, 2026 11:57
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.

CosinePair::with_top_k construction is Theta(n^2) distance evaluations with per-pair allocations; measured ~n^3.48 end-to-end on square inputs

1 participant