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
Summary
ruvector-router-core'sDistanceMetric::DotProductreturns the raw inner product as a distance (src/distance.rs, theDotProductarm), whileindex.rskeeps the smallest distances during both graph construction and search. The result: withDotProduct,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(branchfeat/kge-package) we measured recall@10 ofDotProductHNSW 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, incrates/ruvector-kge/src/ann.rson that branch.The same setup using the standard MIPS→L2 reduction (append
sqrt(phi^2 - ||x||^2)to each indexed vector, append0to the query,DistanceMetric::Euclidean) gives recall@10 0.996, which is whatruvector-kgeships as its workaround.Expected
Either
DotProductdistance =-dot(or1 - dotfor normalised vectors) so that smaller-is-closer holds everywhere the index compares distances, or documentDotProductas unsupported for HNSW and reject it atVectorIndex::new.Notes
Cosineis unaffected (it already returns a distance).🤖 Generated with claude-flow
https://claude.ai/code/session_01LNEfix9xQvKBUcVVSBCWXa