faiss 0.6.4 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/ext/faiss/extconf.rb +1 -1
- data/lib/faiss/version.rb +1 -1
- data/vendor/faiss/faiss/AutoTune.cpp +175 -0
- data/vendor/faiss/faiss/Clustering.cpp +150 -60
- data/vendor/faiss/faiss/Clustering.h +29 -0
- data/vendor/faiss/faiss/IVFlib.cpp +99 -0
- data/vendor/faiss/faiss/IVFlib.h +15 -0
- data/vendor/faiss/faiss/Index.cpp +44 -0
- data/vendor/faiss/faiss/Index.h +19 -14
- data/vendor/faiss/faiss/IndexBinary.cpp +8 -0
- data/vendor/faiss/faiss/IndexBinary.h +8 -0
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +82 -4
- data/vendor/faiss/faiss/IndexBinaryHNSW.h +19 -0
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +34 -0
- data/vendor/faiss/faiss/IndexFastScan.cpp +33 -17
- data/vendor/faiss/faiss/IndexFastScan.h +6 -3
- data/vendor/faiss/faiss/IndexFlat.cpp +56 -0
- data/vendor/faiss/faiss/IndexFlat.h +31 -0
- data/vendor/faiss/faiss/IndexFlatCodes.cpp +6 -1
- data/vendor/faiss/faiss/IndexHNSW.cpp +456 -10
- data/vendor/faiss/faiss/IndexHNSW.h +38 -0
- data/vendor/faiss/faiss/IndexIDMap.cpp +32 -0
- data/vendor/faiss/faiss/IndexIDMap.h +7 -0
- data/vendor/faiss/faiss/IndexIVF.cpp +281 -31
- data/vendor/faiss/faiss/IndexIVF.h +53 -1
- data/vendor/faiss/faiss/IndexIVFFastScan.cpp +46 -14
- data/vendor/faiss/faiss/IndexIVFFastScan.h +4 -2
- data/vendor/faiss/faiss/IndexIVFFlat.cpp +33 -0
- data/vendor/faiss/faiss/IndexIVFFlat.h +16 -0
- data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +1 -0
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +3 -20
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +50 -14
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -2
- data/vendor/faiss/faiss/IndexIVFSQFastScan.cpp +855 -0
- data/vendor/faiss/faiss/IndexIVFSQFastScan.h +145 -0
- data/vendor/faiss/faiss/IndexNNDescent.cpp +0 -3
- data/vendor/faiss/faiss/IndexPiPNN.cpp +201 -0
- data/vendor/faiss/faiss/IndexPiPNN.h +83 -0
- data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +6 -3
- data/vendor/faiss/faiss/IndexRaBitQFastScan.h +10 -4
- data/vendor/faiss/faiss/IndexReplicas.cpp +20 -0
- data/vendor/faiss/faiss/IndexReplicas.h +9 -0
- data/vendor/faiss/faiss/IndexSQFastScan.cpp +648 -0
- data/vendor/faiss/faiss/IndexSQFastScan.h +129 -0
- data/vendor/faiss/faiss/IndexShards.cpp +10 -0
- data/vendor/faiss/faiss/IndexShards.h +5 -0
- data/vendor/faiss/faiss/SuperKMeans.cpp +57 -15
- data/vendor/faiss/faiss/SuperKMeans.h +9 -4
- data/vendor/faiss/faiss/VectorTransform.cpp +3 -2
- data/vendor/faiss/faiss/clone_index.cpp +24 -0
- data/vendor/faiss/faiss/gpu/Dataset.h +112 -0
- data/vendor/faiss/faiss/gpu/GpuCloner.cpp +16 -13
- data/vendor/faiss/faiss/gpu/GpuCloner.h +4 -0
- data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +3 -0
- data/vendor/faiss/faiss/gpu/GpuIndex.h +4 -13
- data/vendor/faiss/faiss/gpu/GpuIndexBinaryCagra.h +26 -13
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +45 -22
- data/vendor/faiss/faiss/gpu/GpuIndexFlat.h +11 -0
- data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +6 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuDatasetHeader.cpp +18 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexFlat.cpp +215 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +98 -0
- data/vendor/faiss/faiss/gpu/utils/DeviceUtils.h +11 -0
- data/vendor/faiss/faiss/gpu/utils/StackDeviceMemory.cpp +1 -1
- data/vendor/faiss/faiss/gpu_metal/MetalCloner.h +11 -0
- data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +92 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +4 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexBinaryFlat.h +69 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +38 -0
- data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +106 -0
- data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +10 -0
- data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +2 -0
- data/vendor/faiss/faiss/impl/AuxIndexStructures.h +1 -6
- data/vendor/faiss/faiss/impl/ClusteringHelpers.cpp +134 -45
- data/vendor/faiss/faiss/impl/ClusteringHelpers.h +36 -0
- data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +286 -136
- data/vendor/faiss/faiss/impl/DistanceComputer.cpp +63 -0
- data/vendor/faiss/faiss/impl/DistanceComputer.h +3 -29
- data/vendor/faiss/faiss/impl/HNSW.cpp +46 -10
- data/vendor/faiss/faiss/impl/HNSW.h +72 -0
- data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +6 -19
- data/vendor/faiss/faiss/impl/NNDescent.cpp +2 -1
- data/vendor/faiss/faiss/impl/NSG.cpp +3 -2
- data/vendor/faiss/faiss/impl/Panorama.h +3 -2
- data/vendor/faiss/faiss/impl/PiPNN.cpp +822 -0
- data/vendor/faiss/faiss/impl/PiPNN.h +122 -0
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +54 -11
- data/vendor/faiss/faiss/impl/RaBitQUtils.h +1 -1
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +81 -57
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +15 -0
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +29 -0
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +1 -6
- data/vendor/faiss/faiss/impl/approx_topk/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/approx_topk/neon.cpp +0 -4
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHash_impl.h +5 -2
- data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/binary_hamming/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/binary_hamming/neon.cpp +0 -4
- data/vendor/faiss/faiss/impl/binary_hamming/rvv.cpp +0 -4
- data/vendor/faiss/faiss/impl/expanded_scanners.h +88 -0
- data/vendor/faiss/faiss/impl/fast_scan/FastScanDistancePostProcessing.h +4 -0
- data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops.h +61 -64
- data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops_512.h +13 -14
- data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +17 -129
- data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +25 -19
- data/vendor/faiss/faiss/impl/fast_scan/fast_scan.cpp +5 -0
- data/vendor/faiss/faiss/impl/fast_scan/impl-avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/fast_scan/impl-avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/fast_scan/impl-neon.cpp +0 -88
- data/vendor/faiss/faiss/impl/fast_scan/impl-riscv.cpp +0 -4
- data/vendor/faiss/faiss/impl/fast_scan/kernels_neon.h +155 -0
- data/vendor/faiss/faiss/impl/fast_scan/kernels_simd256.h +22 -1
- data/vendor/faiss/faiss/impl/fast_scan/kernels_simd512.h +3 -2
- data/vendor/faiss/faiss/impl/fast_scan/simd_result_handlers.h +41 -23
- data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/index_read.cpp +172 -0
- data/vendor/faiss/faiss/impl/index_write.cpp +95 -0
- data/vendor/faiss/faiss/impl/pipnn/HashPrune.cpp +159 -0
- data/vendor/faiss/faiss/impl/pipnn/HashPrune.h +69 -0
- data/vendor/faiss/faiss/impl/pipnn/Partition.cpp +587 -0
- data/vendor/faiss/faiss/impl/pipnn/Partition.h +93 -0
- data/vendor/faiss/faiss/impl/pipnn/kernels.cpp +326 -0
- data/vendor/faiss/faiss/impl/pipnn/kernels.h +92 -0
- data/vendor/faiss/faiss/impl/platform_macros.h +45 -0
- data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +79 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +6 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx2.h +4 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx512.h +4 -1
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +19 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.h +6 -6
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +131 -6
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_rvv_kernel.h +89 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +21 -10
- data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +45 -18
- data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +2 -1
- data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +5 -1
- data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +28 -52
- data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +59 -11
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +226 -77
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +482 -103
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +2 -149
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +3 -698
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +66 -57
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +101 -96
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +3185 -144
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_coefficients.h +48 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_rvv_kernel.h +546 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +2 -2
- data/vendor/faiss/faiss/impl/simd_dispatch.h +7 -0
- data/vendor/faiss/faiss/impl/simdlib/simdlib_avx2.h +14 -0
- data/vendor/faiss/faiss/impl/simdlib/simdlib_dispatch.h +7 -8
- data/vendor/faiss/faiss/impl/sq_fastscan_utils.h +232 -0
- data/vendor/faiss/faiss/index_factory.cpp +76 -0
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +3 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +3 -2
- data/vendor/faiss/faiss/utils/AlignedTable.h +6 -1
- data/vendor/faiss/faiss/utils/Heap.cpp +4 -3
- data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +16 -5
- data/vendor/faiss/faiss/utils/bf16.h +36 -0
- data/vendor/faiss/faiss/utils/distances.cpp +226 -15
- data/vendor/faiss/faiss/utils/distances.h +34 -0
- data/vendor/faiss/faiss/utils/distances_dispatch.h +6 -4
- data/vendor/faiss/faiss/utils/distances_fused/avx512.cpp +2 -5
- data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +0 -14
- data/vendor/faiss/faiss/utils/distances_fused/rvv.cpp +25 -0
- data/vendor/faiss/faiss/utils/distances_fused/simdlib_based.cpp +0 -4
- data/vendor/faiss/faiss/utils/distances_fused/simdlib_based_neon.cpp +0 -4
- data/vendor/faiss/faiss/utils/distances_fused/simdlib_kernel-inl.h +2 -1
- data/vendor/faiss/faiss/utils/distances_simd.cpp +9 -0
- data/vendor/faiss/faiss/utils/fp16-fp16c.h +4 -0
- data/vendor/faiss/faiss/utils/fp16-inl.h +26 -52
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512.cpp +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +33 -5
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_vpopcnt.h +15 -30
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-neon.h +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +2 -1
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_neon.cpp +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_rvv.cpp +0 -4
- data/vendor/faiss/faiss/utils/pq_code_distance.h +8 -4
- data/vendor/faiss/faiss/utils/prefetch.h +1 -26
- data/vendor/faiss/faiss/utils/quantize_lut.cpp +26 -27
- data/vendor/faiss/faiss/utils/quantize_lut.h +11 -0
- data/vendor/faiss/faiss/utils/rabitq_simd.h +51 -16
- data/vendor/faiss/faiss/utils/simd_impl/distances_aarch64.cpp +9 -4
- data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +37 -5
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +64 -5
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +64 -5
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +353 -71
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv_kernel.h +68 -0
- data/vendor/faiss/faiss/utils/simd_impl/exhaustive_L2sqr_blas_cmax.h +93 -0
- data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels.h +76 -0
- data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx2.cpp +52 -0
- data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx512.cpp +52 -0
- data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_neon.cpp +48 -0
- data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx2.h +44 -0
- data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx512.h +43 -0
- data/vendor/faiss/faiss/utils/simd_impl/masked_sum.h +38 -0
- data/vendor/faiss/faiss/utils/simd_impl/partitioning_avx2.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/partitioning_neon.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +54 -15
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +38 -21
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_vpopcnt.cpp +51 -14
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +297 -10
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx2.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx512.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_levels.cpp +19 -3
- data/vendor/faiss/faiss/utils/simd_levels.h +74 -1
- data/vendor/faiss/faiss/utils/transpose/transpose-avx2-inl.h +0 -4
- data/vendor/faiss/faiss/utils/transpose/transpose-avx512-inl.h +0 -4
- data/vendor/faiss/faiss/utils/utils.cpp +80 -0
- data/vendor/faiss/faiss/utils/utils.h +24 -0
- metadata +33 -1
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
#include <faiss/IndexRaBitQ.h>
|
|
11
11
|
|
|
12
|
+
#include <algorithm>
|
|
12
13
|
#include <atomic>
|
|
13
14
|
#include <cinttypes>
|
|
15
|
+
#include <cmath>
|
|
14
16
|
#include <cstdio>
|
|
15
17
|
#include <cstdlib>
|
|
16
18
|
#include <cstring>
|
|
@@ -32,8 +34,10 @@
|
|
|
32
34
|
#include <faiss/impl/VisitedTable.h>
|
|
33
35
|
#include <faiss/impl/hnsw/LockVector.h>
|
|
34
36
|
#include <faiss/impl/hnsw/MinimaxHeap.h>
|
|
37
|
+
#include <faiss/impl/platform_macros.h>
|
|
35
38
|
#include <faiss/utils/random.h>
|
|
36
39
|
#include <faiss/utils/sorting.h>
|
|
40
|
+
#include <faiss/utils/utils.h>
|
|
37
41
|
|
|
38
42
|
namespace faiss {
|
|
39
43
|
|
|
@@ -42,6 +46,13 @@ using NodeDistFarther = HNSW::NodeDistFarther;
|
|
|
42
46
|
|
|
43
47
|
HNSWStats hnsw_stats;
|
|
44
48
|
|
|
49
|
+
void HNSWStats::combine_atomic(const HNSWStats& other) {
|
|
50
|
+
detail::atomic_fetch_add_relaxed(n1, other.n1);
|
|
51
|
+
detail::atomic_fetch_add_relaxed(n2, other.n2);
|
|
52
|
+
detail::atomic_fetch_add_relaxed(ndis, other.ndis);
|
|
53
|
+
detail::atomic_fetch_add_relaxed(nhops, other.nhops);
|
|
54
|
+
}
|
|
55
|
+
|
|
45
56
|
/**************************************************************
|
|
46
57
|
* add / search blocks of descriptors
|
|
47
58
|
**************************************************************/
|
|
@@ -429,6 +440,7 @@ void hnsw_search(
|
|
|
429
440
|
|
|
430
441
|
idx_t check_period = InterruptCallback::get_period_hint(
|
|
431
442
|
hnsw.max_level * index->d * efSearch);
|
|
443
|
+
check_period *= omp_get_max_threads();
|
|
432
444
|
|
|
433
445
|
for (idx_t i0 = 0; i0 < n; i0 += check_period) {
|
|
434
446
|
idx_t i1 = std::min(i0 + check_period, n);
|
|
@@ -466,6 +478,9 @@ void hnsw_search(
|
|
|
466
478
|
rq->stats.reset();
|
|
467
479
|
}
|
|
468
480
|
|
|
481
|
+
// HNSW::search owns the per-query generation advance. The
|
|
482
|
+
// reusable-table acquisition above only clears stale TLS
|
|
483
|
+
// state before this thread starts processing queries.
|
|
469
484
|
HNSWStats stats =
|
|
470
485
|
hnsw.search(*dis, index, *res, *vt, params);
|
|
471
486
|
n1 += stats.n1;
|
|
@@ -477,7 +492,6 @@ void hnsw_search(
|
|
|
477
492
|
n_rabitq_refine += rq->stats.n_refine;
|
|
478
493
|
}
|
|
479
494
|
res->end();
|
|
480
|
-
vt->advance();
|
|
481
495
|
} catch (...) {
|
|
482
496
|
omp_capture_exception(ex, [&] { interrupt = true; });
|
|
483
497
|
}
|
|
@@ -487,8 +501,10 @@ void hnsw_search(
|
|
|
487
501
|
InterruptCallback::check();
|
|
488
502
|
}
|
|
489
503
|
|
|
490
|
-
|
|
491
|
-
|
|
504
|
+
if (get_search_stats_enabled()) {
|
|
505
|
+
hnsw_stats.combine_atomic({n1, n2, ndis, nhops});
|
|
506
|
+
rabitq_stats.add_atomic({n_rabitq_1bit, n_rabitq_refine});
|
|
507
|
+
}
|
|
492
508
|
}
|
|
493
509
|
|
|
494
510
|
} // anonymous namespace
|
|
@@ -699,10 +715,9 @@ void IndexHNSW::search_level_0(
|
|
|
699
715
|
omp_capture_exception(ex, [&] { interrupt = true; });
|
|
700
716
|
}
|
|
701
717
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
rabitq_stats.add(rq_search_stats);
|
|
718
|
+
if (get_search_stats_enabled()) {
|
|
719
|
+
hnsw_stats.combine_atomic(search_stats);
|
|
720
|
+
rabitq_stats.add_atomic(rq_search_stats);
|
|
706
721
|
}
|
|
707
722
|
}
|
|
708
723
|
omp_rethrow_if_exception(ex);
|
|
@@ -772,7 +787,7 @@ void IndexHNSW::init_level_0_from_entry_points(
|
|
|
772
787
|
storage_distance_computer(storage));
|
|
773
788
|
std::vector<float> vec(storage->d);
|
|
774
789
|
|
|
775
|
-
#pragma omp for schedule(
|
|
790
|
+
#pragma omp for schedule(FAISS_OMP_DYNAMIC)
|
|
776
791
|
for (int i = 0; i < n; i++) {
|
|
777
792
|
storage_idx_t pt_id = points[i];
|
|
778
793
|
storage_idx_t nearest = nearests[i];
|
|
@@ -895,6 +910,433 @@ IndexHNSWFlat::IndexHNSWFlat(int d_in, int M, MetricType metric)
|
|
|
895
910
|
is_trained = true;
|
|
896
911
|
}
|
|
897
912
|
|
|
913
|
+
/**************************************************************
|
|
914
|
+
* IndexHNSW::add_level_0_links_from_sample
|
|
915
|
+
**************************************************************/
|
|
916
|
+
|
|
917
|
+
namespace {
|
|
918
|
+
|
|
919
|
+
// Relative difference below which two distances are equal: float rounding,
|
|
920
|
+
// or near-duplicate nodes. Exact duplicates give equal distances already.
|
|
921
|
+
constexpr float kSinkDistanceTolerance = 1e-5f;
|
|
922
|
+
|
|
923
|
+
// True when distance (or similarity) a is better than b by more than the
|
|
924
|
+
// tolerance.
|
|
925
|
+
bool is_better(float a, float b, bool similarity) {
|
|
926
|
+
const float tolerance =
|
|
927
|
+
kSinkDistanceTolerance * std::max(std::fabs(a), std::fabs(b));
|
|
928
|
+
return similarity ? a > b + tolerance : a < b - tolerance;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
constexpr size_t kSinkChunk = 16384;
|
|
932
|
+
|
|
933
|
+
struct SinkScan {
|
|
934
|
+
size_t n_exact_searches = 0;
|
|
935
|
+
std::vector<idx_t> sinks; // sorted
|
|
936
|
+
// For each sink, the nearest nodes of its wrong vectors with their
|
|
937
|
+
// counts, most frequent first. Ties go to the smaller node ID.
|
|
938
|
+
std::vector<std::vector<std::pair<idx_t, idx_t>>> targets;
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
// Finds the sinks from the HNSW labels of the sample vectors. Searches the
|
|
942
|
+
// vectors that arrive at candidate sinks also with the exact search, unless
|
|
943
|
+
// their exact result is already known. exact[i] is the nearer node that the
|
|
944
|
+
// exact search found for labels[i], or labels[i] if none (-1: not computed).
|
|
945
|
+
SinkScan find_sinks(
|
|
946
|
+
size_t ntotal,
|
|
947
|
+
const HNSWLinkSampleSearch& sample,
|
|
948
|
+
const HNSWLevel0LinkParameters& params,
|
|
949
|
+
const std::vector<idx_t>& labels,
|
|
950
|
+
std::vector<idx_t>& exact) {
|
|
951
|
+
const idx_t n = sample.n;
|
|
952
|
+
std::vector<idx_t> counts(ntotal, 0);
|
|
953
|
+
for (idx_t i = 0; i < n; i++) {
|
|
954
|
+
if (labels[i] >= 0) {
|
|
955
|
+
counts[labels[i]]++;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
const double min_count =
|
|
960
|
+
std::max(2.0, params.min_mean_factor * double(n) / ntotal);
|
|
961
|
+
// candidates: vectors that arrive at candidate sinks
|
|
962
|
+
std::vector<idx_t> candidates, unknown, found;
|
|
963
|
+
candidates.reserve(n);
|
|
964
|
+
for (idx_t i = 0; i < n; i++) {
|
|
965
|
+
if (labels[i] >= 0 && counts[labels[i]] >= min_count) {
|
|
966
|
+
candidates.push_back(i);
|
|
967
|
+
if (exact[i] < 0) {
|
|
968
|
+
unknown.push_back(i);
|
|
969
|
+
found.push_back(labels[i]);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
std::vector<idx_t> exact_labels(unknown.size());
|
|
974
|
+
std::vector<uint8_t> nearer(unknown.size(), 0);
|
|
975
|
+
sample.search_exact(
|
|
976
|
+
unknown.size(),
|
|
977
|
+
unknown.data(),
|
|
978
|
+
found.data(),
|
|
979
|
+
exact_labels.data(),
|
|
980
|
+
nearer.data());
|
|
981
|
+
for (size_t j = 0; j < unknown.size(); j++) {
|
|
982
|
+
exact[unknown[j]] = nearer[j] ? exact_labels[j] : found[j];
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// (sink candidate, nearest node) for each vector with a nearer node
|
|
986
|
+
std::vector<std::pair<idx_t, idx_t>> wrong;
|
|
987
|
+
wrong.reserve(candidates.size());
|
|
988
|
+
std::vector<idx_t> closer(ntotal, 0);
|
|
989
|
+
for (size_t j = 0; j < candidates.size(); j++) {
|
|
990
|
+
const idx_t i = candidates[j];
|
|
991
|
+
if (exact[i] != labels[i]) {
|
|
992
|
+
closer[labels[i]]++;
|
|
993
|
+
wrong.emplace_back(labels[i], exact[i]);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
std::sort(wrong.begin(), wrong.end());
|
|
997
|
+
|
|
998
|
+
SinkScan scan;
|
|
999
|
+
scan.n_exact_searches = unknown.size();
|
|
1000
|
+
for (size_t a = 0; a < wrong.size();) {
|
|
1001
|
+
const idx_t sink = wrong[a].first;
|
|
1002
|
+
size_t b = a;
|
|
1003
|
+
while (b < wrong.size() && wrong[b].first == sink) {
|
|
1004
|
+
b++;
|
|
1005
|
+
}
|
|
1006
|
+
if (closer[sink] >= params.min_closer_fraction * counts[sink]) {
|
|
1007
|
+
std::vector<std::pair<idx_t, idx_t>> targets;
|
|
1008
|
+
for (size_t c = a; c < b;) {
|
|
1009
|
+
size_t e = c;
|
|
1010
|
+
while (e < b && wrong[e].second == wrong[c].second) {
|
|
1011
|
+
e++;
|
|
1012
|
+
}
|
|
1013
|
+
targets.emplace_back(wrong[c].second, e - c);
|
|
1014
|
+
c = e;
|
|
1015
|
+
}
|
|
1016
|
+
std::sort(
|
|
1017
|
+
targets.begin(),
|
|
1018
|
+
targets.end(),
|
|
1019
|
+
[](const auto& p, const auto& q) {
|
|
1020
|
+
return p.second != q.second ? p.second > q.second
|
|
1021
|
+
: p.first < q.first;
|
|
1022
|
+
});
|
|
1023
|
+
scan.sinks.push_back(sink);
|
|
1024
|
+
scan.targets.push_back(std::move(targets));
|
|
1025
|
+
}
|
|
1026
|
+
a = b;
|
|
1027
|
+
}
|
|
1028
|
+
return scan;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Gives `sink` level-0 links to up to `max_links` targets that it does not
|
|
1032
|
+
// link to yet: first in free slots, then in place of its farthest links. It
|
|
1033
|
+
// keeps at least half of its links. Returns (links added, links replaced).
|
|
1034
|
+
std::pair<size_t, size_t> link_sink(
|
|
1035
|
+
HNSW& hnsw,
|
|
1036
|
+
DistanceComputer& dis,
|
|
1037
|
+
HNSW::storage_idx_t sink,
|
|
1038
|
+
const std::vector<std::pair<idx_t, idx_t>>& targets,
|
|
1039
|
+
int max_links,
|
|
1040
|
+
bool similarity) {
|
|
1041
|
+
size_t begin, end;
|
|
1042
|
+
hnsw.neighbor_range(sink, 0, &begin, &end);
|
|
1043
|
+
size_t degree = 0;
|
|
1044
|
+
while (begin + degree < end && hnsw.neighbors[begin + degree] >= 0) {
|
|
1045
|
+
degree++;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
std::vector<HNSW::storage_idx_t> fresh;
|
|
1049
|
+
for (const auto& [target, count] : targets) {
|
|
1050
|
+
if (static_cast<int>(fresh.size()) >= max_links) {
|
|
1051
|
+
break;
|
|
1052
|
+
}
|
|
1053
|
+
bool linked = target == sink;
|
|
1054
|
+
for (size_t k = begin; k < begin + degree && !linked; k++) {
|
|
1055
|
+
linked = hnsw.neighbors[k] == target;
|
|
1056
|
+
}
|
|
1057
|
+
if (!linked) {
|
|
1058
|
+
fresh.push_back(static_cast<HNSW::storage_idx_t>(target));
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
const size_t added = std::min(fresh.size(), (end - begin) - degree);
|
|
1063
|
+
for (size_t k = 0; k < added; k++) {
|
|
1064
|
+
hnsw.neighbors[begin + degree + k] = fresh[k];
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
size_t replaced = 0;
|
|
1068
|
+
if (added < fresh.size()) {
|
|
1069
|
+
// (distance from the sink, slot), farthest first
|
|
1070
|
+
std::vector<std::pair<float, size_t>> links;
|
|
1071
|
+
for (size_t k = begin; k < begin + degree; k++) {
|
|
1072
|
+
links.emplace_back(dis.symmetric_dis(sink, hnsw.neighbors[k]), k);
|
|
1073
|
+
}
|
|
1074
|
+
std::sort(
|
|
1075
|
+
links.begin(), links.end(), [&](const auto& p, const auto& q) {
|
|
1076
|
+
if (p.first != q.first) {
|
|
1077
|
+
return similarity ? p.first < q.first
|
|
1078
|
+
: p.first > q.first;
|
|
1079
|
+
}
|
|
1080
|
+
return p.second < q.second;
|
|
1081
|
+
});
|
|
1082
|
+
const size_t replaceable = degree / 2;
|
|
1083
|
+
for (size_t k = added; k < fresh.size() && replaced < replaceable;
|
|
1084
|
+
k++, replaced++) {
|
|
1085
|
+
hnsw.neighbors[links[replaced].second] = fresh[k];
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
return {added, replaced};
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// The sample of an IndexHNSW: fp32 or packed fp16 rows, or rows encoded by a
|
|
1092
|
+
// codec.
|
|
1093
|
+
struct IndexHNSWLinkSample : HNSWLinkSampleSearch {
|
|
1094
|
+
const IndexHNSW& index;
|
|
1095
|
+
const uint8_t* x;
|
|
1096
|
+
NumericType numeric_type;
|
|
1097
|
+
const Index* codec;
|
|
1098
|
+
|
|
1099
|
+
IndexHNSWLinkSample(
|
|
1100
|
+
const IndexHNSW& index,
|
|
1101
|
+
idx_t n,
|
|
1102
|
+
const uint8_t* x,
|
|
1103
|
+
NumericType numeric_type,
|
|
1104
|
+
const Index* codec)
|
|
1105
|
+
: index(index), x(x), numeric_type(numeric_type), codec(codec) {
|
|
1106
|
+
this->n = n;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
size_t line_size() const {
|
|
1110
|
+
return codec ? codec->sa_code_size()
|
|
1111
|
+
: get_numeric_type_size(numeric_type) * index.d;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// Decodes m contiguous rows to fp32.
|
|
1115
|
+
void decode(size_t m, const uint8_t* rows, float* out) const {
|
|
1116
|
+
if (codec) {
|
|
1117
|
+
codec->sa_decode(m, rows, out);
|
|
1118
|
+
} else if (numeric_type == NumericType::Float16) {
|
|
1119
|
+
fp16_to_fp32(
|
|
1120
|
+
m * index.d, reinterpret_cast<const uint16_t*>(rows), out);
|
|
1121
|
+
} else {
|
|
1122
|
+
memcpy(out, rows, m * index.d * sizeof(float));
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
void decode_rows(
|
|
1127
|
+
size_t begin,
|
|
1128
|
+
size_t m,
|
|
1129
|
+
const idx_t* ids,
|
|
1130
|
+
std::vector<uint8_t>& rows,
|
|
1131
|
+
float* out) const {
|
|
1132
|
+
const size_t ls = line_size();
|
|
1133
|
+
if (!ids) {
|
|
1134
|
+
decode(m, x + begin * ls, out);
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
if (!codec && numeric_type == NumericType::Float32) {
|
|
1138
|
+
for (size_t j = 0; j < m; j++) {
|
|
1139
|
+
memcpy(out + j * index.d, x + ids[begin + j] * ls, ls);
|
|
1140
|
+
}
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
rows.resize(m * ls);
|
|
1144
|
+
for (size_t j = 0; j < m; j++) {
|
|
1145
|
+
memcpy(rows.data() + j * ls, x + ids[begin + j] * ls, ls);
|
|
1146
|
+
}
|
|
1147
|
+
decode(m, rows.data(), out);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// Decodes the sample vectors by blocks of kSinkChunk.
|
|
1151
|
+
void search(
|
|
1152
|
+
const SearchParametersHNSW* params,
|
|
1153
|
+
size_t m_total,
|
|
1154
|
+
const idx_t* ids,
|
|
1155
|
+
idx_t* labels) const override {
|
|
1156
|
+
if (!ids && !codec && numeric_type == NumericType::Float32) {
|
|
1157
|
+
std::vector<float> distances(m_total);
|
|
1158
|
+
index.search(
|
|
1159
|
+
m_total,
|
|
1160
|
+
reinterpret_cast<const float*>(x),
|
|
1161
|
+
1,
|
|
1162
|
+
distances.data(),
|
|
1163
|
+
labels,
|
|
1164
|
+
params);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
std::vector<uint8_t> rows;
|
|
1168
|
+
std::vector<float> block, distances(kSinkChunk);
|
|
1169
|
+
for (size_t i0 = 0; i0 < m_total; i0 += kSinkChunk) {
|
|
1170
|
+
const size_t m = std::min(kSinkChunk, m_total - i0);
|
|
1171
|
+
block.resize(m * index.d);
|
|
1172
|
+
decode_rows(i0, m, ids, rows, block.data());
|
|
1173
|
+
index.search(
|
|
1174
|
+
m, block.data(), 1, distances.data(), labels + i0, params);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
void search_exact(
|
|
1179
|
+
size_t m_total,
|
|
1180
|
+
const idx_t* ids,
|
|
1181
|
+
const idx_t* found,
|
|
1182
|
+
idx_t* exact,
|
|
1183
|
+
uint8_t* nearer) const override {
|
|
1184
|
+
const size_t d = index.d;
|
|
1185
|
+
const bool similarity = is_similarity_metric(index.metric_type);
|
|
1186
|
+
std::vector<float> exact_distances(kSinkChunk);
|
|
1187
|
+
std::vector<uint8_t> rows;
|
|
1188
|
+
std::vector<float> chunk;
|
|
1189
|
+
for (size_t begin = 0; begin < m_total; begin += kSinkChunk) {
|
|
1190
|
+
const size_t m = std::min(kSinkChunk, m_total - begin);
|
|
1191
|
+
chunk.resize(m * d);
|
|
1192
|
+
decode_rows(begin, m, ids, rows, chunk.data());
|
|
1193
|
+
index.storage->search(
|
|
1194
|
+
m, chunk.data(), 1, exact_distances.data(), exact + begin);
|
|
1195
|
+
|
|
1196
|
+
// Compute both distances again with the same distance function.
|
|
1197
|
+
// The batched exact search and the HNSW search round
|
|
1198
|
+
// differently, which can make a node look nearer when the two
|
|
1199
|
+
// distances are almost equal.
|
|
1200
|
+
#pragma omp parallel
|
|
1201
|
+
{
|
|
1202
|
+
std::unique_ptr<DistanceComputer> dis(
|
|
1203
|
+
storage_distance_computer(index.storage));
|
|
1204
|
+
#pragma omp for schedule(static)
|
|
1205
|
+
for (int64_t j = 0; j < static_cast<int64_t>(m); j++) {
|
|
1206
|
+
const idx_t e = exact[begin + j], f = found[begin + j];
|
|
1207
|
+
if (e < 0 || e == f) {
|
|
1208
|
+
continue;
|
|
1209
|
+
}
|
|
1210
|
+
dis->set_query(chunk.data() + j * d);
|
|
1211
|
+
nearer[begin + j] =
|
|
1212
|
+
is_better((*dis)(e), (*dis)(f), similarity);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
HNSWLevel0LinkStats add_level_0_links_impl(
|
|
1220
|
+
IndexHNSW& index,
|
|
1221
|
+
const IndexHNSWLinkSample& sample,
|
|
1222
|
+
const HNSWLevel0LinkParameters& params) {
|
|
1223
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
1224
|
+
index.storage, "add_level_0_links_from_sample needs storage");
|
|
1225
|
+
std::unique_ptr<DistanceComputer> dis(
|
|
1226
|
+
storage_distance_computer(index.storage));
|
|
1227
|
+
return hnsw_add_level_0_links_from_sample(
|
|
1228
|
+
index.hnsw,
|
|
1229
|
+
index.ntotal,
|
|
1230
|
+
*dis,
|
|
1231
|
+
is_similarity_metric(index.metric_type),
|
|
1232
|
+
sample,
|
|
1233
|
+
params);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
} // namespace
|
|
1237
|
+
|
|
1238
|
+
HNSWLevel0LinkStats hnsw_add_level_0_links_from_sample(
|
|
1239
|
+
HNSW& hnsw,
|
|
1240
|
+
size_t ntotal,
|
|
1241
|
+
DistanceComputer& dis,
|
|
1242
|
+
bool similarity,
|
|
1243
|
+
const HNSWLinkSampleSearch& sample,
|
|
1244
|
+
const HNSWLevel0LinkParameters& params) {
|
|
1245
|
+
FAISS_THROW_IF_NOT(params.max_rounds >= 0 && params.max_links_per_sink > 0);
|
|
1246
|
+
HNSWLevel0LinkStats stats;
|
|
1247
|
+
if (ntotal == 0 || sample.n == 0) {
|
|
1248
|
+
return stats;
|
|
1249
|
+
}
|
|
1250
|
+
std::vector<idx_t> labels(sample.n), exact(sample.n, -1);
|
|
1251
|
+
sample.search(params.search_params, sample.n, nullptr, labels.data());
|
|
1252
|
+
for (int round = 0;; round++) {
|
|
1253
|
+
const SinkScan scan = find_sinks(ntotal, sample, params, labels, exact);
|
|
1254
|
+
stats.n_exact_searches += scan.n_exact_searches;
|
|
1255
|
+
if (round == 0) {
|
|
1256
|
+
stats.n_sinks_found = scan.sinks.size();
|
|
1257
|
+
}
|
|
1258
|
+
stats.n_sinks_remaining = scan.sinks.size();
|
|
1259
|
+
if (scan.sinks.empty() || round == params.max_rounds) {
|
|
1260
|
+
break;
|
|
1261
|
+
}
|
|
1262
|
+
size_t changed = 0;
|
|
1263
|
+
for (size_t i = 0; i < scan.sinks.size(); i++) {
|
|
1264
|
+
const auto [added, replaced] = link_sink(
|
|
1265
|
+
hnsw,
|
|
1266
|
+
dis,
|
|
1267
|
+
static_cast<HNSW::storage_idx_t>(scan.sinks[i]),
|
|
1268
|
+
scan.targets[i],
|
|
1269
|
+
params.max_links_per_sink,
|
|
1270
|
+
similarity);
|
|
1271
|
+
stats.n_links_added += added;
|
|
1272
|
+
stats.n_links_replaced += replaced;
|
|
1273
|
+
changed += added + replaced;
|
|
1274
|
+
}
|
|
1275
|
+
if (changed == 0) {
|
|
1276
|
+
break;
|
|
1277
|
+
}
|
|
1278
|
+
stats.n_rounds++;
|
|
1279
|
+
|
|
1280
|
+
// The new links leave from the sinks, so search again only the
|
|
1281
|
+
// vectors that arrived at a sink.
|
|
1282
|
+
// Approximate: searches that only pass through a sink can change too.
|
|
1283
|
+
std::vector<idx_t> ids;
|
|
1284
|
+
for (idx_t i = 0; i < sample.n; i++) {
|
|
1285
|
+
if (std::binary_search(
|
|
1286
|
+
scan.sinks.begin(), scan.sinks.end(), labels[i])) {
|
|
1287
|
+
ids.push_back(i);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
std::vector<idx_t> found(ids.size());
|
|
1291
|
+
sample.search(
|
|
1292
|
+
params.search_params, ids.size(), ids.data(), found.data());
|
|
1293
|
+
for (size_t j = 0; j < ids.size(); j++) {
|
|
1294
|
+
if (labels[ids[j]] != found[j]) {
|
|
1295
|
+
labels[ids[j]] = found[j];
|
|
1296
|
+
exact[ids[j]] = -1;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
return stats;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
HNSWLevel0LinkStats IndexHNSW::add_level_0_links_from_sample(
|
|
1304
|
+
idx_t n,
|
|
1305
|
+
const float* x,
|
|
1306
|
+
const HNSWLevel0LinkParameters& params) {
|
|
1307
|
+
return add_level_0_links_from_sample_ex(n, x, NumericType::Float32, params);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
HNSWLevel0LinkStats IndexHNSW::add_level_0_links_from_sample_ex(
|
|
1311
|
+
idx_t n,
|
|
1312
|
+
const void* x,
|
|
1313
|
+
NumericType numeric_type,
|
|
1314
|
+
const HNSWLevel0LinkParameters& params) {
|
|
1315
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
1316
|
+
numeric_type == NumericType::Float32 ||
|
|
1317
|
+
numeric_type == NumericType::Float16,
|
|
1318
|
+
"add_level_0_links_from_sample_ex supports Float32 and Float16");
|
|
1319
|
+
const IndexHNSWLinkSample sample(
|
|
1320
|
+
*this, n, static_cast<const uint8_t*>(x), numeric_type, nullptr);
|
|
1321
|
+
return add_level_0_links_impl(*this, sample, params);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
HNSWLevel0LinkStats IndexHNSW::add_level_0_links_from_sample_encoded(
|
|
1325
|
+
idx_t n,
|
|
1326
|
+
const uint8_t* x,
|
|
1327
|
+
const Index* codec,
|
|
1328
|
+
const HNSWLevel0LinkParameters& params) {
|
|
1329
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
1330
|
+
codec, "add_level_0_links_from_sample_encoded needs a codec");
|
|
1331
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1332
|
+
codec->d == d,
|
|
1333
|
+
"codec dimension %d is not the index dimension %d",
|
|
1334
|
+
int(codec->d),
|
|
1335
|
+
int(d));
|
|
1336
|
+
const IndexHNSWLinkSample sample(*this, n, x, NumericType::Float32, codec);
|
|
1337
|
+
return add_level_0_links_impl(*this, sample, params);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
898
1340
|
/**************************************************************
|
|
899
1341
|
* IndexHNSWFlatPanorama implementation
|
|
900
1342
|
**************************************************************/
|
|
@@ -1230,7 +1672,9 @@ void IndexHNSW2Level::search(
|
|
|
1230
1672
|
}
|
|
1231
1673
|
omp_rethrow_if_exception(ex);
|
|
1232
1674
|
|
|
1233
|
-
|
|
1675
|
+
if (get_search_stats_enabled()) {
|
|
1676
|
+
hnsw_stats.combine_atomic({n1, n2, ndis, nhops});
|
|
1677
|
+
}
|
|
1234
1678
|
}
|
|
1235
1679
|
}
|
|
1236
1680
|
|
|
@@ -1432,7 +1876,9 @@ void IndexHNSWCagra::range_search(
|
|
|
1432
1876
|
result->do_allocation();
|
|
1433
1877
|
pres.copy_result();
|
|
1434
1878
|
|
|
1435
|
-
|
|
1879
|
+
if (get_search_stats_enabled()) {
|
|
1880
|
+
hnsw_stats.combine_atomic({n1, n2, ndis, nhops});
|
|
1881
|
+
}
|
|
1436
1882
|
};
|
|
1437
1883
|
|
|
1438
1884
|
if (is_similarity_metric(metric_type)) {
|
|
@@ -49,6 +49,15 @@ struct IndexHNSW : Index {
|
|
|
49
49
|
// used when GpuIndexCagra::copyFrom(IndexHNSWCagra*) is invoked.
|
|
50
50
|
bool keep_max_size_level0 = false;
|
|
51
51
|
|
|
52
|
+
// When this index is the coarse quantizer of an IndexIVF, IndexIVF::train()
|
|
53
|
+
// calls add_level_0_links_from_sample() after it adds the centroids. The
|
|
54
|
+
// sample is the first min(n, level_0_link_samples_per_node * ntotal)
|
|
55
|
+
// training vectors, in the numeric type or encoding of the training data.
|
|
56
|
+
// 0 disables the call. Training does not call it when
|
|
57
|
+
// quantizer_trains_alone == 1 or when the quantizer is already trained.
|
|
58
|
+
// The index format does not store this value.
|
|
59
|
+
int level_0_link_samples_per_node = 3;
|
|
60
|
+
|
|
52
61
|
// See impl/VisitedTable.h.
|
|
53
62
|
std::optional<bool> use_visited_hashset;
|
|
54
63
|
|
|
@@ -123,6 +132,35 @@ struct IndexHNSW : Index {
|
|
|
123
132
|
|
|
124
133
|
void link_singletons();
|
|
125
134
|
|
|
135
|
+
/** Adds level-0 links so that the search no longer stops at "sinks":
|
|
136
|
+
* nodes where it stops for many vectors whose nearest node is elsewhere.
|
|
137
|
+
* With a small efSearch, HNSW as a coarse quantizer will overfill the
|
|
138
|
+
* IVF lists. Call this function after adding all centroids to this
|
|
139
|
+
* quantizer. Called automatically through IndexIVF::train.
|
|
140
|
+
*/
|
|
141
|
+
HNSWLevel0LinkStats add_level_0_links_from_sample(
|
|
142
|
+
idx_t n,
|
|
143
|
+
const float* x,
|
|
144
|
+
const HNSWLevel0LinkParameters& params =
|
|
145
|
+
HNSWLevel0LinkParameters());
|
|
146
|
+
|
|
147
|
+
/// add_level_0_links_from_sample() for Float32 or packed Float16 vectors
|
|
148
|
+
HNSWLevel0LinkStats add_level_0_links_from_sample_ex(
|
|
149
|
+
idx_t n,
|
|
150
|
+
const void* x,
|
|
151
|
+
NumericType numeric_type,
|
|
152
|
+
const HNSWLevel0LinkParameters& params =
|
|
153
|
+
HNSWLevel0LinkParameters());
|
|
154
|
+
|
|
155
|
+
/// add_level_0_links_from_sample() for n vectors that `codec` encoded with
|
|
156
|
+
/// sa_encode()
|
|
157
|
+
HNSWLevel0LinkStats add_level_0_links_from_sample_encoded(
|
|
158
|
+
idx_t n,
|
|
159
|
+
const uint8_t* x,
|
|
160
|
+
const Index* codec,
|
|
161
|
+
const HNSWLevel0LinkParameters& params =
|
|
162
|
+
HNSWLevel0LinkParameters());
|
|
163
|
+
|
|
126
164
|
virtual void permute_entries(const idx_t* perm);
|
|
127
165
|
|
|
128
166
|
DistanceComputer* get_distance_computer() const override;
|
|
@@ -96,11 +96,18 @@ void IndexIDMapTemplate<IndexT>::train(
|
|
|
96
96
|
component_t_to_numeric<typename IndexT::component_t>());
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
template <typename IndexT>
|
|
100
|
+
void IndexIDMapTemplate<IndexT>::train(const gpu::Dataset& dataset) {
|
|
101
|
+
index->train(dataset);
|
|
102
|
+
this->is_trained = index->is_trained;
|
|
103
|
+
}
|
|
104
|
+
|
|
99
105
|
template <typename IndexT>
|
|
100
106
|
void IndexIDMapTemplate<IndexT>::reset() {
|
|
101
107
|
index->reset();
|
|
102
108
|
id_map.clear();
|
|
103
109
|
this->ntotal = 0;
|
|
110
|
+
this->is_trained = index->is_trained;
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
template <typename IndexT>
|
|
@@ -128,6 +135,20 @@ void IndexIDMapTemplate<IndexT>::add_with_ids(
|
|
|
128
135
|
xids);
|
|
129
136
|
}
|
|
130
137
|
|
|
138
|
+
template <typename IndexT>
|
|
139
|
+
void IndexIDMapTemplate<IndexT>::add_with_ids(
|
|
140
|
+
const gpu::Dataset& dataset,
|
|
141
|
+
const idx_t* xids) {
|
|
142
|
+
index->add(dataset);
|
|
143
|
+
FAISS_THROW_IF_NOT(index->ntotal >= this->ntotal);
|
|
144
|
+
// CAGRA may already have built the graph during train(dataset).
|
|
145
|
+
for (idx_t i = 0; i < index->ntotal - this->ntotal; i++) {
|
|
146
|
+
id_map.push_back(xids[i]);
|
|
147
|
+
}
|
|
148
|
+
this->ntotal = index->ntotal;
|
|
149
|
+
this->is_trained = index->is_trained;
|
|
150
|
+
}
|
|
151
|
+
|
|
131
152
|
template <typename IndexT>
|
|
132
153
|
size_t IndexIDMapTemplate<IndexT>::sa_code_size() const {
|
|
133
154
|
return index->sa_code_size();
|
|
@@ -335,6 +356,17 @@ void IndexIDMap2Template<IndexT>::add_with_ids(
|
|
|
335
356
|
xids);
|
|
336
357
|
}
|
|
337
358
|
|
|
359
|
+
template <typename IndexT>
|
|
360
|
+
void IndexIDMap2Template<IndexT>::add_with_ids(
|
|
361
|
+
const gpu::Dataset& dataset,
|
|
362
|
+
const idx_t* xids) {
|
|
363
|
+
idx_t prev_ntotal = this->ntotal;
|
|
364
|
+
IndexIDMapTemplate<IndexT>::add_with_ids(dataset, xids);
|
|
365
|
+
for (idx_t i = prev_ntotal; i < this->ntotal; i++) {
|
|
366
|
+
rev_map[this->id_map[i]] = i;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
338
370
|
template <typename IndexT>
|
|
339
371
|
void IndexIDMap2Template<IndexT>::add_sa_codes(
|
|
340
372
|
idx_t n,
|
|
@@ -37,6 +37,10 @@ struct IndexIDMapTemplate : IndexT {
|
|
|
37
37
|
NumericType numeric_type,
|
|
38
38
|
const idx_t* xids) override;
|
|
39
39
|
|
|
40
|
+
/// Add a Dataset and assign one xids entry per unmapped vector, including
|
|
41
|
+
/// vectors already built by train(dataset).
|
|
42
|
+
virtual void add_with_ids(const gpu::Dataset& dataset, const idx_t* xids);
|
|
43
|
+
|
|
40
44
|
/// this will fail. Use add_with_ids
|
|
41
45
|
void add(idx_t n, const component_t* x) override;
|
|
42
46
|
void add_ex(idx_t n, const void* x, NumericType numeric_type) override;
|
|
@@ -58,6 +62,7 @@ struct IndexIDMapTemplate : IndexT {
|
|
|
58
62
|
const SearchParameters* params = nullptr) const override;
|
|
59
63
|
|
|
60
64
|
void train(idx_t n, const component_t* x) override;
|
|
65
|
+
void train(const gpu::Dataset& dataset) override;
|
|
61
66
|
void train_ex(idx_t n, const void* x, NumericType numeric_type) override;
|
|
62
67
|
|
|
63
68
|
void reset() override;
|
|
@@ -102,6 +107,8 @@ struct IndexIDMap2Template : IndexIDMapTemplate<IndexT> {
|
|
|
102
107
|
/// make the rev_map from scratch
|
|
103
108
|
void construct_rev_map();
|
|
104
109
|
|
|
110
|
+
void add_with_ids(const gpu::Dataset& dataset, const idx_t* xids) override;
|
|
111
|
+
|
|
105
112
|
void add_with_ids(idx_t n, const component_t* x, const idx_t* xids)
|
|
106
113
|
override;
|
|
107
114
|
void add_with_ids_ex(
|