perf(cosinepair): exact query_row_top_k, halved Theta(n^2) build, zero-copy row views - #443
Merged
Conversation
…, 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #442
Checklist
Current behaviour
CosinePair::with_top_kbuilds with an all-pairs ordered double loop:n*(n-1)cosine-distance evaluations regardless oftop_k; every evaluation materialises two heap-allocatedVecs throughVec::from_iteratorand recomputes both row norms. Measured ~n^3.48 end-to-end on square inputs (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 #442).query_row_top_kalways samples onlytop_kevenly strided candidates (step = n / top_k) with no documentation, and its bounded heap pops the closest candidate when full, so it can return the farthest of the sampled rows.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 toCosine::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_kdoes not make it sub-quadratic; docs now state this.query_row_top_kis exact wheneverapproximateisfalse(the default) and caps the result count attop_k; strided sampling is gated behindCosinePairParameters { approximate: true }and documented as approximate.query()anddistances_from()reuse the precomputed norms as well.query_row_top_kvs a brute-force oracle,new()vswith_top_k(n-1)parity (build state and queries), per-row closest neighbour vs brute force, and documented stride sampling underapproximate: true. All distance computations still route through the bespokenumbers/ArrayView1abstractions; nounsafe, no macros.Change logs
Changed
CosinePairgained a privaterow_normsfield (precomputed row norms). Construct throughnew/with_top_k/with_parametersinstead of struct literals.CHANGELOG.mdupdated.