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
@@ -36,6 +36,7 @@
36
36
 
37
37
  #include <algorithm>
38
38
  #include <cstdio>
39
+ #include <cstring>
39
40
  #include <limits>
40
41
  #include <memory>
41
42
  #include <vector>
@@ -148,6 +149,7 @@ void hammings_knn_mc_impl(
148
149
  std::unique_ptr<int64_t[]> all_ids_per_dis(new int64_t[na * nBuckets * k]);
149
150
 
150
151
  std::vector<HCounterState<HammingComputer>> cs;
152
+ cs.reserve(na);
151
153
  for (size_t i = 0; i < na; ++i) {
152
154
  cs.push_back(
153
155
  HCounterState<HammingComputer>(
@@ -298,6 +300,132 @@ void generalized_hammings_knn_hc_impl(
298
300
  }
299
301
  }
300
302
 
303
+ /* Both number of words and remainder are constants, so the compiler flattens
304
+ * the per-chunk loop. The query is copied into qw/qt once, not re-read for
305
+ * every candidate. */
306
+ template <int NW, int REM>
307
+ void hammings_ragged_fixed(
308
+ const uint8_t* __restrict a,
309
+ const uint8_t* __restrict b,
310
+ size_t na,
311
+ size_t nb,
312
+ size_t ncodes,
313
+ hamdis_t* __restrict dis) {
314
+ for (size_t i = 0; i < na; i++) {
315
+ const uint8_t* __restrict ai = a + i * ncodes;
316
+ hamdis_t* __restrict dis_ = dis + i * nb;
317
+ // Read the query once here, not once per candidate.
318
+ uint64_t qw[NW == 0 ? 1 : NW];
319
+ for (int k = 0; k < NW; k++) {
320
+ memcpy(&qw[k], ai + k * 8, 8);
321
+ }
322
+ uint64_t qt = 0;
323
+ if constexpr (REM > 0) {
324
+ memcpy(&qt, ai + NW * 8, REM);
325
+ }
326
+ for (size_t j = 0; j < nb; j++) {
327
+ const uint8_t* __restrict bj = b + j * ncodes;
328
+ hamdis_t h = 0;
329
+ uint64_t y;
330
+ for (int k = 0; k < NW; k++) {
331
+ memcpy(&y, bj + k * 8, 8);
332
+ h += popcount64(qw[k] ^ y);
333
+ }
334
+ if constexpr (REM > 0) {
335
+ y = 0;
336
+ memcpy(&y, bj + NW * 8, REM);
337
+ h += popcount64(qt ^ y);
338
+ }
339
+ dis_[j] = h;
340
+ }
341
+ }
342
+ }
343
+
344
+ /* Same loop with the word count read at run time, for codes long enough that
345
+ * a constant count gives no performance improvement. */
346
+ template <int REM>
347
+ void hammings_ragged_var(
348
+ const uint8_t* __restrict a,
349
+ const uint8_t* __restrict b,
350
+ size_t na,
351
+ size_t nb,
352
+ size_t ncodes,
353
+ hamdis_t* __restrict dis) {
354
+ const size_t nwords = ncodes / 8;
355
+ for (size_t i = 0; i < na; i++) {
356
+ const uint8_t* __restrict ai = a + i * ncodes;
357
+ hamdis_t* __restrict dis_ = dis + i * nb;
358
+ for (size_t j = 0; j < nb; j++) {
359
+ const uint8_t* __restrict bj = b + j * ncodes;
360
+ hamdis_t h = 0;
361
+ uint64_t x, y;
362
+ for (size_t k = 0; k < nwords; k++) {
363
+ memcpy(&x, ai + k * 8, 8);
364
+ memcpy(&y, bj + k * 8, 8);
365
+ h += popcount64(x ^ y);
366
+ }
367
+ if constexpr (REM > 0) {
368
+ x = 0;
369
+ y = 0;
370
+ memcpy(&x, ai + nwords * 8, REM);
371
+ memcpy(&y, bj + nwords * 8, REM);
372
+ h += popcount64(x ^ y);
373
+ }
374
+ dis_[j] = h;
375
+ }
376
+ }
377
+ }
378
+
379
+ /* Makes the word count a constant for short codes when calling
380
+ * hammings_ragged_fixed, worth roughly 2x perf improvement. Longer codes
381
+ * gain nothing from it, so they share hammings_ragged_var. */
382
+ template <int REM>
383
+ void hammings_ragged_by_nwords(
384
+ const uint8_t* __restrict a,
385
+ const uint8_t* __restrict b,
386
+ size_t na,
387
+ size_t nb,
388
+ size_t ncodes,
389
+ hamdis_t* __restrict dis) {
390
+ switch (ncodes / 8) {
391
+ case 0:
392
+ return hammings_ragged_fixed<0, REM>(a, b, na, nb, ncodes, dis);
393
+ case 1:
394
+ return hammings_ragged_fixed<1, REM>(a, b, na, nb, ncodes, dis);
395
+ case 2:
396
+ return hammings_ragged_fixed<2, REM>(a, b, na, nb, ncodes, dis);
397
+ case 3:
398
+ return hammings_ragged_fixed<3, REM>(a, b, na, nb, ncodes, dis);
399
+ default:
400
+ return hammings_ragged_var<REM>(a, b, na, nb, ncodes, dis);
401
+ }
402
+ }
403
+
404
+ void hammings_ragged_dispatch(
405
+ const uint8_t* __restrict a,
406
+ const uint8_t* __restrict b,
407
+ size_t na,
408
+ size_t nb,
409
+ size_t ncodes,
410
+ hamdis_t* __restrict dis) {
411
+ switch (ncodes % 8) {
412
+ case 1:
413
+ return hammings_ragged_by_nwords<1>(a, b, na, nb, ncodes, dis);
414
+ case 2:
415
+ return hammings_ragged_by_nwords<2>(a, b, na, nb, ncodes, dis);
416
+ case 3:
417
+ return hammings_ragged_by_nwords<3>(a, b, na, nb, ncodes, dis);
418
+ case 4:
419
+ return hammings_ragged_by_nwords<4>(a, b, na, nb, ncodes, dis);
420
+ case 5:
421
+ return hammings_ragged_by_nwords<5>(a, b, na, nb, ncodes, dis);
422
+ case 6:
423
+ return hammings_ragged_by_nwords<6>(a, b, na, nb, ncodes, dis);
424
+ default:
425
+ return hammings_ragged_by_nwords<7>(a, b, na, nb, ncodes, dis);
426
+ }
427
+ }
428
+
301
429
  } // anonymous namespace
302
430
 
303
431
  /******************************************************************
@@ -356,6 +484,19 @@ void hamming_range_search_fixSL<THE_SIMD_LEVEL>(
356
484
  });
357
485
  }
358
486
 
487
+ /* Its own entry point, so the word-multiple kernels keep the code the
488
+ * compiler already generates for them. */
489
+ template <>
490
+ void hammings_ragged_fixSL<THE_SIMD_LEVEL>(
491
+ const uint8_t* a,
492
+ const uint8_t* b,
493
+ size_t na,
494
+ size_t nb,
495
+ size_t ncodes,
496
+ hamdis_t* dis) {
497
+ hammings_ragged_dispatch(a, b, na, nb, ncodes, dis);
498
+ }
499
+
359
500
  template <>
360
501
  void hammings_fixSL<THE_SIMD_LEVEL>(
361
502
  const uint8_t* a,
@@ -75,7 +75,7 @@ void round_uint8_per_column(
75
75
  max_span = span;
76
76
  }
77
77
  }
78
- float a = 255 / max_span;
78
+ float a = max_span > 0 ? 255.0f / max_span : 0.0f;
79
79
  float b = 0;
80
80
  for (size_t i = 0; i < n; i++) {
81
81
  b += mins[i];
@@ -111,7 +111,7 @@ void round_uint8_per_column_multi(
111
111
  max_span = span;
112
112
  }
113
113
  }
114
- float a = 255 / max_span;
114
+ float a = max_span > 0 ? 255.0f / max_span : 0.0f;
115
115
  float b = 0;
116
116
  for (size_t i = 0; i < n; i++) {
117
117
  b += mins[i];
@@ -143,7 +143,8 @@ void quantize_LUT_and_bias(
143
143
  float* b_out) {
144
144
  float a, b;
145
145
  if (!bias) {
146
- FAISS_THROW_IF_NOT(!lut_is_3d);
146
+ FAISS_THROW_IF_MSG(
147
+ lut_is_3d, "3d LUT is not supported when bias is null");
147
148
  std::vector<float> mins(M);
148
149
  float max_span_LUT = -HUGE_VAL, max_span_dis = 0;
149
150
  b = 0;
@@ -154,7 +155,12 @@ void quantize_LUT_and_bias(
154
155
  max_span_dis += span;
155
156
  b += mins[i];
156
157
  }
157
- a = std::min(255 / max_span_LUT, 65535 / max_span_dis);
158
+ a = std::min(
159
+ max_span_LUT > 0 ? 255.0f / max_span_LUT : HUGE_VALF,
160
+ max_span_dis > 0 ? 65535.0f / max_span_dis : HUGE_VALF);
161
+ if (!std::isfinite(a)) {
162
+ a = 0.0f;
163
+ }
158
164
 
159
165
  for (size_t i = 0; i < M; i++) {
160
166
  round_tab(LUT + i * ksub, ksub, a, mins[i], LUTq + i * ksub);
@@ -174,7 +180,12 @@ void quantize_LUT_and_bias(
174
180
  max_span_dis += span;
175
181
  b += mins[i];
176
182
  }
177
- a = std::min(255 / max_span_LUT, 65535 / max_span_dis);
183
+ a = std::min(
184
+ max_span_LUT > 0 ? 255.0f / max_span_LUT : HUGE_VALF,
185
+ max_span_dis > 0 ? 65535.0f / max_span_dis : HUGE_VALF);
186
+ if (!std::isfinite(a)) {
187
+ a = 0.0f;
188
+ }
178
189
  b += bias_min;
179
190
 
180
191
  for (size_t i = 0; i < M; i++) {
@@ -208,7 +219,12 @@ void quantize_LUT_and_bias(
208
219
  b = std::min(b, b2j);
209
220
  }
210
221
 
211
- a = std::min(255 / max_span_LUT, 65535 / max_span_dis);
222
+ a = std::min(
223
+ max_span_LUT > 0 ? 255.0f / max_span_LUT : HUGE_VALF,
224
+ max_span_dis > 0 ? 65535.0f / max_span_dis : HUGE_VALF);
225
+ if (!std::isfinite(a)) {
226
+ a = 0.0f;
227
+ }
212
228
 
213
229
  ij = 0;
214
230
  size_t ij_2 = 0;
@@ -256,7 +272,7 @@ void quantize_LUT_and_bias(
256
272
  max_span = std::max(max_span, span);
257
273
  b += mins[i];
258
274
  }
259
- a = 255 / max_span;
275
+ a = max_span > 0 ? 255.0f / max_span : 0.0f;
260
276
  ij = 0;
261
277
  size_t ij_2 = 0;
262
278
  for (size_t j = 0; j < nprobe; j++) {
@@ -305,7 +321,12 @@ void aq_quantize_LUT_and_bias(
305
321
  max_span_dis += (i >= M - M_norm ? span * norm_scale : span);
306
322
  b += mins[i];
307
323
  }
308
- a = std::min(255 / max_span_LUT, 65535 / max_span_dis);
324
+ a = std::min(
325
+ max_span_LUT > 0 ? 255.0f / max_span_LUT : HUGE_VALF,
326
+ max_span_dis > 0 ? 65535.0f / max_span_dis : HUGE_VALF);
327
+ if (!std::isfinite(a)) {
328
+ a = 0.0f;
329
+ }
309
330
  b += bias_min;
310
331
 
311
332
  for (size_t i = 0; i < M; i++) {
@@ -7,6 +7,7 @@
7
7
 
8
8
  #pragma once
9
9
 
10
+ #include <algorithm>
10
11
  #include <cstddef>
11
12
  #include <cstdint>
12
13
  #include <cstring>
@@ -16,6 +17,15 @@
16
17
 
17
18
  namespace faiss::rabitq {
18
19
 
20
+ /// SIMD levels with RaBitQ query/LUT quantization implementations.
21
+ constexpr int RABITQ_QUANTIZATION_SIMD_LEVELS = (1 << int(SIMDLevel::NONE)) |
22
+ (1 << int(SIMDLevel::AVX2)) | (1 << int(SIMDLevel::AVX512));
23
+
24
+ struct BitwiseAndDotProductResult {
25
+ uint64_t dot_product;
26
+ uint64_t popcount;
27
+ };
28
+
19
29
  /**
20
30
  * Compute dot product between query and binary data using popcount on AND.
21
31
  *
@@ -32,6 +42,17 @@ uint64_t bitwise_and_dot_product(
32
42
  size_t size,
33
43
  size_t qb);
34
44
 
45
+ /**
46
+ * Compute bitwise_and_dot_product(query, data, size, qb) and popcount(data,
47
+ * size) in one pass over data.
48
+ */
49
+ template <SIMDLevel SL = SINGLE_SIMD_LEVEL>
50
+ BitwiseAndDotProductResult bitwise_and_dot_product_with_popcount(
51
+ const uint8_t* query,
52
+ const uint8_t* data,
53
+ size_t size,
54
+ size_t qb);
55
+
35
56
  /**
36
57
  * Compute dot product between query and binary data using popcount on XOR.
37
58
  *
@@ -58,6 +79,56 @@ uint64_t bitwise_xor_dot_product(
58
79
  template <SIMDLevel SL = SINGLE_SIMD_LEVEL>
59
80
  uint64_t popcount(const uint8_t* data, size_t size);
60
81
 
82
+ /**
83
+ * Rearrange per-dimension quantized query codes into bit-plane layout.
84
+ *
85
+ * @p rotated_qq holds one qb-bit code per dimension (one byte each, value in
86
+ * [0, 2^qb)). @p out receives qb bit-planes of ((d + 7) / 8) bytes each:
87
+ * bit-plane j packs bit j of code i into bit (i % 8) of byte (i / 8). @p out
88
+ * must have room for qb * ((d + 7) / 8) bytes and is fully overwritten.
89
+ *
90
+ * @param rotated_qq per-dimension codes (d bytes)
91
+ * @param d dimensionality
92
+ * @param qb quantization bits per dimension (1..8)
93
+ * @param out bit-plane output buffer (qb * ((d + 7) / 8) bytes)
94
+ */
95
+ template <SIMDLevel SL = SINGLE_SIMD_LEVEL>
96
+ void rearrange_bit_planes(
97
+ const uint8_t* rotated_qq,
98
+ size_t d,
99
+ size_t qb,
100
+ uint8_t* out);
101
+
102
+ /// Find min/max of one 16-entry FastScan LUT row.
103
+ template <SIMDLevel SL = SINGLE_SIMD_LEVEL>
104
+ void lut_minmax_16(const float* tab, float& mn, float& mx);
105
+
106
+ /// Find min/max of an arbitrary-length float vector. For n == 0, mn and mx
107
+ /// are left unchanged.
108
+ template <SIMDLevel SL = SINGLE_SIMD_LEVEL>
109
+ void minmax_values(const float* values, size_t n, float& mn, float& mx);
110
+
111
+ /// Quantize one 16-entry FastScan LUT row with non-negative half-up rounding.
112
+ template <SIMDLevel SL = SINGLE_SIMD_LEVEL>
113
+ void lut_quantize_16_to_uint8(
114
+ const float* tab,
115
+ float mn,
116
+ float a,
117
+ uint8_t* out);
118
+
119
+ /// Quantize rotated query values and accumulate query correction terms.
120
+ template <SIMDLevel SL = SINGLE_SIMD_LEVEL>
121
+ void quantize_query_values(
122
+ const float* rq,
123
+ size_t d,
124
+ float v_min,
125
+ float inv_delta,
126
+ uint8_t max_code,
127
+ bool centered,
128
+ uint8_t* rqq,
129
+ size_t& sum_qq,
130
+ int64_t& sum2_signed_odd_int);
131
+
61
132
  // NONE specializations — scalar fallbacks
62
133
 
63
134
  template <>
@@ -85,6 +156,35 @@ inline uint64_t bitwise_and_dot_product<SIMDLevel::NONE>(
85
156
  return sum;
86
157
  }
87
158
 
159
+ template <>
160
+ inline BitwiseAndDotProductResult bitwise_and_dot_product_with_popcount<
161
+ SIMDLevel::NONE>(
162
+ const uint8_t* query,
163
+ const uint8_t* data,
164
+ size_t size,
165
+ size_t qb) {
166
+ uint64_t dot_product = 0;
167
+ uint64_t popcount_sum = 0;
168
+ size_t offset = 0;
169
+ for (size_t step = 64 / 8; offset + step <= size; offset += step) {
170
+ const auto yv = *(const uint64_t*)(data + offset);
171
+ popcount_sum += popcount64(yv);
172
+ for (int j = 0; j < qb; j++) {
173
+ const auto qv = *(const uint64_t*)(query + j * size + offset);
174
+ dot_product += popcount64(qv & yv) << j;
175
+ }
176
+ }
177
+ for (; offset < size; ++offset) {
178
+ const auto yv = *(data + offset);
179
+ popcount_sum += popcount32(yv);
180
+ for (int j = 0; j < qb; j++) {
181
+ const auto qv = *(query + j * size + offset);
182
+ dot_product += popcount32(qv & yv) << j;
183
+ }
184
+ }
185
+ return {dot_product, popcount_sum};
186
+ }
187
+
88
188
  template <>
89
189
  inline uint64_t bitwise_xor_dot_product<SIMDLevel::NONE>(
90
190
  const uint8_t* query,
@@ -125,6 +225,108 @@ inline uint64_t popcount<SIMDLevel::NONE>(const uint8_t* data, size_t size) {
125
225
  return sum;
126
226
  }
127
227
 
228
+ template <>
229
+ inline void rearrange_bit_planes<SIMDLevel::NONE>(
230
+ const uint8_t* rotated_qq,
231
+ size_t d,
232
+ size_t qb,
233
+ uint8_t* out) {
234
+ const size_t offset = (d + 7) / 8;
235
+ memset(out, 0, offset * qb);
236
+ for (size_t idim = 0; idim < d; idim++) {
237
+ for (size_t iv = 0; iv < qb; iv++) {
238
+ const bool bit = ((rotated_qq[idim] & (1 << iv)) != 0);
239
+ out[iv * offset + idim / 8] |= bit ? (1 << (idim % 8)) : 0;
240
+ }
241
+ }
242
+ }
243
+
244
+ inline uint8_t round_clamped_byte_scalar(float x, uint8_t max_code) {
245
+ if (x <= 0.0f) {
246
+ return 0;
247
+ }
248
+ if (x >= max_code) {
249
+ return max_code;
250
+ }
251
+ return static_cast<uint8_t>(static_cast<int>(x + 0.5f));
252
+ }
253
+
254
+ inline uint8_t round_nonnegative_byte_scalar(float x) {
255
+ return round_clamped_byte_scalar(x, 255);
256
+ }
257
+
258
+ template <>
259
+ inline void lut_minmax_16<SIMDLevel::NONE>(
260
+ const float* tab,
261
+ float& mn,
262
+ float& mx) {
263
+ mn = tab[0];
264
+ mx = tab[0];
265
+ for (size_t s = 1; s < 16; s++) {
266
+ mn = std::min(mn, tab[s]);
267
+ mx = std::max(mx, tab[s]);
268
+ }
269
+ }
270
+
271
+ template <>
272
+ inline void minmax_values<SIMDLevel::NONE>(
273
+ const float* values,
274
+ size_t n,
275
+ float& mn,
276
+ float& mx) {
277
+ if (n == 0) {
278
+ return;
279
+ }
280
+ mn = values[0];
281
+ mx = values[0];
282
+ for (size_t i = 1; i < n; i++) {
283
+ mn = std::min(mn, values[i]);
284
+ mx = std::max(mx, values[i]);
285
+ }
286
+ }
287
+
288
+ template <>
289
+ inline void lut_quantize_16_to_uint8<SIMDLevel::NONE>(
290
+ const float* tab,
291
+ float mn,
292
+ float a,
293
+ uint8_t* out) {
294
+ for (size_t s = 0; s < 16; s++) {
295
+ out[s] = round_nonnegative_byte_scalar(a * (tab[s] - mn));
296
+ }
297
+ }
298
+
299
+ template <>
300
+ inline void quantize_query_values<SIMDLevel::NONE>(
301
+ const float* rq,
302
+ size_t d,
303
+ float v_min,
304
+ float inv_delta,
305
+ uint8_t max_code,
306
+ bool centered,
307
+ uint8_t* rqq,
308
+ size_t& sum_qq,
309
+ int64_t& sum2_signed_odd_int) {
310
+ if (centered) {
311
+ for (size_t i = 0; i < d; i++) {
312
+ const uint8_t v_qq = round_clamped_byte_scalar(
313
+ (rq[i] - v_min) * inv_delta, max_code);
314
+ rqq[i] = v_qq;
315
+ sum_qq += v_qq;
316
+
317
+ const int64_t signed_odd_int = int64_t(v_qq) * 2 - max_code;
318
+ sum2_signed_odd_int += signed_odd_int * signed_odd_int;
319
+ }
320
+ } else {
321
+ for (size_t i = 0; i < d; i++) {
322
+ const uint8_t v_qq = round_clamped_byte_scalar(
323
+ (rq[i] - v_min) * inv_delta, max_code);
324
+ rqq[i] = v_qq;
325
+ sum_qq += v_qq;
326
+ }
327
+ }
328
+ }
329
+
128
330
  } // namespace faiss::rabitq
129
331
 
130
332
  /*********************************************************