faiss 0.6.1 → 0.6.3

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 (186) 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/IVFlib.cpp +14 -3
  7. data/vendor/faiss/faiss/Index.h +2 -2
  8. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  10. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
  11. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +10 -12
  12. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  13. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +5 -7
  14. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  15. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  16. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  17. data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
  18. data/vendor/faiss/faiss/IndexHNSW.cpp +177 -148
  19. data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
  20. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  21. data/vendor/faiss/faiss/IndexIVF.cpp +19 -8
  22. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +3 -3
  23. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  24. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  25. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  26. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  27. data/vendor/faiss/faiss/IndexIVFFlat.cpp +6 -5
  28. data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +3 -3
  29. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  30. data/vendor/faiss/faiss/IndexIVFPQ.cpp +42 -25
  31. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  32. data/vendor/faiss/faiss/IndexIVFPQR.cpp +2 -3
  33. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +23 -62
  34. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  35. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  36. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  37. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  38. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  39. data/vendor/faiss/faiss/IndexNNDescent.cpp +10 -3
  40. data/vendor/faiss/faiss/IndexNSG.cpp +8 -4
  41. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  42. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  43. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  44. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  45. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  46. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  47. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  48. data/vendor/faiss/faiss/IndexScalarQuantizer.cpp +68 -6
  49. data/vendor/faiss/faiss/IndexScalarQuantizer.h +10 -0
  50. data/vendor/faiss/faiss/IndexShards.cpp +2 -2
  51. data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
  52. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  53. data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
  54. data/vendor/faiss/faiss/SuperKMeans.h +30 -0
  55. data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
  56. data/vendor/faiss/faiss/clone_index.cpp +5 -0
  57. data/vendor/faiss/faiss/cppcontrib/SaDecodeKernels.h +1 -1
  58. data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-neon-inl.h +902 -12
  59. data/vendor/faiss/faiss/cppcontrib/sa_decode/PQ-neon-inl.h +702 -10
  60. data/vendor/faiss/faiss/factory_tools.cpp +51 -4
  61. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  62. data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
  63. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
  64. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  65. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  66. data/vendor/faiss/faiss/gpu/GpuResources.h +3 -2
  67. data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +11 -12
  68. data/vendor/faiss/faiss/gpu/StandardGpuResources.h +3 -3
  69. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  70. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  71. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  72. data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +87 -0
  73. data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +7 -0
  74. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +177 -0
  75. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  76. data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +48 -3
  77. data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +45 -0
  78. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFFlat.h +193 -0
  79. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  80. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
  81. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  82. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  83. data/vendor/faiss/faiss/impl/HNSW.cpp +658 -344
  84. data/vendor/faiss/faiss/impl/HNSW.h +51 -13
  85. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  86. data/vendor/faiss/faiss/impl/NSG.cpp +18 -12
  87. data/vendor/faiss/faiss/impl/Panorama.h +20 -7
  88. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  89. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +59 -24
  90. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +45 -37
  91. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  92. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +175 -68
  93. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
  94. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
  95. data/vendor/faiss/faiss/impl/ResultHandler.h +26 -31
  96. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +522 -58
  97. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +70 -0
  98. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  99. data/vendor/faiss/faiss/impl/VisitedTable.cpp +33 -13
  100. data/vendor/faiss/faiss/impl/VisitedTable.h +88 -33
  101. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
  102. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  103. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +38 -3
  104. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  105. data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.cpp +35 -43
  106. data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.h +64 -15
  107. data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +86 -40
  108. data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +81 -50
  109. data/vendor/faiss/faiss/impl/index_read.cpp +476 -75
  110. data/vendor/faiss/faiss/impl/index_write.cpp +56 -4
  111. data/vendor/faiss/faiss/impl/io_macros.h +25 -0
  112. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  113. data/vendor/faiss/faiss/impl/platform_macros.h +15 -9
  114. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  115. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  116. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  117. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  118. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +6 -4
  119. data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +2 -0
  120. data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +2 -0
  121. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +20 -0
  122. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +36 -0
  123. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +5 -0
  124. data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +105 -0
  125. data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +2 -0
  126. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  127. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  128. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  129. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  130. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  131. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
  132. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +6 -0
  133. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +336 -26
  134. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +331 -32
  135. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +553 -0
  136. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +558 -0
  137. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +284 -45
  138. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +502 -3
  139. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +157 -32
  140. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +26 -0
  141. data/vendor/faiss/faiss/impl/simd_dispatch.h +86 -8
  142. data/vendor/faiss/faiss/index_factory.cpp +37 -7
  143. data/vendor/faiss/faiss/index_io.h +16 -0
  144. data/vendor/faiss/faiss/invlists/DirectMap.cpp +5 -2
  145. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +15 -15
  146. data/vendor/faiss/faiss/invlists/InvertedLists.h +2 -2
  147. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
  148. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  149. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  150. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  151. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  152. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  153. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  154. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +150 -23
  155. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +30 -7
  156. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +3 -2
  157. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
  158. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +65 -25
  159. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +3 -2
  160. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  161. data/vendor/faiss/faiss/utils/bf16.h +34 -0
  162. data/vendor/faiss/faiss/utils/distances.cpp +14 -2
  163. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -4
  164. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  165. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  166. data/vendor/faiss/faiss/utils/hamming.cpp +9 -9
  167. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +2 -1
  168. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +15 -0
  169. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +6 -30
  170. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_spr.h +171 -0
  171. data/vendor/faiss/faiss/utils/partitioning.cpp +0 -2
  172. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  173. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  174. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  175. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  176. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
  177. data/vendor/faiss/faiss/utils/simd_impl/partitioning_simdlib256.h +14 -68
  178. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  179. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
  180. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +435 -0
  181. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  182. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  183. data/vendor/faiss/faiss/utils/simd_levels.cpp +56 -2
  184. data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
  185. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  186. metadata +27 -2
