faiss 0.6.4 → 0.6.5

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/ext/faiss/extconf.rb +1 -1
  4. data/lib/faiss/version.rb +1 -1
  5. data/vendor/faiss/faiss/AutoTune.cpp +175 -0
  6. data/vendor/faiss/faiss/Clustering.cpp +150 -60
  7. data/vendor/faiss/faiss/Clustering.h +29 -0
  8. data/vendor/faiss/faiss/IVFlib.cpp +99 -0
  9. data/vendor/faiss/faiss/IVFlib.h +15 -0
  10. data/vendor/faiss/faiss/Index.cpp +44 -0
  11. data/vendor/faiss/faiss/Index.h +19 -14
  12. data/vendor/faiss/faiss/IndexBinary.cpp +8 -0
  13. data/vendor/faiss/faiss/IndexBinary.h +8 -0
  14. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +82 -4
  15. data/vendor/faiss/faiss/IndexBinaryHNSW.h +19 -0
  16. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +34 -0
  17. data/vendor/faiss/faiss/IndexFastScan.cpp +33 -17
  18. data/vendor/faiss/faiss/IndexFastScan.h +6 -3
  19. data/vendor/faiss/faiss/IndexFlat.cpp +56 -0
  20. data/vendor/faiss/faiss/IndexFlat.h +31 -0
  21. data/vendor/faiss/faiss/IndexFlatCodes.cpp +6 -1
  22. data/vendor/faiss/faiss/IndexHNSW.cpp +456 -10
  23. data/vendor/faiss/faiss/IndexHNSW.h +38 -0
  24. data/vendor/faiss/faiss/IndexIDMap.cpp +32 -0
  25. data/vendor/faiss/faiss/IndexIDMap.h +7 -0
  26. data/vendor/faiss/faiss/IndexIVF.cpp +281 -31
  27. data/vendor/faiss/faiss/IndexIVF.h +53 -1
  28. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +46 -14
  29. data/vendor/faiss/faiss/IndexIVFFastScan.h +4 -2
  30. data/vendor/faiss/faiss/IndexIVFFlat.cpp +33 -0
  31. data/vendor/faiss/faiss/IndexIVFFlat.h +16 -0
  32. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +1 -0
  33. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +3 -20
  34. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +50 -14
  35. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -2
  36. data/vendor/faiss/faiss/IndexIVFSQFastScan.cpp +855 -0
  37. data/vendor/faiss/faiss/IndexIVFSQFastScan.h +145 -0
  38. data/vendor/faiss/faiss/IndexNNDescent.cpp +0 -3
  39. data/vendor/faiss/faiss/IndexPiPNN.cpp +201 -0
  40. data/vendor/faiss/faiss/IndexPiPNN.h +83 -0
  41. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +6 -3
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +10 -4
  43. data/vendor/faiss/faiss/IndexReplicas.cpp +20 -0
  44. data/vendor/faiss/faiss/IndexReplicas.h +9 -0
  45. data/vendor/faiss/faiss/IndexSQFastScan.cpp +648 -0
  46. data/vendor/faiss/faiss/IndexSQFastScan.h +129 -0
  47. data/vendor/faiss/faiss/IndexShards.cpp +10 -0
  48. data/vendor/faiss/faiss/IndexShards.h +5 -0
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +57 -15
  50. data/vendor/faiss/faiss/SuperKMeans.h +9 -4
  51. data/vendor/faiss/faiss/VectorTransform.cpp +3 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +24 -0
  53. data/vendor/faiss/faiss/gpu/Dataset.h +112 -0
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +16 -13
  55. data/vendor/faiss/faiss/gpu/GpuCloner.h +4 -0
  56. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +3 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndex.h +4 -13
  58. data/vendor/faiss/faiss/gpu/GpuIndexBinaryCagra.h +26 -13
  59. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +45 -22
  60. data/vendor/faiss/faiss/gpu/GpuIndexFlat.h +11 -0
  61. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +6 -0
  62. data/vendor/faiss/faiss/gpu/test/TestGpuDatasetHeader.cpp +18 -0
  63. data/vendor/faiss/faiss/gpu/test/TestGpuIndexFlat.cpp +215 -0
  64. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +98 -0
  65. data/vendor/faiss/faiss/gpu/utils/DeviceUtils.h +11 -0
  66. data/vendor/faiss/faiss/gpu/utils/StackDeviceMemory.cpp +1 -1
  67. data/vendor/faiss/faiss/gpu_metal/MetalCloner.h +11 -0
  68. data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +92 -0
  69. data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +4 -0
  70. data/vendor/faiss/faiss/gpu_metal/MetalIndexBinaryFlat.h +69 -0
  71. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +38 -0
  72. data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +106 -0
  73. data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +10 -0
  74. data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +2 -0
  75. data/vendor/faiss/faiss/impl/AuxIndexStructures.h +1 -6
  76. data/vendor/faiss/faiss/impl/ClusteringHelpers.cpp +134 -45
  77. data/vendor/faiss/faiss/impl/ClusteringHelpers.h +36 -0
  78. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +286 -136
  79. data/vendor/faiss/faiss/impl/DistanceComputer.cpp +63 -0
  80. data/vendor/faiss/faiss/impl/DistanceComputer.h +3 -29
  81. data/vendor/faiss/faiss/impl/HNSW.cpp +46 -10
  82. data/vendor/faiss/faiss/impl/HNSW.h +72 -0
  83. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +6 -19
  84. data/vendor/faiss/faiss/impl/NNDescent.cpp +2 -1
  85. data/vendor/faiss/faiss/impl/NSG.cpp +3 -2
  86. data/vendor/faiss/faiss/impl/Panorama.h +3 -2
  87. data/vendor/faiss/faiss/impl/PiPNN.cpp +822 -0
  88. data/vendor/faiss/faiss/impl/PiPNN.h +122 -0
  89. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +54 -11
  90. data/vendor/faiss/faiss/impl/RaBitQUtils.h +1 -1
  91. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +81 -57
  92. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +15 -0
  93. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +29 -0
  94. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +1 -6
  95. data/vendor/faiss/faiss/impl/approx_topk/avx2.cpp +0 -4
  96. data/vendor/faiss/faiss/impl/approx_topk/neon.cpp +0 -4
  97. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHash_impl.h +5 -2
  98. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +0 -4
  99. data/vendor/faiss/faiss/impl/binary_hamming/avx512.cpp +0 -4
  100. data/vendor/faiss/faiss/impl/binary_hamming/neon.cpp +0 -4
  101. data/vendor/faiss/faiss/impl/binary_hamming/rvv.cpp +0 -4
  102. data/vendor/faiss/faiss/impl/expanded_scanners.h +88 -0
  103. data/vendor/faiss/faiss/impl/fast_scan/FastScanDistancePostProcessing.h +4 -0
  104. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops.h +61 -64
  105. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops_512.h +13 -14
  106. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +17 -129
  107. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +25 -19
  108. data/vendor/faiss/faiss/impl/fast_scan/fast_scan.cpp +5 -0
  109. data/vendor/faiss/faiss/impl/fast_scan/impl-avx2.cpp +0 -4
  110. data/vendor/faiss/faiss/impl/fast_scan/impl-avx512.cpp +0 -4
  111. data/vendor/faiss/faiss/impl/fast_scan/impl-neon.cpp +0 -88
  112. data/vendor/faiss/faiss/impl/fast_scan/impl-riscv.cpp +0 -4
  113. data/vendor/faiss/faiss/impl/fast_scan/kernels_neon.h +155 -0
  114. data/vendor/faiss/faiss/impl/fast_scan/kernels_simd256.h +22 -1
  115. data/vendor/faiss/faiss/impl/fast_scan/kernels_simd512.h +3 -2
  116. data/vendor/faiss/faiss/impl/fast_scan/simd_result_handlers.h +41 -23
  117. data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +0 -4
  118. data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +0 -4
  119. data/vendor/faiss/faiss/impl/index_read.cpp +172 -0
  120. data/vendor/faiss/faiss/impl/index_write.cpp +95 -0
  121. data/vendor/faiss/faiss/impl/pipnn/HashPrune.cpp +159 -0
  122. data/vendor/faiss/faiss/impl/pipnn/HashPrune.h +69 -0
  123. data/vendor/faiss/faiss/impl/pipnn/Partition.cpp +587 -0
  124. data/vendor/faiss/faiss/impl/pipnn/Partition.h +93 -0
  125. data/vendor/faiss/faiss/impl/pipnn/kernels.cpp +326 -0
  126. data/vendor/faiss/faiss/impl/pipnn/kernels.h +92 -0
  127. data/vendor/faiss/faiss/impl/platform_macros.h +45 -0
  128. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +0 -4
  129. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +79 -4
  130. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +6 -0
  131. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +0 -4
  132. data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +0 -4
  133. data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +0 -4
  134. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx2.h +4 -0
  135. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx512.h +4 -1
  136. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +19 -0
  137. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.h +6 -6
  138. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +131 -6
  139. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +0 -4
  140. data/vendor/faiss/faiss/impl/pq_code_distance/pq_rvv_kernel.h +89 -0
  141. data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +21 -10
  142. data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +45 -18
  143. data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +2 -1
  144. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +0 -4
  145. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +0 -4
  146. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +0 -4
  147. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +0 -4
  148. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +5 -1
  149. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +28 -52
  150. data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +59 -11
  151. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +226 -77
  152. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +482 -103
  153. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +2 -149
  154. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +3 -698
  155. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +66 -57
  156. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +101 -96
  157. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +3185 -144
  158. data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_coefficients.h +48 -0
  159. data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_rvv_kernel.h +546 -0
  160. data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +2 -2
  161. data/vendor/faiss/faiss/impl/simd_dispatch.h +7 -0
  162. data/vendor/faiss/faiss/impl/simdlib/simdlib_avx2.h +14 -0
  163. data/vendor/faiss/faiss/impl/simdlib/simdlib_dispatch.h +7 -8
  164. data/vendor/faiss/faiss/impl/sq_fastscan_utils.h +232 -0
  165. data/vendor/faiss/faiss/index_factory.cpp +76 -0
  166. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +3 -1
  167. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +3 -2
  168. data/vendor/faiss/faiss/utils/AlignedTable.h +6 -1
  169. data/vendor/faiss/faiss/utils/Heap.cpp +4 -3
  170. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +16 -5
  171. data/vendor/faiss/faiss/utils/bf16.h +36 -0
  172. data/vendor/faiss/faiss/utils/distances.cpp +226 -15
  173. data/vendor/faiss/faiss/utils/distances.h +34 -0
  174. data/vendor/faiss/faiss/utils/distances_dispatch.h +6 -4
  175. data/vendor/faiss/faiss/utils/distances_fused/avx512.cpp +2 -5
  176. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +0 -14
  177. data/vendor/faiss/faiss/utils/distances_fused/rvv.cpp +25 -0
  178. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based.cpp +0 -4
  179. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based_neon.cpp +0 -4
  180. data/vendor/faiss/faiss/utils/distances_fused/simdlib_kernel-inl.h +2 -1
  181. data/vendor/faiss/faiss/utils/distances_simd.cpp +9 -0
  182. data/vendor/faiss/faiss/utils/fp16-fp16c.h +4 -0
  183. data/vendor/faiss/faiss/utils/fp16-inl.h +26 -52
  184. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +0 -4
  185. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512.cpp +0 -4
  186. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +0 -4
  187. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +33 -5
  188. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_vpopcnt.h +15 -30
  189. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-neon.h +0 -4
  190. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +2 -1
  191. data/vendor/faiss/faiss/utils/hamming_distance/hamming_neon.cpp +0 -4
  192. data/vendor/faiss/faiss/utils/hamming_distance/hamming_rvv.cpp +0 -4
  193. data/vendor/faiss/faiss/utils/pq_code_distance.h +8 -4
  194. data/vendor/faiss/faiss/utils/prefetch.h +1 -26
  195. data/vendor/faiss/faiss/utils/quantize_lut.cpp +26 -27
  196. data/vendor/faiss/faiss/utils/quantize_lut.h +11 -0
  197. data/vendor/faiss/faiss/utils/rabitq_simd.h +51 -16
  198. data/vendor/faiss/faiss/utils/simd_impl/distances_aarch64.cpp +9 -4
  199. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +37 -5
  200. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +64 -5
  201. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +64 -5
  202. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +353 -71
  203. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv_kernel.h +68 -0
  204. data/vendor/faiss/faiss/utils/simd_impl/exhaustive_L2sqr_blas_cmax.h +93 -0
  205. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels.h +76 -0
  206. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx2.cpp +52 -0
  207. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx512.cpp +52 -0
  208. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_neon.cpp +48 -0
  209. data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx2.h +44 -0
  210. data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx512.h +43 -0
  211. data/vendor/faiss/faiss/utils/simd_impl/masked_sum.h +38 -0
  212. data/vendor/faiss/faiss/utils/simd_impl/partitioning_avx2.cpp +0 -4
  213. data/vendor/faiss/faiss/utils/simd_impl/partitioning_neon.cpp +0 -4
  214. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +54 -15
  215. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +38 -21
  216. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_vpopcnt.cpp +51 -14
  217. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +297 -10
  218. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +0 -4
  219. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx2.cpp +0 -4
  220. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx512.cpp +0 -4
  221. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +0 -4
  222. data/vendor/faiss/faiss/utils/simd_levels.cpp +19 -3
  223. data/vendor/faiss/faiss/utils/simd_levels.h +74 -1
  224. data/vendor/faiss/faiss/utils/transpose/transpose-avx2-inl.h +0 -4
  225. data/vendor/faiss/faiss/utils/transpose/transpose-avx512-inl.h +0 -4
  226. data/vendor/faiss/faiss/utils/utils.cpp +80 -0
  227. data/vendor/faiss/faiss/utils/utils.h +24 -0
  228. metadata +33 -1
