faiss 0.6.2 → 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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -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 +4 -5
  12. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  13. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +2 -4
  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 +10 -11
  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 +17 -6
  22. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  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 +3 -4
  28. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  29. data/vendor/faiss/faiss/IndexIVFPQ.cpp +40 -22
  30. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  31. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  32. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  33. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  34. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  35. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  36. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  37. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  38. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  39. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  40. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  41. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  43. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  44. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  45. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  46. data/vendor/faiss/faiss/IndexShards.cpp +2 -2
  47. data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
  48. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
  50. data/vendor/faiss/faiss/SuperKMeans.h +30 -0
  51. data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +5 -0
  53. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  55. data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
  56. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  58. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  59. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  60. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  61. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  62. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  63. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  64. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  65. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
  66. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  67. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  68. data/vendor/faiss/faiss/impl/HNSW.cpp +109 -152
  69. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  70. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  71. data/vendor/faiss/faiss/impl/Panorama.h +9 -7
  72. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  73. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +34 -22
  74. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +44 -36
  75. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  76. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +168 -67
  77. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
  78. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
  79. data/vendor/faiss/faiss/impl/ResultHandler.h +25 -31
  80. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +258 -57
  81. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +20 -0
  82. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  83. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  84. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  85. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
  86. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  87. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  88. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  89. data/vendor/faiss/faiss/impl/index_read.cpp +376 -36
  90. data/vendor/faiss/faiss/impl/index_write.cpp +55 -4
  91. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  92. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  93. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  94. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  95. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  96. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  97. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  98. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  99. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  100. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  101. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  102. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  103. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
  104. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  105. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +85 -23
  106. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  107. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  108. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  109. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +136 -0
  110. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +16 -16
  111. data/vendor/faiss/faiss/impl/simd_dispatch.h +30 -9
  112. data/vendor/faiss/faiss/index_factory.cpp +32 -6
  113. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  114. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +2 -2
  115. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
  116. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  117. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  118. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  119. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  120. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  121. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  122. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +31 -1
  123. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +15 -2
  124. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +1 -2
  125. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  126. data/vendor/faiss/faiss/utils/distances.cpp +14 -2
  127. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  128. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  129. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  130. data/vendor/faiss/faiss/utils/hamming.cpp +1 -1
  131. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  132. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  133. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  134. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  135. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
  136. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  137. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
  138. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +92 -0
  139. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  140. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  141. data/vendor/faiss/faiss/utils/simd_levels.cpp +44 -0
  142. data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
  143. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  144. metadata +16 -1
@@ -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.
@@ -28,7 +28,6 @@
28
28
  // NOLINTNEXTLINE(facebook-hte-InlineHeader)
29
29
  // NOLINTNEXTLINE(facebook-hte-InlineHeader)
30
30
  #include <faiss/impl/binary_hamming/IndexIVFSpectralHash_impl.h>
31
- #include <faiss/utils/hamming_distance/hamming_computer-generic.h>
32
31
  #undef THE_SIMD_LEVEL
33
32
 
