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
@@ -17,16 +17,20 @@
17
17
  #include <cinttypes>
18
18
  #include <cstdio>
19
19
  #include <limits>
20
+ #include <optional>
20
21
 
22
+ #include <faiss/utils/random.h>
21
23
  #include <faiss/utils/utils.h>
22
24
 
23
25
  #include <faiss/IndexFlat.h>
26
+ #include <faiss/IndexHNSW.h>
24
27
  #include <faiss/impl/AuxIndexStructures.h>
25
28
  #include <faiss/impl/CodePacker.h>
26
29
  #include <faiss/impl/FaissAssert.h>
27
30
  #include <faiss/impl/IDSelector.h>
28
31
  #include <faiss/impl/ResultHandler.h>
29
32
  #include <faiss/impl/expanded_scanners.h>
33
+ #include <faiss/impl/platform_macros.h>
30
34
 
31
35
  namespace faiss {
32
36
 
@@ -53,28 +57,89 @@ Level1Quantizer::~Level1Quantizer() {
53
57
  }
54
58
  }
55
59
 
56
- void Level1Quantizer::train_q1(
60
+ namespace {
61
+
62
+ void add_hnsw_quantizer_links(
63
+ IndexHNSW& hnsw,
57
64
  size_t n,
58
- const float* x,
65
+ const uint8_t* x,
66
+ NumericType numeric_type,
67
+ const Index* codec,
68
+ bool verbose) {
69
+ const size_t samples = std::min(
70
+ n,
71
+ size_t(hnsw.level_0_link_samples_per_node) * size_t(hnsw.ntotal));
72
+ const HNSWLevel0LinkStats stats = codec
73
+ ? hnsw.add_level_0_links_from_sample_encoded(samples, x, codec)
74
+ : hnsw.add_level_0_links_from_sample_ex(samples, x, numeric_type);
75
+ if (verbose) {
76
+ printf("HNSW quantizer level-0 links from %zd samples: %zd sinks found, "
77
+ "%zd remaining, %zd links added, %zd replaced\n",
78
+ samples,
79
+ stats.n_sinks_found,
80
+ stats.n_sinks_remaining,
81
+ stats.n_links_added,
82
+ stats.n_links_replaced);
83
+ }
84
+ }
85
+
86
+ /** Trains `q1` on rows encoded by `codec` or, if codec is null, stored as
87
+ * `numeric_type`. */
88
+ void train_q1_impl(
89
+ Level1Quantizer& q1,
90
+ size_t n,
91
+ const uint8_t* x,
92
+ NumericType numeric_type,
93
+ const Index* codec,
59
94
  bool verbose,
60
95
  MetricType metric_type) {
96
+ Index* quantizer = q1.quantizer;
97
+ const size_t nlist = q1.nlist;
98
+ const ClusteringParameters& cp = q1.cp;
99
+ Index* clustering_index = q1.clustering_index;
100
+
101
+ auto add_quantizer_links = [&]() {
102
+ auto* hnsw = dynamic_cast<IndexHNSW*>(quantizer);
103
+ if (hnsw && hnsw->level_0_link_samples_per_node > 0) {
104
+ add_hnsw_quantizer_links(*hnsw, n, x, numeric_type, codec, verbose);
105
+ }
106
+ };
107
+
108
+ // k-means on the training rows, with `assigner` as assignment index
109
+ auto train_clustering = [&](Clustering& clus, Index& assigner) {
110
+ if (codec) {
111
+ clus.train_encoded(n, x, codec, assigner);
112
+ } else {
113
+ clus.train_ex(n, x, numeric_type, assigner);
114
+ }
115
+ };
116
+
61
117
  FAISS_THROW_IF_NOT_MSG(quantizer, "IVF quantizer must not be null");
62
118
  size_t d = quantizer->d;
119
+ FAISS_THROW_IF_NOT_FMT(
120
+ !codec || static_cast<size_t>(codec->d) == d,
121
+ "Codec dimension %d not the same as quantizer dimension %d",
122
+ codec ? codec->d : 0,
123
+ int(d));
63
124
  if (quantizer->is_trained &&
64
125
  (static_cast<size_t>(quantizer->ntotal) == nlist)) {
65
126
  if (verbose) {
66
127
  printf("IVF quantizer does not need training.\n");
67
128
  }
68
- } else if (quantizer_trains_alone == 1) {
129
+ } else if (q1.quantizer_trains_alone == 1) {
130
+ FAISS_THROW_IF_MSG(
131
+ codec,
132
+ "encoded training is not supported when the IVF quantizer "
133
+ "trains alone");
69
134
  if (verbose) {
70
135
  printf("IVF quantizer trains alone...\n");
71
136
  }
72
137
  quantizer->verbose = verbose;
73
- quantizer->train(n, x);
138
+ quantizer->train_ex(n, x, numeric_type);
74
139
  FAISS_THROW_IF_NOT_MSG(
75
140
  static_cast<size_t>(quantizer->ntotal) == nlist,
76
141
  "nlist not consistent with quantizer size");
77
- } else if (quantizer_trains_alone == 0) {
142
+ } else if (q1.quantizer_trains_alone == 0) {
78
143
  if (verbose) {
79
144
  printf("Training level-1 quantizer on %zd vectors in %zdD\n", n, d);
80
145
  }
@@ -83,6 +148,16 @@ void Level1Quantizer::train_q1(
83
148
  cp.use_super_kmeans && clustering_index,
84
149
  "cp.use_super_kmeans is incompatible with a user-provided "
85
150
  "clustering_index: SuperKMeans assigns with its own index");
151
+ FAISS_THROW_IF_MSG(
152
+ cp.use_super_kmeans && codec,
153
+ "SuperKMeans does not support encoded training data");
154
+ FAISS_THROW_IF_MSG(
155
+ cp.use_super_kmeans &&
156
+ !(metric_type == METRIC_L2 ||
157
+ (metric_type == METRIC_INNER_PRODUCT &&
158
+ cp.spherical)),
159
+ "SuperKMeans requires L2 or spherical inner-product "
160
+ "clustering");
86
161
 
87
162
  quantizer->reset();
88
163
  if (cp.use_super_kmeans) {
@@ -90,19 +165,20 @@ void Level1Quantizer::train_q1(
90
165
  static_cast<ClusteringParameters&>(super_cp) = cp;
91
166
  SuperKMeans clus(
92
167
  static_cast<int>(d), static_cast<int>(nlist), super_cp);
93
- clus.train(n, x);
168
+ clus.train_ex(n, x, numeric_type);
94
169
  quantizer->add(nlist, clus.centroids.data());
95
170
  } else {
96
171
  Clustering clus(static_cast<int>(d), static_cast<int>(nlist), cp);
97
172
  if (clustering_index) {
98
- clus.train(n, x, *clustering_index);
173
+ train_clustering(clus, *clustering_index);
99
174
  quantizer->add(nlist, clus.centroids.data());
100
175
  } else {
101
- clus.train(n, x, *quantizer);
176
+ train_clustering(clus, *quantizer);
102
177
  }
103
178
  }
104
179
  quantizer->is_trained = true;
105
- } else if (quantizer_trains_alone == 2) {
180
+ add_quantizer_links();
181
+ } else if (q1.quantizer_trains_alone == 2) {
106
182
  if (verbose) {
107
183
  printf("Training L2 quantizer on %zd vectors in %zdD%s\n",
108
184
  n,
@@ -118,9 +194,9 @@ void Level1Quantizer::train_q1(
118
194
  Clustering clus(static_cast<int>(d), static_cast<int>(nlist), cp);
119
195
  if (!clustering_index) {
120
196
  IndexFlatL2 assigner(d);
121
- clus.train(n, x, assigner);
197
+ train_clustering(clus, assigner);
122
198
  } else {
123
- clus.train(n, x, *clustering_index);
199
+ train_clustering(clus, *clustering_index);
124
200
  }
125
201
  if (verbose) {
126
202
  printf("Adding centroids to quantizer\n");
@@ -132,9 +208,46 @@ void Level1Quantizer::train_q1(
132
208
  quantizer->train(nlist, clus.centroids.data());
133
209
  }
134
210
  quantizer->add(nlist, clus.centroids.data());
211
+ add_quantizer_links();
135
212
  }
136
213
  }
137
214
 
215
+ } // namespace
216
+
217
+ void Level1Quantizer::train_q1(
218
+ size_t n,
219
+ const float* x,
220
+ bool verbose,
221
+ MetricType metric_type) {
222
+ train_q1_ex(n, x, NumericType::Float32, verbose, metric_type);
223
+ }
224
+
225
+ void Level1Quantizer::train_q1_ex(
226
+ size_t n,
227
+ const void* x,
228
+ NumericType numeric_type,
229
+ bool verbose,
230
+ MetricType metric_type) {
231
+ train_q1_impl(
232
+ *this,
233
+ n,
234
+ static_cast<const uint8_t*>(x),
235
+ numeric_type,
236
+ nullptr,
237
+ verbose,
238
+ metric_type);
239
+ }
240
+
241
+ void Level1Quantizer::train_q1_encoded(
242
+ size_t n,
243
+ const uint8_t* x,
244
+ const Index* codec,
245
+ bool verbose,
246
+ MetricType metric_type) {
247
+ train_q1_impl(
248
+ *this, n, x, NumericType::Float32, codec, verbose, metric_type);
249
+ }
250
+
138
251
  size_t Level1Quantizer::coarse_code_size() const {
139
252
  size_t nl = nlist - 1;
140
253
  size_t nbyte = 0;
@@ -437,19 +550,21 @@ void IndexIVF::search_preassigned(
437
550
  const bool ensure_topk_full = params ? params->ensure_topk_full : false;
438
551
 
439
552
  IDSelector* sel = params ? params->sel : nullptr;
553
+ FAISS_THROW_IF_NOT_MSG(
554
+ !(sel && store_pairs),
555
+ "selector and store_pairs cannot be combined");
556
+
440
557
  const IDSelectorRange* selr = dynamic_cast<const IDSelectorRange*>(sel);
441
558
  if (selr) {
442
- if (selr->assume_sorted) {
559
+ // The sorted-range shortcut bounds a section of an array-backed list,
560
+ // so an iterable list has to keep the selector and filter per entry.
561
+ if (selr->assume_sorted && !invlists->use_iterator) {
443
562
  sel = nullptr; // use special IDSelectorRange processing
444
563
  } else {
445
564
  selr = nullptr; // use generic processing
446
565
  }
447
566
  }
448
567
 
449
- FAISS_THROW_IF_NOT_MSG(
450
- !(sel && store_pairs),
451
- "selector and store_pairs cannot be combined");
452
-
453
568
  FAISS_THROW_IF_NOT_MSG(
454
569
  !invlists->use_iterator ||
455
570
  (cur_max_codes == 0 && store_pairs == false),
@@ -568,10 +683,9 @@ void IndexIVF::search_preassigned(
568
683
  return (size_t)0;
569
684
  }
570
685
 
571
- scanner->set_list(key, coarse_dis_i);
572
-
573
686
  nlistv++;
574
687
  if (invlists->use_iterator) {
688
+ scanner->set_list(key, coarse_dis_i);
575
689
  size_t list_size = 0;
576
690
  std::unique_ptr<InvertedListsIterator> it(
577
691
  invlists->get_iterator(key, inverted_list_context));
@@ -586,31 +700,30 @@ void IndexIVF::search_preassigned(
586
700
  list_size = static_cast<size_t>(list_size_max);
587
701
  }
588
702
 
589
- InvertedLists::ScopedCodes scodes(invlists, key);
590
- const uint8_t* codes = scodes.get();
591
-
592
- std::unique_ptr<InvertedLists::ScopedIds> sids;
703
+ std::optional<InvertedLists::ScopedIds> sids;
593
704
  const idx_t* ids = nullptr;
594
-
595
705
  if (!store_pairs) {
596
- sids = std::make_unique<InvertedLists::ScopedIds>(
597
- invlists, key);
706
+ sids.emplace(invlists, key);
598
707
  ids = sids->get();
599
708
  }
600
709
 
710
+ size_t jmin = 0;
601
711
  if (selr) { // IDSelectorRange
602
712
  // restrict search to a section of the inverted list
603
- size_t jmin, jmax;
713
+ size_t jmax;
604
714
  selr->find_sorted_ids_bounds(
605
715
  list_size, ids, &jmin, &jmax);
606
716
  list_size = jmax - jmin;
607
717
  if (list_size == 0) {
608
718
  return (size_t)0;
609
719
  }
610
- codes += jmin * code_size;
611
720
  ids += jmin;
612
721
  }
613
722
 
723
+ scanner->set_list(key, coarse_dis_i);
724
+ InvertedLists::ScopedCodes scodes(invlists, key);
725
+ const uint8_t* codes = scodes.get() + jmin * code_size;
726
+
614
727
  size_t old_scan_cnt = 0;
615
728
  size_t old_heap_updates = 0;
616
729
  if (is_similarity_metric(metric_type)) {
@@ -692,7 +805,7 @@ void IndexIVF::search_preassigned(
692
805
  scanner->set_query(x + i * d);
693
806
  init_result(local_dis.data(), local_idx.data());
694
807
 
695
- #pragma omp for schedule(dynamic)
808
+ #pragma omp for schedule(FAISS_OMP_DYNAMIC)
696
809
  for (idx_t ik = 0; ik < cur_nprobe; ik++) {
697
810
  try {
698
811
  ndis += scan_one_list(
@@ -734,7 +847,7 @@ void IndexIVF::search_preassigned(
734
847
  init_result(distances + i * k, labels + i * k);
735
848
  }
736
849
 
737
- #pragma omp for schedule(dynamic)
850
+ #pragma omp for schedule(FAISS_OMP_DYNAMIC)
738
851
  for (int64_t ij = 0; ij < n * cur_nprobe; ij++) {
739
852
  try {
740
853
  size_t i = ij / cur_nprobe;
@@ -957,7 +1070,7 @@ void IndexIVF::range_search_preassigned(
957
1070
 
958
1071
  RangeQueryResult& qres = pres.new_result(i);
959
1072
 
960
- #pragma omp for schedule(dynamic)
1073
+ #pragma omp for schedule(FAISS_OMP_DYNAMIC)
961
1074
  for (int64_t ik = 0; ik < cur_nprobe; ik++) {
962
1075
  try {
963
1076
  scan_list_func(i, ik, qres);
@@ -969,7 +1082,7 @@ void IndexIVF::range_search_preassigned(
969
1082
  } else if (parallel_mode == 2) {
970
1083
  RangeQueryResult* qres = nullptr;
971
1084
 
972
- #pragma omp for schedule(dynamic)
1085
+ #pragma omp for schedule(FAISS_OMP_DYNAMIC)
973
1086
  for (idx_t iik = 0; iik < nx * (idx_t)cur_nprobe; iik++) {
974
1087
  try {
975
1088
  idx_t i = iik / (idx_t)cur_nprobe;
@@ -1308,6 +1421,125 @@ void IndexIVF::update_vectors(int n, const idx_t* new_ids, const float* x) {
1308
1421
  invlists, n, new_ids, assign.data(), flat_codes.data());
1309
1422
  }
1310
1423
 
1424
+ namespace {
1425
+
1426
+ /** Trains the encoder of `ivf` on training rows that are not fp32.
1427
+ *
1428
+ * Only the train_encoder_num_vectors() rows sampled for the encoder are
1429
+ * widened to fp32. `decode_rows(n, rows, out)` widens n consecutive rows of
1430
+ * `row_size` bytes.
1431
+ */
1432
+ template <class DecodeRows>
1433
+ void train_encoder_on_encoded_rows(
1434
+ IndexIVF& ivf,
1435
+ idx_t n,
1436
+ const uint8_t* x,
1437
+ size_t row_size,
1438
+ const DecodeRows& decode_rows) {
1439
+ const size_t d = ivf.d;
1440
+ idx_t max_nt = ivf.train_encoder_num_vectors();
1441
+ if (max_nt <= 0 || n <= max_nt) {
1442
+ max_nt = n;
1443
+ }
1444
+
1445
+ std::vector<float> decoded(static_cast<size_t>(max_nt) * d);
1446
+ if (max_nt == n) {
1447
+ decode_rows(n, x, decoded.data());
1448
+ } else {
1449
+ FAISS_THROW_IF_NOT_FMT(
1450
+ n <= static_cast<idx_t>(std::numeric_limits<int>::max()),
1451
+ "Dataset too large (%" PRId64 ") for standard subsampling",
1452
+ n);
1453
+ if (ivf.verbose) {
1454
+ printf(" Input training set too big (max size is %" PRId64
1455
+ "), sampling %" PRId64 " / %" PRId64 " vectors\n",
1456
+ max_nt,
1457
+ max_nt,
1458
+ n);
1459
+ }
1460
+ std::vector<int> subset(static_cast<size_t>(n));
1461
+ rand_perm(subset.data(), n, 1234);
1462
+ for (idx_t i = 0; i < max_nt; ++i) {
1463
+ decode_rows(
1464
+ 1,
1465
+ x + static_cast<size_t>(subset[i]) * row_size,
1466
+ decoded.data() + static_cast<size_t>(i) * d);
1467
+ }
1468
+ }
1469
+ n = max_nt;
1470
+
1471
+ if (ivf.by_residual) {
1472
+ FAISS_THROW_IF_NOT_MSG(ivf.quantizer, "IVF quantizer must not be null");
1473
+ std::vector<idx_t> assign(n);
1474
+ ivf.quantizer->assign(n, decoded.data(), assign.data());
1475
+
1476
+ std::vector<float> residuals(static_cast<size_t>(n) * d);
1477
+ ivf.quantizer->compute_residual_n(
1478
+ n, decoded.data(), residuals.data(), assign.data());
1479
+
1480
+ ivf.train_encoder(n, residuals.data(), assign.data());
1481
+ } else {
1482
+ ivf.train_encoder(n, decoded.data(), nullptr);
1483
+ }
1484
+ }
1485
+
1486
+ } // namespace
1487
+
1488
+ void IndexIVF::train_ex(idx_t n, const void* x, NumericType numeric_type) {
1489
+ if (numeric_type != NumericType::Float16) {
1490
+ Index::train_ex(n, x, numeric_type);
1491
+ return;
1492
+ }
1493
+ FAISS_THROW_IF_NOT_MSG(x, "training data must not be null");
1494
+ if (verbose) {
1495
+ printf("Training level-1 quantizer on fp16 data\n");
1496
+ }
1497
+
1498
+ train_q1_ex(n, x, numeric_type, verbose, metric_type);
1499
+
1500
+ if (verbose) {
1501
+ printf("Training IVF residual\n");
1502
+ }
1503
+
1504
+ const size_t dim = d;
1505
+ train_encoder_on_encoded_rows(
1506
+ *this,
1507
+ n,
1508
+ static_cast<const uint8_t*>(x),
1509
+ sizeof(uint16_t) * dim,
1510
+ [dim](idx_t ni, const uint8_t* rows, float* out) {
1511
+ fp16_to_fp32(
1512
+ ni * dim, reinterpret_cast<const uint16_t*>(rows), out);
1513
+ });
1514
+
1515
+ is_trained = true;
1516
+ }
1517
+
1518
+ void IndexIVF::train_encoded(idx_t n, const uint8_t* x, const Index* codec) {
1519
+ FAISS_THROW_IF_NOT_MSG(codec, "encoded training requires a codec");
1520
+ FAISS_THROW_IF_NOT_MSG(x, "encoded training data must not be null");
1521
+ if (verbose) {
1522
+ printf("Training level-1 quantizer\n");
1523
+ }
1524
+
1525
+ train_q1_encoded(n, x, codec, verbose, metric_type);
1526
+
1527
+ if (verbose) {
1528
+ printf("Training IVF residual\n");
1529
+ }
1530
+
1531
+ train_encoder_on_encoded_rows(
1532
+ *this,
1533
+ n,
1534
+ x,
1535
+ codec->sa_code_size(),
1536
+ [codec](idx_t ni, const uint8_t* rows, float* out) {
1537
+ codec->sa_decode(ni, rows, out);
1538
+ });
1539
+
1540
+ is_trained = true;
1541
+ }
1542
+
1311
1543
  void IndexIVF::train(idx_t n, const float* x) {
1312
1544
  if (verbose) {
1313
1545
  printf("Training level-1 quantizer\n");
@@ -1462,6 +1694,15 @@ size_t InvertedListScanner::scan_codes(
1462
1694
  return run_scan_codes(*this, list_size, codes, ids, handler);
1463
1695
  }
1464
1696
 
1697
+ void InvertedListScanner::distances_to_codes(
1698
+ size_t n,
1699
+ const uint8_t* codes,
1700
+ float* distances) const {
1701
+ for (size_t j = 0; j < n; j++) {
1702
+ distances[j] = distance_to_code(codes + j * code_size);
1703
+ }
1704
+ }
1705
+
1465
1706
  void InvertedListScanner::set_list(idx_t list_no_in, float /* coarse_dis */) {
1466
1707
  this->list_no = list_no_in;
1467
1708
  }
@@ -1498,6 +1739,9 @@ size_t InvertedListScanner::iterate_codes(
1498
1739
  if (!keep_max) {
1499
1740
  for (; it->is_available(); it->next()) {
1500
1741
  auto id_and_codes = it->get_id_and_codes();
1742
+ if (sel && !sel->is_member(id_and_codes.first)) {
1743
+ continue;
1744
+ }
1501
1745
  float dis = distance_to_code(id_and_codes.second);
1502
1746
  if (has_cb) {
1503
1747
  it->on_distance_computed(id_and_codes.first, dis);
@@ -1514,6 +1758,9 @@ size_t InvertedListScanner::iterate_codes(
1514
1758
  } else {
1515
1759
  for (; it->is_available(); it->next()) {
1516
1760
  auto id_and_codes = it->get_id_and_codes();
1761
+ if (sel && !sel->is_member(id_and_codes.first)) {
1762
+ continue;
1763
+ }
1517
1764
  float dis = distance_to_code(id_and_codes.second);
1518
1765
  if (has_cb) {
1519
1766
  it->on_distance_computed(id_and_codes.first, dis);
@@ -1561,6 +1808,9 @@ void InvertedListScanner::iterate_codes_range(
1561
1808
  list_size = 0;
1562
1809
  for (; it->is_available(); it->next()) {
1563
1810
  auto id_and_codes = it->get_id_and_codes();
1811
+ if (sel && !sel->is_member(id_and_codes.first)) {
1812
+ continue;
1813
+ }
1564
1814
  float dis = distance_to_code(id_and_codes.second);
1565
1815
  bool keep = !keep_max
1566
1816
  ? dis < radius
@@ -46,13 +46,41 @@ struct Level1Quantizer {
46
46
  /// to override index used during clustering
47
47
  Index* clustering_index = nullptr;
48
48
 
49
- /// Trains the quantizer and calls train_residual to train sub-quantizers
49
+ /// Trains the level-1 quantizer from fp32 vectors.
50
50
  void train_q1(
51
51
  size_t n,
52
52
  const float* x,
53
53
  bool verbose,
54
54
  MetricType metric_type);
55
55
 
56
+ /** Trains the level-1 quantizer from Float32 or packed Float16 vectors.
57
+ *
58
+ * Float16 runs k-means natively on the fp16 rows (see
59
+ * Clustering::train_ex). With quantizer_trains_alone == 1 the data is
60
+ * passed on to quantizer->train_ex(). SuperKMeans supports raw Float32
61
+ * and Float16 input, but not vectors decoded through a codec.
62
+ */
63
+ void train_q1_ex(
64
+ size_t n,
65
+ const void* x,
66
+ NumericType numeric_type,
67
+ bool verbose,
68
+ MetricType metric_type);
69
+
70
+ /** Trains the level-1 quantizer from vectors encoded by `codec`.
71
+ *
72
+ * The codec must remain alive for the duration of this call and decode
73
+ * each input row to `quantizer->d` floats. Encoded training is supported
74
+ * when clustering trains the quantizer (quantizer_trains_alone 0 or 2)
75
+ * without SuperKMeans.
76
+ */
77
+ void train_q1_encoded(
78
+ size_t n,
79
+ const uint8_t* x,
80
+ const Index* codec,
81
+ bool verbose,
82
+ MetricType metric_type);
83
+
56
84
  /// compute the number of bytes required to store list ids
57
85
  size_t coarse_code_size() const;
58
86
  void encode_listno(idx_t list_no, uint8_t* code) const;
@@ -236,6 +264,23 @@ struct IndexIVF : Index, IndexIVFInterface {
236
264
  /// Trains the quantizer and calls train_encoder to train sub-quantizers
237
265
  void train(idx_t n, const float* x) override;
238
266
 
267
+ /** Train from vectors decoded on demand by `codec`.
268
+ *
269
+ * Coarse quantizer training decodes in bounded batches. Subclasses with a
270
+ * trainable encoder decode at most train_encoder_num_vectors() rows into
271
+ * fp32; IndexIVFFlat overrides this method and needs no fp32 corpus buffer.
272
+ */
273
+ virtual void train_encoded(idx_t n, const uint8_t* x, const Index* codec);
274
+
275
+ /** Supports Float32 and packed IEEE binary16 (Float16) inputs.
276
+ *
277
+ * Float16 trains natively without a codec: the coarse quantizer runs
278
+ * fp16 k-means (Clustering::train_ex) and only the
279
+ * train_encoder_num_vectors() rows sampled for the encoder are widened
280
+ * to fp32. IndexIVFFlat overrides this and widens no rows at all.
281
+ */
282
+ void train_ex(idx_t n, const void* x, NumericType numeric_type) override;
283
+
239
284
  /// Calls add_with_ids with NULL ids
240
285
  void add(idx_t n, const float* x) override;
241
286
 
@@ -521,6 +566,13 @@ struct InvertedListScanner {
521
566
  /// compute a single query-to-code distance
522
567
  virtual float distance_to_code(const uint8_t* code) const = 0;
523
568
 
569
+ /** compute the distances to n consecutive codes of code_size bytes. A
570
+ * distance can differ in the last bits from distance_to_code. */
571
+ virtual void distances_to_codes(
572
+ size_t n,
573
+ const uint8_t* codes,
574
+ float* distances) const;
575
+
524
576
  /** scan a set of codes, compute distances to current query, and
525
577
  * update heap of results if necessary. Default implementation
526
578
  * calls distance_to_code.