Skip to content

router-core: DistanceMetric::DotProduct is inverted — HNSW search returns the LEAST similar vectors #1009

Description

@ruvnet

Summary

ruvector-router-core's DistanceMetric::DotProduct returns the raw inner product as a distance (src/distance.rs, the DotProduct arm), while index.rs keeps the smallest distances during both graph construction and search. The result: with DotProduct, search() ranks the least-similar vectors first, and the graph itself is built with neighbours chosen by minimum inner product, so the index is not navigable for maximum-inner-product search.

Evidence

While building ruvector-kge (branch feat/kge-package) we measured recall@10 of DotProduct HNSW vs exhaustive maximum-inner-product on 2,000 random 64-d vectors: 0.000. Negating the query does not help, because the entity–entity graph edges are also oriented by minimum dot product. The measurement is preserved as an #[ignore] test, raw_dotproduct_recall_for_the_record, in crates/ruvector-kge/src/ann.rs on that branch.

The same setup using the standard MIPS→L2 reduction (append sqrt(phi^2 - ||x||^2) to each indexed vector, append 0 to the query, DistanceMetric::Euclidean) gives recall@10 0.996, which is what ruvector-kge ships as its workaround.

Expected

Either DotProduct distance = -dot (or 1 - dot for normalised vectors) so that smaller-is-closer holds everywhere the index compares distances, or document DotProduct as unsupported for HNSW and reject it at VectorIndex::new.

Notes

🤖 Generated with claude-flow

https://claude.ai/code/session_01LNEfix9xQvKBUcVVSBCWXa

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions