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.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/ext/faiss/extconf.rb +1 -1
  4. data/lib/faiss/version.rb +1 -1
  5. data/vendor/faiss/faiss/AutoTune.cpp +175 -0
  6. data/vendor/faiss/faiss/Clustering.cpp +150 -60
  7. data/vendor/faiss/faiss/Clustering.h +29 -0
  8. data/vendor/faiss/faiss/IVFlib.cpp +99 -0
  9. data/vendor/faiss/faiss/IVFlib.h +15 -0
  10. data/vendor/faiss/faiss/Index.cpp +44 -0
  11. data/vendor/faiss/faiss/Index.h +19 -14
  12. data/vendor/faiss/faiss/IndexBinary.cpp +8 -0
  13. data/vendor/faiss/faiss/IndexBinary.h +8 -0
  14. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +82 -4
  15. data/vendor/faiss/faiss/IndexBinaryHNSW.h +19 -0
  16. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +34 -0
  17. data/vendor/faiss/faiss/IndexFastScan.cpp +33 -17
  18. data/vendor/faiss/faiss/IndexFastScan.h +6 -3
  19. data/vendor/faiss/faiss/IndexFlat.cpp +56 -0
  20. data/vendor/faiss/faiss/IndexFlat.h +31 -0
  21. data/vendor/faiss/faiss/IndexFlatCodes.cpp +6 -1
  22. data/vendor/faiss/faiss/IndexHNSW.cpp +456 -10
  23. data/vendor/faiss/faiss/IndexHNSW.h +38 -0
  24. data/vendor/faiss/faiss/IndexIDMap.cpp +32 -0
  25. data/vendor/faiss/faiss/IndexIDMap.h +7 -0
  26. data/vendor/faiss/faiss/IndexIVF.cpp +281 -31
  27. data/vendor/faiss/faiss/IndexIVF.h +53 -1
  28. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +46 -14
  29. data/vendor/faiss/faiss/IndexIVFFastScan.h +4 -2
  30. data/vendor/faiss/faiss/IndexIVFFlat.cpp +33 -0
  31. data/vendor/faiss/faiss/IndexIVFFlat.h +16 -0
  32. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +1 -0
  33. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +3 -20
  34. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +50 -14
  35. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -2
  36. data/vendor/faiss/faiss/IndexIVFSQFastScan.cpp +855 -0
  37. data/vendor/faiss/faiss/IndexIVFSQFastScan.h +145 -0
  38. data/vendor/faiss/faiss/IndexNNDescent.cpp +0 -3
  39. data/vendor/faiss/faiss/IndexPiPNN.cpp +201 -0
  40. data/vendor/faiss/faiss/IndexPiPNN.h +83 -0
  41. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +6 -3
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +10 -4
  43. data/vendor/faiss/faiss/IndexReplicas.cpp +20 -0
  44. data/vendor/faiss/faiss/IndexReplicas.h +9 -0
  45. data/vendor/faiss/faiss/IndexSQFastScan.cpp +648 -0
  46. data/vendor/faiss/faiss/IndexSQFastScan.h +129 -0
  47. data/vendor/faiss/faiss/IndexShards.cpp +10 -0
  48. data/vendor/faiss/faiss/IndexShards.h +5 -0
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +57 -15
  50. data/vendor/faiss/faiss/SuperKMeans.h +9 -4
  51. data/vendor/faiss/faiss/VectorTransform.cpp +3 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +24 -0
  53. data/vendor/faiss/faiss/gpu/Dataset.h +112 -0
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +16 -13
  55. data/vendor/faiss/faiss/gpu/GpuCloner.h +4 -0
  56. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +3 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndex.h +4 -13
  58. data/vendor/faiss/faiss/gpu/GpuIndexBinaryCagra.h +26 -13
  59. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +45 -22
  60. data/vendor/faiss/faiss/gpu/GpuIndexFlat.h +11 -0
  61. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +6 -0
  62. data/vendor/faiss/faiss/gpu/test/TestGpuDatasetHeader.cpp +18 -0
  63. data/vendor/faiss/faiss/gpu/test/TestGpuIndexFlat.cpp +215 -0
  64. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +98 -0
  65. data/vendor/faiss/faiss/gpu/utils/DeviceUtils.h +11 -0
  66. data/vendor/faiss/faiss/gpu/utils/StackDeviceMemory.cpp +1 -1
  67. data/vendor/faiss/faiss/gpu_metal/MetalCloner.h +11 -0
  68. data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +92 -0
  69. data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +4 -0
  70. data/vendor/faiss/faiss/gpu_metal/MetalIndexBinaryFlat.h +69 -0
  71. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +38 -0
  72. data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +106 -0
  73. data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +10 -0
  74. data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +2 -0
  75. data/vendor/faiss/faiss/impl/AuxIndexStructures.h +1 -6
  76. data/vendor/faiss/faiss/impl/ClusteringHelpers.cpp +134 -45
  77. data/vendor/faiss/faiss/impl/ClusteringHelpers.h +36 -0
  78. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +286 -136
  79. data/vendor/faiss/faiss/impl/DistanceComputer.cpp +63 -0
  80. data/vendor/faiss/faiss/impl/DistanceComputer.h +3 -29
  81. data/vendor/faiss/faiss/impl/HNSW.cpp +46 -10
  82. data/vendor/faiss/faiss/impl/HNSW.h +72 -0
  83. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +6 -19
  84. data/vendor/faiss/faiss/impl/NNDescent.cpp +2 -1
  85. data/vendor/faiss/faiss/impl/NSG.cpp +3 -2
  86. data/vendor/faiss/faiss/impl/Panorama.h +3 -2
  87. data/vendor/faiss/faiss/impl/PiPNN.cpp +822 -0
  88. data/vendor/faiss/faiss/impl/PiPNN.h +122 -0
  89. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +54 -11
  90. data/vendor/faiss/faiss/impl/RaBitQUtils.h +1 -1
  91. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +81 -57
  92. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +15 -0
  93. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +29 -0
  94. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +1 -6
  95. data/vendor/faiss/faiss/impl/approx_topk/avx2.cpp +0 -4
  96. data/vendor/faiss/faiss/impl/approx_topk/neon.cpp +0 -4
  97. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHash_impl.h +5 -2
  98. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +0 -4
  99. data/vendor/faiss/faiss/impl/binary_hamming/avx512.cpp +0 -4
  100. data/vendor/faiss/faiss/impl/binary_hamming/neon.cpp +0 -4
  101. data/vendor/faiss/faiss/impl/binary_hamming/rvv.cpp +0 -4
  102. data/vendor/faiss/faiss/impl/expanded_scanners.h +88 -0
  103. data/vendor/faiss/faiss/impl/fast_scan/FastScanDistancePostProcessing.h +4 -0
  104. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops.h +61 -64
  105. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops_512.h +13 -14
  106. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +17 -129
  107. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +25 -19
  108. data/vendor/faiss/faiss/impl/fast_scan/fast_scan.cpp +5 -0
  109. data/vendor/faiss/faiss/impl/fast_scan/impl-avx2.cpp +0 -4
  110. data/vendor/faiss/faiss/impl/fast_scan/impl-avx512.cpp +0 -4
  111. data/vendor/faiss/faiss/impl/fast_scan/impl-neon.cpp +0 -88
  112. data/vendor/faiss/faiss/impl/fast_scan/impl-riscv.cpp +0 -4
  113. data/vendor/faiss/faiss/impl/fast_scan/kernels_neon.h +155 -0
  114. data/vendor/faiss/faiss/impl/fast_scan/kernels_simd256.h +22 -1
  115. data/vendor/faiss/faiss/impl/fast_scan/kernels_simd512.h +3 -2
  116. data/vendor/faiss/faiss/impl/fast_scan/simd_result_handlers.h +41 -23
  117. data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +0 -4
  118. data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +0 -4
  119. data/vendor/faiss/faiss/impl/index_read.cpp +172 -0
  120. data/vendor/faiss/faiss/impl/index_write.cpp +95 -0
  121. data/vendor/faiss/faiss/impl/pipnn/HashPrune.cpp +159 -0
  122. data/vendor/faiss/faiss/impl/pipnn/HashPrune.h +69 -0
  123. data/vendor/faiss/faiss/impl/pipnn/Partition.cpp +587 -0
  124. data/vendor/faiss/faiss/impl/pipnn/Partition.h +93 -0
  125. data/vendor/faiss/faiss/impl/pipnn/kernels.cpp +326 -0
  126. data/vendor/faiss/faiss/impl/pipnn/kernels.h +92 -0
  127. data/vendor/faiss/faiss/impl/platform_macros.h +45 -0
  128. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +0 -4
  129. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +79 -4
  130. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +6 -0
  131. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +0 -4
  132. data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +0 -4
  133. data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +0 -4
  134. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx2.h +4 -0
  135. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx512.h +4 -1
  136. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +19 -0
  137. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.h +6 -6
  138. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +131 -6
  139. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +0 -4
  140. data/vendor/faiss/faiss/impl/pq_code_distance/pq_rvv_kernel.h +89 -0
  141. data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +21 -10
  142. data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +45 -18
  143. data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +2 -1
  144. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +0 -4
  145. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +0 -4
  146. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +0 -4
  147. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +0 -4
  148. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +5 -1
  149. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +28 -52
  150. data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +59 -11
  151. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +226 -77
  152. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +482 -103
  153. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +2 -149
  154. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +3 -698
  155. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +66 -57
  156. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +101 -96
  157. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +3185 -144
  158. data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_coefficients.h +48 -0
  159. data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_rvv_kernel.h +546 -0
  160. data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +2 -2
  161. data/vendor/faiss/faiss/impl/simd_dispatch.h +7 -0
  162. data/vendor/faiss/faiss/impl/simdlib/simdlib_avx2.h +14 -0
  163. data/vendor/faiss/faiss/impl/simdlib/simdlib_dispatch.h +7 -8
  164. data/vendor/faiss/faiss/impl/sq_fastscan_utils.h +232 -0
  165. data/vendor/faiss/faiss/index_factory.cpp +76 -0
  166. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +3 -1
  167. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +3 -2
  168. data/vendor/faiss/faiss/utils/AlignedTable.h +6 -1
  169. data/vendor/faiss/faiss/utils/Heap.cpp +4 -3
  170. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +16 -5
  171. data/vendor/faiss/faiss/utils/bf16.h +36 -0
  172. data/vendor/faiss/faiss/utils/distances.cpp +226 -15
  173. data/vendor/faiss/faiss/utils/distances.h +34 -0
  174. data/vendor/faiss/faiss/utils/distances_dispatch.h +6 -4
  175. data/vendor/faiss/faiss/utils/distances_fused/avx512.cpp +2 -5
  176. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +0 -14
  177. data/vendor/faiss/faiss/utils/distances_fused/rvv.cpp +25 -0
  178. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based.cpp +0 -4
  179. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based_neon.cpp +0 -4
  180. data/vendor/faiss/faiss/utils/distances_fused/simdlib_kernel-inl.h +2 -1
  181. data/vendor/faiss/faiss/utils/distances_simd.cpp +9 -0
  182. data/vendor/faiss/faiss/utils/fp16-fp16c.h +4 -0
  183. data/vendor/faiss/faiss/utils/fp16-inl.h +26 -52
  184. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +0 -4
  185. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512.cpp +0 -4
  186. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +0 -4
  187. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +33 -5
  188. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_vpopcnt.h +15 -30
  189. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-neon.h +0 -4
  190. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +2 -1
  191. data/vendor/faiss/faiss/utils/hamming_distance/hamming_neon.cpp +0 -4
  192. data/vendor/faiss/faiss/utils/hamming_distance/hamming_rvv.cpp +0 -4
  193. data/vendor/faiss/faiss/utils/pq_code_distance.h +8 -4
  194. data/vendor/faiss/faiss/utils/prefetch.h +1 -26
  195. data/vendor/faiss/faiss/utils/quantize_lut.cpp +26 -27
  196. data/vendor/faiss/faiss/utils/quantize_lut.h +11 -0
  197. data/vendor/faiss/faiss/utils/rabitq_simd.h +51 -16
  198. data/vendor/faiss/faiss/utils/simd_impl/distances_aarch64.cpp +9 -4
  199. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +37 -5
  200. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +64 -5
  201. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +64 -5
  202. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +353 -71
  203. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv_kernel.h +68 -0
  204. data/vendor/faiss/faiss/utils/simd_impl/exhaustive_L2sqr_blas_cmax.h +93 -0
  205. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels.h +76 -0
  206. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx2.cpp +52 -0
  207. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx512.cpp +52 -0
  208. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_neon.cpp +48 -0
  209. data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx2.h +44 -0
  210. data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx512.h +43 -0
  211. data/vendor/faiss/faiss/utils/simd_impl/masked_sum.h +38 -0
  212. data/vendor/faiss/faiss/utils/simd_impl/partitioning_avx2.cpp +0 -4
  213. data/vendor/faiss/faiss/utils/simd_impl/partitioning_neon.cpp +0 -4
  214. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +54 -15
  215. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +38 -21
  216. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_vpopcnt.cpp +51 -14
  217. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +297 -10
  218. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +0 -4
  219. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx2.cpp +0 -4
  220. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx512.cpp +0 -4
  221. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +0 -4
  222. data/vendor/faiss/faiss/utils/simd_levels.cpp +19 -3
  223. data/vendor/faiss/faiss/utils/simd_levels.h +74 -1
  224. data/vendor/faiss/faiss/utils/transpose/transpose-avx2-inl.h +0 -4
  225. data/vendor/faiss/faiss/utils/transpose/transpose-avx512-inl.h +0 -4
  226. data/vendor/faiss/faiss/utils/utils.cpp +80 -0
  227. data/vendor/faiss/faiss/utils/utils.h +24 -0
  228. metadata +33 -1
