mirror of
https://github.com/facebookresearch/faiss.git
synced 2026-10-12 02:13:36 +00:00
Fix Jaccard heap selection in IVF search (#5408)
Summary: - Use `is_similarity_metric()` for IVF heap initialization, merging, reordering, and result handling. - Add Jaccard IVFFlat regression coverage across parallel modes 0-3. ## Why Jaccard scanners use similarity ordering, but `IndexIVF::search_preassigned` selected distance heaps for every metric except inner product. The resulting `FLT_MAX` threshold rejected all Jaccard candidates, returning `-1` even when the only inverted list was fully scanned. Fixes https://github.com/facebookresearch/faiss/issues/5399. ## Validation - [x] `cmake --build build --target faiss_test --config Release -j 8` - [x] `ctest --test-dir build --output-on-failure -C Release` (251 tests, 5 skipped) - [x] `clang-format-21 --dry-run --Werror faiss/IndexIVF.cpp tests/test_ivf_index.cpp` ## Notes for reviewers Python and GPU suites were not run locally; the fix and regression exercise the shared C++ IVF path. Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5408 Reviewed By: pankajsingh88, mnorris11 Differential Revision: D112830842 Pulled By: bshethmeta fbshipit-source-id: c6431d2bee1b4fecd2e46cfda397722947a5407a
This commit is contained in:
committed by
meta-codesync[bot]
parent
3110fd2424
commit
7437cac4d9
+4
-4
@@ -500,7 +500,7 @@ void IndexIVF::search_preassigned(
|
||||
if (!do_heap_init) {
|
||||
return;
|
||||
}
|
||||
if (metric_type == METRIC_INNER_PRODUCT) {
|
||||
if (is_similarity_metric(metric_type)) {
|
||||
heap_heapify<HeapForIP>(k, simi, idxi);
|
||||
} else {
|
||||
heap_heapify<HeapForL2>(k, simi, idxi);
|
||||
@@ -511,7 +511,7 @@ void IndexIVF::search_preassigned(
|
||||
const idx_t* local_idx,
|
||||
float* simi,
|
||||
idx_t* idxi) {
|
||||
if (metric_type == METRIC_INNER_PRODUCT) {
|
||||
if (is_similarity_metric(metric_type)) {
|
||||
heap_addn<HeapForIP>(
|
||||
k, simi, idxi, local_dis, local_idx, k);
|
||||
} else {
|
||||
@@ -524,7 +524,7 @@ void IndexIVF::search_preassigned(
|
||||
if (!do_heap_init) {
|
||||
return;
|
||||
}
|
||||
if (metric_type == METRIC_INNER_PRODUCT) {
|
||||
if (is_similarity_metric(metric_type)) {
|
||||
heap_reorder<HeapForIP>(k, simi, idxi);
|
||||
} else {
|
||||
heap_reorder<HeapForL2>(k, simi, idxi);
|
||||
@@ -598,7 +598,7 @@ void IndexIVF::search_preassigned(
|
||||
|
||||
size_t old_scan_cnt = 0;
|
||||
size_t old_heap_updates = 0;
|
||||
if (metric_type == METRIC_INNER_PRODUCT) {
|
||||
if (is_similarity_metric(metric_type)) {
|
||||
HeapResultHandler<HeapForIP, false> handler(
|
||||
k, simi, idxi);
|
||||
old_scan_cnt = handler.stats.scan_cnt;
|
||||
|
||||
@@ -256,6 +256,28 @@ TEST(IVF, list_context) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IVF, jaccard_search_returns_most_similar_vector) {
|
||||
constexpr int d = 3;
|
||||
constexpr int nb = 3;
|
||||
const float xb[nb * d] = {
|
||||
1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f};
|
||||
|
||||
faiss::IndexFlatL2 quantizer(d);
|
||||
quantizer.add(1, xb);
|
||||
faiss::IndexIVFFlat index(&quantizer, d, 1, faiss::METRIC_Jaccard);
|
||||
index.add(nb, xb);
|
||||
|
||||
for (int parallel_mode = 0; parallel_mode < 4; parallel_mode++) {
|
||||
index.parallel_mode = parallel_mode;
|
||||
float distance;
|
||||
faiss::idx_t label;
|
||||
index.search(1, xb, 1, &distance, &label);
|
||||
|
||||
EXPECT_EQ(label, 0) << "parallel mode " << parallel_mode;
|
||||
EXPECT_FLOAT_EQ(distance, 1.0f) << "parallel mode " << parallel_mode;
|
||||
}
|
||||
}
|
||||
|
||||
// Test: search_preassigned with out-of-range keys throws a catchable
|
||||
// FaissException instead of calling std::terminate from an uncaught
|
||||
// exception inside the OpenMP parallel region.
|
||||
|
||||
Reference in New Issue
Block a user