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
@@ -196,6 +196,12 @@ struct QuantizerTemplate<
196
196
  return simd8float32(_mm256_fmadd_ps(
197
197
  xi, _mm256_set1_ps(this->vdiff), _mm256_set1_ps(this->vmin)));
198
198
  }
199
+
200
+ /// Raw codec decode without denormalization
201
+ FAISS_ALWAYS_INLINE simd8float32
202
+ decode_8_raw(const uint8_t* code, int i) const {
203
+ return Codec::decode_8_components(code, i);
204
+ }
199
205
  };
200
206
 
201
207
  template <class Codec>
@@ -226,41 +232,148 @@ struct QuantizerTemplate<
226
232
  };
227
233
 
228
234
  /**********************************************************
229
- * TurboQuant MSE quantizer
235
+ * Lloyd-Max scalar quantizer
230
236
  **********************************************************/
231
237
 
232
- #define DEFINE_TQMSE_AVX2_SPECIALIZATION(NBITS, INDEX_EXPR) \
233
- template <> \
234
- struct QuantizerTurboQuantMSE<NBITS, SIMDLevel::AVX2> \
235
- : QuantizerTurboQuantMSE<NBITS, SIMDLevel::NONE> { \
236
- using Base = QuantizerTurboQuantMSE<NBITS, SIMDLevel::NONE>; \
237
- \
238
- QuantizerTurboQuantMSE(size_t d, const std::vector<float>& trained) \
239
- : Base(d, trained) { \
240
- assert(d % 8 == 0); \
241
- } \
242
- \
243
- FAISS_ALWAYS_INLINE simd8float32 \
244
- reconstruct_8_components(const uint8_t* code, int i) const { \
245
- const __m256i indices = (INDEX_EXPR); \
246
- return simd8float32(_mm256_i32gather_ps( \
247
- this->centroids, indices, sizeof(float))); \
248
- } \
249
- }
250
-
251
- DEFINE_TQMSE_AVX2_SPECIALIZATION(1, unpack_8x1bit_to_u32(code, i));
252
- DEFINE_TQMSE_AVX2_SPECIALIZATION(2, unpack_8x2bit_to_u32(code, i));
253
- DEFINE_TQMSE_AVX2_SPECIALIZATION(3, unpack_8x3bit_to_u32(code, i));
254
- DEFINE_TQMSE_AVX2_SPECIALIZATION(4, unpack_8x4bit_to_u32(code, i));
255
-
256
- #undef DEFINE_TQMSE_AVX2_SPECIALIZATION
238
+ // 1-bit MSE: boundary is always at centroids midpoint.
239
+ // Encode: 8 comparisons → 1 byte via movemask.
240
+ // Decode: gather 8 centroids via index unpack.
241
+ // NOLINTNEXTLINE(facebook-hte-MisplacedTemplateSpecialization,facebook-hte-ShadowingClass)
242
+ template <>
243
+ struct QuantizerLloydMax<1, SIMDLevel::AVX2>
244
+ : QuantizerLloydMax<1, SIMDLevel::NONE> {
245
+ using Base = QuantizerLloydMax<1, SIMDLevel::NONE>;
246
+
247
+ QuantizerLloydMax(size_t d, const std::vector<float>& trained)
248
+ : Base(d, trained) {
249
+ assert(d % 8 == 0);
250
+ }
251
+
252
+ FAISS_ALWAYS_INLINE simd8float32
253
+ reconstruct_8_components(const uint8_t* code, int i) const {
254
+ return simd8float32(_mm256_i32gather_ps(
255
+ this->centroids, unpack_8x1bit_to_u32(code, i), sizeof(float)));
256
+ }
257
+
258
+ void encode_vector(const float* x, uint8_t* code) const final {
259
+ __m256 boundary = _mm256_set1_ps(this->boundaries[0]);
260
+ for (size_t i = 0; i < this->d; i += 8) {
261
+ __m256 vals = _mm256_loadu_ps(x + i);
262
+ int mask = _mm256_movemask_ps(
263
+ _mm256_cmp_ps(vals, boundary, _CMP_GT_OQ));
264
+ code[i / 8] = static_cast<uint8_t>(mask);
265
+ }
266
+ }
267
+
268
+ void decode_vector(const uint8_t* code, float* x) const final {
269
+ for (size_t i = 0; i < this->d; i += 8) {
270
+ simd8float32 xi =
271
+ reconstruct_8_components(code, static_cast<int>(i));
272
+ _mm256_storeu_ps(x + i, xi.f);
273
+ }
274
+ }
275
+ };
276
+
277
+ // 2-bit MSE: 4 centroids, 3 boundaries.
278
+ // Encode: branchless index = sum of 3 comparisons per component.
279
+ // Decode: gather via index unpack.
280
+ // NOLINTNEXTLINE(facebook-hte-MisplacedTemplateSpecialization,facebook-hte-ShadowingClass)
281
+ template <>
282
+ struct QuantizerLloydMax<2, SIMDLevel::AVX2>
283
+ : QuantizerLloydMax<2, SIMDLevel::NONE> {
284
+ using Base = QuantizerLloydMax<2, SIMDLevel::NONE>;
285
+
286
+ QuantizerLloydMax(size_t d, const std::vector<float>& trained)
287
+ : Base(d, trained) {
288
+ assert(d % 8 == 0);
289
+ }
290
+
291
+ FAISS_ALWAYS_INLINE simd8float32
292
+ reconstruct_8_components(const uint8_t* code, int i) const {
293
+ return simd8float32(_mm256_i32gather_ps(
294
+ this->centroids, unpack_8x2bit_to_u32(code, i), sizeof(float)));
295
+ }
296
+
297
+ void encode_vector(const float* x, uint8_t* code) const final {
298
+ // 3 boundaries → branchless: idx = (x>b0) + (x>b1) + (x>b2)
299
+ // _mm256_cmp_ps returns all-ones (-1 as int32) for true,
300
+ // so we negate the sum to get positive indices.
301
+ __m256 b0 = _mm256_set1_ps(this->boundaries[0]);
302
+ __m256 b1 = _mm256_set1_ps(this->boundaries[1]);
303
+ __m256 b2 = _mm256_set1_ps(this->boundaries[2]);
304
+ for (size_t i = 0; i < this->d; i += 8) {
305
+ __m256 vals = _mm256_loadu_ps(x + i);
306
+ __m256i gt0 =
307
+ _mm256_castps_si256(_mm256_cmp_ps(vals, b0, _CMP_GT_OQ));
308
+ __m256i gt1 =
309
+ _mm256_castps_si256(_mm256_cmp_ps(vals, b1, _CMP_GT_OQ));
310
+ __m256i gt2 =
311
+ _mm256_castps_si256(_mm256_cmp_ps(vals, b2, _CMP_GT_OQ));
312
+ // Each gt is 0 or -1 (0xFFFFFFFF). Sum = -(index).
313
+ __m256i idx = _mm256_sub_epi32(
314
+ _mm256_setzero_si256(),
315
+ _mm256_add_epi32(_mm256_add_epi32(gt0, gt1), gt2));
316
+ // Pack 8 x 2-bit indices into 2 bytes.
317
+ // Store to temp array and pack scalarly - faster than
318
+ // extract+permute.
319
+ alignas(32) int32_t idx_array[8];
320
+ _mm256_store_si256((__m256i*)idx_array, idx);
321
+ for (int j = 0; j < 8; j++) {
322
+ this->encode_index(
323
+ static_cast<uint8_t>(idx_array[j] & 0x3), code, i + j);
324
+ }
325
+ }
326
+ }
327
+
328
+ void decode_vector(const uint8_t* code, float* x) const final {
329
+ for (size_t i = 0; i < this->d; i += 8) {
330
+ simd8float32 xi =
331
+ reconstruct_8_components(code, static_cast<int>(i));
332
+ _mm256_storeu_ps(x + i, xi.f);
333
+ }
334
+ }
335
+ };
257
336
 
337
+ // 3-bit and 4-bit Lloyd-Max: use branchless comparison chain for encode.
338
+ // k boundaries → idx = sum of k-1 comparisons.
339
+ #define DEFINE_LLOYD_MAX_AVX2_MULTIBIT(NBITS, UNPACK_EXPR) \
340
+ template <> \
341
+ struct QuantizerLloydMax<NBITS, SIMDLevel::AVX2> \
342
+ : QuantizerLloydMax<NBITS, SIMDLevel::NONE> { \
343
+ using Base = QuantizerLloydMax<NBITS, SIMDLevel::NONE>; \
344
+ \
345
+ QuantizerLloydMax(size_t d, const std::vector<float>& trained) \
346
+ : Base(d, trained) { \
347
+ assert(d % 8 == 0); \
348
+ } \
349
+ \
350
+ FAISS_ALWAYS_INLINE simd8float32 \
351
+ reconstruct_8_components(const uint8_t* code, int i) const { \
352
+ return simd8float32(_mm256_i32gather_ps( \
353
+ this->centroids, (UNPACK_EXPR), sizeof(float))); \
354
+ } \
355
+ \
356
+ void decode_vector(const uint8_t* code, float* x) const final { \
357
+ for (size_t i = 0; i < this->d; i += 8) { \
358
+ simd8float32 xi = \
359
+ reconstruct_8_components(code, static_cast<int>(i)); \
360
+ _mm256_storeu_ps(x + i, xi.f); \
361
+ } \
362
+ } \
363
+ }
364
+
365
+ DEFINE_LLOYD_MAX_AVX2_MULTIBIT(3, unpack_8x3bit_to_u32(code, i));
366
+ DEFINE_LLOYD_MAX_AVX2_MULTIBIT(4, unpack_8x4bit_to_u32(code, i));
367
+
368
+ #undef DEFINE_LLOYD_MAX_AVX2_MULTIBIT
369
+
370
+ // 8-bit Lloyd-Max: indices are raw bytes, no bit packing.
258
371
  template <>
259
- struct QuantizerTurboQuantMSE<8, SIMDLevel::AVX2>
260
- : QuantizerTurboQuantMSE<8, SIMDLevel::NONE> {
261
- using Base = QuantizerTurboQuantMSE<8, SIMDLevel::NONE>;
372
+ struct QuantizerLloydMax<8, SIMDLevel::AVX2>
373
+ : QuantizerLloydMax<8, SIMDLevel::NONE> {
374
+ using Base = QuantizerLloydMax<8, SIMDLevel::NONE>;
262
375
 
263
- QuantizerTurboQuantMSE(size_t d, const std::vector<float>& trained)
376
+ QuantizerLloydMax(size_t d, const std::vector<float>& trained)
264
377
  : Base(d, trained) {
265
378
  assert(d % 8 == 0);
266
379
  }
@@ -273,6 +386,14 @@ struct QuantizerTurboQuantMSE<8, SIMDLevel::AVX2>
273
386
  return simd8float32(
274
387
  _mm256_i32gather_ps(this->centroids, indices, sizeof(float)));
275
388
  }
389
+
390
+ void decode_vector(const uint8_t* code, float* x) const final {
391
+ for (size_t i = 0; i < this->d; i += 8) {
392
+ simd8float32 xi =
393
+ reconstruct_8_components(code, static_cast<int>(i));
394
+ _mm256_storeu_ps(x + i, xi.f);
395
+ }
396
+ }
276
397
  };
277
398
 
