faiss 0.6.2 → 0.6.4

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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/AutoTune.cpp +3 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +9 -1
  6. data/vendor/faiss/faiss/Clustering.h +8 -0
  7. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  8. data/vendor/faiss/faiss/Index.h +2 -2
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  10. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  11. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +24 -4
  12. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +16 -145
  13. data/vendor/faiss/faiss/IndexBinaryHNSW.h +0 -6
  14. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  15. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +8 -18
  16. data/vendor/faiss/faiss/IndexBinaryIVF.h +8 -1
  17. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  18. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  19. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  20. data/vendor/faiss/faiss/IndexFlat.cpp +21 -54
  21. data/vendor/faiss/faiss/IndexFlat.h +2 -2
  22. data/vendor/faiss/faiss/IndexHNSW.cpp +311 -102
  23. data/vendor/faiss/faiss/IndexHNSW.h +31 -7
  24. data/vendor/faiss/faiss/IndexIDMap.cpp +26 -8
  25. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  26. data/vendor/faiss/faiss/IndexIVF.cpp +36 -10
  27. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  28. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  29. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  30. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  31. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  32. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  33. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  34. data/vendor/faiss/faiss/IndexIVFPQ.cpp +49 -23
  35. data/vendor/faiss/faiss/IndexIVFPQ.h +11 -0
  36. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  37. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  38. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  39. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  40. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  41. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  42. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  43. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  44. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  45. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  46. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  47. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  48. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  49. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  50. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  51. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  52. data/vendor/faiss/faiss/IndexShards.cpp +5 -5
  53. data/vendor/faiss/faiss/IndexShardsIVF.cpp +6 -5
  54. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  55. data/vendor/faiss/faiss/SuperKMeans.cpp +286 -247
  56. data/vendor/faiss/faiss/SuperKMeans.h +33 -2
  57. data/vendor/faiss/faiss/VectorTransform.cpp +71 -2
  58. data/vendor/faiss/faiss/VectorTransform.h +3 -0
  59. data/vendor/faiss/faiss/clone_index.cpp +8 -0
  60. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  61. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  62. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +1 -5
  63. data/vendor/faiss/faiss/gpu/GpuDistance.h +2 -5
  64. data/vendor/faiss/faiss/gpu/GpuIndex.h +38 -16
  65. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +71 -1
  66. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  67. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  68. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  69. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  70. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  71. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  72. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  73. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  74. data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
  75. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +7 -4
  76. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  77. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  78. data/vendor/faiss/faiss/impl/HNSW.cpp +528 -267
  79. data/vendor/faiss/faiss/impl/HNSW.h +46 -7
  80. data/vendor/faiss/faiss/impl/IDSelector.h +44 -0
  81. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  82. data/vendor/faiss/faiss/impl/NNDescent.cpp +10 -3
  83. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  84. data/vendor/faiss/faiss/impl/Panorama.h +20 -9
  85. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  86. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +38 -26
  87. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +45 -37
  88. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  89. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +239 -72
  90. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +66 -4
  91. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +4 -13
  92. data/vendor/faiss/faiss/impl/ResultHandler.h +34 -34
  93. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +287 -84
  94. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +26 -10
  95. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  96. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  97. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  98. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +90 -14
  99. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  100. data/vendor/faiss/faiss/impl/expanded_scanners.h +5 -1
  101. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +1 -0
  102. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  103. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  104. data/vendor/faiss/faiss/impl/index_read.cpp +491 -50
  105. data/vendor/faiss/faiss/impl/index_write.cpp +86 -30
  106. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  107. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  108. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  109. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  110. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +73 -39
  111. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  112. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  113. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  114. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +4 -4
  115. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  116. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  117. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  118. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  119. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  120. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +887 -0
  121. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +2 -2
  122. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  123. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +90 -24
  124. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  125. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  126. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  127. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +169 -13
  128. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +125 -26
  129. data/vendor/faiss/faiss/impl/simd_dispatch.h +70 -31
  130. data/vendor/faiss/faiss/index_factory.cpp +40 -7
  131. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  132. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +9 -6
  133. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +29 -8
  134. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  135. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  136. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  137. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  138. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  139. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  140. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +47 -5
  141. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +23 -3
  142. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +4 -2
  143. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
  144. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +10 -4
  145. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +2 -1
  146. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  147. data/vendor/faiss/faiss/utils/distances.cpp +30 -11
  148. data/vendor/faiss/faiss/utils/distances_dispatch.h +30 -24
  149. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +1 -1
  150. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  151. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  152. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  153. data/vendor/faiss/faiss/utils/hamming.cpp +16 -10
  154. data/vendor/faiss/faiss/utils/hamming.h +10 -1
  155. data/vendor/faiss/faiss/utils/hamming_distance/common.h +14 -3
  156. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +24 -0
  157. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +1 -1
  158. data/vendor/faiss/faiss/utils/hamming_distance/{hamming_computer-avx512_spr.h → hamming_computer-avx512_vpopcnt.h} +85 -24
  159. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +141 -0
  160. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  161. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  162. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +194 -30
  163. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  164. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  165. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +198 -18
  166. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  167. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +330 -40
  168. data/vendor/faiss/faiss/utils/simd_impl/{rabitq_avx512_spr.cpp → rabitq_avx512_vpopcnt.cpp} +112 -23
  169. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  170. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  171. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_dispatch.h +2 -7
  172. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +6 -1
  173. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +34 -0
  174. data/vendor/faiss/faiss/utils/simd_levels.cpp +196 -47
  175. data/vendor/faiss/faiss/utils/simd_levels.h +33 -8
  176. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  177. metadata +21 -5
  178. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +0 -15