@@ -8,6 +8,7 @@
8
8
  #include <faiss/IVFlib.h>
9
9
  #include <omp.h>
10
10
 
11
+ #include <algorithm>
11
12
  #include <memory>
12
13
 
13
14
  #include <faiss/IndexAdditiveQuantizer.h>
@@ -20,6 +21,7 @@
20
21
  #include <faiss/impl/FaissAssert.h>
21
22
  #include <faiss/impl/simd_dispatch.h>
22
23
  #include <faiss/index_io.h>
24
+ #include <faiss/utils/Heap.h>
23
25
  #include <faiss/utils/distances.h>
24
26
  #include <faiss/utils/hamming.h>
25
27
  #include <faiss/utils/utils.h>
@@ -407,6 +409,103 @@ void search_with_parameters(
407
409
  }
408
410
  }
409
411
 
412
+ namespace {
413
+
414
+ template <class C>
415
+ void scan_one_query_per_code(
416
+ const IndexIVF& index_ivf,
417
+ InvertedListScanner& scanner,
418
+ const idx_t* keys,
419
+ const float* coarse_dis,
420
+ size_t nprobe,
421
+ idx_t k,
422
+ float* distances,
423
+ idx_t* labels) {
424
+ heap_heapify<C>(k, distances, labels);
425
+ const InvertedLists* invlists = index_ivf.invlists;
426
+ for (size_t j = 0; j < nprobe; j++) {
427
+ const idx_t key = keys[j];
428
+ if (key < 0) {
429
+ continue;
430
+ }
431
+ scanner.set_list(key, coarse_dis[j]);
432
+ const size_t list_size = invlists->list_size(key);
433
+ InvertedLists::ScopedCodes codes(invlists, key);
434
+ InvertedLists::ScopedIds ids(invlists, key);
435
+ const uint8_t* code = codes.get();
436
+ for (size_t i = 0; i < list_size; i++, code += index_ivf.code_size) {
437
+ const float dis = scanner.distance_to_code(code);
438
+ if (C::cmp(distances[0], dis)) {
439
+ heap_replace_top<C>(k, distances, labels, dis, ids[i]);
440
+ }
441
+ }
442
+ }
443
+ heap_reorder<C>(k, distances, labels);
444
+ }
445
+
446
+ } // namespace
447
+
448
+ void search_with_distance_to_code(
449
+ const Index* index,
450
+ idx_t n,
451
+ const float* x,
452
+ idx_t k,
453
+ float* distances,
454
+ idx_t* labels,
455
+ size_t nprobe) {
456
+ std::unique_ptr<const float[]> del;
457
+ if (auto ip = dynamic_cast<const IndexPreTransform*>(index)) {
458
+ const float* xt = ip->apply_chain(n, x);
459
+ if (xt != x) {
460
+ del.reset(xt);
461
+ }
462
+ x = xt;
463
+ index = ip->index;
464
+ }
465
+ const IndexIVF* index_ivf = dynamic_cast<const IndexIVF*>(index);
466
+ FAISS_THROW_IF_NOT(index_ivf);
467
+ nprobe = std::min(nprobe, index_ivf->nlist);
468
+
469
+ std::vector<idx_t> Iq(nprobe * n);
470
+ std::vector<float> Dq(nprobe * n);
471
+ index_ivf->quantizer->search(n, x, nprobe, Dq.data(), Iq.data());
472
+
473
+ #pragma omp parallel
474
+ {
475
+ std::unique_ptr<InvertedListScanner> scanner(
476
+ index_ivf->get_InvertedListScanner());
477
+ #pragma omp for
478
+ for (idx_t q = 0; q < n; q++) {
479
+ scanner->set_query(x + q * index_ivf->d);
480
+ const idx_t* keys = Iq.data() + q * nprobe;
481
+ const float* coarse_dis = Dq.data() + q * nprobe;
482
+ float* D = distances + q * k;
483
+ idx_t* I = labels + q * k;
484
+ if (scanner->keep_max) {
485
+ scan_one_query_per_code<CMin<float, idx_t>>(
486
+ *index_ivf,
487
+ *scanner,
488
+ keys,
489
+ coarse_dis,
490
+ nprobe,
491
+ k,
492
+ D,
493
+ I);
494
+ } else {
495
+ scan_one_query_per_code<CMax<float, idx_t>>(
496
+ *index_ivf,
497
+ *scanner,
498
+ keys,
499
+ coarse_dis,
500
+ nprobe,
501
+ k,
502
+ D,
503
+ I);
504
+ }
505
+ }
506
+ }
507
+ }
508
+
410
509
  void range_search_with_parameters(
411
510
  const Index* index,
412
511
  idx_t n,
@@ -136,6 +136,21 @@ void search_with_parameters(
136
136
  size_t* nb_dis = nullptr,
137
137
  double* ms_per_stage = nullptr);
138
138
 
139
+ /** search an IndexIVF, possibly embedded in an IndexPreTransform, scoring
140
+ * every code with InvertedListScanner::distance_to_code, one call per code,
141
+ * as callers that run their own scan loop do (search() uses scan_codes,
142
+ * which batches codes). Meant for benchmarking that path. Each query is
143
+ * scanned by one thread; queries run in parallel.
144
+ */
145
+ void search_with_distance_to_code(
146
+ const Index* index,
147
+ idx_t n,
148
+ const float* x,
149
+ idx_t k,
150
+ float* distances,
151
+ idx_t* labels,
152
+ size_t nprobe);
153
+
139
154
  /** same as search_with_parameters but for range search */
140
155
  void range_search_with_parameters(
141
156
  const Index* index,
@@ -13,8 +13,11 @@
13
13
  #include <faiss/impl/DistanceComputer.h>
14
14
  #include <faiss/impl/FaissAssert.h>
15
15
  #include <faiss/utils/distances.h>
16
+ #include <faiss/utils/utils.h>
16
17
 
18
+ #include <algorithm>
17
19
  #include <cstring>
20
+ #include <vector>
18
21
 
19
22
  namespace faiss {
20
23
 
@@ -24,6 +27,14 @@ void Index::train(idx_t /*n*/, const float* /*x*/) {
24
27
  // does nothing by default
25
28
  }
26
29
 
30
+ void Index::train(const gpu::Dataset&) {
31
+ FAISS_THROW_MSG("Dataset training not implemented for this type of index");
32
+ }
33
+
34
+ void Index::add(const gpu::Dataset&) {
35
+ FAISS_THROW_MSG("Dataset addition not implemented for this type of index");
36
+ }
37
+
27
38
  void Index::train_with_queries(
28
39
  idx_t /*n*/,
29
40
  const float* /*x*/,
@@ -123,6 +134,39 @@ void Index::search_subset(
123
134
  FAISS_THROW_MSG("search_subset not implemented for this type of index");
124
135
  }
125
136
 
137
+ void Index::search_ex(
138
+ idx_t n,
139
+ const void* x,
140
+ NumericType numeric_type,
141
+ idx_t k,
142
+ float* distances,
143
+ idx_t* labels,
144
+ const SearchParameters* params) const {
145
+ if (numeric_type == NumericType::Float32) {
146
+ search(n, static_cast<const float*>(x), k, distances, labels, params);
147
+ return;
148
+ }
149
+ FAISS_THROW_IF_NOT_MSG(
150
+ numeric_type == NumericType::Float16,
151
+ "Index::search: unsupported numeric type");
152
+ FAISS_THROW_IF_NOT(d > 0);
153
+ // widen blocks of at most 64 MiB of fp32 queries
154
+ const idx_t bs = std::max<idx_t>(
155
+ 1, idx_t(64) * 1024 * 1024 / (sizeof(float) * idx_t(d)));
156
+ const uint16_t* x16 = static_cast<const uint16_t*>(x);
157
+ std::vector<float> buffer(size_t(std::min(n, bs)) * d);
158
+ for (idx_t i0 = 0; i0 < n; i0 += bs) {
159
+ const idx_t i1 = std::min(n, i0 + bs);
160
+ fp16_to_fp32(size_t(i1 - i0) * d, x16 + i0 * d, buffer.data());
161
+ search(i1 - i0,
162
+ buffer.data(),
163
+ k,
164
+ distances + i0 * k,
165
+ labels + i0 * k,
166
+ params);
167
+ }
168
+ }
169
+
126
170
  void Index::search1(const float*, ResultHandler&, SearchParameters*) const {
127
171
  FAISS_THROW_MSG("search1 not implemented for this type of index");
128
172
  }
@@ -16,8 +16,8 @@
16
16
  #include <cstdio>
17
17
 
18
18
  #define FAISS_VERSION_MAJOR 1
19
- #define FAISS_VERSION_MINOR 15
20
- #define FAISS_VERSION_PATCH 1
19
+ #define FAISS_VERSION_MINOR 16
20
+ #define FAISS_VERSION_PATCH 0
21
21
 
22
22
  // Macro to combine the version components into a single string
23
23
  #ifndef FAISS_STRINGIFY
@@ -49,6 +49,10 @@
49
49
 
50
50
  namespace faiss {
51
51
 
52
+ namespace gpu {
53
+ class Dataset;
54
+ }
55
+
52
56
  /// Forward declarations see impl/AuxIndexStructures.h, impl/IDSelector.h
53
57
  /// and impl/DistanceComputer.h
54
58
  struct IDSelector;
@@ -131,6 +135,9 @@ struct Index {
131
135
  */
132
136
  virtual void train(idx_t n, const float* x);
133
137
 
138
+ /// Train from caller-controlled storage. Unsupported by default.
139
+ virtual void train(const gpu::Dataset& dataset);
140
+
134
141
  /** Perform training on a representative set of vectors and a representative
135
142
  * set of queries
136
143
  *
@@ -163,6 +170,9 @@ struct Index {
163
170
  */
164
171
  virtual void add(idx_t n, const float* x) = 0;
165
172
 
173
+ /// Add caller-controlled storage. Unsupported by default.
174
+ virtual void add(const gpu::Dataset& dataset);
175
+
166
176
  virtual void add_ex(idx_t n, const void* x, NumericType numeric_type) {
167
177
  if (numeric_type == NumericType::Float32) {
168
178
  add(n, static_cast<const float*>(x));
@@ -212,6 +222,12 @@ struct Index {
212
222
  idx_t* labels,
213
223
  const SearchParameters* params = nullptr) const = 0;
214
224
 
225
+ /** search() for queries of the given numeric type
226
+ *
227
+ * Float32 calls search(). By default, Float16 queries (packed IEEE
228
+ * binary16, size n * d) are widened to fp32 in bounded blocks and passed
229
+ * to search(); indexes with a native fp16 search override this.
230
+ */
215
231
  virtual void search_ex(
216
232
  idx_t n,
217
233
  const void* x,
@@ -219,18 +235,7 @@ struct Index {
219
235
  idx_t k,
220
236
  float* distances,
221
237
  idx_t* labels,
222
- const SearchParameters* params = nullptr) const {
223
- if (numeric_type == NumericType::Float32) {
224
- search(n,
225
- static_cast<const float*>(x),
226
- k,
227
- distances,
228
- labels,
229
- params);
230
- } else {
231
- FAISS_THROW_MSG("Index::search: unsupported numeric type");
232
- }
233
- }
238
+ const SearchParameters* params = nullptr) const;
234
239
 
235
240
  /** search one vector with a custom result handler */
236
241
  virtual void search1(
@@ -29,6 +29,14 @@ void IndexBinary::train(idx_t, const uint8_t*) {
29
29
  // Does nothing by default.
30
30
  }
31
31
 
32
+ void IndexBinary::train(const gpu::Dataset&) {
33
+ FAISS_THROW_MSG("Dataset training not implemented for this type of index");
34
+ }
35
+
36
+ void IndexBinary::add(const gpu::Dataset&) {
37
+ FAISS_THROW_MSG("Dataset addition not implemented for this type of index");
38
+ }
39
+
32
40
  void IndexBinary::range_search(
33
41
  idx_t,
34
42
  const uint8_t*,
@@ -52,6 +52,10 @@ struct IndexBinary {
52
52
  * @param x training vectors, size n * d / 8
53
53
  */
54
54
  virtual void train(idx_t n, const uint8_t* x);
55
+
56
+ /// Train from caller-controlled storage. Unsupported by default.
57
+ virtual void train(const gpu::Dataset& dataset);
58
+
55
59
  virtual void train_ex(idx_t n, const void* x, NumericType numeric_type) {
56
60
  if (numeric_type == NumericType::UInt8) {
57
61
  train(n, static_cast<const uint8_t*>(x));
@@ -66,6 +70,10 @@ struct IndexBinary {
66
70
  * @param x input matrix, size n * d / 8
67
71
  */
68
72
  virtual void add(idx_t n, const uint8_t* x) = 0;
73
+
74
+ /// Add caller-controlled storage. Unsupported by default.
75
+ virtual void add(const gpu::Dataset& dataset);
76
+
69
77
  virtual void add_ex(idx_t n, const void* x, NumericType numeric_type) {
70
78
  if (numeric_type == NumericType::UInt8) {
71
79
  add(n, static_cast<const uint8_t*>(x));
@@ -24,6 +24,7 @@
24
24
  #include <faiss/impl/VisitedTable.h>
25
25
  #include <faiss/utils/Heap.h>
26
26
  #include <faiss/utils/random.h>
27
+ #include <faiss/utils/utils.h>
27
28
 
28
29
  #include <faiss/impl/simd_dispatch.h>
29
30
 
@@ -121,7 +122,9 @@ void IndexBinaryHNSW::search(
121
122
  }
122
123
  }
123
124
 
124
- hnsw_stats.combine({n1, n2, ndis, nhops});
125
+ if (get_search_stats_enabled()) {
126
+ hnsw_stats.combine_atomic({n1, n2, ndis, nhops});
127
+ }
125
128
 
126
129
  #pragma omp parallel for
127
130
  for (idx_t i = 0; i < n * k; ++i) {
@@ -173,6 +176,81 @@ DistanceComputer* IndexBinaryHNSW::get_distance_computer() const {
173
176
  });
174
177
  }
175
178
 
179
+ namespace {
180
+
181
+ // The sample of an IndexBinaryHNSW. Hamming distances are integers, so a
182
+ // nearer node has a strictly smaller distance.
183
+ struct BinaryLinkSample : HNSWLinkSampleSearch {
184
+ const IndexBinaryHNSW& index;
185
+ const uint8_t* x;
186
+
187
+ BinaryLinkSample(const IndexBinaryHNSW& index, idx_t n, const uint8_t* x)
188
+ : index(index), x(x) {
189
+ this->n = n;
190
+ }
191
+
192
+ std::vector<uint8_t> gather(size_t m, const idx_t* ids) const {
193
+ const size_t cs = index.code_size;
194
+ std::vector<uint8_t> rows(m * cs);
195
+ for (size_t j = 0; j < m; j++) {
196
+ memcpy(rows.data() + j * cs, x + ids[j] * cs, cs);
197
+ }
198
+ return rows;
199
+ }
200
+
201
+ void search(
202
+ const SearchParametersHNSW* params,
203
+ size_t m,
204
+ const idx_t* ids,
205
+ idx_t* labels) const override {
206
+ std::vector<int32_t> distances(m);
207
+ if (!ids) {
208
+ index.search(m, x, 1, distances.data(), labels, params);
209
+ return;
210
+ }
211
+ index.search(
212
+ m, gather(m, ids).data(), 1, distances.data(), labels, params);
213
+ }
214
+
215
+ void search_exact(
216
+ size_t m,
217
+ const idx_t* ids,
218
+ const idx_t* found,
219
+ idx_t* exact,
220
+ uint8_t* nearer) const override {
221
+ const size_t cs = index.code_size;
222
+ const std::vector<uint8_t> rows = gather(m, ids);
223
+ std::vector<int32_t> distances(m);
224
+ index.storage->search(m, rows.data(), 1, distances.data(), exact);
225
+ #pragma omp parallel
226
+ {
227
+ std::unique_ptr<DistanceComputer> dis(
228
+ index.get_distance_computer());
229
+ #pragma omp for schedule(static)
230
+ for (int64_t j = 0; j < static_cast<int64_t>(m); j++) {
231
+ if (exact[j] < 0 || exact[j] == found[j]) {
232
+ continue;
233
+ }
234
+ dis->set_query(
235
+ reinterpret_cast<const float*>(rows.data() + j * cs));
236
+ nearer[j] = (*dis)(exact[j]) < (*dis)(found[j]);
237
+ }
238
+ }
239
+ }
240
+ };
241
+
242
+ } // namespace
243
+
244
+ HNSWLevel0LinkStats IndexBinaryHNSW::add_level_0_links_from_sample(
245
+ idx_t n,
246
+ const uint8_t* x,
247
+ const HNSWLevel0LinkParameters& params) {
248
+ const BinaryLinkSample sample(*this, n, x);
249
+ std::unique_ptr<DistanceComputer> dis(get_distance_computer());
250
+ return hnsw_add_level_0_links_from_sample(
251
+ hnsw, ntotal, *dis, false, sample, params);
252
+ }
253
+
176
254
  /**************************************************************
177
255
  * IndexBinaryHNSWCagra implementation
178
256
  **************************************************************/
@@ -269,10 +347,10 @@ void IndexBinaryHNSWCagra::search(
269
347
  params);
270
348
 
271
349
  res.end();
350
+ vt->advance();
272
351
  }
273
- #pragma omp critical
274
- {
275
- hnsw_stats.combine(search_stats);
352
+ if (get_search_stats_enabled()) {
353
+ hnsw_stats.combine_atomic(search_stats);
276
354
  }
277
355
  }
278
356
 
@@ -40,6 +40,14 @@ struct IndexBinaryHNSW : IndexBinary {
40
40
  // used when GpuIndexBinaryCagra::copyFrom(IndexBinaryHNSW*) is called.
41
41
  bool keep_max_size_level0 = false;
42
42
 
43
+ // When this index is the coarse quantizer of an IndexBinaryIVF,
44
+ // IndexBinaryIVF::train() calls add_level_0_links_from_sample() after it
45
+ // adds the centroids. The sample is the first
46
+ // min(n, level_0_link_samples_per_node * ntotal) training vectors. 0
47
+ // disables the call. Training does not call it when the quantizer is
48
+ // already trained. The index format does not store this value.
49
+ int level_0_link_samples_per_node = 3;
50
+
43
51
  explicit IndexBinaryHNSW();
44
52
  explicit IndexBinaryHNSW(int d, int M = 32);
45
53
  explicit IndexBinaryHNSW(IndexBinary* storage, int M = 32);
@@ -65,6 +73,17 @@ struct IndexBinaryHNSW : IndexBinary {
65
73
  void reconstruct(idx_t key, uint8_t* recons) const override;
66
74
 
67
75
  void reset() override;
76
+
77
+ /** Adds level-0 links so that the search no longer stops at "sinks":
78
+ * nodes where it stops for many vectors whose nearest node is elsewhere.
79
+ * See IndexHNSW::add_level_0_links_from_sample(). x holds n binary
80
+ * vectors. Call this function after the last add().
81
+ */
82
+ HNSWLevel0LinkStats add_level_0_links_from_sample(
83
+ idx_t n,
84
+ const uint8_t* x,
85
+ const HNSWLevel0LinkParameters& params =
86
+ HNSWLevel0LinkParameters());
68
87
  };
69
88
 
70
89
  struct IndexBinaryHNSWCagra : IndexBinaryHNSW {
@@ -17,6 +17,7 @@
17
17
  #include <limits>
18
18
  #include <memory>
19
19
 
20
+ #include <faiss/IndexBinaryHNSW.h>
20
21
  #include <faiss/IndexFlat.h>
21
22
  #include <faiss/IndexLSH.h>
22
23
  #include <faiss/impl/AuxIndexStructures.h>
@@ -254,6 +255,21 @@ void IndexBinaryIVF::reconstruct_from_offset(
254
255
  idx_t list_no,
255
256
  idx_t offset,
256
257
  uint8_t* recons) const {
258
+ FAISS_THROW_IF_NOT_FMT(
259
+ list_no >= 0 &&
260
+ static_cast<uint64_t>(list_no) <
261
+ static_cast<uint64_t>(nlist),
262
+ "Invalid list_no %" PRId64 " (nlist=%zd)",
263
+ list_no,
264
+ nlist);
265
+ FAISS_THROW_IF_NOT_FMT(
266
+ offset >= 0 &&
267
+ static_cast<uint64_t>(offset) <
268
+ static_cast<uint64_t>(invlists->list_size(list_no)),
269
+ "Invalid offset %" PRId64 " for list %" PRId64 " (size=%zd)",
270
+ offset,
271
+ list_no,
272
+ invlists->list_size(list_no));
257
273
  memcpy(recons,
258
274
  InvertedLists::ScopedCodes(invlists, list_no, offset).get(),
259
275
  code_size);
@@ -308,6 +324,24 @@ void IndexBinaryIVF::train(idx_t n, const uint8_t* x) {
308
324
 
309
325
  quantizer->add(clus.k, x_b.get());
310
326
  quantizer->is_trained = true;
327
+
328
+ auto* hnsw = dynamic_cast<IndexBinaryHNSW*>(quantizer);
329
+ if (hnsw && hnsw->level_0_link_samples_per_node > 0) {
330
+ const idx_t samples = std::min<idx_t>(
331
+ n, idx_t(hnsw->level_0_link_samples_per_node) * nlist);
332
+ const HNSWLevel0LinkStats stats =
333
+ hnsw->add_level_0_links_from_sample(samples, x);
334
+ if (verbose) {
335
+ printf("HNSW quantizer level-0 links from %" PRId64
336
+ " samples: %zd sinks found, %zd remaining, %zd links "
337
+ "added, %zd replaced\n",
338
+ samples,
339
+ stats.n_sinks_found,
340
+ stats.n_sinks_remaining,
341
+ stats.n_links_added,
342
+ stats.n_links_replaced);
343
+ }
344
+ }
311
345
  }
312
346
 
313
347
  is_trained = true;
@@ -55,7 +55,7 @@ IndexFastScan::IndexFastScan()
55
55
 
56
56
  void IndexFastScan::reset() {
57
57
  codes.resize(0);
58
- ntotal = 0;
58
+ ntotal = ntotal2 = 0;
59
59
  }
60
60
 
61
61
  void IndexFastScan::add(idx_t n, const float* x) {
@@ -267,15 +267,16 @@ void IndexFastScan::search(
267
267
  float* distances,
268
268
  idx_t* labels,
269
269
  const SearchParameters* params) const {
270
- FAISS_THROW_IF_MSG(params, "search params not supported for this index");
271
270
  FAISS_THROW_IF_NOT(k > 0);
271
+ const IDSelector* sel = params ? params->sel : nullptr;
272
272
 
273
273
  FastScanDistancePostProcessing empty_context{};
274
274
  if (metric_type == METRIC_L2) {
275
- search_dispatch_implem<true>(n, x, k, distances, labels, empty_context);
275
+ search_dispatch_implem<true>(
276
+ n, x, k, distances, labels, empty_context, sel);
276
277
  } else {
277
278
  search_dispatch_implem<false>(
278
- n, x, k, distances, labels, empty_context);
279
+ n, x, k, distances, labels, empty_context, sel);
279
280
  }
280
281
  }
281
282
 
@@ -286,7 +287,8 @@ void IndexFastScan::search_dispatch_implem(
286
287
  idx_t k,
287
288
  float* distances,
288
289
  idx_t* labels,
289
- const FastScanDistancePostProcessing& context) const {
290
+ const FastScanDistancePostProcessing& context,
291
+ const IDSelector* sel) const {
290
292
  using Cfloat = typename std::conditional<
291
293
  is_max,
292
294
  CMax<float, int64_t>,
@@ -317,6 +319,10 @@ void IndexFastScan::search_dispatch_implem(
317
319
  FAISS_THROW_MSG("not implemented");
318
320
  } else if (implem == 2 || implem == 3 || implem == 4) {
319
321
  FAISS_THROW_IF_NOT(orig_codes);
322
+ FAISS_THROW_IF_MSG(
323
+ sel,
324
+ "IDSelector is not supported by implem 2/3/4; leave implem at "
325
+ "0 so a selector-aware kernel is chosen");
320
326
  search_implem_234<Cfloat>(n, x, k, distances, labels, context);
321
327
  } else if (impl >= 12 && impl <= 15) {
322
328
  FAISS_THROW_IF_NOT(ntotal < INT_MAX);
@@ -328,9 +334,11 @@ void IndexFastScan::search_dispatch_implem(
328
334
  // - OpenMP disabled (omp_get_max_threads() = 1)
329
335
  if (nt < 2) {
330
336
  if (impl == 12 || impl == 13) {
331
- search_implem_12<C>(n, x, k, distances, labels, impl, context);
337
+ search_implem_12<C>(
338
+ n, x, k, distances, labels, impl, context, sel);
332
339
  } else {
333
- search_implem_14<C>(n, x, k, distances, labels, impl, context);
340
+ search_implem_14<C>(
341
+ n, x, k, distances, labels, impl, context, sel);
334
342
  }
335
343
  } else {
336
344
  // explicitly slice over threads
@@ -355,7 +363,8 @@ void IndexFastScan::search_dispatch_implem(
355
363
  dis_i,
356
364
  lab_i,
357
365
  impl,
358
- thread_context);
366
+ thread_context,
367
+ sel);
359
368
  } else {
360
369
  search_implem_14<C>(
361
370
  i1 - i0,
@@ -364,7 +373,8 @@ void IndexFastScan::search_dispatch_implem(
364
373
  dis_i,
365
374
  lab_i,
366
375
  impl,
367
- thread_context);
376
+ thread_context,
377
+ sel);
368
378
  }
369
379
  }
370
380
  }
@@ -440,7 +450,8 @@ void IndexFastScan::search_implem_12(
440
450
  float* distances,
441
451
  idx_t* labels,
442
452
  int impl,
443
- const FastScanDistancePostProcessing& context) const {
453
+ const FastScanDistancePostProcessing& context,
454
+ const IDSelector* sel) const {
444
455
  FAISS_THROW_IF_NOT(bbs == 32);
445
456
 
446
457
  // handle qbs2 blocking by recursive call
@@ -460,7 +471,8 @@ void IndexFastScan::search_implem_12(
460
471
  distances + i0 * k,
461
472
  labels + i0 * k,
462
473
  impl,
463
- sub_context);
474
+ sub_context,
475
+ sel);
464
476
  }
465
477
  return;
466
478
  }
@@ -492,7 +504,7 @@ void IndexFastScan::search_implem_12(
492
504
  FAISS_THROW_IF_NOT(LUT_nq == n);
493
505
 
494
506
  auto scanner = make_knn_scanner(
495
- C::is_max, n, k, ntotal, distances, labels, nullptr, impl, context);
507
+ C::is_max, n, k, ntotal, distances, labels, sel, impl, context);
496
508
  auto* rh = scanner->handler();
497
509
  rh->normalizers = normalizers.get();
498
510
  // Note: skip & 2 previously set handler->disable (run kernel,
@@ -525,7 +537,8 @@ void IndexFastScan::search_implem_14(
525
537
  float* distances,
526
538
  idx_t* labels,
527
539
  int impl,
528
- const FastScanDistancePostProcessing& context) const {
540
+ const FastScanDistancePostProcessing& context,
541
+ const IDSelector* sel) const {
529
542
  FAISS_THROW_IF_NOT(bbs % 32 == 0);
530
543
 
531
544
  // The accumulate loop dispatch table only instantiates certain
@@ -553,7 +566,8 @@ void IndexFastScan::search_implem_14(
553
566
  distances + i0 * k,
554
567
  labels + i0 * k,
555
568
  impl,
556
- sub_context);
569
+ sub_context,
570
+ sel);
557
571
  }
558
572
  return;
559
573
  }
@@ -577,7 +591,7 @@ void IndexFastScan::search_implem_14(
577
591
  LUT.get());
578
592
 
579
593
  auto scanner = make_knn_scanner(
580
- C::is_max, n, k, ntotal, distances, labels, nullptr, impl, context);
594
+ C::is_max, n, k, ntotal, distances, labels, sel, impl, context);
581
595
  auto* rh = scanner->handler();
582
596
  rh->normalizers = normalizers.get();
583
597
  // Note: skip & 2 previously set handler->disable (run kernel,
@@ -606,7 +620,8 @@ template void IndexFastScan::search_dispatch_implem<true>(
606
620
  idx_t k,
607
621
  float* distances,
608
622
  idx_t* labels,
609
- const FastScanDistancePostProcessing& context) const;
623
+ const FastScanDistancePostProcessing& context,
624
+ const IDSelector* sel) const;
610
625
 
611
626
  template void IndexFastScan::search_dispatch_implem<false>(
612
627
  idx_t n,
@@ -614,7 +629,8 @@ template void IndexFastScan::search_dispatch_implem<false>(
614
629
  idx_t k,
615
630
  float* distances,
616
631
  idx_t* labels,
617
- const FastScanDistancePostProcessing& context) const;
632
+ const FastScanDistancePostProcessing& context,
633
+ const IDSelector* sel) const;
618
634
 
619
635
  void IndexFastScan::reconstruct(idx_t key, float* recons) const {
620
636
  FAISS_THROW_IF_NOT_FMT(