@@ -13,12 +13,15 @@
13
13
  #include <cstdio>
14
14
  #include <cstring>
15
15
  #include <memory>
16
+ #include <type_traits>
16
17
 
17
18
  #include <algorithm>
18
19
 
19
20
  #include <faiss/IndexFlat.h>
20
21
  #include <faiss/VectorTransform.h>
21
22
  #include <faiss/impl/FaissAssert.h>
23
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader)
24
+ #include <faiss/impl/pq_code_distance/pq_code_distance-inl.h>
22
25
  #include <faiss/impl/simd_dispatch.h>
23
26
  #include <faiss/utils/distances.h>
24
27
 
@@ -719,8 +722,28 @@ void pq_knn_search_with_tables(
719
722
 
720
723
  switch (nbits) {
721
724
  case 8:
722
- pq_estimators_from_tables<uint8_t, C>(
723
- pq, codes, ncodes, dis_table, k, heap_dis, heap_ids);
725
+ if (ksub == 256) {
726
+ constexpr bool max_heap =
727
+ std::is_same_v<C, CMax<float, int64_t>>;
728
+ pq_code_distance::pq_scan_8bit(
729
+ M,
730
+ dis_table,
731
+ codes,
732
+ ncodes,
733
+ k,
734
+ heap_dis,
735
+ heap_ids,
736
+ max_heap);
737
+ } else {
738
+ pq_estimators_from_tables<uint8_t, C>(
739
+ pq,
740
+ codes,
741
+ ncodes,
742
+ dis_table,
743
+ k,
744
+ heap_dis,
745
+ heap_ids);
746
+ }
724
747
  break;
725
748
 
726
749
  case 16:
@@ -839,32 +862,44 @@ void ProductQuantizer::search_sdc(
839
862
  size_t k = res->k;
840
863
  int64_t nq_signed = nq;
841
864
 
842
- #pragma omp parallel for
843
- for (int64_t i = 0; i < nq_signed; i++) {
844
- /* Compute distances and keep smallest values */
845
- idx_t* heap_ids = res->ids + i * k;
846
- float* heap_dis = res->val + i * k;
847
- const uint8_t* qcode = qcodes + i * code_size;
848
-
849
- if (init_finalize_heap)
850
- maxheap_heapify(k, heap_dis, heap_ids);
851
-
852
- const uint8_t* bcode = bcodes;
853
- for (size_t j = 0; j < nb; j++) {
854
- float dis = 0;
855
- const float* tab = sdc_table.data();
865
+ #pragma omp parallel
866
+ {
867
+ // One allocation per OMP thread instead of one per query.
868
+ std::vector<const float*> q_row(M);
869
+ #pragma omp for
870
+ for (int64_t i = 0; i < nq_signed; i++) {
871
+ idx_t* heap_ids = res->ids + i * k;
872
+ float* heap_dis = res->val + i * k;
873
+ const uint8_t* qcode = qcodes + i * code_size;
874
+
875
+ if (init_finalize_heap)
876
+ maxheap_heapify(k, heap_dis, heap_ids);
877
+
878
+ // Precompute per-subquantizer row pointers: q_row[m] points to
879
+ // sdc_table[m*ksub^2 + qcode[m]*ksub], eliminating M
880
+ // multiplications and M pointer advances per database vector in the
881
+ // j-loop.
882
+ const float* sdc = sdc_table.data();
856
883
  for (size_t m = 0; m < M; m++) {
857
- dis += tab[bcode[m] + qcode[m] * ksub];
858
- tab += ksub * ksub;
884
+ q_row[m] = sdc + m * (size_t)(ksub * ksub) +
885
+ (size_t)qcode[m] * ksub;
859
886
  }
860
- if (dis < heap_dis[0]) {
861
- maxheap_replace_top(k, heap_dis, heap_ids, dis, j);
887
+
888
+ const uint8_t* bcode = bcodes;
889
+ for (size_t j = 0; j < nb; j++) {
890
+ float dis = 0;
891
+ for (size_t m = 0; m < M; m++) {
892
+ dis += q_row[m][bcode[m]];
893
+ }
894
+ if (dis < heap_dis[0]) {
895
+ maxheap_replace_top(k, heap_dis, heap_ids, dis, j);
896
+ }
897
+ bcode += code_size;
862
898
  }
863
- bcode += code_size;
864
- }
865
899
 
866
- if (init_finalize_heap)
867
- maxheap_reorder(k, heap_dis, heap_ids);
900
+ if (init_finalize_heap)
901
+ maxheap_reorder(k, heap_dis, heap_ids);
902
+ }
868
903
  }
869
904
  }
870
905
 
@@ -188,46 +188,54 @@ QueryFactorsData compute_query_factors(
188
188
 
189
189
  const float inv_d_sqrt = 1.0f / std::sqrt(static_cast<float>(d));
190
190
 
191
- // Compute quantization range
192
- float v_min = std::numeric_limits<float>::max();
193
- float v_max = std::numeric_limits<float>::lowest();
194
-
195
191
  const float* rq = rotated_q.data();
196
- if (centered) {
197
- float z_max = Z_MAX_BY_QB[qb - 1];
198
- float v_radius = z_max * std::sqrt(query_factors.qr_to_c_L2sqr / d);
199
- v_min = -v_radius;
200
- v_max = v_radius;
201
- } else {
202
- for (size_t i = 0; i < d; i++) {
203
- const float v_q = rq[i];
204
- v_min = std::min(v_min, v_q);
205
- v_max = std::max(v_max, v_q);
206
- }
207
- }
208
-
209
- // Quantize the query
210
- const uint8_t max_code = (1 << qb) - 1;
211
- const float delta = (v_max - v_min) / max_code;
212
- const float inv_delta = 1.0f / delta;
213
-
214
- rotated_qq.resize(d);
192
+ float v_min;
193
+ float v_max;
194
+ float delta;
215
195
  size_t sum_qq = 0;
216
196
  int64_t sum2_signed_odd_int = 0;
217
-
197
+ const uint8_t max_code = (1 << qb) - 1;
198
+ rotated_qq.resize(d);
218
199
  uint8_t* rqq = rotated_qq.data();
219
- for (size_t i = 0; i < d; i++) {
220
- const float v_q = rq[i];
221
- const uint8_t v_qq = std::clamp<float>(
222
- std::round((v_q - v_min) * inv_delta), 0, max_code);
223
- rqq[i] = v_qq;
224
- sum_qq += v_qq;
225
-
226
- if (centered) {
227
- int64_t signed_odd_int = int64_t(v_qq) * 2 - max_code;
228
- sum2_signed_odd_int += signed_odd_int * signed_odd_int;
229
- }
230
- }
200
+
201
+ // Select the SIMD implementation once for both range computation and
202
+ // quantization. This function runs once per query/probe pair.
203
+ with_selected_simd_levels<rabitq::RABITQ_QUANTIZATION_SIMD_LEVELS>(
204
+ [&]<SIMDLevel SL>() {
205
+ if (centered) {
206
+ const float z_max = Z_MAX_BY_QB[qb - 1];
207
+ const float v_radius =
208
+ z_max * std::sqrt(query_factors.qr_to_c_L2sqr / d);
209
+ v_min = -v_radius;
210
+ v_max = v_radius;
211
+ } else {
212
+ v_min = std::numeric_limits<float>::max();
213
+ v_max = std::numeric_limits<float>::lowest();
214
+ rabitq::minmax_values<SL>(rq, d, v_min, v_max);
215
+ }
216
+
217
+ delta = (v_max - v_min) / max_code;
218
+ // A constant (or zero-norm) query has delta == 0. Preserve the
219
+ // scalar path's centered correction terms while avoiding
220
+ // 0 * inf during quantization.
221
+ if (delta <= 0.0f) {
222
+ memset(rqq, 0, d * sizeof(uint8_t));
223
+ if (centered) {
224
+ sum2_signed_odd_int = int64_t(d) * max_code * max_code;
225
+ }
226
+ } else {
227
+ rabitq::quantize_query_values<SL>(
228
+ rq,
229
+ d,
230
+ v_min,
231
+ 1.0f / delta,
232
+ max_code,
233
+ centered,
234
+ rqq,
235
+ sum_qq,
236
+ sum2_signed_odd_int);
237
+ }
238
+ });
231
239
 
232
240
  // Compute query factors
233
241
  query_factors.c1 = 2 * delta * inv_d_sqrt;
@@ -321,7 +329,7 @@ float compute_full_multibit_distance(
321
329
  size_t d,
322
330
  size_t ex_bits,
323
331
  MetricType metric_type) {
324
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0>(
332
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0_SPR>(
325
333
  [&]<SIMDLevel SL>() {
326
334
  return compute_full_multibit_distance<SL>(
327
335
  sign_bits,
@@ -13,6 +13,7 @@
13
13
  #include <faiss/utils/rabitq_simd.h>
14
14
  #include <faiss/utils/simd_levels.h>
15
15
  #include <algorithm>
16
+ #include <cassert>
16
17
  #include <cstddef>
17
18
  #include <cstdint>
18
19
  #include <cstring>
@@ -86,6 +87,40 @@ struct QueryFactorsData {
86
87
  */
87
88
  FAISS_API extern const float Z_MAX_BY_QB[8];
88
89
 
90
+ /** Fast half-away rounding for non-negative RaBitQ quantization values.
91
+ *
92
+ * Contract: x must be finite, non-NaN, and in [0, 255.5). This is not a
93
+ * bit-exact replacement for roundf within a few ulps below k + 0.5, where the
94
+ * addition may round across the tie. It is intended only for RaBitQ LUT
95
+ * quantization paths where a +/- 1 code difference at those boundaries is
96
+ * acceptable.
97
+ */
98
+ inline uint8_t round_nonnegative_to_uint8(float x) {
99
+ assert(x == x);
100
+ assert(x >= 0.0f);
101
+ assert(x < 255.5f);
102
+ return rabitq::round_nonnegative_byte_scalar(x);
103
+ }
104
+
105
+ /** Same as round_nonnegative_to_uint8 for uint16 RaBitQ bias values. */
106
+ inline uint16_t round_nonnegative_to_uint16(float x) {
107
+ assert(x == x);
108
+ assert(x >= 0.0f);
109
+ assert(x < 65535.5f);
110
+ return static_cast<uint16_t>(static_cast<int>(x + 0.5f));
111
+ }
112
+
113
+ /** Fast clamped rounding for query-byte quantization.
114
+ *
115
+ * Contract: x must be non-NaN. Values outside [0, max_code] are clamped before
116
+ * applying the same non-bit-exact rounding used by
117
+ * round_nonnegative_to_uint8().
118
+ */
119
+ inline uint8_t round_clamped_to_uint8(float x, uint8_t max_code) {
120
+ assert(x == x);
121
+ return rabitq::round_clamped_byte_scalar(x, max_code);
122
+ }
123
+
89
124
  /** Compute factors for a single database vector using RaBitQ algorithm.
90
125
  * This function consolidates the mathematical logic that was duplicated
91
126
  * between IndexRaBitQ and IndexRaBitQFastScan.
@@ -8,13 +8,15 @@
8
8
  #include <faiss/impl/RaBitQuantizer.h>
9
9
 
10
10
  #include <faiss/impl/FaissAssert.h>
11
+ #include <faiss/impl/IDSelector.h>
11
12
  #include <faiss/impl/RaBitQUtils.h>
12
13
  #include <faiss/impl/RaBitQuantizerMultiBit.h>
14
+ #include <faiss/impl/ResultHandler.h>
13
15
  #include <faiss/impl/simd_dispatch.h>
16
+ #include <faiss/invlists/DirectMap.h>
14
17
  #include <faiss/utils/distances.h>
15
18
  #include <faiss/utils/rabitq_simd.h>
16
19
 
17
- #include <algorithm>
18
20
  #include <cmath>
19
21
  #include <cstring>
20
22
  #include <memory>
@@ -183,8 +185,10 @@ void RaBitQuantizer::decode_core(
183
185
  float* x,
184
186
  size_t n,
185
187
  const float* centroid_in) const {
186
- FAISS_ASSERT(codes != nullptr);
187
- FAISS_ASSERT(x != nullptr);
188
+ FAISS_THROW_IF_MSG(
189
+ codes == nullptr, "RaBitQuantizer::decode_core: null codes buffer");
190
+ FAISS_THROW_IF_MSG(
191
+ x == nullptr, "RaBitQuantizer::decode_core: null output buffer");
188
192
 
189
193
  const float inv_d_sqrt = (d == 0) ? 1.0f : (1.0f / std::sqrt((float)d));
190
194
  const size_t ex_bits = nb_bits - 1;
@@ -228,7 +232,7 @@ namespace {
228
232
  // directly to the SIMD-specialized code.
229
233
 
230
234
  template <SIMDLevel SL>
231
- struct RaBitQDistanceComputerNotQ : RaBitQDistanceComputer {
235
+ struct RaBitQDistanceComputerNotQ final : RaBitQDistanceComputer {
232
236
  // the rotated query (qr - c)
233
237
  std::vector<float> rotated_q;
234
238
  // some additional numbers for the query
@@ -237,26 +241,9 @@ struct RaBitQDistanceComputerNotQ : RaBitQDistanceComputer {
237
241
  RaBitQDistanceComputerNotQ() = default;
238
242
 
239
243
  // Compute distance using only 1-bit codes (fast)
240
- float distance_to_code_1bit(const uint8_t* code) override {
241
- FAISS_ASSERT(code != nullptr);
242
- FAISS_ASSERT(
243
- (metric_type == MetricType::METRIC_L2 ||
244
- metric_type == MetricType::METRIC_INNER_PRODUCT));
245
- FAISS_ASSERT(rotated_q.size() == d);
246
-
247
- // split the code into parts
248
- const uint8_t* binary_data = code;
249
-
250
- // Cast to appropriate type based on nb_bits
251
- // For 1-bit: use SignBitFactors (8 bytes)
252
- // For multi-bit: use SignBitFactorsWithError (12 bytes) which includes
253
- // f_error
254
- size_t ex_bits = nb_bits - 1;
255
- const SignBitFactors* base_fac = (ex_bits == 0)
256
- ? reinterpret_cast<const SignBitFactors*>(code + (d + 7) / 8)
257
- : reinterpret_cast<const SignBitFactorsWithError*>(
258
- code + (d + 7) / 8);
259
-
244
+ float distance_to_code_1bit_impl(
245
+ const uint8_t* binary_data,
246
+ const SignBitFactors* base_fac) const {
260
247
  // this is the baseline code
261
248
  //
262
249
  // compute <q,o> using floats
@@ -286,7 +273,7 @@ struct RaBitQDistanceComputerNotQ : RaBitQDistanceComputer {
286
273
 
287
274
  if (metric_type == MetricType::METRIC_L2) {
288
275
  // ||or - q||^ 2
289
- return pre_dist;
276
+ return std::max(0.0f, pre_dist);
290
277
  } else {
291
278
  // metric == MetricType::METRIC_INNER_PRODUCT
292
279
  // 2 * (or, q) = (||or - q||^2 - ||q||^2 - ||or||^2)
@@ -294,8 +281,24 @@ struct RaBitQDistanceComputerNotQ : RaBitQDistanceComputer {
294
281
  }
295
282
  }
296
283
 
284
+ float distance_to_code_1bit(const uint8_t* code) final {
285
+ FAISS_ASSERT(code != nullptr);
286
+ FAISS_ASSERT(
287
+ (metric_type == MetricType::METRIC_L2 ||
288
+ metric_type == MetricType::METRIC_INNER_PRODUCT));
289
+ FAISS_ASSERT(rotated_q.size() == d);
290
+
291
+ const size_t code_size_base = (d + 7) / 8;
292
+ const size_t ex_bits = nb_bits - 1;
293
+ const SignBitFactors* base_fac = (ex_bits == 0)
294
+ ? reinterpret_cast<const SignBitFactors*>(code + code_size_base)
295
+ : reinterpret_cast<const SignBitFactorsWithError*>(
296
+ code + code_size_base);
297
+ return distance_to_code_1bit_impl(code, base_fac);
298
+ }
299
+
297
300
  // Compute full distance using 1-bit + ex-bits (accurate)
298
- float distance_to_code_full(const uint8_t* code) override {
301
+ float distance_to_code_full(const uint8_t* code) final {
299
302
  FAISS_ASSERT(code != nullptr);
300
303
  FAISS_ASSERT(
301
304
  (metric_type == MetricType::METRIC_L2 ||
@@ -331,7 +334,7 @@ struct RaBitQDistanceComputerNotQ : RaBitQDistanceComputer {
331
334
  metric_type);
332
335
  }
333
336
 
334
- void set_query(const float* x) override {
337
+ void set_query(const float* x) final {
335
338
  q = x;
336
339
  FAISS_ASSERT(x != nullptr);
337
340
  FAISS_ASSERT(
@@ -372,10 +375,62 @@ struct RaBitQDistanceComputerNotQ : RaBitQDistanceComputer {
372
375
  centroid ? fvec_inner_product(x, centroid, d) : 0.0f;
373
376
  }
374
377
  }
378
+
379
+ size_t scan_codes_multibit(
380
+ size_t list_size,
381
+ const uint8_t* codes,
382
+ const idx_t* ids,
383
+ size_t code_size,
384
+ idx_t list_no,
385
+ bool store_pairs,
386
+ const IDSelector* sel,
387
+ bool keep_max,
388
+ ResultHandler& handler) final {
389
+ const size_t code_size_base = (d + 7) / 8;
390
+ const size_t ex_bits = nb_bits - 1;
391
+ FAISS_ASSERT(ex_bits > 0);
392
+
393
+ size_t nup = 0;
394
+ for (size_t j = 0; j < list_size; j++) {
395
+ if (sel != nullptr) {
396
+ idx_t id = store_pairs ? lo_build(list_no, j) : ids[j];
397
+ if (!sel->is_member(id)) {
398
+ codes += code_size;
399
+ continue;
400
+ }
401
+ }
402
+
403
+ const auto* base_fac =
404
+ reinterpret_cast<const SignBitFactorsWithError*>(
405
+ codes + code_size_base);
406
+ const float est_distance =
407
+ distance_to_code_1bit_impl(codes, base_fac);
408
+
409
+ const bool should_refine = rabitq_utils::should_refine_candidate(
410
+ est_distance,
411
+ base_fac->f_error,
412
+ g_error,
413
+ handler.threshold,
414
+ keep_max);
415
+ if (should_refine) {
416
+ handler.stats.scan_cnt++;
417
+ const float dis = distance_to_code_full(codes);
418
+ idx_t id = store_pairs ? lo_build(list_no, j) : ids[j];
419
+
420
+ if (handler.add_result(dis, id)) {
421
+ handler.stats.nheap_updates++;
422
+ nup++;
423
+ }
424
+ }
425
+ codes += code_size;
426
+ }
427
+
428
+ return nup;
429
+ }
375
430
  };
376
431
 
377
432
  template <SIMDLevel SL>
378
- struct RaBitQDistanceComputerQ : RaBitQDistanceComputer {
433
+ struct RaBitQDistanceComputerQ final : RaBitQDistanceComputer {
379
434
  // the rotated and quantized query (qr - c)
380
435
  std::vector<float> rotated_q;
381
436
  // the rotated and quantized query (qr - c) for fast 1-bit computation
@@ -395,25 +450,10 @@ struct RaBitQDistanceComputerQ : RaBitQDistanceComputer {
395
450
  RaBitQDistanceComputerQ() = default;
396
451
 
397
452
  // Compute distance using only 1-bit codes (fast)
398
- float distance_to_code_1bit(const uint8_t* code) override {
399
- FAISS_ASSERT(code != nullptr);
400
- FAISS_ASSERT(
401
- (metric_type == MetricType::METRIC_L2 ||
402
- metric_type == MetricType::METRIC_INNER_PRODUCT));
403
-
404
- // split the code into parts
405
- size_t size = (d + 7) / 8;
406
- const uint8_t* binary_data = code;
407
-
408
- // Cast to appropriate type based on nb_bits
409
- // For 1-bit: use SignBitFactors (8 bytes)
410
- // For multi-bit: use SignBitFactorsWithError (12 bytes) which
411
- // includes f_error
412
- size_t ex_bits = nb_bits - 1;
413
- const SignBitFactors* base_fac = (ex_bits == 0)
414
- ? reinterpret_cast<const SignBitFactors*>(code + size)
415
- : reinterpret_cast<const SignBitFactorsWithError*>(code + size);
416
-
453
+ float distance_to_code_1bit_impl(
454
+ const uint8_t* binary_data,
455
+ const SignBitFactors* base_fac,
456
+ size_t size) const {
417
457
  // this is ||or - c||^2 - (IP ? ||or||^2 : 0)
418
458
  float final_dot = 0;
419
459
  if (centered) {
@@ -428,17 +468,16 @@ struct RaBitQDistanceComputerQ : RaBitQDistanceComputer {
428
468
  qb);
429
469
  final_dot += int_dot * query_fac.int_dot_scale;
430
470
  } else {
431
- auto dot_qo = rabitq::bitwise_and_dot_product<SL>(
432
- rearranged_rotated_qq.data(), binary_data, size, qb);
433
- // It was a willful decision (after the discussion) to not to
434
- // pre-cache the sum of all bits, just in order to reduce the
435
- // overhead per vector.
436
- // process 64-bit popcounts
437
- auto sum_q = rabitq::popcount<SL>(binary_data, size);
471
+ auto bitwise_result =
472
+ rabitq::bitwise_and_dot_product_with_popcount<SL>(
473
+ rearranged_rotated_qq.data(),
474
+ binary_data,
475
+ size,
476
+ qb);
438
477
  // dot-product itself
439
- final_dot += query_fac.c1 * dot_qo;
478
+ final_dot += query_fac.c1 * bitwise_result.dot_product;
440
479
  // normalizer coefficients
441
- final_dot += query_fac.c2 * sum_q;
480
+ final_dot += query_fac.c2 * bitwise_result.popcount;
442
481
  // normalizer coefficients
443
482
  final_dot -= query_fac.c34;
444
483
  }
@@ -449,7 +488,7 @@ struct RaBitQDistanceComputerQ : RaBitQDistanceComputer {
449
488
 
450
489
  if (metric_type == MetricType::METRIC_L2) {
451
490
  // ||or - q||^ 2
452
- return pre_dist;
491
+ return std::max(0.0f, pre_dist);
453
492
  } else {
454
493
  // metric == MetricType::METRIC_INNER_PRODUCT
455
494
  // 2 * (or, q) = (||or - q||^2 - ||q||^2 - ||or||^2)
@@ -457,8 +496,22 @@ struct RaBitQDistanceComputerQ : RaBitQDistanceComputer {
457
496
  }
458
497
  }
459
498
 
499
+ float distance_to_code_1bit(const uint8_t* code) final {
500
+ FAISS_ASSERT(code != nullptr);
501
+ FAISS_ASSERT(
502
+ (metric_type == MetricType::METRIC_L2 ||
503
+ metric_type == MetricType::METRIC_INNER_PRODUCT));
504
+
505
+ const size_t size = (d + 7) / 8;
506
+ const size_t ex_bits = nb_bits - 1;
507
+ const SignBitFactors* base_fac = (ex_bits == 0)
508
+ ? reinterpret_cast<const SignBitFactors*>(code + size)
509
+ : reinterpret_cast<const SignBitFactorsWithError*>(code + size);
510
+ return distance_to_code_1bit_impl(code, base_fac, size);
511
+ }
512
+
460
513
  // Compute full distance using 1-bit + ex-bits (accurate)
461
- float distance_to_code_full(const uint8_t* code) override {
514
+ float distance_to_code_full(const uint8_t* code) final {
462
515
  FAISS_ASSERT(code != nullptr);
463
516
  FAISS_ASSERT(
464
517
  (metric_type == MetricType::METRIC_L2 ||
@@ -494,7 +547,7 @@ struct RaBitQDistanceComputerQ : RaBitQDistanceComputer {
494
547
  metric_type);
495
548
  }
496
549
 
497
- void set_query(const float* x) override {
550
+ void set_query(const float* x) final {
498
551
  q = x;
499
552
  FAISS_ASSERT(x != nullptr);
500
553
  FAISS_ASSERT(
@@ -526,16 +579,64 @@ struct RaBitQDistanceComputerQ : RaBitQDistanceComputer {
526
579
  size_t offset = (d + 7) / 8;
527
580
 
528
581
  rearranged_rotated_qq.resize(offset * qb);
529
- std::fill(
530
- rearranged_rotated_qq.begin(), rearranged_rotated_qq.end(), 0);
531
-
532
- for (size_t idim = 0; idim < d; idim++) {
533
- for (size_t iv = 0; iv < qb; iv++) {
534
- const bool bit = ((rotated_qq[idim] & (1 << iv)) != 0);
535
- rearranged_rotated_qq[iv * offset + idim / 8] |=
536
- bit ? (1 << (idim % 8)) : 0;
582
+ with_selected_simd_levels<
583
+ AVAILABLE_SIMD_LEVELS_NONE | (1 << int(SIMDLevel::AVX2)) |
584
+ (1 << int(SIMDLevel::AVX512))>([&]<SIMDLevel RSL>() {
585
+ rabitq::rearrange_bit_planes<RSL>(
586
+ rotated_qq.data(), d, qb, rearranged_rotated_qq.data());
587
+ });
588
+ }
589
+
590
+ size_t scan_codes_multibit(
591
+ size_t list_size,
592
+ const uint8_t* codes,
593
+ const idx_t* ids,
594
+ size_t code_size,
595
+ idx_t list_no,
596
+ bool store_pairs,
597
+ const IDSelector* sel,
598
+ bool keep_max,
599
+ ResultHandler& handler) final {
600
+ const size_t code_size_base = (d + 7) / 8;
601
+ const size_t ex_bits = nb_bits - 1;
602
+ FAISS_ASSERT(ex_bits > 0);
603
+
604
+ size_t nup = 0;
605
+ for (size_t j = 0; j < list_size; j++) {
606
+ if (sel != nullptr) {
607
+ idx_t id = store_pairs ? lo_build(list_no, j) : ids[j];
608
+ if (!sel->is_member(id)) {
609
+ codes += code_size;
610
+ continue;
611
+ }
537
612
  }
613
+
614
+ const auto* base_fac =
615
+ reinterpret_cast<const SignBitFactorsWithError*>(
616
+ codes + code_size_base);
617
+ const float est_distance =
618
+ distance_to_code_1bit_impl(codes, base_fac, code_size_base);
619
+
620
+ const bool should_refine = rabitq_utils::should_refine_candidate(
621
+ est_distance,
622
+ base_fac->f_error,
623
+ g_error,
624
+ handler.threshold,
625
+ keep_max);
626
+ if (should_refine) {
627
+ handler.stats.scan_cnt++;
628
+ const float dis = distance_to_code_full(codes);
629
+ idx_t id = store_pairs ? lo_build(list_no, j) : ids[j];
630
+
631
+ if (handler.add_result(dis, id)) {
632
+ handler.stats.nheap_updates++;
633
+ nup++;
634
+ }
635
+ }
636
+ codes += code_size;
538
637
  }
638
+
639
+ return nup;
539
640
  }
540
641
  };
541
642
 
@@ -551,7 +652,13 @@ FlatCodesDistanceComputer* RaBitQuantizer::get_distance_computer(
551
652
  // Dispatch on SIMDLevel once here so the distance computer methods
552
653
  // call the SIMD-specialized rabitq functions directly (no per-call
553
654
  // with_simd_level overhead).
554
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0>(
655
+ //
656
+ // Use A0_SPR (which includes AVX512_SPR) so that on Sapphire Rapids
657
+ // and later x86 microarchitectures the VPOPCNTDQ-based RaBitQ
658
+ // specialization in rabitq_avx512_spr.cpp is selected. On AVX-512
659
+ // CPUs without VPOPCNTDQ, dispatch falls through to the AVX512
660
+ // specialization in rabitq_avx512.cpp.
661
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0_SPR>(
555
662
  [&]<SIMDLevel SL>() -> FlatCodesDistanceComputer* {
556
663
  if (qb == 0) {
557
664
  auto dc =
@@ -16,6 +16,10 @@
16
16
 
17
17
  namespace faiss {
18
18
 
19
+ struct IDSelector;
20
+ template <typename T, typename TI>
21
+ struct ResultHandlerUnordered;
22
+
19
23
  // the reference implementation of the https://arxiv.org/pdf/2405.12497
20
24
  // Jianyang Gao, Cheng Long, "RaBitQ: Quantizing High-Dimensional Vectors
21
25
  // with a Theoretical Error Bound for Approximate Nearest Neighbor Search".
@@ -129,6 +133,21 @@ struct RaBitQDistanceComputer : FlatCodesDistanceComputer {
129
133
  // Compute full multi-bit distance (accurate)
130
134
  virtual float distance_to_code_full(const uint8_t* code) = 0;
131
135
 
136
+ virtual void set_centroid(const float* centroid_in) {
137
+ centroid = centroid_in;
138
+ }
139
+
140
+ virtual size_t scan_codes_multibit(
141
+ size_t list_size,
142
+ const uint8_t* codes,
143
+ const idx_t* ids,
144
+ size_t code_size,
145
+ idx_t list_no,
146
+ bool store_pairs,
147
+ const IDSelector* sel,
148
+ bool keep_max,
149
+ ResultHandlerUnordered<float, idx_t>& handler) = 0;
150
+
132
151
  // Override from FlatCodesDistanceComputer
133
152
  // Delegates to distance_to_code_full() for multi-bit distance computation
134
153
  float distance_to_code(const uint8_t* code) final {