@@ -65,7 +65,7 @@ void IndexIVFFlat::add_core(
65
65
  void* inverted_list_context) {
66
66
  FAISS_THROW_IF_NOT(is_trained);
67
67
  FAISS_THROW_IF_NOT(coarse_idx);
68
- FAISS_THROW_IF_NOT(!by_residual);
68
+ FAISS_THROW_IF_MSG(by_residual, "by_residual not supported for this index");
69
69
  FAISS_THROW_IF_NOT_MSG(invlists, "invlists not initialized");
70
70
  direct_map.check_can_add(xids);
71
71
 
@@ -110,7 +110,7 @@ void IndexIVFFlat::encode_vectors(
110
110
  const idx_t* list_nos,
111
111
  uint8_t* codes,
112
112
  bool include_listnos) const {
113
- FAISS_THROW_IF_NOT(!by_residual);
113
+ FAISS_THROW_IF_MSG(by_residual, "by_residual not supported for this index");
114
114
  if (!include_listnos) {
115
115
  memcpy(codes, x, code_size * n);
116
116
  } else {
@@ -291,8 +291,7 @@ void IndexIVFFlatDedup::search_preassigned(
291
291
  bool store_pairs,
292
292
  const IVFSearchParameters* params,
293
293
  IndexIVFStats* /*stats*/) const {
294
- FAISS_THROW_IF_NOT_MSG(
295
- !store_pairs, "store_pairs not supported in IVFDedup");
294
+ FAISS_THROW_IF_MSG(store_pairs, "store_pairs not supported in IVFDedup");
296
295
 
297
296
  IndexIVFFlat::search_preassigned(
298
297
  n, x, k, assign, centroid_dis, distances, labels, false, params);
@@ -90,7 +90,7 @@ void IndexIVFIndependentQuantizer::search(
90
90
  float* distances,
91
91
  idx_t* labels,
92
92
  const SearchParameters* params) const {
93
- FAISS_THROW_IF_NOT_MSG(!params, "search parameters not supported");
93
+ FAISS_THROW_IF_MSG(params, "search parameters not supported");
94
94
  size_t nprobe = index_ivf->nprobe;
95
95
  std::vector<float> D(n * nprobe);
96
96
  std::vector<idx_t> I(n * nprobe);
@@ -14,6 +14,8 @@
14
14
  #include <cstdint>
15
15
  #include <cstdio>
16
16
 
17
+ #include <omp.h>
18
+
17
19
  #include <algorithm>
18
20
 
19
21
  #include <faiss/utils/distances_dispatch.h>
@@ -28,6 +30,7 @@
28
30
  #include <faiss/impl/IDSelector.h>
29
31
  #include <faiss/impl/ProductQuantizer.h>
30
32
  #include <faiss/impl/ResultHandler.h>
33
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
31
34
  #include <faiss/impl/pq_code_distance/pq_code_distance-generic.h>
32
35
  #include <faiss/impl/simd_dispatch.h>
33
36
 
@@ -292,34 +295,49 @@ void IndexIVFPQ::add_core_o(
292
295
  pq.compute_codes(to_encode, xcodes.get(), n);
293
296
 
294
297
  double t2 = getmillisecs();
295
- // TODO: parallelize?
296
298
  size_t n_ignore = 0;
297
- for (idx_t i = 0; i < n; i++) {
298
- idx_t key = idx[i];
299
- idx_t id = xids ? xids[i] : ntotal + i;
300
- if (key < 0) {
301
- direct_map.add_single_id(id, -1, 0);
302
- n_ignore++;
303
- if (residuals_2) {
304
- memset(residuals_2, 0, sizeof(*residuals_2) * d);
299
+ DirectMapAdd dm_adder(direct_map, n, xids);
300
+
301
+ #pragma omp parallel reduction(+ : n_ignore)
302
+ {
303
+ int nt = omp_get_num_threads();
304
+ int rank = omp_get_thread_num();
305
+
306
+ // each thread takes care of a subset of lists
307
+ for (idx_t i = 0; i < n; i++) {
308
+ idx_t key = idx[i];
309
+ if (key < 0) {
310
+ if (rank == 0) {
311
+ dm_adder.add(i, -1, 0);
312
+ n_ignore++;
313
+ if (residuals_2) {
314
+ memset(residuals_2 + i * d,
315
+ 0,
316
+ sizeof(*residuals_2) * d);
317
+ }
318
+ }
319
+ continue;
320
+ }
321
+ if (key % nt != rank) {
322
+ continue;
305
323
  }
306
- continue;
307
- }
308
324
 
309
- uint8_t* code = xcodes.get() + i * code_size;
310
- size_t offset =
311
- invlists->add_entry(key, id, code, inverted_list_context);
325
+ idx_t id = xids ? xids[i] : ntotal + i;
326
+ uint8_t* code = xcodes.get() + i * code_size;
327
+ size_t offset =
328
+ invlists->add_entry(key, id, code, inverted_list_context);
312
329
 
313
- if (residuals_2) {
314
- float* res2 = residuals_2 + i * d;
315
- const float* xi = to_encode + i * d;
316
- pq.decode(code, res2);
317
- for (int j = 0; j < d; j++) {
318
- res2[j] = xi[j] - res2[j];
330
+ if (residuals_2) {
331
+ float* res2 = residuals_2 + i * d;
332
+ const float* xi = to_encode + i * d;
333
+ pq.decode(code, res2);
334
+ for (int j = 0; j < d; j++) {
335
+ res2[j] = xi[j] - res2[j];
336
+ }
319
337
  }
320
- }
321
338
 
322
- direct_map.add_single_id(id, key, offset);
339
+ dm_adder.add(i, key, offset);
340
+ }
323
341
  }
324
342
 
325
343
  double t3 = getmillisecs();
@@ -508,9 +526,17 @@ InvertedListScanner* IndexIVFPQ::get_InvertedListScanner(
508
526
  bool store_pairs,
509
527
  const IDSelector* sel,
510
528
  const IVFSearchParameters*) const {
529
+ return get_InvertedListScanner(
530
+ store_pairs, sel, IndexIVFPQ::ScannerMode::Precomputed);
531
+ }
532
+
533
+ InvertedListScanner* IndexIVFPQ::get_InvertedListScanner(
534
+ bool store_pairs,
535
+ const IDSelector* sel,
536
+ IndexIVFPQ::ScannerMode mode) const {
511
537
  return with_simd_level([&]<SIMDLevel SL>() -> InvertedListScanner* {
512
538
  return pq_code_distance::make_IVFPQInvertedListScanner<SL>(
513
- *this, store_pairs, sel);
539
+ *this, store_pairs, sel, mode);
514
540
  });
515
541
  }
516
542
 
@@ -32,6 +32,11 @@ FAISS_API extern size_t precomputed_table_max_bytes;
32
32
  * vector is encoded as a product quantizer code.
33
33
  */
34
34
  struct IndexIVFPQ : IndexIVF {
35
+ enum class ScannerMode {
36
+ Precomputed,
37
+ OnTheFly,
38
+ };
39
+
35
40
  ProductQuantizer pq; ///< produces the codes
36
41
 
37
42
  bool do_polysemous_training; ///< reorder PQ centroids after training?
@@ -144,6 +149,12 @@ struct IndexIVFPQ : IndexIVF {
144
149
  const IDSelector* sel,
145
150
  const IVFSearchParameters* params) const override;
146
151
 
152
+ /** Returns a scanner with a fixed PQ distance-computation strategy. */
153
+ InvertedListScanner* get_InvertedListScanner(
154
+ bool store_pairs,
155
+ const IDSelector* sel,
156
+ ScannerMode mode) const;
157
+
147
158
  /// build precomputed table
148
159
  void precompute_table();
149
160
 
@@ -16,7 +16,6 @@
16
16
  #include <faiss/impl/FaissAssert.h>
17
17
  #include <faiss/impl/ResultHandler.h>
18
18
  #include <faiss/impl/simdlib/simdlib_dispatch.h>
19
- #include <faiss/utils/distances.h>
20
19
  #include <faiss/utils/distances_dispatch.h>
21
20
  #include <faiss/utils/extra_distances.h>
22
21
 
@@ -229,60 +229,30 @@ struct RaBitInvertedListScanner : InvertedListScanner {
229
229
  }
230
230
 
231
231
  // Multi-bit: Two-stage search with adaptive filtering
232
- size_t nup = 0;
233
-
234
- for (size_t j = 0; j < list_size; j++) {
235
- if (sel != nullptr) {
236
- int64_t id = store_pairs ? lo_build(list_no, j) : ids[j];
237
- if (!sel->is_member(id)) {
238
- codes += code_size;
239
- continue;
240
- }
241
- }
242
-
243
- float est_distance = rabitq_dc->distance_to_code_1bit(codes);
244
-
245
- size_t code_size_base = (ivf_rabitq.d + 7) / 8;
246
- const rabitq_utils::SignBitFactorsWithError* base_fac =
247
- reinterpret_cast<
248
- const rabitq_utils::SignBitFactorsWithError*>(
249
- codes + code_size_base);
250
-
251
- bool should_refine = rabitq_utils::should_refine_candidate(
252
- est_distance,
253
- base_fac->f_error,
254
- rabitq_dc->g_error,
255
- handler.threshold,
256
- keep_max);
257
- if (should_refine) {
258
- // Refining computes the full distance — counts as a
259
- // post-filter "distance computed" for stats purposes.
260
- handler.stats.scan_cnt++;
261
- float dis = distance_to_code(codes);
262
- int64_t id = store_pairs ? lo_build(list_no, j) : ids[j];
263
-
264
- if (handler.add_result(dis, id)) {
265
- handler.stats.nheap_updates++;
266
- nup++;
267
- }
268
- }
269
- codes += code_size;
270
- }
271
-
272
- return nup;
232
+ return rabitq_dc->scan_codes_multibit(
233
+ list_size,
234
+ codes,
235
+ ids,
236
+ code_size,
237
+ list_no,
238
+ store_pairs,
239
+ sel,
240
+ keep_max,
241
+ handler);
273
242
  }
274
243
 
275
244
  void internal_try_setup_dc() {
276
245
  if (!query_vector.empty() && !reconstructed_centroid.empty()) {
277
- // both query_vector and centroid are available!
278
- // set up DistanceComputer
279
- dc.reset(ivf_rabitq.rabitq.get_distance_computer(
280
- qb, reconstructed_centroid.data(), centered));
281
-
246
+ // both query_vector and centroid are available
247
+ if (!dc) {
248
+ dc.reset(ivf_rabitq.rabitq.get_distance_computer(
249
+ qb, nullptr, centered));
250
+ // Try to cast to RaBitQDistanceComputer for multi-bit support
251
+ rabitq_dc = dynamic_cast<RaBitQDistanceComputer*>(dc.get());
252
+ FAISS_THROW_IF_NOT(rabitq_dc);
253
+ }
254
+ rabitq_dc->set_centroid(reconstructed_centroid.data());
282
255
  dc->set_query(query_vector.data());
283
-
284
- // Try to cast to RaBitQDistanceComputer for multi-bit support
285
- rabitq_dc = dynamic_cast<RaBitQDistanceComputer*>(dc.get());
286
256
  }
287
257
  }
288
258
  };
@@ -19,8 +19,10 @@
19
19
  #include <faiss/impl/ResultHandler.h>
20
20
  #include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
21
21
  #include <faiss/impl/fast_scan/fast_scan.h>
22
+ #include <faiss/impl/simd_dispatch.h>
22
23
  #include <faiss/invlists/BlockInvertedLists.h>
23
24
  #include <faiss/utils/distances.h>
25
+ #include <faiss/utils/rabitq_simd.h>
24
26
  #include <faiss/utils/utils.h>
25
27
 
26
28
  namespace faiss {
@@ -28,6 +30,7 @@ namespace faiss {
28
30
  // Import shared utilities from RaBitQUtils
29
31
  using rabitq_utils::ExtraBitsFactors;
30
32
  using rabitq_utils::QueryFactorsData;
33
+ using rabitq_utils::round_nonnegative_to_uint16;
31
34
  using rabitq_utils::SignBitFactors;
32
35
  using rabitq_utils::SignBitFactorsWithError;
33
36
 
@@ -62,8 +65,9 @@ IndexIVFRaBitQFastScan::IndexIVFRaBitQFastScan(
62
65
  metric == METRIC_L2 || metric == METRIC_INNER_PRODUCT,
63
66
  "RaBitQ only supports L2 and Inner Product metrics");
64
67
  FAISS_THROW_IF_NOT_MSG(
65
- bbs_in % 32 == 0, "Batch size must be multiple of 32");
66
- FAISS_THROW_IF_NOT_MSG(quantizer_in != nullptr, "Quantizer cannot be null");
68
+ bbs_in > 0 && bbs_in % 32 == 0,
69
+ "Batch size must be positive and a multiple of 32");
70
+ FAISS_THROW_IF_MSG(quantizer_in == nullptr, "Quantizer cannot be null");
67
71
 
68
72
  by_residual = true;
69
73
  qb = 8; // RaBitQ quantization bits
@@ -91,10 +95,12 @@ IndexIVFRaBitQFastScan::IndexIVFRaBitQFastScan(
91
95
  }
92
96
  }
93
97
 
94
- // Constructor that converts an existing IndexIVFRaBitQ to FastScan format
98
+ // Constructor that converts an existing IndexIVFRaBitQ to FastScan format.
99
+ // Like other IVF FastScan conversion constructors, this borrows orig's
100
+ // quantizer and orig_invlists; orig must outlive the converted index.
95
101
  IndexIVFRaBitQFastScan::IndexIVFRaBitQFastScan(
96
102
  const IndexIVFRaBitQ& orig,
97
- int /* bbs */)
103
+ int bbs_in)
98
104
  : IndexIVFFastScan(
99
105
  orig.quantizer,
100
106
  orig.d,
@@ -102,7 +108,93 @@ IndexIVFRaBitQFastScan::IndexIVFRaBitQFastScan(
102
108
  0,
103
109
  orig.metric_type,
104
110
  false),
105
- rabitq(orig.rabitq) {}
111
+ rabitq(orig.rabitq) {
112
+ FAISS_THROW_IF_NOT_MSG(orig.d > 0, "Dimension must be positive");
113
+ FAISS_THROW_IF_NOT_MSG(
114
+ orig.metric_type == METRIC_L2 ||
115
+ orig.metric_type == METRIC_INNER_PRODUCT,
116
+ "RaBitQ only supports L2 and Inner Product metrics");
117
+ FAISS_THROW_IF_NOT_MSG(
118
+ bbs_in > 0 && bbs_in % 32 == 0,
119
+ "Batch size must be positive and a multiple of 32");
120
+ FAISS_THROW_IF_NOT_MSG(orig.invlists != nullptr, "Source invlists null");
121
+
122
+ by_residual = true;
123
+ qb = orig.qb;
124
+ centered = false;
125
+
126
+ const size_t M_fastscan = (orig.d + 3) / 4;
127
+ constexpr size_t nbits_fastscan = 4;
128
+
129
+ this->bbs = bbs_in;
130
+ this->fine_quantizer = &rabitq;
131
+ this->M = M_fastscan;
132
+ this->nbits = nbits_fastscan;
133
+ this->ksub = (1 << nbits_fastscan);
134
+ this->M2 = roundup(M_fastscan, 2);
135
+
136
+ const size_t bit_pattern_size = (d + 7) / 8;
137
+ const size_t storage_size = compute_per_vector_storage_size();
138
+ this->code_size = bit_pattern_size + storage_size;
139
+ FAISS_THROW_IF_NOT_MSG(
140
+ orig.code_size == code_size,
141
+ "Source IndexIVFRaBitQ code size is incompatible");
142
+
143
+ ntotal = orig.ntotal;
144
+ is_trained = orig.is_trained;
145
+ nprobe = orig.nprobe;
146
+
147
+ replace_invlists(new BlockInvertedLists(nlist, get_CodePacker()), true);
148
+
149
+ #pragma omp parallel for if (nlist > 100)
150
+ for (idx_t list_no = 0; list_no < static_cast<idx_t>(nlist); list_no++) {
151
+ const size_t nb = orig.invlists->list_size(list_no);
152
+ if (nb == 0) {
153
+ continue;
154
+ }
155
+
156
+ AlignedTable<uint8_t> flat_codes(nb * code_size);
157
+ memset(flat_codes.get(), 0, nb * code_size);
158
+
159
+ InvertedLists::ScopedCodes orig_codes(orig.invlists, list_no);
160
+ for (size_t i = 0; i < nb; i++) {
161
+ const uint8_t* orig_code = orig_codes.get() + i * orig.code_size;
162
+ uint8_t* fs_code = flat_codes.get() + i * code_size;
163
+
164
+ for (size_t j = 0; j < static_cast<size_t>(d); j++) {
165
+ const size_t orig_byte_idx = j / 8;
166
+ const size_t orig_bit_offset = j % 8;
167
+ const bool bit_value =
168
+ (orig_code[orig_byte_idx] >> orig_bit_offset) & 1;
169
+ if (bit_value) {
170
+ rabitq_utils::set_bit_fastscan(fs_code, j);
171
+ }
172
+ }
173
+
174
+ memcpy(fs_code + bit_pattern_size,
175
+ orig_code + bit_pattern_size,
176
+ storage_size);
177
+ }
178
+
179
+ std::unique_ptr<CodePacker> packer(get_CodePacker());
180
+ const size_t nb2 = roundup(nb, bbs);
181
+ AlignedTable<uint8_t> block_codes(nb2 / bbs * packer->block_size);
182
+ memset(block_codes.get(), 0, block_codes.size());
183
+
184
+ for (size_t i = 0; i < nb; i++) {
185
+ packer->pack_1(
186
+ flat_codes.get() + i * code_size, i, block_codes.get());
187
+ }
188
+
189
+ invlists->add_entries(
190
+ list_no,
191
+ nb,
192
+ InvertedLists::ScopedIds(orig.invlists, list_no).get(),
193
+ block_codes.get());
194
+ }
195
+
196
+ orig_invlists = orig.invlists;
197
+ }
106
198
 
107
199
  size_t IndexIVFRaBitQFastScan::compute_per_vector_storage_size() const {
108
200
  return rabitq_utils::compute_per_vector_storage_size(rabitq.nb_bits, d);
@@ -161,8 +253,10 @@ void IndexIVFRaBitQFastScan::train_encoder(
161
253
  const float* x,
162
254
  const idx_t* assign) {
163
255
  FAISS_THROW_IF_NOT(n > 0);
164
- FAISS_THROW_IF_NOT(x != nullptr);
165
- FAISS_THROW_IF_NOT(assign != nullptr || !by_residual);
256
+ FAISS_THROW_IF_NOT(x);
257
+ FAISS_THROW_IF_MSG(
258
+ assign == nullptr && by_residual,
259
+ "assign is required when by_residual is set");
166
260
 
167
261
  rabitq.train(n, x);
168
262
  is_trained = true;
@@ -176,9 +270,9 @@ void IndexIVFRaBitQFastScan::encode_vectors(
176
270
  uint8_t* codes,
177
271
  bool include_listnos) const {
178
272
  FAISS_THROW_IF_NOT(n > 0);
179
- FAISS_THROW_IF_NOT(x != nullptr);
180
- FAISS_THROW_IF_NOT(list_nos != nullptr);
181
- FAISS_THROW_IF_NOT(codes != nullptr);
273
+ FAISS_THROW_IF_NOT(x);
274
+ FAISS_THROW_IF_NOT(list_nos);
275
+ FAISS_THROW_IF_NOT(codes);
182
276
  FAISS_THROW_IF_NOT(is_trained);
183
277
 
184
278
  size_t coarse_size = include_listnos ? coarse_code_size() : 0;
@@ -416,9 +510,9 @@ void IndexIVFRaBitQFastScan::search_preassigned(
416
510
  IndexIVFStats* stats) const {
417
511
  FAISS_THROW_IF_NOT(is_trained);
418
512
  FAISS_THROW_IF_NOT(k > 0);
419
- FAISS_THROW_IF_NOT_MSG(
420
- !store_pairs, "store_pairs not supported for RaBitQFastScan");
421
- FAISS_THROW_IF_NOT_MSG(!stats, "stats not supported for this index");
513
+ FAISS_THROW_IF_MSG(
514
+ store_pairs, "store_pairs not supported for RaBitQFastScan");
515
+ FAISS_THROW_IF_MSG(stats, "stats not supported for this index");
422
516
 
423
517
  size_t cur_nprobe = this->nprobe;
424
518
  uint8_t used_qb = qb;
@@ -453,6 +547,7 @@ void IndexIVFRaBitQFastScan::compute_LUT(
453
547
  const FastScanDistancePostProcessing& context) const {
454
548
  FAISS_THROW_IF_NOT(is_trained);
455
549
  FAISS_THROW_IF_NOT(by_residual);
550
+ FAISS_ASSERT(ksub == 16);
456
551
 
457
552
  // Use overridden qb/centered from context if provided, else index defaults
458
553
  const uint8_t used_qb = context.qb > 0 ? context.qb : qb;
@@ -515,6 +610,7 @@ void IndexIVFRaBitQFastScan::compute_LUT_uint8(
515
610
  const FastScanDistancePostProcessing& context) const {
516
611
  FAISS_THROW_IF_NOT(is_trained);
517
612
  FAISS_THROW_IF_NOT(by_residual);
613
+ FAISS_ASSERT(ksub == 16);
518
614
 
519
615
  const uint8_t used_qb = context.qb > 0 ? context.qb : qb;
520
616
  const bool used_centered = context.qb > 0 ? context.centered : centered;
@@ -572,45 +668,53 @@ void IndexIVFRaBitQFastScan::compute_LUT_uint8(
572
668
  float glob_max_span = -HUGE_VAL;
573
669
  float glob_max_dis = -HUGE_VAL;
574
670
  float glob_b = HUGE_VAL;
575
- for (size_t j2 = 0; j2 < cur_nprobe; j2++) {
576
- float b_j = 0;
577
- float span_j = 0;
578
- for (size_t m = 0; m < M; m++) {
579
- const float* tab = lut_float.get() + j2 * dim12 + m * ksub;
580
- float mn = tab[0], mx = tab[0];
581
- for (size_t s = 1; s < ksub; s++) {
582
- mn = std::min(mn, tab[s]);
583
- mx = std::max(mx, tab[s]);
584
- }
585
- all_mins[j2 * M + m] = mn;
586
- float span = mx - mn;
587
- glob_max_span = std::max(glob_max_span, span);
588
- b_j += mn;
589
- span_j += span;
590
- }
591
- probe_b[j2] = b_j;
592
- glob_max_dis = std::max(glob_max_dis, span_j);
593
- glob_b = std::min(glob_b, b_j);
594
- }
595
- float a = std::min(255.0f / glob_max_span, 65535.0f / glob_max_dis);
596
-
597
- // Second pass: quantize LUT and compute biasq
598
- uint8_t* out_base = dis_tables.get() + i * cur_nprobe * dim12_2;
599
- uint16_t* bq = biases.get() + i * cur_nprobe;
600
- for (size_t j2 = 0; j2 < cur_nprobe; j2++) {
601
- for (size_t m = 0; m < M; m++) {
602
- const float* tab = lut_float.get() + j2 * dim12 + m * ksub;
603
- float mn = all_mins[j2 * M + m];
604
- uint8_t* out = out_base + j2 * dim12_2 + m * ksub;
605
- for (size_t s = 0; s < ksub; s++) {
606
- out[s] = static_cast<uint8_t>(
607
- std::roundf(a * (tab[s] - mn)));
608
- }
609
- }
610
- memset(out_base + j2 * dim12_2 + M * ksub, 0, (M2 - M) * ksub);
611
- bq[j2] = static_cast<uint16_t>(
612
- std::roundf(a * (probe_b[j2] - glob_b)));
613
- }
671
+ float a;
672
+ with_selected_simd_levels<rabitq::RABITQ_QUANTIZATION_SIMD_LEVELS>(
673
+ [&]<SIMDLevel SL>() {
674
+ for (size_t j2 = 0; j2 < cur_nprobe; j2++) {
675
+ float b_j = 0;
676
+ float span_j = 0;
677
+ for (size_t m = 0; m < M; m++) {
678
+ const float* tab =
679
+ lut_float.get() + j2 * dim12 + m * ksub;
680
+ float mn, mx;
681
+ rabitq::lut_minmax_16<SL>(tab, mn, mx);
682
+ all_mins[j2 * M + m] = mn;
683
+ float span = mx - mn;
684
+ glob_max_span = std::max(glob_max_span, span);
685
+ b_j += mn;
686
+ span_j += span;
687
+ }
688
+ probe_b[j2] = b_j;
689
+ glob_max_dis = std::max(glob_max_dis, span_j);
690
+ glob_b = std::min(glob_b, b_j);
691
+ }
692
+
693
+ a = std::min(
694
+ 255.0f / glob_max_span,
695
+ 65535.0f / glob_max_dis);
696
+
697
+ // Second pass: quantize LUT and compute biasq.
698
+ uint8_t* out_base =
699
+ dis_tables.get() + i * cur_nprobe * dim12_2;
700
+ uint16_t* bq = biases.get() + i * cur_nprobe;
701
+ for (size_t j2 = 0; j2 < cur_nprobe; j2++) {
702
+ for (size_t m = 0; m < M; m++) {
703
+ const float* tab =
704
+ lut_float.get() + j2 * dim12 + m * ksub;
705
+ const float mn = all_mins[j2 * M + m];
706
+ uint8_t* out =
707
+ out_base + j2 * dim12_2 + m * ksub;
708
+ rabitq::lut_quantize_16_to_uint8<SL>(
709
+ tab, mn, a, out);
710
+ }
711
+ memset(out_base + j2 * dim12_2 + M * ksub,
712
+ 0,
713
+ (M2 - M) * ksub);
714
+ bq[j2] = round_nonnegative_to_uint16(
715
+ a * (probe_b[j2] - glob_b));
716
+ }
717
+ });
614
718
  normalizers[2 * i] = a;
615
719
  normalizers[2 * i + 1] = glob_b;
616
720
  }
@@ -676,8 +780,8 @@ void IndexIVFRaBitQFastScan::sa_decode(idx_t n, const uint8_t* bytes, float* x)
676
780
  const {
677
781
  FAISS_THROW_IF_NOT(is_trained);
678
782
  FAISS_THROW_IF_NOT(n > 0);
679
- FAISS_THROW_IF_NOT(bytes != nullptr);
680
- FAISS_THROW_IF_NOT(x != nullptr);
783
+ FAISS_THROW_IF_NOT(bytes);
784
+ FAISS_THROW_IF_NOT(x);
681
785
 
682
786
  size_t coarse_size = coarse_code_size();
683
787
  size_t total_code_size = code_size + coarse_size;
@@ -826,35 +930,35 @@ struct IVFRaBitQFastScanScanner : InvertedListScanner {
826
930
  const size_t M = index.M;
827
931
  const size_t M2 = index.M2;
828
932
  const size_t ksub = index.ksub;
933
+ FAISS_ASSERT(ksub == 16);
829
934
 
830
935
  float max_span = -HUGE_VAL;
831
936
  float max_dis = 0;
832
937
  float b = 0;
833
938
  float* mins = mins_buf.data();
834
939
 
835
- for (size_t m = 0; m < M; m++) {
836
- const float* tab = lut_float.get() + m * ksub;
837
- float mn = tab[0], mx = tab[0];
838
- for (size_t s = 1; s < ksub; s++) {
839
- mn = std::min(mn, tab[s]);
840
- mx = std::max(mx, tab[s]);
841
- }
842
- mins[m] = mn;
843
- float span = mx - mn;
844
- max_span = std::max(max_span, span);
845
- max_dis += span;
846
- b += mn;
847
- }
848
-
849
- float a = std::min(255.0f / max_span, 65535.0f / max_dis);
940
+ float a;
850
941
  uint8_t* out = dis_tables.get();
851
- for (size_t m = 0; m < M; m++) {
852
- const float* tab = lut_float.get() + m * ksub;
853
- for (size_t s = 0; s < ksub; s++) {
854
- out[m * ksub + s] = static_cast<uint8_t>(
855
- std::roundf(a * (tab[s] - mins[m])));
856
- }
857
- }
942
+ with_selected_simd_levels<rabitq::RABITQ_QUANTIZATION_SIMD_LEVELS>(
943
+ [&]<SIMDLevel SL>() {
944
+ for (size_t m = 0; m < M; m++) {
945
+ const float* tab = lut_float.get() + m * ksub;
946
+ float mn, mx;
947
+ rabitq::lut_minmax_16<SL>(tab, mn, mx);
948
+ mins[m] = mn;
949
+ float span = mx - mn;
950
+ max_span = std::max(max_span, span);
951
+ max_dis += span;
952
+ b += mn;
953
+ }
954
+
955
+ a = std::min(255.0f / max_span, 65535.0f / max_dis);
956
+ for (size_t m = 0; m < M; m++) {
957
+ const float* tab = lut_float.get() + m * ksub;
958
+ rabitq::lut_quantize_16_to_uint8<SL>(
959
+ tab, mins[m], a, out + m * ksub);
960
+ }
961
+ });
858
962
  memset(out + M * ksub, 0, (M2 - M) * ksub);
859
963
  biases[0] = 0;
860
964
  normalizers[0] = a;
@@ -267,11 +267,12 @@ void IVFRaBitQHeapHandler<C, SL>::handle(
267
267
  }
268
268
  const size_t max_positions = std::min<size_t>(32, this->ntotal - idx_base);
269
269
 
270
- // Hoist aux pointer base out of loop: all 32 elements in this block share
271
- // the same block base. Only the per-element offset (j * storage_size)
272
- // varies.
270
+ // Hoist aux pointer base out of loop: it points at this 32-lane sub-block's
271
+ // factors, i.e. the bbs block base plus the loop-invariant intra-block
272
+ // offset ((idx_base % bbs)). Only the per-element j term varies below.
273
273
  const uint8_t* aux_base = this->list_codes_ptr +
274
- (idx_base / index->bbs) * full_block_size + packed_block_size;
274
+ (idx_base / index->bbs) * full_block_size + packed_block_size +
275
+ (idx_base % index->bbs) * storage_size;
275
276
 
276
277
  // Cache index fields used in the inner loop.
277
278
  // Use overridden qb/centered from context if provided, else index defaults.