278
399
  /**********************************************************
@@ -399,6 +520,22 @@ struct SimilarityL2<SIMDLevel::AVX2> {
399
520
  const __m128 v3 = _mm_add_ps(v1, v2);
400
521
  return _mm_cvtss_f32(v3);
401
522
  }
523
+
524
+ static void adjust_query_for_raw_decode(
525
+ const float* x,
526
+ float* q_adj,
527
+ size_t d,
528
+ float vmin,
529
+ float vdiff,
530
+ float& scale_factor,
531
+ float& bias) {
532
+ float inv_vdiff = (vdiff != 0) ? 1.0f / vdiff : 0.0f;
533
+ for (size_t i = 0; i < d; i++) {
534
+ q_adj[i] = (x[i] - vmin) * inv_vdiff;
535
+ }
536
+ scale_factor = vdiff * vdiff;
537
+ bias = 0;
538
+ }
402
539
  };
403
540
 
404
541
  template <>
@@ -442,6 +579,23 @@ struct SimilarityIP<SIMDLevel::AVX2> {
442
579
  const __m128 v3 = _mm_add_ps(v1, v2);
443
580
  return _mm_cvtss_f32(v3);
444
581
  }
582
+
583
+ static void adjust_query_for_raw_decode(
584
+ const float* x,
585
+ float* q_adj,
586
+ size_t d,
587
+ float vmin,
588
+ float vdiff,
589
+ float& scale_factor,
590
+ float& bias) {
591
+ float sum_q = 0;
592
+ for (size_t i = 0; i < d; i++) {
593
+ q_adj[i] = x[i];
594
+ sum_q += x[i];
595
+ }
596
+ scale_factor = vdiff;
597
+ bias = vmin * sum_q;
598
+ }
445
599
  };
446
600
 
447
601
  /**********************************************************
@@ -454,8 +608,23 @@ struct DCTemplate<Quantizer, Similarity, SIMDLevel::AVX2> : SQDistanceComputer {
454
608
 
455
609
  Quantizer quant;
456
610
 
611
+ // Pre-adjusted query buffer for uniform quantizers
612
+ std::vector<float> q_adj;
613
+ float scale_factor = 0;
614
+ float bias = 0;
615
+
616
+ static constexpr bool has_decode_raw() {
617
+ return requires(const Quantizer& q, const uint8_t* c, int i) {
618
+ { q.decode_8_raw(c, i) };
619
+ };
620
+ }
621
+
457
622
  DCTemplate(size_t d, const std::vector<float>& trained)
458
- : quant(d, trained) {}
623
+ : quant(d, trained) {
624
+ if constexpr (has_decode_raw()) {
625
+ q_adj.resize(d);
626
+ }
627
+ }
459
628
 
460
629
  float compute_distance(const float* x, const uint8_t* code) const {
461
630
  Similarity sim(x);
@@ -484,6 +653,26 @@ struct DCTemplate<Quantizer, Similarity, SIMDLevel::AVX2> : SQDistanceComputer {
484
653
 
485
654
  void set_query(const float* x) final {
486
655
  q = x;
656
+ if constexpr (has_decode_raw()) {
657
+ Sim::adjust_query_for_raw_decode(
658
+ x,
659
+ q_adj.data(),
660
+ quant.d,
661
+ quant.vmin,
662
+ quant.vdiff,
663
+ scale_factor,
664
+ bias);
665
+ }
666
+ }
667
+
668
+ float query_to_code_predecoded(const uint8_t* code) const {
669
+ Similarity sim(q_adj.data());
670
+ sim.begin_8();
671
+ for (size_t i = 0; i < quant.d; i += 8) {
672
+ simd8float32 xi = quant.decode_8_raw(code, static_cast<int>(i));
673
+ sim.add_8_components(xi);
674
+ }
675
+ return bias + scale_factor * sim.result_8();
487
676
  }
488
677
 
489
678
  float symmetric_dis(idx_t i, idx_t j) override {
@@ -492,7 +681,11 @@ struct DCTemplate<Quantizer, Similarity, SIMDLevel::AVX2> : SQDistanceComputer {
492
681
  }
493
682
 
494
683
  float query_to_code(const uint8_t* code) const final {
495
- return compute_distance(q, code);
684
+ if constexpr (has_decode_raw()) {
685
+ return query_to_code_predecoded(code);
686
+ } else {
687
+ return compute_distance(q, code);
688
+ }
496
689
  }
497
690
 
498
691
  void query_to_codes_batch_4(
@@ -594,6 +787,112 @@ struct DistanceComputerByte<Similarity, SIMDLevel::AVX2> : SQDistanceComputer {
594
787
  }
595
788
  };
596
789
 
790
+ template <class Similarity>
791
+ struct DistanceComputerByteSigned<Similarity, SIMDLevel::AVX2>
792
+ : SQDistanceComputer {
793
+ using Sim = Similarity;
794
+
795
+ int d;
796
+ std::vector<uint8_t> tmp;
797
+
798
+ DistanceComputerByteSigned(int d, const std::vector<float>&)
799
+ : d(d), tmp(d) {}
800
+
801
+ int compute_code_distance(const uint8_t* code1, const uint8_t* code2)
802
+ const {
803
+ // codes store value + 128. madd_epi16 is signed, so IP unbiases the
804
+ // bytes before multiplying; for L2 the +128 cancels in the difference.
805
+ // Only dispatched for d % 16 == 0, so the loop needs no tail.
806
+ __m256i accu = _mm256_setzero_si256();
807
+ for (int i = 0; i < d; i += 16) {
808
+ __m256i c1 = _mm256_cvtepu8_epi16(
809
+ _mm_loadu_si128((const __m128i*)(code1 + i)));
810
+ __m256i c2 = _mm256_cvtepu8_epi16(
811
+ _mm_loadu_si128((const __m128i*)(code2 + i)));
812
+ __m256i prod32;
813
+ if (Sim::metric_type == METRIC_INNER_PRODUCT) {
814
+ const __m256i bias = _mm256_set1_epi16(128);
815
+ c1 = _mm256_sub_epi16(c1, bias);
816
+ c2 = _mm256_sub_epi16(c2, bias);
817
+ prod32 = _mm256_madd_epi16(c1, c2);
818
+ } else {
819
+ __m256i diff = _mm256_sub_epi16(c1, c2);
820
+ prod32 = _mm256_madd_epi16(diff, diff);
821
+ }
822
+ accu = _mm256_add_epi32(accu, prod32);
823
+ }
824
+ __m128i sum = _mm256_extractf128_si256(accu, 0);
825
+ sum = _mm_add_epi32(sum, _mm256_extractf128_si256(accu, 1));
826
+ sum = _mm_hadd_epi32(sum, sum);
827
+ sum = _mm_hadd_epi32(sum, sum);
828
+ return _mm_cvtsi128_si32(sum);
829
+ }
830
+
831
+ void set_query(const float* x) final {
832
+ for (int i = 0; i < d; i++) {
833
+ tmp[i] = uint8_t(int(x[i]) + 128);
834
+ }
835
+ }
836
+
837
+ int compute_distance(const float* x, const uint8_t* code) {
838
+ set_query(x);
839
+ return compute_code_distance(tmp.data(), code);
840
+ }
841
+
842
+ float symmetric_dis(idx_t i, idx_t j) override {
843
+ return compute_code_distance(
844
+ codes + i * code_size, codes + j * code_size);
845
+ }
846
+
847
+ float query_to_code(const uint8_t* code) const final {
848
+ return compute_code_distance(tmp.data(), code);
849
+ }
850
+ };
851
+
852
+ /**********************************************************
853
+ * TurboQuant masked_sum AVX2 specialization
854
+ **********************************************************/
855
+
856
+ template <SIMDLevel SL0>
857
+ float turboq_masked_sum(const float* arr, const uint8_t* bits, size_t d);
858
+
859
+ template <>
860
+ float turboq_masked_sum<SIMDLevel::AVX2>(
861
+ const float* arr,
862
+ const uint8_t* bits,
863
+ size_t d) {
864
+ const __m256i bit_masks = _mm256_set_epi32(128, 64, 32, 16, 8, 4, 2, 1);
865
+ __m256 acc = _mm256_setzero_ps();
866
+ size_t full_bytes = d / 8;
867
+ for (size_t byte_idx = 0; byte_idx < full_bytes; byte_idx++) {
868
+ __m256i byte_broadcast =
869
+ _mm256_set1_epi32(static_cast<int>(bits[byte_idx]));
870
+ __m256i masked = _mm256_and_si256(byte_broadcast, bit_masks);
871
+ __m256i cmp = _mm256_cmpeq_epi32(masked, bit_masks);
872
+ __m256 mask = _mm256_castsi256_ps(cmp);
873
+ __m256 vals = _mm256_loadu_ps(arr + byte_idx * 8);
874
+ acc = _mm256_add_ps(acc, _mm256_and_ps(mask, vals));
875
+ }
876
+ __m128 hi = _mm256_extractf128_ps(acc, 1);
877
+ __m128 lo = _mm256_castps256_ps128(acc);
878
+ __m128 sum128 = _mm_add_ps(lo, hi);
879
+ __m128 shuf = _mm_movehdup_ps(sum128);
880
+ __m128 sums = _mm_add_ps(sum128, shuf);
881
+ shuf = _mm_movehl_ps(shuf, sums);
882
+ sums = _mm_add_ss(sums, shuf);
883
+ float result = _mm_cvtss_f32(sums);
884
+ size_t tail_start = full_bytes * 8;
885
+ if (tail_start < d) {
886
+ uint8_t last_byte = bits[full_bytes];
887
+ for (size_t j = tail_start; j < d; j++) {
888
+ if (last_byte & (1 << (j - tail_start))) {
889
+ result += arr[j];
890
+ }
891
+ }
892
+ }
893
+ return result;
894
+ }
895
+
597
896
  } // namespace scalar_quantizer
598
897
  } // namespace faiss
599
898