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
@@ -13,11 +13,15 @@
13
13
  #include <limits>
14
14
  #include <random>
15
15
  #include <unordered_set>
16
+ #include <utility>
16
17
  #include <vector>
17
18
 
19
+ #include <faiss/impl/ClusteringHelpers.h>
18
20
  #include <faiss/impl/FaissAssert.h>
21
+ #include <faiss/impl/simd_dispatch.h>
19
22
  #include <faiss/utils/distances_dispatch.h>
20
23
  #include <faiss/utils/random.h>
24
+ #include <faiss/utils/simd_impl/fp16_kernels.h>
21
25
 
22
26
  namespace faiss {
23
27
 
@@ -32,37 +36,90 @@ uint64_t get_seed(int64_t seed) {
32
36
  .count());
33
37
  }
34
38
 
39
+ struct FloatRows {
40
+ static constexpr bool needs_scratch = false;
41
+ static constexpr bool has_fixed_simd_level = false;
42
+
43
+ size_t d;
44
+ const float* x;
45
+
46
+ const float* row(size_t idx, float*) const {
47
+ return x + idx * d;
48
+ }
49
+
50
+ void copy_row(size_t idx, float* out) const {
51
+ std::memcpy(out, x + idx * d, d * sizeof(float));
52
+ }
53
+ };
54
+
55
+ template <SIMDLevel SL>
56
+ struct Fp16Rows {
57
+ static constexpr bool needs_scratch = true;
58
+ static constexpr bool has_fixed_simd_level = true;
59
+ static constexpr SIMDLevel simd_level = SL;
60
+
61
+ size_t d;
62
+ const uint16_t* x;
63
+
64
+ const float* row(size_t idx, float* scratch) const {
65
+ detail::fp16_to_fp32_kernel<SL>(d, x + idx * d, scratch);
66
+ return scratch;
67
+ }
68
+
69
+ void copy_row(size_t idx, float* out) const {
70
+ detail::fp16_to_fp32_kernel<SL>(d, x + idx * d, out);
71
+ }
72
+
73
+ void copy_rows(size_t idx, size_t count, float* out) const {
74
+ detail::fp16_to_fp32_kernel<SL>(count * d, x + idx * d, out);
75
+ }
76
+ };
77
+
78
+ template <typename Rows, typename Action>
79
+ auto with_rows_simd_level(Action&& action) {
80
+ if constexpr (Rows::has_fixed_simd_level) {
81
+ return action.template operator()<Rows::simd_level>();
82
+ } else {
83
+ return with_simd_level(std::forward<Action>(action));
84
+ }
85
+ }
86
+
87
+ template <typename Rows>
88
+ std::vector<float> row_scratch(size_t d) {
89
+ return std::vector<float>(Rows::needs_scratch ? d : 0);
90
+ }
91
+
35
92
  /// Compute distance from point idx to its nearest centroid.
36
93
  /// Optionally checks both primary and secondary centroid sets.
94
+ template <typename Rows>
37
95
  float distance_to_nearest_centroid(
38
- size_t d,
96
+ const Rows& rows,
39
97
  size_t n_centroids,
40
- const float* x,
41
98
  size_t idx,
42
99
  const float* centroids,
43
- size_t n_existing_centroids = 0,
44
- const float* existing_centroids = nullptr) {
100
+ size_t n_existing_centroids,
101
+ const float* existing_centroids,
102
+ float* scratch) {
45
103
  if (n_centroids == 0 && n_existing_centroids == 0) {
46
104
  return std::numeric_limits<float>::infinity();
47
105
  }
48
106
 
49
- const float* point = x + idx * d;
107
+ const float* point = rows.row(idx, scratch);
50
108
  float min_dist = std::numeric_limits<float>::max();
51
109
 
52
110
  auto check_centroids = [&]<SIMDLevel SL>() {
53
- // Check primary centroids
54
111
  for (size_t c = 0; c < n_centroids; c++) {
55
- float dist = fvec_L2sqr<SL>(point, centroids + c * d, d);
112
+ float dist = fvec_L2sqr<SL>(point, centroids + c * rows.d, rows.d);
56
113
  min_dist = std::min(min_dist, dist);
57
114
  }
58
115
 
59
- // Check existing centroids if provided
60
116
  for (size_t c = 0; c < n_existing_centroids; c++) {
61
- float dist = fvec_L2sqr<SL>(point, existing_centroids + c * d, d);
117
+ float dist = fvec_L2sqr<SL>(
118
+ point, existing_centroids + c * rows.d, rows.d);
62
119
  min_dist = std::min(min_dist, dist);
63
120
  }
64
121
  };
65
- with_simd_level(check_centroids);
122
+ with_rows_simd_level<Rows>(check_centroids);
66
123
  return min_dist;
67
124
  }