@@ -0,0 +1,648 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #include <faiss/IndexSQFastScan.h>
9
+ #include <faiss/impl/sq_fastscan_utils.h>
10
+
11
+ #include <omp.h>
12
+
13
+ #include <algorithm>
14
+ #include <cstring>
15
+ #include <memory>
16
+ #include <vector>
17
+
18
+ #include <faiss/IndexScalarQuantizer.h>
19
+ #include <faiss/impl/AuxIndexStructures.h>
20
+ #include <faiss/impl/CodePacker.h>
21
+ #include <faiss/impl/FaissAssert.h>
22
+ #include <faiss/impl/IDSelector.h>
23
+ #include <faiss/impl/ResultHandler.h>
24
+ #include <faiss/impl/ScalarQuantizer.h>
25
+ #include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
26
+ #include <faiss/impl/fast_scan/fast_scan.h>
27
+ #include <faiss/utils/distances.h>
28
+ #include <faiss/utils/utils.h>
29
+
30
+ namespace faiss {
31
+
32
+ using namespace faiss::sq_fastscan;
33
+
34
+ // -----------------------------------------------------------------------
35
+ // Constructors
36
+ // -----------------------------------------------------------------------
37
+
38
+ IndexSQFastScan::IndexSQFastScan(
39
+ int d_in,
40
+ ScalarQuantizer::QuantizerType qtype,
41
+ MetricType metric,
42
+ int bbs_in)
43
+ : sq(d_in, qtype) {
44
+ FAISS_THROW_IF_NOT_MSG(
45
+ !is_fallback(qtype),
46
+ "IndexSQFastScan supports the native 4-bit and the reranked "
47
+ "types. QT_fp16 and QT_bf16 have no fast-scan path; use "
48
+ "IndexScalarQuantizer for those.");
49
+ init_fastscan(d_in, d_in, 4, metric, bbs_in);
50
+ }
51
+
52
+ IndexSQFastScan::IndexSQFastScan() = default;
53
+
54
+ IndexSQFastScan::IndexSQFastScan(const IndexScalarQuantizer& orig, int bbs_in)
55
+ : sq(orig.sq) {
56
+ FAISS_THROW_IF_NOT_MSG(
57
+ !is_fallback(sq.qtype),
58
+ "IndexSQFastScan conversion constructor supports the native "
59
+ "4-bit and the reranked types only.");
60
+ init_fastscan(orig.d, orig.d, 4, orig.metric_type, bbs_in);
61
+ ntotal = orig.ntotal;
62
+ is_trained = orig.is_trained;
63
+
64
+ ntotal2 = roundup(ntotal, bbs_in);
65
+ codes.resize(ntotal2 * M2 / 2);
66
+ if (is_native_4bit(sq.qtype)) {
67
+ pq4_pack_codes(
68
+ orig.codes.data(), ntotal, M, ntotal2, bbs_in, M2, codes.get());
69
+ } else {
70
+ // High nibbles feed the scan, low nibbles the rerank.
71
+ std::vector<uint8_t> nibbles(ntotal * (M2 / 2));
72
+ const int bits = sq_bits(sq.qtype), lo_bits = bits - 4;
73
+ const size_t half = sq_rerank_size(d, sq.qtype);
74
+ lo_codes.assign(size_t(ntotal) * half, 0);
75
+ for (idx_t i = 0; i < ntotal; i++) {
76
+ const uint8_t* src = orig.codes.data() + size_t(i) * sq.code_size;
77
+ uint8_t* hi = nibbles.data() + size_t(i) * (M2 / 2);
78
+ BitstringReader bsr(src, sq.code_size);
79
+ BitstringWriter bsw(lo_codes.data() + size_t(i) * half, half);
80
+ for (int m = 0; m < d; m++) {
81
+ const int code = int(bsr.read(bits));
82
+ const int h = code >> lo_bits;
83
+ hi[m / 2] |= (m & 1) ? uint8_t(h << 4) : uint8_t(h);
84
+ bsw.write(code & ((1 << lo_bits) - 1), lo_bits);
85
+ }
86
+ }
87
+ pq4_pack_codes(
88
+ nibbles.data(), ntotal, M, ntotal2, bbs_in, M2, codes.get());
89
+ }
90
+ }
91
+
92
+ // -----------------------------------------------------------------------
93
+ // train / add / reset
94
+ // -----------------------------------------------------------------------
95
+
96
+ void IndexSQFastScan::train(idx_t n, const float* x) {
97
+ if (is_trained) {
98
+ return;
99
+ }
100
+ sq.train(n, x);
101
+ is_trained = true;
102
+ }
103
+
104
+ void IndexSQFastScan::add(idx_t n, const float* x) {
105
+ FAISS_THROW_IF_NOT(is_trained);
106
+ const size_t before = lo_codes.size();
107
+ IndexFastScan::add(n, x);
108
+ if (!needs_rerank(sq.qtype) || lo_codes.size() != before) {
109
+ // IndexFastScan::add blocks a large batch and re-enters this override
110
+ // once per block, so the blocks have already appended the low bits.
111
+ return;
112
+ }
113
+ const int bits = sq_bits(sq.qtype), lo_bits = bits - 4;
114
+ const size_t stride = sq_rerank_size(d, sq.qtype);
115
+ lo_codes.resize(before + size_t(n) * stride, 0);
116
+ const SQRanges ranges(sq, d);
117
+ for (idx_t i = 0; i < n; i++) {
118
+ const float* xi = x + i * d;
119
+ uint8_t* dst = lo_codes.data() + before + size_t(i) * stride;
120
+ BitstringWriter bsw(dst, stride);
121
+ for (int m = 0; m < d; m++) {
122
+ const int code =
123
+ sq_code_of(xi[m], ranges.vmin(m), ranges.vdiff(m), bits);
124
+ bsw.write(code & ((1 << lo_bits) - 1), lo_bits);
125
+ }
126
+ }
127
+ }
128
+
129
+ void IndexSQFastScan::reset() {
130
+ lo_codes.clear();
131
+ IndexFastScan::reset();
132
+ }
133
+
134
+ // -----------------------------------------------------------------------
135
+ // search
136
+ // -----------------------------------------------------------------------
137
+
138
+ void IndexSQFastScan::search(
139
+ idx_t n,
140
+ const float* x,
141
+ idx_t k,
142
+ float* distances,
143
+ idx_t* labels,
144
+ const SearchParameters* params) const {
145
+ FAISS_THROW_IF_NOT(k > 0);
146
+ FAISS_THROW_IF_NOT(is_trained);
147
+ // The kernel applies the selector itself, so no generic-scanner fallback.
148
+ if (!needs_rerank(sq.qtype)) {
149
+ IndexFastScan::search(n, x, k, distances, labels, params);
150
+ return;
151
+ }
152
+
153
+ // The 4-bit scan is a shortlist. rerank_factor bounds the recall.
154
+ idx_t k_coarse = idx_t(double(k) * rerank_factor);
155
+ FAISS_THROW_IF_NOT(k_coarse >= k);
156
+ FAISS_THROW_IF_NOT_MSG(
157
+ n <= INT64_MAX / k_coarse, "n * k_coarse would overflow int64");
158
+ std::vector<float> coarse_dis(n * k_coarse);
159
+ std::vector<idx_t> coarse_ids(n * k_coarse);
160
+ IndexFastScan::search(
161
+ n, x, k_coarse, coarse_dis.data(), coarse_ids.data(), params);
162
+
163
+ const bool is_max = metric_type == METRIC_L2;
164
+ #pragma omp parallel
165
+ {
166
+ std::unique_ptr<CodePacker> packer(get_CodePacker());
167
+ std::vector<uint8_t> hi_buf(M2 / 2);
168
+ std::vector<int> code_buf(d);
169
+ const size_t lo_stride = sq_rerank_size(d, sq.qtype);
170
+ const int maxv = (1 << sq_bits(sq.qtype)) - 1;
171
+ const SQRanges ranges(sq, d);
172
+ #pragma omp for
173
+ for (idx_t i = 0; i < n; i++) {
174
+ float* D = distances + i * k;
175
+ idx_t* I = labels + i * k;
176
+ if (is_max) {
177
+ maxheap_heapify(k, D, I);
178
+ } else {
179
+ minheap_heapify(k, D, I);
180
+ }
181
+ for (idx_t j = 0; j < k_coarse; j++) {
182
+ idx_t id = coarse_ids[i * k_coarse + j];
183
+ if (id < 0) {
184
+ continue;
185
+ }
186
+ float dis;
187
+ {
188
+ // Recombine the two halves into the original code.
189
+ packer->unpack_1(codes.get(), id, hi_buf.data());
190
+ split_to_codes(
191
+ hi_buf.data(),
192
+ lo_codes.data() + size_t(id) * lo_stride,
193
+ d,
194
+ sq.qtype,
195
+ code_buf.data());
196
+ const float* xi = x + i * d;
197
+ float acc = 0;
198
+ for (int m = 0; m < d; m++) {
199
+ // Matches Codec*bit::decode_component: (c+0.5)/maxv.
200
+ const float v = ranges.vmin(m) +
201
+ ((code_buf[m] + 0.5f) / float(maxv)) *
202
+ ranges.vdiff(m);
203
+ if (is_max) {
204
+ const float df = xi[m] - v;
205
+ acc += df * df;
206
+ } else {
207
+ acc += xi[m] * v;
208
+ }
209
+ }
210
+ dis = acc;
211
+ }
212
+ if (is_max) {
213
+ if (dis < D[0]) {
214
+ maxheap_replace_top(k, D, I, dis, id);
215
+ }
216
+ } else {
217
+ if (dis > D[0]) {
218
+ minheap_replace_top(k, D, I, dis, id);
219
+ }
220
+ }
221
+ }
222
+ if (is_max) {
223
+ maxheap_reorder(k, D, I);
224
+ } else {
225
+ minheap_reorder(k, D, I);
226
+ }
227
+ }
228
+ }
229
+ }
230
+
231
+ // -----------------------------------------------------------------------
232
+ // compute_codes -- called by IndexFastScan::add
233
+ // -----------------------------------------------------------------------
234
+
235
+ void IndexSQFastScan::compute_codes(uint8_t* out_codes, idx_t n, const float* x)
236
+ const {
237
+ if (is_native_4bit(sq.qtype)) {
238
+ sq.compute_codes(x, out_codes, n);
239
+ } else {
240
+ // The scanned nibble is the top half. lo_codes holds the bottom.
241
+ std::vector<uint8_t> lo_discard(
242
+ size_t(n) * sq_rerank_size(d, sq.qtype));
243
+ float_to_split_codes(x, out_codes, lo_discard.data(), n, d, M2, sq);
244
+ }
245
+ }
246
+
247
+ // -----------------------------------------------------------------------
248
+ // compute_float_LUT -- builds the d x 16 distance LUT for vpshufb
249
+ // -----------------------------------------------------------------------
250
+
251
+ void IndexSQFastScan::compute_float_LUT(
252
+ float* lut,
253
+ idx_t n,
254
+ const float* x,
255
+ const FastScanDistancePostProcessing&) const {
256
+ const size_t dim = d;
257
+ const size_t ksub_val = 16;
258
+
259
+ bool is_uniform = (sq.qtype == ScalarQuantizer::QT_4bit_uniform) ||
260
+ is_uniform_range(sq.qtype);
261
+
262
+ std::vector<float> recon_table(dim * ksub_val);
263
+
264
+ const float lut_step = float(1 << sq_rerank_bits(sq.qtype));
265
+ const float lut_offset = lut_step / 2.0f;
266
+ const float lut_scale = float((1 << sq_bits(sq.qtype)) - 1);
267
+
268
+ if (is_uniform) {
269
+ float vmin, vdiff;
270
+ get_uniform_range(sq, vmin, vdiff);
271
+ for (size_t c = 0; c < ksub_val; c++) {
272
+ float recon =
273
+ vmin + ((c * lut_step + lut_offset) / lut_scale) * vdiff;
274
+ for (size_t m = 0; m < dim; m++) {
275
+ recon_table[m * ksub_val + c] = recon;
276
+ }
277
+ }
278
+ } else {
279
+ // QT_4bit: per-dimension ranges
280
+ const float* vmin = sq.trained.data();
281
+ const float* vdiff = sq.trained.data() + dim;
282
+ for (size_t m = 0; m < dim; m++) {
283
+ for (size_t c = 0; c < ksub_val; c++) {
284
+ recon_table[m * ksub_val + c] = vmin[m] +
285
+ ((c * lut_step + lut_offset) / lut_scale) * vdiff[m];
286
+ }
287
+ }
288
+ }
289
+
290
+ if (metric_type == METRIC_L2) {
291
+ for (idx_t i = 0; i < n; i++) {
292
+ const float* xi = x + i * dim;
293
+ float* lut_i = lut + i * dim * ksub_val;
294
+ for (size_t m = 0; m < dim; m++) {
295
+ float qi = xi[m];
296
+ const float* recon_m = recon_table.data() + m * ksub_val;
297
+ float* lut_m = lut_i + m * ksub_val;
298
+ for (size_t c = 0; c < ksub_val; c++) {
299
+ float diff = qi - recon_m[c];
300
+ lut_m[c] = diff * diff;
301
+ }
302
+ }
303
+ }
304
+ } else {
305
+ for (idx_t i = 0; i < n; i++) {
306
+ const float* xi = x + i * dim;
307
+ float* lut_i = lut + i * dim * ksub_val;
308
+ for (size_t m = 0; m < dim; m++) {
309
+ float qi = xi[m];
310
+ const float* recon_m = recon_table.data() + m * ksub_val;
311
+ float* lut_m = lut_i + m * ksub_val;
312
+ for (size_t c = 0; c < ksub_val; c++) {
313
+ lut_m[c] = qi * recon_m[c];
314
+ }
315
+ }
316
+ }
317
+ }
318
+ }
319
+
320
+ void IndexSQFastScan::sa_decode(idx_t n, const uint8_t* bytes, float* x) const {
321
+ sq.decode(bytes, x, n);
322
+ }
323
+
324
+ void IndexSQFastScan::fill_sa_code(
325
+ const CodePacker& packer,
326
+ idx_t id,
327
+ uint8_t* scratch,
328
+ int* values,
329
+ uint8_t* code_out) const {
330
+ packer.unpack_1(codes.data(), id, scratch);
331
+ if (lo_codes.empty()) {
332
+ memcpy(code_out, scratch, sq.code_size);
333
+ return;
334
+ }
335
+ split_to_codes(
336
+ scratch,
337
+ lo_codes.data() + size_t(id) * sq_rerank_size(d, sq.qtype),
338
+ d,
339
+ sq.qtype,
340
+ values);
341
+ memset(code_out, 0, sq.code_size);
342
+ BitstringWriter out(code_out, sq.code_size);
343
+ const int bits = sq_bits(sq.qtype);
344
+ for (int m = 0; m < d; m++) {
345
+ out.write(values[m], bits);
346
+ }
347
+ }
348
+
349
+ void IndexSQFastScan::reconstruct(idx_t key, float* recons) const {
350
+ FAISS_THROW_IF_NOT(key >= 0 && key < ntotal);
351
+ std::unique_ptr<CodePacker> packer(get_CodePacker());
352
+ std::vector<uint8_t> scratch(M2 / 2), flat_code(sq.code_size);
353
+ std::vector<int> values(d);
354
+ fill_sa_code(*packer, key, scratch.data(), values.data(), flat_code.data());
355
+ sq.decode(flat_code.data(), recons, 1);
356
+ }
357
+
358
+ size_t IndexSQFastScan::sa_code_size() const {
359
+ return sq.code_size;
360
+ }
361
+
362
+ size_t IndexSQFastScan::fast_scan_code_size() const {
363
+ return M2 / 2;
364
+ }
365
+
366
+ // -----------------------------------------------------------------------
367
+ // sa_encode
368
+ // -----------------------------------------------------------------------
369
+
370
+ void IndexSQFastScan::sa_encode(idx_t n, const float* x, uint8_t* bytes) const {
371
+ FAISS_THROW_IF_NOT(is_trained);
372
+ sq.compute_codes(x, bytes, n);
373
+ }
374
+
375
+ // -----------------------------------------------------------------------
376
+ // reconstruct_n
377
+ // -----------------------------------------------------------------------
378
+
379
+ void IndexSQFastScan::reconstruct_n(idx_t i0, idx_t ni, float* recons) const {
380
+ FAISS_THROW_IF_NOT(i0 >= 0 && i0 + ni <= ntotal);
381
+ std::unique_ptr<CodePacker> packer(get_CodePacker());
382
+ std::vector<uint8_t> scratch(M2 / 2), flat_code(sq.code_size);
383
+ std::vector<int> values(d);
384
+ for (idx_t i = 0; i < ni; i++) {
385
+ fill_sa_code(
386
+ *packer,
387
+ i0 + i,
388
+ scratch.data(),
389
+ values.data(),
390
+ flat_code.data());
391
+ sq.decode(flat_code.data(), recons + i * d, 1);
392
+ }
393
+ }
394
+
395
+ // -----------------------------------------------------------------------
396
+ // add_sa_codes
397
+ // -----------------------------------------------------------------------
398
+
399
+ void IndexSQFastScan::add_sa_codes(
400
+ idx_t n,
401
+ const uint8_t* code,
402
+ const idx_t* /*xids*/) {
403
+ FAISS_THROW_IF_NOT(is_trained);
404
+ // Decode to float then re-add via IndexFastScan for SIMD packing
405
+ std::vector<float> recon(n * d);
406
+ sq.decode(code, recon.data(), n);
407
+ add(n, recon.data());
408
+ }
409
+
410
+ // -----------------------------------------------------------------------
411
+ // permute_entries
412
+ // -----------------------------------------------------------------------
413
+
414
+ void IndexSQFastScan::permute_entries(const idx_t* perm) {
415
+ std::vector<uint8_t> flat_old;
416
+ {
417
+ std::unique_ptr<CodePacker> packer(get_CodePacker());
418
+ size_t code_sz = code_size;
419
+ flat_old.resize(ntotal * code_sz, 0);
420
+ for (idx_t i = 0; i < ntotal; i++) {
421
+ packer->unpack_1(codes.data(), i, flat_old.data() + i * code_sz);
422
+ }
423
+ }
424
+
425
+ size_t code_sz = code_size;
426
+ std::vector<uint8_t> flat_new(ntotal2 * code_sz, 0);
427
+ for (idx_t i = 0; i < ntotal; i++) {
428
+ memcpy(flat_new.data() + i * code_sz,
429
+ flat_old.data() + perm[i] * code_sz,
430
+ code_sz);
431
+ }
432
+ pq4_pack_codes(flat_new.data(), ntotal, M, ntotal2, bbs, M2, codes.get());
433
+
434
+ if (!lo_codes.empty()) {
435
+ const size_t lo_sz = sq_rerank_size(d, sq.qtype);
436
+ std::vector<uint8_t> lo_new(size_t(ntotal) * lo_sz);
437
+ for (idx_t i = 0; i < ntotal; i++) {
438
+ memcpy(lo_new.data() + size_t(i) * lo_sz,
439
+ lo_codes.data() + size_t(perm[i]) * lo_sz,
440
+ lo_sz);
441
+ }
442
+ lo_codes.swap(lo_new);
443
+ }
444
+ }
445
+
446
+ // -----------------------------------------------------------------------
447
+ // get_distance_computer / get_FlatCodesDistanceComputer
448
+ // -----------------------------------------------------------------------
449
+
450
+ namespace {
451
+
452
+ /// Wrapper that owns an unpacked code buffer and delegates distance
453
+ /// computation to an underlying SQDistanceComputer.
454
+ struct OwningFlatCodesDistanceComputer : FlatCodesDistanceComputer {
455
+ std::vector<uint8_t> owned_codes;
456
+ std::unique_ptr<ScalarQuantizer::SQDistanceComputer> dc;
457
+
458
+ OwningFlatCodesDistanceComputer(
459
+ std::vector<uint8_t>&& codes_buf,
460
+ ScalarQuantizer::SQDistanceComputer* dc_in)
461
+ : owned_codes(std::move(codes_buf)), dc(dc_in) {
462
+ this->codes = owned_codes.data();
463
+ this->code_size = dc->code_size;
464
+ dc->codes = owned_codes.data();
465
+ }
466
+
467
+ void set_query(const float* x) override {
468
+ dc->set_query(x);
469
+ }
470
+
471
+ float distance_to_code(const uint8_t* code) override {
472
+ return dc->distance_to_code(code);
473
+ }
474
+
475
+ void distance_to_code_batch_4(
476
+ const uint8_t* c0,
477
+ const uint8_t* c1,
478
+ const uint8_t* c2,
479
+ const uint8_t* c3,
480
+ float& d0,
481
+ float& d1,
482
+ float& d2,
483
+ float& d3) override {
484
+ dc->distance_to_code_batch_4(c0, c1, c2, c3, d0, d1, d2, d3);
485
+ }
486
+
487
+ float symmetric_dis(idx_t i, idx_t j) override {
488
+ return dc->symmetric_dis(i, j);
489
+ }
490
+ };
491
+
492
+ } // anonymous namespace
493
+
494
+ FlatCodesDistanceComputer* IndexSQFastScan::get_FlatCodesDistanceComputer()
495
+ const {
496
+ std::unique_ptr<CodePacker> packer(get_CodePacker());
497
+ size_t cs = sq.code_size;
498
+ std::vector<uint8_t> flat(ntotal * cs);
499
+ std::vector<uint8_t> scratch(M2 / 2);
500
+ std::vector<int> values(d);
501
+ for (idx_t i = 0; i < ntotal; i++) {
502
+ fill_sa_code(
503
+ *packer,
504
+ i,
505
+ scratch.data(),
506
+ values.data(),
507
+ flat.data() + i * cs);
508
+ }
509
+
510
+ ScalarQuantizer::SQDistanceComputer* dc =
511
+ sq.get_distance_computer(metric_type);
512
+ dc->code_size = cs;
513
+
514
+ return new OwningFlatCodesDistanceComputer(std::move(flat), dc);
515
+ }
516
+
517
+ DistanceComputer* IndexSQFastScan::get_distance_computer() const {
518
+ return get_FlatCodesDistanceComputer();
519
+ }
520
+
521
+ // -----------------------------------------------------------------------
522
+ // range_search
523
+ // -----------------------------------------------------------------------
524
+
525
+ void IndexSQFastScan::range_search(
526
+ idx_t n,
527
+ const float* x,
528
+ float radius,
529
+ RangeSearchResult* result,
530
+ const SearchParameters* params) const {
531
+ FAISS_THROW_IF_NOT(is_trained);
532
+ const IDSelector* sel = params ? params->sel : nullptr;
533
+
534
+ // Unpack codes for range search
535
+ std::vector<uint8_t> flat_codes(ntotal * sq.code_size);
536
+ {
537
+ std::unique_ptr<CodePacker> packer(get_CodePacker());
538
+ std::vector<uint8_t> scratch(M2 / 2);
539
+ std::vector<int> values(d);
540
+ for (idx_t i = 0; i < ntotal; i++) {
541
+ fill_sa_code(
542
+ *packer,
543
+ i,
544
+ scratch.data(),
545
+ values.data(),
546
+ flat_codes.data() + i * sq.code_size);
547
+ }
548
+ }
549
+
550
+ std::vector<RangeSearchPartialResult*> partial_results(
551
+ omp_get_max_threads());
552
+
553
+ size_t cs = sq.code_size;
554
+
555
+ #pragma omp parallel
556
+ {
557
+ int rank = omp_get_thread_num();
558
+ RangeSearchPartialResult* pres = new RangeSearchPartialResult(result);
559
+ partial_results[rank] = pres;
560
+
561
+ std::vector<float> decoded(d);
562
+
563
+ #pragma omp for
564
+ for (idx_t i = 0; i < n; i++) {
565
+ const float* qi = x + i * d;
566
+ RangeQueryResult& qres = pres->new_result(i);
567
+
568
+ for (idx_t j = 0; j < ntotal; j++) {
569
+ if (sel && !sel->is_member(j)) {
570
+ continue;
571
+ }
572
+ sq.decode(flat_codes.data() + j * cs, decoded.data(), 1);
573
+ float dis;
574
+ if (metric_type == METRIC_INNER_PRODUCT) {
575
+ dis = fvec_inner_product(qi, decoded.data(), d);
576
+ if (dis > radius) {
577
+ qres.add(dis, j);
578
+ }
579
+ } else {
580
+ dis = fvec_L2sqr(qi, decoded.data(), d);
581
+ if (dis < radius) {
582
+ qres.add(dis, j);
583
+ }
584
+ }
585
+ }
586
+ }
587
+ }
588
+
589
+ RangeSearchPartialResult::merge(partial_results);
590
+ }
591
+
592
+ // -----------------------------------------------------------------------
593
+ // remove_ids
594
+ // -----------------------------------------------------------------------
595
+
596
+ size_t IndexSQFastScan::remove_ids(const IDSelector& sel) {
597
+ return IndexFastScan::remove_ids(sel);
598
+ }
599
+
600
+ // -----------------------------------------------------------------------
601
+ // check_compatible_for_merge
602
+ // -----------------------------------------------------------------------
603
+
604
+ void IndexSQFastScan::check_compatible_for_merge(
605
+ const Index& otherIndex) const {
606
+ IndexFastScan::check_compatible_for_merge(otherIndex);
607
+ const IndexSQFastScan* other =
608
+ dynamic_cast<const IndexSQFastScan*>(&otherIndex);
609
+ FAISS_THROW_IF_NOT_MSG(other, "merge requires IndexSQFastScan");
610
+ FAISS_THROW_IF_NOT_MSG(
611
+ other->sq.qtype == sq.qtype,
612
+ "merge requires matching ScalarQuantizer types");
613
+ }
614
+
615
+ // -----------------------------------------------------------------------
616
+ // merge_from
617
+ // -----------------------------------------------------------------------
618
+
619
+ void IndexSQFastScan::merge_from(Index& otherIndex, idx_t add_id) {
620
+ check_compatible_for_merge(otherIndex);
621
+ const IndexSQFastScan* other =
622
+ static_cast<const IndexSQFastScan*>(&otherIndex);
623
+ lo_codes.insert(
624
+ lo_codes.end(), other->lo_codes.begin(), other->lo_codes.end());
625
+ IndexFastScan::merge_from(otherIndex, add_id);
626
+ }
627
+
628
+ // -----------------------------------------------------------------------
629
+ // search1
630
+ // -----------------------------------------------------------------------
631
+
632
+ void IndexSQFastScan::search1(
633
+ const float* x,
634
+ ResultHandler& handler,
635
+ SearchParameters* params) const {
636
+ const IDSelector* sel = params ? params->sel : nullptr;
637
+ std::unique_ptr<DistanceComputer> dc(get_distance_computer());
638
+ dc->set_query(x);
639
+ for (idx_t i = 0; i < ntotal; i++) {
640
+ if (sel && !sel->is_member(i)) {
641
+ continue;
642
+ }
643
+ float dis = (*dc)(i);
644
+ handler.add_result(dis, i);
645
+ }
646
+ }
647
+
648
+ } // namespace faiss