34
33
  namespace faiss {
@@ -88,7 +87,7 @@ void IndexIVFSpectralHash::train_encoder(
88
87
  if (!vt->is_trained) {
89
88
  vt->train(n, x);
90
89
  }
91
- FAISS_THROW_IF_NOT(!by_residual);
90
+ FAISS_THROW_IF_MSG(by_residual, "by_residual not supported for this index");
92
91
 
93
92
  if (threshold_type == Thresh_global) {
94
93
  // nothing to do
@@ -185,7 +184,7 @@ void IndexIVFSpectralHash::encode_vectors(
185
184
  uint8_t* codes,
186
185
  bool include_listnos) const {
187
186
  FAISS_THROW_IF_NOT(is_trained);
188
- FAISS_THROW_IF_NOT(!by_residual);
187
+ FAISS_THROW_IF_MSG(by_residual, "by_residual not supported for this index");
189
188
  float freq = 2.0 / period;
190
189
  size_t coarse_size = include_listnos ? coarse_code_size() : 0;
191
190
 
@@ -222,7 +221,7 @@ InvertedListScanner* IndexIVFSpectralHash::get_InvertedListScanner(
222
221
  bool store_pairs,
223
222
  const IDSelector* sel,
224
223
  const IVFSearchParameters*) const {
225
- FAISS_THROW_IF_NOT(!sel);
224
+ FAISS_THROW_IF_MSG(sel, "id selector not supported for this index");
226
225
  return with_simd_level([&]<SIMDLevel SL>() {
227
226
  return make_spectral_hash_scanner_fixSL<SL>(
228
227
  code_size, this, store_pairs);
@@ -254,8 +253,11 @@ void IndexIVFSpectralHash::replace_vt(IndexPreTransform* encoder, bool own) {
254
253
  auto sub_index = dynamic_cast<IndexLSH*>(encoder->index);
255
254
  FAISS_THROW_IF_NOT_MSG(sub_index, "final index should be LSH");
256
255
  FAISS_THROW_IF_NOT(sub_index->nbits == nbit);
257
- FAISS_THROW_IF_NOT(!sub_index->rotate_data);
258
- FAISS_THROW_IF_NOT(!sub_index->train_thresholds);
256
+ FAISS_THROW_IF_MSG(
257
+ sub_index->rotate_data, "LSH sub-index must not rotate data");
258
+ FAISS_THROW_IF_MSG(
259
+ sub_index->train_thresholds,
260
+ "LSH sub-index thresholds must already be trained");
259
261
  replace_vt(encoder->chain[0], own);
260
262
  }
261
263
 
@@ -119,8 +119,7 @@ void IndexLSH::search(
119
119
  float* distances,
120
120
  idx_t* labels,
121
121
  const SearchParameters* params) const {
122
- FAISS_THROW_IF_NOT_MSG(
123
- !params, "search params not supported for this index");
122
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
124
123
  FAISS_THROW_IF_NOT(k > 0);
125
124
  FAISS_THROW_IF_NOT(is_trained);
126
125
  const float* xt = apply_preprocess(n, x);
@@ -151,7 +150,7 @@ void IndexLSH::transfer_thresholds(LinearTransform* vt) {
151
150
  vt->b.resize(nbits, 0);
152
151
  vt->have_bias = true;
153
152
  }
154
- FAISS_THROW_IF_NOT(!vt->b.empty());
153
+ FAISS_THROW_IF_MSG(vt->b.empty(), "bias vector must not be empty");
155
154
  for (int i = 0; i < nbits; i++) {
156
155
  vt->b[i] -= thresholds[i];
157
156
  }
@@ -26,6 +26,11 @@ IndexLattice::IndexLattice(idx_t d_in, int nsq_in, int scale_nbit_in, int r2)
26
26
  lattice_nbit = 0;
27
27
  while (!(((uint64_t)1 << lattice_nbit) >= zn_sphere_codec.nv)) {
28
28
  lattice_nbit++;
29
+ FAISS_THROW_IF_NOT_FMT(
30
+ lattice_nbit < 64,
31
+ "IndexLattice: nv=%zu too large, lattice code would exceed "
32
+ "63 bits (likely corrupt r2/dsq)",
33
+ (size_t)zn_sphere_codec.nv);
29
34
  }
30
35
 
31
36
  int total_nbit = (lattice_nbit + scale_nbit_in) * nsq_in;
@@ -101,8 +101,7 @@ void IndexNNDescent::search(
101
101
  float* distances,
102
102
  idx_t* labels,
103
103
  const SearchParameters* params) const {
104
- FAISS_THROW_IF_NOT_MSG(
105
- !params, "search params not supported for this index");
104
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
106
105
  FAISS_THROW_IF_NOT_MSG(
107
106
  storage,
108
107
  "Please use IndexNNDescentFlat (or variants) "
@@ -183,11 +182,19 @@ void IndexNNDescent::add(idx_t n, const float* x) {
183
182
 
184
183
  void IndexNNDescent::reset() {
185
184
  nndescent.reset();
185
+ FAISS_THROW_IF_NOT_MSG(
186
+ storage,
187
+ "Please use IndexNNDescentFlat (or variants) "
188
+ "instead of IndexNNDescent directly");
186
189
  storage->reset();
187
190
  ntotal = 0;
188
191
  }
189
192
 
190
193
  void IndexNNDescent::reconstruct(idx_t key, float* recons) const {
194
+ FAISS_THROW_IF_NOT_MSG(
195
+ storage,
196
+ "Please use IndexNNDescentFlat (or variants) "
197
+ "instead of IndexNNDescent directly");
191
198
  storage->reconstruct(key, recons);
192
199
  }
193
200
 
@@ -62,8 +62,7 @@ void IndexNSG::search(
62
62
  float* distances,
63
63
  idx_t* labels,
64
64
  const SearchParameters* params) const {
65
- FAISS_THROW_IF_NOT_MSG(
66
- !params, "search params not supported for this index");
65
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
67
66
  FAISS_THROW_IF_NOT_MSG(
68
67
  storage,
69
68
  "Please use IndexNSGFlat (or variants) instead of IndexNSG directly");
@@ -245,12 +244,18 @@ void IndexNSG::add(idx_t n, const float* x) {
245
244
 
246
245
  void IndexNSG::reset() {
247
246
  nsg.reset();
247
+ FAISS_THROW_IF_NOT_MSG(
248
+ storage,
249
+ "Please use IndexNSGFlat (or variants) instead of IndexNSG directly");
248
250
  storage->reset();
249
251
  ntotal = 0;
250
252
  is_built = false;
251
253
  }
252
254
 
253
255
  void IndexNSG::reconstruct(idx_t key, float* recons) const {
256
+ FAISS_THROW_IF_NOT_MSG(
257
+ storage,
258
+ "Please use IndexNSGFlat (or variants) instead of IndexNSG directly");
254
259
  storage->reconstruct(key, recons);
255
260
  }
256
261
 
@@ -107,7 +107,7 @@ void IndexPQ::search(
107
107
  if (iparams) {
108
108
  params = dynamic_cast<const SearchParametersPQ*>(iparams);
109
109
  FAISS_THROW_IF_NOT_MSG(params, "invalid search params");
110
- FAISS_THROW_IF_NOT_MSG(!params->sel, "selector not supported");
110
+ FAISS_THROW_IF_MSG(params->sel, "selector not supported");
111
111
  param_search_type = params->search_type;
112
112
  }
113
113
 
@@ -459,7 +459,7 @@ struct SortedArray {
459
459
 
460
460
  void init(const T* x_2) {
461
461
  this->x = x_2;
462
- FAISS_THROW_IF_NOT(!perm.empty());
462
+ FAISS_THROW_IF_MSG(perm.empty(), "permutation array must not be empty");
463
463
  for (int n = 0; n < N; n++) {
464
464
  perm[n] = n;
465
465
  }
@@ -543,7 +543,7 @@ struct SemiSortedArray {
543
543
 
544
544
  void init(const T* x_2) {
545
545
  this->x = x_2;
546
- FAISS_THROW_IF_NOT(!perm.empty());
546
+ FAISS_THROW_IF_MSG(perm.empty(), "permutation array must not be empty");
547
547
  for (int n = 0; n < N; n++) {
548
548
  perm[n] = n;
549
549
  }
@@ -665,7 +665,7 @@ struct MinSumK {
665
665
 
666
666
  void mark_seen(int64_t i) {
667
667
  if (use_seen) {
668
- FAISS_THROW_IF_NOT(!seen.empty());
668
+ FAISS_THROW_IF_MSG(seen.empty(), "seen bitmap must not be empty");
669
669
  seen[i >> 3] |= 1 << (i & 7);
670
670
  }
671
671
  }
@@ -795,8 +795,7 @@ void MultiIndexQuantizer::search(
795
795
  float* distances,
796
796
  idx_t* labels,
797
797
  const SearchParameters* params) const {
798
- FAISS_THROW_IF_NOT_MSG(
799
- !params, "search params not supported for this index");
798
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
800
799
  if (n == 0) {
801
800
  return;
802
801
  }
@@ -943,8 +942,7 @@ void MultiIndexQuantizer2::search(
943
942
  float* distances,
944
943
  idx_t* labels,
945
944
  const SearchParameters* params) const {
946
- FAISS_THROW_IF_NOT_MSG(
947
- !params, "search params not supported for this index");
945
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
948
946
 
949
947
  if (n == 0) {
950
948
  return;
@@ -229,6 +229,7 @@ size_t IndexPreTransform::remove_ids(const IDSelector& sel) {
229
229
  }
230
230
 
231
231
  void IndexPreTransform::reconstruct(idx_t key, float* recons) const {
232
+ FAISS_THROW_IF_NOT_MSG(index, "IndexPreTransform: null sub-index");
232
233
  float* x = chain.empty() ? recons : new float[index->d];
233
234
  std::unique_ptr<float[]> del(recons == x ? nullptr : x);
234
235
  // Initial reconstruction
@@ -239,6 +240,7 @@ void IndexPreTransform::reconstruct(idx_t key, float* recons) const {
239
240
  }
240
241
 
241
242
  void IndexPreTransform::reconstruct_n(idx_t i0, idx_t ni, float* recons) const {
243
+ FAISS_THROW_IF_NOT_MSG(index, "IndexPreTransform: null sub-index");
242
244
  float* x = chain.empty() ? recons : new float[ni * index->d];
243
245
  std::unique_ptr<float[]> del(recons == x ? nullptr : x);
244
246
  // Initial reconstruction
@@ -345,6 +347,19 @@ struct PreTransformDistanceComputer : DistanceComputer {
345
347
  float operator()(idx_t i) override {
346
348
  return (*sub_dc)(i);
347
349
  }
350
+
351
+ void distances_batch_4(
352
+ const idx_t idx0,
353
+ const idx_t idx1,
354
+ const idx_t idx2,
355
+ const idx_t idx3,
356
+ float& dis0,
357
+ float& dis1,
358
+ float& dis2,
359
+ float& dis3) override {
360
+ sub_dc->distances_batch_4(
361
+ idx0, idx1, idx2, idx3, dis0, dis1, dis2, dis3);
362
+ }
348
363
  };
349
364
 
350
365
  } // anonymous namespace
@@ -122,8 +122,8 @@ struct Run_search_with_dc_res {
122
122
  // RaBitQuantizer.cpp for details.
123
123
  auto* dc = dynamic_cast<RaBitQDistanceComputer*>(
124
124
  dc_base.get());
125
- FAISS_THROW_IF_NOT_MSG(
126
- dc != nullptr,
125
+ FAISS_THROW_IF_MSG(
126
+ dc == nullptr,
127
127
  "Failed to cast to RaBitQDistanceComputer for two-stage search");
128
128
 
129
129
  bool is_similarity =
@@ -512,8 +512,7 @@ void IndexRaBitQFastScan::search(
512
512
  float* distances,
513
513
  idx_t* labels,
514
514
  const SearchParameters* params) const {
515
- FAISS_THROW_IF_NOT_MSG(
516
- !params, "search params not supported for this index");
515
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
517
516
 
518
517
  // Create query factors array on stack - memory managed by caller
519
518
  std::vector<rabitq_utils::QueryFactorsData> query_factors_storage(n);
@@ -209,8 +209,12 @@ struct RaBitQHeapHandler
209
209
  : 0;
210
210
 
211
211
  const size_t block_idx = base_db_idx / rabitq_index->bbs;
212
+ // aux_base points at this 32-lane sub-block's factors: the bbs block
213
+ // base plus the loop-invariant intra-block offset
214
+ // ((base_db_idx % bbs)). Only the per-element i term varies below.
212
215
  const uint8_t* aux_base = rabitq_index->codes.get() +
213
- block_idx * full_block_size + packed_block_size;
216
+ block_idx * full_block_size + packed_block_size +
217
+ (base_db_idx % rabitq_index->bbs) * storage_size;
214
218
 
215
219
  for (size_t i = 0; i < max_vectors; i++) {
216
220
  const size_t db_idx = base_db_idx + i;