68
125
 
@@ -78,10 +135,10 @@ struct InitDistancesResult {
78
135
  /// Otherwise, selects first centroid randomly and computes distances to it.
79
136
  /// Returns first_new_centroid_idx (0 if existing, 1 if random first),
80
137
  /// sum of squared distances, and the first selected index (if applicable).
138
+ template <typename Rows>
81
139
  InitDistancesResult init_distances_for_d2_sampling(
82
- size_t d,
83
140
  size_t n,
84
- const float* x,
141
+ const Rows& rows,
85
142
  float* centroids,
86
143
  size_t n_existing_centroids,
87
144
  const float* existing_centroids,
@@ -89,25 +146,30 @@ InitDistancesResult init_distances_for_d2_sampling(
89
146
  std::mt19937_64& rng) {
90
147
  double sum_d2 = 0.0;
91
148
  size_t first_selected_idx = 0;
149
+ auto scratch = row_scratch<Rows>(rows.d);
92
150
 
93
151
  if (n_existing_centroids > 0 && existing_centroids != nullptr) {
94
- // Compute distances to nearest existing centroid
95
152
  for (size_t i = 0; i < n; i++) {
96
153
  distances[i] = distance_to_nearest_centroid(
97
- d, n_existing_centroids, x, i, existing_centroids);
154
+ rows,
155
+ n_existing_centroids,
156
+ i,
157
+ existing_centroids,
158
+ 0,
159
+ nullptr,
160
+ scratch.data());
98
161
  sum_d2 += distances[i];
99
162
  }
100
163
  return {0, sum_d2, 0};
101
164
  } else {
102
- // Select first centroid randomly
103
165
  std::uniform_int_distribution<size_t> uniform_dist(0, n - 1);
104
166
  first_selected_idx = uniform_dist(rng);
105
- std::memcpy(centroids, x + first_selected_idx * d, d * sizeof(float));
167
+ rows.copy_row(first_selected_idx, centroids);
106
168
 
107
- // Compute distances to first centroid
108
- with_simd_level([&]<SIMDLevel SL>() {
169
+ with_rows_simd_level<Rows>([&]<SIMDLevel SL>() {
109
170
  for (size_t i = 0; i < n; i++) {
110
- distances[i] = fvec_L2sqr<SL>(x + i * d, centroids, d);
171
+ const float* point = rows.row(i, scratch.data());
172
+ distances[i] = fvec_L2sqr<SL>(point, centroids, rows.d);
111
173
  sum_d2 += distances[i];
112
174
  }
113
175
  });
@@ -140,138 +202,97 @@ size_t sample_from_cumsum(
140
202
  return std::min(idx, n - 1);
141
203
  }
142
204
 
143
- } // namespace
144
-
145
- ClusteringInitialization::ClusteringInitialization(size_t d_in, size_t k_in)
146
- : d(d_in), k(k_in) {}
147
-
148
- void ClusteringInitialization::init_centroids(
149
- size_t n,
150
- const float* x,
151
- float* centroids,
152
- size_t n_existing_centroids,
153
- const float* existing_centroids) const {
154
- FAISS_THROW_IF_NOT_FMT(
155
- n >= k,
156
- "Number of points (%zu) must be >= number of centroids (%zu)",
157
- n,
158
- k);
159
- FAISS_THROW_IF_NOT(d > 0);
160
- FAISS_THROW_IF_NOT(x);
161
- FAISS_THROW_IF_NOT(centroids);
162
- FAISS_THROW_IF_NOT(
163
- n_existing_centroids == 0 || existing_centroids != nullptr);
164
-
165
- switch (method) {
166
- case ClusteringInitMethod::RANDOM:
167
- init_random(n, x, centroids);
168
- break;
169
- case ClusteringInitMethod::KMEANS_PLUS_PLUS:
170
- init_kmeans_plus_plus(
171
- n, x, centroids, n_existing_centroids, existing_centroids);
172
- break;
173
- case ClusteringInitMethod::AFK_MC2:
174
- init_afkmc2(
175
- n, x, centroids, n_existing_centroids, existing_centroids);
176
- break;
177
- default:
178
- FAISS_THROW_MSG("Unknown initialization method");
179
- }
180
- }
181
-
182
- void ClusteringInitialization::init_random(
205
+ template <typename Rows>
206
+ void init_kmeans_plus_plus_impl(
207
+ const ClusteringInitialization& initializer,
183
208
  size_t n,
184
- const float* x,
185
- float* centroids) const {
186
- // Use rand_perm for backward compatibility with Clustering.cpp
187
- // This ensures the same random sequence as the original implementation
188
- std::vector<int> perm(n);
189
- rand_perm(perm.data(), n, seed);
190
-
191
- // Copy selected points to centroids
192
- for (size_t i = 0; i < k; i++) {
193
- std::memcpy(centroids + i * d, x + perm[i] * d, d * sizeof(float));
194
- }
195
- }
196
-
197
- void ClusteringInitialization::init_kmeans_plus_plus(
198
- size_t n,
199
- const float* x,
209
+ const Rows& rows,
200
210
  float* centroids,
201
211
  size_t n_existing_centroids,
202
- const float* existing_centroids) const {
203
- std::mt19937_64 rng(get_seed(seed));
212
+ const float* existing_centroids) {
213
+ std::mt19937_64 rng(get_seed(initializer.seed));
204
214
 
205
215
  std::vector<double> min_distances(n);
206
216
  auto result = init_distances_for_d2_sampling(
207
- d,
208
217
  n,
209
- x,
218
+ rows,
210
219
  centroids,
211
220
  n_existing_centroids,
212
221
  existing_centroids,
213
222
  min_distances,
214
223
  rng);
215
224
 
216
- if (result.first_new_centroid_idx == 1 && k == 1) {
225
+ if (result.first_new_centroid_idx == 1 && initializer.k == 1) {
217
226
  return;
218
227
  }
219
228
 
220
- // Reusable buffer for cumulative sum
221
229
  std::vector<double> cumsum(n);
222
230
 
223
- // Select remaining centroids using D² sampling
224
- with_simd_level([&]<SIMDLevel SL>() {
225
- for (size_t c = result.first_new_centroid_idx; c < k; c++) {
226
- // Compute cumulative sum
231
+ with_rows_simd_level<Rows>([&]<SIMDLevel SL>() {
232
+ for (size_t c = result.first_new_centroid_idx; c < initializer.k; c++) {
227
233
  cumsum[0] = min_distances[0];
228
234
  for (size_t i = 1; i < n; i++) {
229
235
  cumsum[i] = cumsum[i - 1] + min_distances[i];
230
236
  }
231
237
 
232
- // Sample using precomputed cumsum
233
238
  size_t next_idx = sample_from_cumsum(cumsum, rng);
234
239
 
235
- float* new_centroid = centroids + c * d;
236
- std::memcpy(new_centroid, x + next_idx * d, d * sizeof(float));
237
-
238
- // Update min distances incrementally. The writes are independent,
239
- // so the loop parallelizes without changing the output.
240
- const int64_t ni = static_cast<int64_t>(n);
240
+ float* new_centroid = centroids + c * rows.d;
241
+ rows.copy_row(next_idx, new_centroid);
242
+
243
+ if constexpr (Rows::needs_scratch) {
244
+ constexpr size_t kRowsPerBlock = 64;
245
+ const int64_t nblocks = static_cast<int64_t>(
246
+ (n + kRowsPerBlock - 1) / kRowsPerBlock);
247
+ #pragma omp parallel
248
+ {
249
+ std::vector<float> block(kRowsPerBlock * rows.d);
250
+ #pragma omp for
251
+ for (int64_t b = 0; b < nblocks; b++) {
252
+ const size_t first =
253
+ static_cast<size_t>(b) * kRowsPerBlock;
254
+ const size_t count = std::min(kRowsPerBlock, n - first);
255
+ rows.copy_rows(first, count, block.data());
256
+ for (size_t j = 0; j < count; j++) {
257
+ double dist = fvec_L2sqr<SL>(
258
+ block.data() + j * rows.d,
259
+ new_centroid,
260
+ rows.d);
261
+ min_distances[first + j] =
262
+ std::min(min_distances[first + j], dist);
263
+ }
264
+ }
265
+ }
266
+ } else {
267
+ const int64_t ni = static_cast<int64_t>(n);
241
268
  #pragma omp parallel for
242
- for (int64_t i = 0; i < ni; i++) {
243
- double dist = fvec_L2sqr<SL>(x + i * d, new_centroid, d);
244
- min_distances[i] = std::min(min_distances[i], dist);
269
+ for (int64_t i = 0; i < ni; i++) {
270
+ double dist = fvec_L2sqr<SL>(
271
+ rows.row(i, nullptr), new_centroid, rows.d);
272
+ min_distances[i] = std::min(min_distances[i], dist);
273
+ }
245
274
  }
246
275
  }
247
276
  });
248
277
  }
249
278
 
250
- void ClusteringInitialization::init_afkmc2(
279
+ template <typename Rows>
280
+ void init_afkmc2_impl(
281
+ const ClusteringInitialization& initializer,
251
282
  size_t n,
252
- const float* x,
283
+ const Rows& rows,
253
284
  float* centroids,
254
285
  size_t n_existing_centroids,
255
- const float* existing_centroids) const {
256
- // AFK-MC² (Assumption-Free K-MC²) algorithm:
257
- // Reference: Bachem et al., "Fast and Provably Good Seedings for
258
- // k-Means"
259
-
260
- std::mt19937_64 rng(get_seed(seed));
286
+ const float* existing_centroids) {
287
+ std::mt19937_64 rng(get_seed(initializer.seed));
261
288
  std::uniform_real_distribution<double> uniform_01(0.0, 1.0);
262
289
 
263
- // Track selected centroids to prevent duplicates
264
290
  std::unordered_set<size_t> selected_centroids;
265
291
 
266
- // Compute proposal distribution q(x)
267
- // If existing centroids: base q on distance to nearest existing
268
- // centroid Otherwise: select first centroid randomly and base q on
269
- // it
270
292
  std::vector<double> dist_to_nearest(n);
271
293
  auto result = init_distances_for_d2_sampling(
272
- d,
273
294
  n,
274
- x,
295
+ rows,
275
296
  centroids,
276
297
  n_existing_centroids,
277
298
  existing_centroids,
@@ -280,12 +301,11 @@ void ClusteringInitialization::init_afkmc2(
280
301
 
281
302
  if (result.first_new_centroid_idx == 1) {
282
303
  selected_centroids.insert(result.first_selected_idx);
283
- if (k == 1) {
304
+ if (initializer.k == 1) {
284
305
  return;
285
306
  }
286
307
  }
287
308
 
288
- // Compute q(x) and cumulative sum in a single pass
289
309
  std::vector<double> q(n);
290
310
  std::vector<double> q_cumsum(n);
291
311
  double uniform_term = 0.5 / static_cast<double>(n);
@@ -298,55 +318,42 @@ void ClusteringInitialization::init_afkmc2(
298
318
  q_cumsum[i] = (i > 0 ? q_cumsum[i - 1] : 0.0) + q[i];
299
319
  }
300
320
 
301
- // Main loop: Select remaining centroids using MCMC
302
- for (size_t c = result.first_new_centroid_idx; c < k; c++) {
303
- // Sample initial candidate from proposal distribution q, skip
304
- // duplicates
321
+ auto scratch = row_scratch<Rows>(rows.d);
322
+ for (size_t c = result.first_new_centroid_idx; c < initializer.k; c++) {
305
323
  size_t current_idx;
306
324
  do {
307
325
  current_idx = sample_from_cumsum(q_cumsum, rng);
308
326
  } while (selected_centroids.count(current_idx) > 0);
309
327
 
310
- // Compute distance to nearest centroid (existing + newly
311
- // selected)
312
328
  double current_dist = distance_to_nearest_centroid(
313
- d,
329
+ rows,
314
330
  c,
315
- x,
316
331
  current_idx,
317
332
  centroids,
318
333
  n_existing_centroids,
319
- existing_centroids);
334
+ existing_centroids,
335
+ scratch.data());
320
336
  double current_q = q[current_idx];
321
337
 
322
- // Run Markov chain
323
- for (size_t m = 0; m < afkmc2_chain_length; m++) {
324
- // Sample proposal from q
338
+ for (size_t m = 0; m < initializer.afkmc2_chain_length; m++) {
325
339
  size_t proposed_idx = sample_from_cumsum(q_cumsum, rng);
326
340
 
327
- // Skip duplicates before expensive distance computation
328
341
  if (selected_centroids.count(proposed_idx) > 0) {
329
342
  continue;
330
343
  }
331
344
 
332
- // Compute distance to nearest centroid (existing + newly
333
- // selected)
334
345
  double proposed_dist = distance_to_nearest_centroid(
335
- d,
346
+ rows,
336
347
  c,
337
- x,
338
348
  proposed_idx,
339
349
  centroids,
340
350
  n_existing_centroids,
341
- existing_centroids);
351
+ existing_centroids,
352
+ scratch.data());
342
353
  double proposed_q = q[proposed_idx];
343
354
 
344
- // Metropolis-Hastings acceptance ratio:
345
- // accept = min(1, d(y,C)² · q(x) / (d(x,C)² · q(y)))
346
355
  double acceptance_prob = 0.0;
347
356
  if (current_dist <= 0) {
348
- // Current point is a centroid (distance = 0), never
349
- // leave
350
357
  acceptance_prob = 0.0;
351
358
  } else if (proposed_q > 0) {
352
359
  double numerator = proposed_dist * current_q;
@@ -361,10 +368,153 @@ void ClusteringInitialization::init_afkmc2(
361
368
  }
362
369
  }
363
370
 
364
- // Use final chain state as new centroid
365
371
  selected_centroids.insert(current_idx);
366
- std::memcpy(centroids + c * d, x + current_idx * d, d * sizeof(float));
372
+ rows.copy_row(current_idx, centroids + c * rows.d);
367
373
  }
368
374
  }
369
375
 
376
+ void validate_initialization(
377
+ const ClusteringInitialization& initializer,
378
+ size_t n,
379
+ const void* x,
380
+ float* centroids,
381
+ size_t n_existing_centroids,
382
+ const float* existing_centroids) {
383
+ FAISS_THROW_IF_NOT_FMT(
384
+ n >= initializer.k,
385
+ "Number of points (%zu) must be >= number of centroids (%zu)",
386
+ n,
387
+ initializer.k);
388
+ FAISS_THROW_IF_NOT(initializer.d > 0);
389
+ FAISS_THROW_IF_NOT(x);
390
+ FAISS_THROW_IF_NOT(centroids);
391
+ FAISS_THROW_IF_NOT(
392
+ n_existing_centroids == 0 || existing_centroids != nullptr);
393
+ }
394
+
395
+ } // namespace
396
+
397
+ ClusteringInitialization::ClusteringInitialization(size_t d_in, size_t k_in)
398
+ : d(d_in), k(k_in) {}
399
+
400
+ void ClusteringInitialization::init_centroids(
401
+ size_t n,
402
+ const float* x,
403
+ float* centroids,
404
+ size_t n_existing_centroids,
405
+ const float* existing_centroids) const {
406
+ validate_initialization(
407
+ *this, n, x, centroids, n_existing_centroids, existing_centroids);
408
+
409
+ switch (method) {
410
+ case ClusteringInitMethod::RANDOM:
411
+ init_random(n, x, centroids);
412
+ break;
413
+ case ClusteringInitMethod::KMEANS_PLUS_PLUS:
414
+ init_kmeans_plus_plus(
415
+ n, x, centroids, n_existing_centroids, existing_centroids);
416
+ break;
417
+ case ClusteringInitMethod::AFK_MC2:
418
+ init_afkmc2(
419
+ n, x, centroids, n_existing_centroids, existing_centroids);
420
+ break;
421
+ default:
422
+ FAISS_THROW_MSG("Unknown initialization method");
423
+ }
424
+ }
425
+
426
+ void ClusteringInitialization::init_random(
427
+ size_t n,
428
+ const float* x,
429
+ float* centroids) const {
430
+ // Use rand_perm for backward compatibility with Clustering.cpp
431
+ // This ensures the same random sequence as the original implementation
432
+ std::vector<int> perm(n);
433
+ rand_perm(perm.data(), n, seed);
434
+
435
+ // Copy selected points to centroids
436
+ for (size_t i = 0; i < k; i++) {
437
+ std::memcpy(centroids + i * d, x + perm[i] * d, d * sizeof(float));
438
+ }
439
+ }
440
+
441
+ void ClusteringInitialization::init_kmeans_plus_plus(
442
+ size_t n,
443
+ const float* x,
444
+ float* centroids,
445
+ size_t n_existing_centroids,
446
+ const float* existing_centroids) const {
447
+ init_kmeans_plus_plus_impl(
448
+ *this,
449
+ n,
450
+ FloatRows{d, x},
451
+ centroids,
452
+ n_existing_centroids,
453
+ existing_centroids);
454
+ }
455
+
456
+ void ClusteringInitialization::init_afkmc2(
457
+ size_t n,
458
+ const float* x,
459
+ float* centroids,
460
+ size_t n_existing_centroids,
461
+ const float* existing_centroids) const {
462
+ init_afkmc2_impl(
463
+ *this,
464
+ n,
465
+ FloatRows{d, x},
466
+ centroids,
467
+ n_existing_centroids,
468
+ existing_centroids);
469
+ }
470
+
471
+ namespace detail {
472
+
473
+ void init_centroids_fp16(
474
+ const ClusteringInitialization& initializer,
475
+ size_t n,
476
+ const uint16_t* x,
477
+ float* centroids,
478
+ size_t n_existing_centroids,
479
+ const float* existing_centroids) {
480
+ validate_initialization(
481
+ initializer,
482
+ n,
483
+ x,
484
+ centroids,
485
+ n_existing_centroids,
486
+ existing_centroids);
487
+
488
+ with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE>([&]<SIMDLevel SL>() {
489
+ switch (initializer.method) {
490
+ case ClusteringInitMethod::KMEANS_PLUS_PLUS:
491
+ init_kmeans_plus_plus_impl(
492
+ initializer,
493
+ n,
494
+ Fp16Rows<SL>{initializer.d, x},
495
+ centroids,
496
+ n_existing_centroids,
497
+ existing_centroids);
498
+ break;
499
+ case ClusteringInitMethod::AFK_MC2:
500
+ init_afkmc2_impl(
501
+ initializer,
502
+ n,
503
+ Fp16Rows<SL>{initializer.d, x},
504
+ centroids,
505
+ n_existing_centroids,
506
+ existing_centroids);
507
+ break;
508
+ case ClusteringInitMethod::RANDOM:
509
+ FAISS_THROW_MSG(
510
+ "fp16 centroid helper requires non-random "
511
+ "initialization");
512
+ default:
513
+ FAISS_THROW_MSG("Unknown initialization method");
514
+ }
515
+ });
516
+ }
517
+
518
+ } // namespace detail
519
+
370
520
  } // namespace faiss
@@ -0,0 +1,63 @@
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/impl/DistanceComputer.h>
9
+
10
+ #include <faiss/impl/FaissAssert.h>
11
+
12
+ namespace faiss {
13
+
14
+ void DistanceComputer::distances_batch_4(
15
+ const idx_t idx0,
16
+ const idx_t idx1,
17
+ const idx_t idx2,
18
+ const idx_t idx3,
19
+ float& dis0,
20
+ float& dis1,
21
+ float& dis2,
22
+ float& dis3) {
23
+ // compute first, assign next
24
+ const float d0 = this->operator()(idx0);
25
+ const float d1 = this->operator()(idx1);
26
+ const float d2 = this->operator()(idx2);
27
+ const float d3 = this->operator()(idx3);
28
+ dis0 = d0;
29
+ dis1 = d1;
30
+ dis2 = d2;
31
+ dis3 = d3;
32
+ }
33
+
34
+ float FlatCodesDistanceComputer::partial_dot_product(
35
+ const idx_t /* i */,
36
+ const uint32_t /* offset */,
37
+ const uint32_t /* num_components */) {
38
+ FAISS_THROW_MSG("partial_dot_product not implemented");
39
+ }
40
+
41
+ void FlatCodesDistanceComputer::partial_dot_product_batch_4(
42
+ const idx_t idx0,
43
+ const idx_t idx1,
44
+ const idx_t idx2,
45
+ const idx_t idx3,
46
+ float& dp0,
47
+ float& dp1,
48
+ float& dp2,
49
+ float& dp3,
50
+ const uint32_t offset,
51
+ const uint32_t num_components) {
52
+ // default implementation for correctness
53
+ const float d0 = this->partial_dot_product(idx0, offset, num_components);
54
+ const float d1 = this->partial_dot_product(idx1, offset, num_components);
55
+ const float d2 = this->partial_dot_product(idx2, offset, num_components);
56
+ const float d3 = this->partial_dot_product(idx3, offset, num_components);
57
+ dp0 = d0;
58
+ dp1 = d1;
59
+ dp2 = d2;
60
+ dp3 = d3;
61
+ }
62
+
63
+ } // namespace faiss