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,587 @@
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/pipnn/Partition.h>
9
+
10
+ #include <omp.h>
11
+
12
+ #include <algorithm>
13
+ #include <atomic>
14
+ #include <cmath>
15
+ #include <exception>
16
+ #include <iterator>
17
+ #include <limits>
18
+ #include <memory>
19
+ #include <utility>
20
+
21
+ #include <faiss/Index.h>
22
+ #include <faiss/impl/AuxIndexStructures.h>
23
+ #include <faiss/impl/FaissAssert.h>
24
+ #include <faiss/impl/FaissException.h>
25
+ #include <faiss/impl/pipnn/kernels.h>
26
+ #include <faiss/impl/platform_macros.h>
27
+ #include <faiss/utils/AlignedTable.h>
28
+ #include <faiss/utils/distances.h>
29
+ #include <faiss/utils/random.h>
30
+
31
+ namespace faiss {
32
+ namespace pipnn {
33
+
34
+ int PartitionParams::fanout_at(int depth) const {
35
+ FAISS_ASSERT(depth >= 0);
36
+ return static_cast<size_t>(depth) < fanout.size() ? fanout[depth] : 1;
37
+ }
38
+
39
+ uint64_t mix_seed(uint64_t a, uint64_t b) {
40
+ return SplitMix64RandomGenerator(int64_t(a ^ (b + (a << 6) + (a >> 2))))
41
+ .next();
42
+ }
43
+
44
+ namespace detail {
45
+
46
+ size_t leader_count(size_t size, const PartitionParams& p, int depth) {
47
+ size_t by_fraction = static_cast<size_t>(std::ceil(
48
+ static_cast<double>(p.leader_fraction) *
49
+ static_cast<double>(size)));
50
+ size_t floor4 = 4 * static_cast<size_t>(p.fanout_at(depth));
51
+ size_t count = std::max(by_fraction, floor4);
52
+ count = std::min(count, static_cast<size_t>(p.leader_cap));
53
+ return std::min(count, size);
54
+ }
55
+
56
+ void sample_positions(
57
+ size_t N,
58
+ size_t k,
59
+ uint64_t seed,
60
+ std::vector<size_t>& out) {
61
+ FAISS_ASSERT(k <= N);
62
+ out.clear();
63
+ out.reserve(k);
64
+ SplitMix64RandomGenerator rng(static_cast<int64_t>(seed));
65
+ for (size_t j = N - k; j < N; j++) {
66
+ size_t t = static_cast<size_t>(
67
+ rng.next() % (static_cast<uint64_t>(j) + 1));
68
+ auto it = std::lower_bound(out.begin(), out.end(), t);
69
+ if (it != out.end() && *it == t) {
70
+ out.push_back(j); // every sampled position is < j
71
+ } else {
72
+ out.insert(it, t);
73
+ }
74
+ }
75
+ }
76
+
77
+ Ranking ranking_for(const PartitionParams& p, bool force_l2) {
78
+ if (p.metric == METRIC_L2 || force_l2) {
79
+ return Ranking::L2;
80
+ }
81
+ return p.ip_partition_by_angle ? Ranking::Angle : Ranking::IP;
82
+ }
83
+
84
+ } // namespace detail
85
+
86
+ namespace {
87
+
88
+ /// Positions per counting-sort block; unlike kStripe, it does not affect
89
+ /// leaves.
90
+ constexpr size_t kScatterBlock = 65536;
91
+ /// Above any leader index, so the merge-order seed never equals a child's.
92
+ constexpr uint64_t kMergeShuffleIndex = uint64_t(1) << 33;
93
+
94
+ constexpr size_t kNoSplit = std::numeric_limits<size_t>::max();
95
+
96
+ /// `ids` is sorted and owned by a LevelStorage (or the root list).
97
+ struct Node {
98
+ const int32_t* ids;
99
+ size_t size;
100
+ uint64_t seed;
101
+ int depth;
102
+ bool force_l2; // L2 ranking: this or an ancestor was a > 90% IP child
103
+ };
104
+
105
+ /// Owns the id lists of every node of one level.
106
+ struct LevelStorage {
107
+ std::unique_ptr<int32_t[]> child_ids;
108
+ std::vector<std::vector<int32_t>> merge_pools;
109
+ };
110
+
111
+ /// Inputs and stats shared by every level of one partition.
112
+ struct LevelContext {
113
+ const Index& storage;
114
+ const PartitionParams& p;
115
+ const LeafVisitor& on_leaf;
116
+ PartitionStats& stats;
117
+ };
118
+
119
+ /// A leaf (split == kNoSplit) or kStripe points of a split node from `begin`.
120
+ struct WorkItem {
121
+ size_t node;
122
+ size_t split;
123
+ size_t begin;
124
+ };
125
+
126
+ struct SplitInfo {
127
+ size_t node = 0;
128
+ std::vector<int32_t> leaders; // ascending ids
129
+ int fanout = 0;
130
+ Ranking ranking = Ranking::L2;
131
+ size_t offset = 0; // start in the level assignment / child-id buffers
132
+ size_t n_blocks = 0;
133
+ size_t count_offset = 0; // start in the level count buffer
134
+ std::vector<size_t> child_off; // |L| + 1 offsets into the child-id buffer
135
+ };
136
+
137
+ struct LevelPlan {
138
+ std::vector<WorkItem> items;
139
+ std::vector<SplitInfo> splits;
140
+ size_t n_instances = 0; // (point, chosen leader) pairs of all splits
141
+ size_t n_counts = 0; // entries of the count buffer
142
+ };
143
+
144
+ /// Next-level nodes of one split. Merged groups have ids == nullptr until
145
+ /// collect_children points them into their pool.
146
+ struct SplitOutput {
147
+ std::vector<Node> nodes;
148
+ std::vector<size_t> pool_offsets;
149
+ std::vector<int32_t> pool;
150
+ size_t n_slices = 0;
151
+ size_t n_l2 = 0;
152
+ };
153
+
154
+ void check_params(const Index& storage, const PartitionParams& p) {
155
+ FAISS_THROW_IF_NOT_MSG(
156
+ p.c_min >= 1 && p.c_min <= p.c_max,
157
+ "PiPNN partition: need 1 <= c_min <= c_max");
158
+ FAISS_THROW_IF_NOT_FMT(
159
+ p.c_max <= kMaxCMax,
160
+ "PiPNN partition: need c_max <= %d (got %d): each thread holds a "
161
+ "c_max x c_max float matrix",
162
+ kMaxCMax,
163
+ p.c_max);
164
+ FAISS_THROW_IF_NOT_FMT(
165
+ p.leader_cap >= 1 && p.leader_cap <= kMaxLeaderCap,
166
+ "PiPNN partition: need 1 <= leader_cap <= %d",
167
+ kMaxLeaderCap);
168
+ FAISS_THROW_IF_NOT_MSG(
169
+ p.leader_fraction >= 0 && p.leader_fraction <= 1,
170
+ "PiPNN partition: need 0 <= leader_fraction <= 1");
171
+ for (int f : p.fanout) {
172
+ FAISS_THROW_IF_NOT_MSG(
173
+ f >= 1, "PiPNN partition: every fanout must be >= 1");
174
+ }
175
+ FAISS_THROW_IF_NOT_MSG(
176
+ p.metric == METRIC_L2 || p.metric == METRIC_INNER_PRODUCT,
177
+ "PiPNN partition: metric must be L2 or inner product");
178
+ FAISS_THROW_IF_NOT_MSG(
179
+ storage.ntotal >= 0 &&
180
+ storage.ntotal <= std::numeric_limits<int32_t>::max(),
181
+ "PiPNN partition: need 0 <= n <= INT32_MAX");
182
+ }
183
+
184
+ void record_leaf(PartitionStats& stats, size_t size, int depth) {
185
+ stats.n_leaves++;
186
+ stats.n_point_instances += size;
187
+ stats.max_leaf = std::max(stats.max_leaf, size);
188
+ stats.max_depth_seen = std::max(stats.max_depth_seen, depth);
189
+ const size_t bucket = (size - 1) / 64;
190
+ if (stats.leaf_size_histogram.size() <= bucket) {
191
+ stats.leaf_size_histogram.resize(bucket + 1, 0);
192
+ }
193
+ stats.leaf_size_histogram[bucket]++;
194
+ }
195
+
196
+ void push_slices(const Node& child, size_t c_max, SplitOutput& out) {
197
+ for (size_t o = 0; o < child.size; o += c_max) {
198
+ out.nodes.push_back(
199
+ Node{child.ids + o,
200
+ std::min(c_max, child.size - o),
201
+ child.seed,
202
+ child.depth,
203
+ child.force_l2});
204
+ }
205
+ }
206
+
207
+ LevelPlan plan_level(const LevelContext& ctx, const std::vector<Node>& nodes) {
208
+ const PartitionParams& p = ctx.p;
209
+ const size_t c_max = static_cast<size_t>(p.c_max);
210
+ size_t n_items = 0;
211
+ size_t n_splits = 0;
212
+ for (const Node& nd : nodes) {
213
+ if (nd.size <= c_max) {
214
+ n_items++;
215
+ } else {
216
+ n_items += (nd.size + kStripe - 1) / kStripe;
217
+ n_splits++;
218
+ }
219
+ }
220
+ LevelPlan plan;
221
+ plan.splits.reserve(n_splits);
222
+ plan.items.reserve(n_items);
223
+ for (size_t i = 0; i < nodes.size(); i++) {
224
+ const Node& nd = nodes[i];
225
+ if (nd.size <= c_max) {
226
+ record_leaf(ctx.stats, nd.size, nd.depth);
227
+ plan.items.push_back(WorkItem{i, kNoSplit, 0});
228
+ continue;
229
+ }
230
+ SplitInfo sp;
231
+ sp.node = i;
232
+ const size_t n_leaders = detail::leader_count(nd.size, p, nd.depth);
233
+ std::vector<size_t> positions;
234
+ detail::sample_positions(nd.size, n_leaders, nd.seed, positions);
235
+ sp.leaders.resize(n_leaders);
236
+ for (size_t l = 0; l < n_leaders; l++) {
237
+ sp.leaders[l] = nd.ids[positions[l]];
238
+ }
239
+ sp.fanout =
240
+ std::min(p.fanout_at(nd.depth), static_cast<int>(n_leaders));
241
+ sp.ranking = detail::ranking_for(p, nd.force_l2);
242
+ sp.offset = plan.n_instances;
243
+ plan.n_instances += nd.size * sp.fanout;
244
+ sp.n_blocks = (nd.size + kScatterBlock - 1) / kScatterBlock;
245
+ sp.count_offset = plan.n_counts;
246
+ plan.n_counts += sp.n_blocks * n_leaders;
247
+ for (size_t b = 0; b < nd.size; b += kStripe) {
248
+ plan.items.push_back(WorkItem{i, plan.splits.size(), b});
249
+ }
250
+ plan.splits.push_back(std::move(sp));
251
+ }
252
+ return plan;
253
+ }
254
+
255
+ /// Runs the leaf and stripe items; returns each point's `fanout` nearest
256
+ /// leaders.
257
+ std::unique_ptr<uint16_t[]> assign_leaders(
258
+ const LevelContext& ctx,
259
+ const std::vector<Node>& nodes,
260
+ const LevelPlan& plan) {
261
+ const Index& storage = ctx.storage;
262
+ const size_t d = static_cast<size_t>(storage.d);
263
+ // new T[n], not std::vector: every entry is written first; zeroing is
264
+ // serial.
265
+ std::unique_ptr<uint16_t[]> assign(new uint16_t[plan.n_instances]);
266
+ std::exception_ptr ex;
267
+ std::atomic<bool> interrupt{false};
268
+ #pragma omp parallel
269
+ {
270
+ AlignedTable<float, 64> stripe_vecs, leader_vecs, dist;
271
+ std::vector<float> leader_norms;
272
+ size_t n_done = 0;
273
+ #pragma omp for schedule(FAISS_OMP_DYNAMIC)
274
+ for (int64_t i = 0; i < static_cast<int64_t>(plan.items.size()); i++) {
275
+ if (interrupt.load(std::memory_order_relaxed)) {
276
+ continue;
277
+ }
278
+ try {
279
+ const WorkItem& item = plan.items[i];
280
+ const Node& nd = nodes[item.node];
281
+ if (item.split == kNoSplit) {
282
+ ctx.on_leaf(nd.ids, nd.size);
283
+ } else {
284
+ const SplitInfo& sp = plan.splits[item.split];
285
+ const size_t S = std::min(kStripe, nd.size - item.begin);
286
+ const size_t L = sp.leaders.size();
287
+ ensure_size(stripe_vecs, S * d);
288
+ ensure_size(leader_vecs, L * d);
289
+ ensure_size(dist, S * L);
290
+ leader_norms.resize(L);
291
+ gather_rows(
292
+ storage,
293
+ nd.ids + item.begin,
294
+ S,
295
+ stripe_vecs.data());
296
+ gather_rows(
297
+ storage, sp.leaders.data(), L, leader_vecs.data());
298
+ for (size_t l = 0; l < L; l++) {
299
+ leader_norms[l] =
300
+ fvec_norm_L2sqr(leader_vecs.data() + l * d, d);
301
+ }
302
+ stripe_assign(
303
+ stripe_vecs.data(),
304
+ S,
305
+ leader_vecs.data(),
306
+ leader_norms.data(),
307
+ L,
308
+ d,
309
+ sp.ranking,
310
+ sp.fanout,
311
+ dist.data(),
312
+ assign.get() + sp.offset + item.begin * sp.fanout);
313
+ }
314
+ if (omp_get_thread_num() == 0 && n_done++ % 64 == 0 &&
315
+ InterruptCallback::is_interrupted()) {
316
+ FAISS_THROW_MSG("computation interrupted");
317
+ }
318
+ } catch (...) {
319
+ omp_capture_exception(ex, [&] { interrupt = true; });
320
+ }
321
+ }
322
+ }
323
+ omp_rethrow_if_exception(ex);
324
+ return assign;
325
+ }
326
+
327
+ /// Counting sort per (split, block): count, prefix sums in (leader, block)
328
+ /// order, scatter. Every child comes out in parent order. Fills child_off and
329
+ /// returns the child ids, owned by next_storage.
330
+ const int32_t* scatter_children(
331
+ const std::vector<Node>& nodes,
332
+ LevelPlan& plan,
333
+ const uint16_t* assign,
334
+ LevelStorage& next_storage) {
335
+ std::vector<SplitInfo>& splits = plan.splits;
336
+ std::vector<std::pair<size_t, size_t>> blocks;
337
+ for (size_t s = 0; s < splits.size(); s++) {
338
+ for (size_t b = 0; b < splits[s].n_blocks; b++) {
339
+ blocks.emplace_back(s, b);
340
+ }
341
+ }
342
+ std::unique_ptr<size_t[]> counts(new size_t[plan.n_counts]);
343
+ #pragma omp parallel for schedule(FAISS_OMP_DYNAMIC)
344
+ for (int64_t i = 0; i < static_cast<int64_t>(blocks.size()); i++) {
345
+ const SplitInfo& sp = splits[blocks[i].first];
346
+ const size_t b = blocks[i].second;
347
+ const Node& nd = nodes[sp.node];
348
+ const size_t L = sp.leaders.size();
349
+ size_t* cnt = counts.get() + sp.count_offset + b * L;
350
+ std::fill(cnt, cnt + L, 0);
351
+ const uint16_t* a = assign + sp.offset;
352
+ const size_t end = std::min(nd.size, (b + 1) * kScatterBlock);
353
+ for (size_t pos = b * kScatterBlock; pos < end; pos++) {
354
+ for (int j = 0; j < sp.fanout; j++) {
355
+ cnt[a[pos * sp.fanout + j]]++;
356
+ }
357
+ }
358
+ }
359
+ for (SplitInfo& sp : splits) {
360
+ const size_t L = sp.leaders.size();
361
+ sp.child_off.resize(L + 1);
362
+ size_t running = sp.offset;
363
+ for (size_t l = 0; l < L; l++) {
364
+ sp.child_off[l] = running;
365
+ for (size_t b = 0; b < sp.n_blocks; b++) {
366
+ size_t& c = counts[sp.count_offset + b * L + l];
367
+ const size_t block_count = c;
368
+ c = running;
369
+ running += block_count;
370
+ }
371
+ }
372
+ sp.child_off[L] = running;
373
+ }
374
+ next_storage.child_ids.reset(new int32_t[plan.n_instances]);
375
+ int32_t* child_ids = next_storage.child_ids.get();
376
+ #pragma omp parallel for schedule(FAISS_OMP_DYNAMIC)
377
+ for (int64_t i = 0; i < static_cast<int64_t>(blocks.size()); i++) {
378
+ const SplitInfo& sp = splits[blocks[i].first];
379
+ const size_t b = blocks[i].second;
380
+ const Node& nd = nodes[sp.node];
381
+ size_t* cursor = counts.get() + sp.count_offset + b * sp.leaders.size();
382
+ const uint16_t* a = assign + sp.offset;
383
+ const size_t end = std::min(nd.size, (b + 1) * kScatterBlock);
384
+ for (size_t pos = b * kScatterBlock; pos < end; pos++) {
385
+ for (int j = 0; j < sp.fanout; j++) {
386
+ child_ids[cursor[a[pos * sp.fanout + j]]++] = nd.ids[pos];
387
+ }
388
+ }
389
+ }
390
+ return child_ids;
391
+ }
392
+
393
+ /// Child rules, in order: below c_min it is merged; up to c_max it is a leaf;
394
+ /// at max_depth it is sliced; an IP-ranked child above 90% of its parent is
395
+ /// re-split by L2; a child equal to its parent is sliced; else it is split.
396
+ void classify_children(
397
+ const PartitionParams& p,
398
+ const Node& nd,
399
+ const SplitInfo& sp,
400
+ const int32_t* child_ids,
401
+ SplitOutput& out) {
402
+ const size_t c_min = static_cast<size_t>(p.c_min);
403
+ const size_t c_max = static_cast<size_t>(p.c_max);
404
+ const int child_depth = nd.depth + 1;
405
+ std::vector<uint32_t> small_children;
406
+ for (size_t l = 0; l < sp.leaders.size(); l++) {
407
+ Node child{
408
+ child_ids + sp.child_off[l],
409
+ sp.child_off[l + 1] - sp.child_off[l],
410
+ mix_seed(nd.seed, l),
411
+ child_depth,
412
+ nd.force_l2};
413
+ if (child.size < c_min) {
414
+ small_children.push_back(static_cast<uint32_t>(l));
415
+ } else if (child.size <= c_max) {
416
+ out.nodes.push_back(child);
417
+ } else if (child_depth >= p.max_depth) {
418
+ push_slices(child, c_max, out);
419
+ out.n_slices++;
420
+ } else if (sp.ranking != Ranking::L2 && 10 * child.size > 9 * nd.size) {
421
+ child.force_l2 = true;
422
+ out.nodes.push_back(child);
423
+ out.n_l2++;
424
+ } else if (child.size == nd.size) {
425
+ push_slices(child, c_max, out);
426
+ out.n_slices++;
427
+ } else {
428
+ out.nodes.push_back(child);
429
+ }
430
+ }
431
+
432
+ std::vector<int> merge_order(small_children.size());
433
+ rand_perm_splitmix64(
434
+ merge_order.data(),
435
+ merge_order.size(),
436
+ static_cast<int64_t>(mix_seed(nd.seed, kMergeShuffleIndex)));
437
+ std::vector<int32_t> group;
438
+ auto close_group = [&]() {
439
+ std::sort(group.begin(), group.end());
440
+ group.erase(std::unique(group.begin(), group.end()), group.end());
441
+ out.pool_offsets.push_back(out.pool.size());
442
+ out.pool.insert(out.pool.end(), group.begin(), group.end());
443
+ // At most c_max points, so it is a leaf and its seed is never used.
444
+ out.nodes.push_back(
445
+ Node{nullptr, group.size(), 0, child_depth, nd.force_l2});
446
+ group.clear();
447
+ };
448
+ for (int i : merge_order) {
449
+ const uint32_t l = small_children[i];
450
+ const int32_t* c = child_ids + sp.child_off[l];
451
+ const size_t size = sp.child_off[l + 1] - sp.child_off[l];
452
+ if (!group.empty() && group.size() + size > c_max) {
453
+ close_group();
454
+ }
455
+ group.insert(group.end(), c, c + size);
456
+ }
457
+ if (!group.empty()) {
458
+ close_group();
459
+ }
460
+ }
461
+
462
+ void collect_children(
463
+ const LevelContext& ctx,
464
+ const std::vector<Node>& nodes,
465
+ const std::vector<SplitInfo>& splits,
466
+ const int32_t* child_ids,
467
+ std::vector<Node>& next,
468
+ LevelStorage& next_storage) {
469
+ std::vector<SplitOutput> outputs(splits.size());
470
+ std::exception_ptr ex;
471
+ #pragma omp parallel for schedule(FAISS_OMP_DYNAMIC)
472
+ for (int64_t s = 0; s < static_cast<int64_t>(splits.size()); s++) {
473
+ try {
474
+ classify_children(
475
+ ctx.p,
476
+ nodes[splits[s].node],
477
+ splits[s],
478
+ child_ids,
479
+ outputs[s]);
480
+ } catch (...) {
481
+ omp_capture_exception(ex);
482
+ }
483
+ }
484
+ omp_rethrow_if_exception(ex);
485
+ size_t n_next = next.size();
486
+ for (const SplitOutput& out : outputs) {
487
+ n_next += out.nodes.size();
488
+ }
489
+ next.reserve(n_next);
490
+ next_storage.merge_pools.reserve(outputs.size());
491
+ for (SplitOutput& out : outputs) {
492
+ ctx.stats.n_slice_fallbacks += out.n_slices;
493
+ ctx.stats.n_l2_repartitions += out.n_l2;
494
+ next_storage.merge_pools.push_back(std::move(out.pool));
495
+ const int32_t* pool = next_storage.merge_pools.back().data();
496
+ size_t k = 0;
497
+ for (Node& child : out.nodes) {
498
+ if (child.ids == nullptr) {
499
+ child.ids = pool + out.pool_offsets[k++];
500
+ }
501
+ }
502
+ next.insert(
503
+ next.end(),
504
+ std::make_move_iterator(out.nodes.begin()),
505
+ std::make_move_iterator(out.nodes.end()));
506
+ std::vector<Node>().swap(out.nodes);
507
+ std::vector<size_t>().swap(out.pool_offsets);
508
+ }
509
+ }
510
+
511
+ void run_level(
512
+ const LevelContext& ctx,
513
+ const std::vector<Node>& nodes,
514
+ std::vector<Node>& next,
515
+ LevelStorage& next_storage) {
516
+ LevelPlan plan = plan_level(ctx, nodes);
517
+ const std::unique_ptr<uint16_t[]> assign = assign_leaders(ctx, nodes, plan);
518
+ if (plan.splits.empty()) {
519
+ return;
520
+ }
521
+ const int32_t* child_ids =
522
+ scatter_children(nodes, plan, assign.get(), next_storage);
523
+ collect_children(ctx, nodes, plan.splits, child_ids, next, next_storage);
524
+ }
525
+
526
+ } // namespace
527
+
528
+ void partition_and_visit(
529
+ const Index& storage,
530
+ const PartitionParams& p,
531
+ uint64_t root_seed,
532
+ const LeafVisitor& on_leaf,
533
+ PartitionStats* stats,
534
+ const std::vector<int32_t>& ids) {
535
+ check_params(storage, p);
536
+ PartitionStats st;
537
+ const LevelContext ctx{storage, p, on_leaf, st};
538
+ const size_t n =
539
+ ids.empty() ? static_cast<size_t>(storage.ntotal) : ids.size();
540
+ if (n > 0) {
541
+ InterruptCallback::check();
542
+ std::unique_ptr<int32_t[]> root_ids(new int32_t[n]);
543
+ for (size_t i = 0; i < n; i++) {
544
+ root_ids[i] = ids.empty() ? static_cast<int32_t>(i) : ids[i];
545
+ }
546
+ std::vector<Node> root_children;
547
+ LevelStorage root_storage;
548
+ run_level(
549
+ ctx,
550
+ {Node{root_ids.get(), n, root_seed, 0, false}},
551
+ root_children,
552
+ root_storage);
553
+ root_ids.reset();
554
+
555
+ size_t w0 = 0;
556
+ while (w0 < root_children.size()) {
557
+ size_t w1 = w0;
558
+ size_t load = 0;
559
+ while (w1 < root_children.size() &&
560
+ (w1 == w0 ||
561
+ load + root_children[w1].size <= p.wave_budget)) {
562
+ load += root_children[w1].size;
563
+ w1++;
564
+ }
565
+ st.n_waves++;
566
+ std::vector<Node> nodes(
567
+ root_children.begin() + w0, root_children.begin() + w1);
568
+ std::unique_ptr<LevelStorage> level_storage;
569
+ while (!nodes.empty()) {
570
+ InterruptCallback::check();
571
+ std::vector<Node> next;
572
+ auto next_storage = std::make_unique<LevelStorage>();
573
+ run_level(ctx, nodes, next, *next_storage);
574
+ nodes = std::move(next);
575
+ level_storage =
576
+ std::move(next_storage); // frees the parent lists
577
+ }
578
+ w0 = w1;
579
+ }
580
+ }
581
+ if (stats) {
582
+ *stats = std::move(st);
583
+ }
584
+ }
585
+
586
+ } // namespace pipnn
587
+ } // namespace faiss
@@ -0,0 +1,93 @@
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
+ #pragma once
9
+
10
+ #include <faiss/MetricType.h>
11
+ #include <cstddef>
12
+ #include <cstdint>
13
+ #include <functional>
14
+ #include <vector>
15
+
16
+ namespace faiss {
17
+
18
+ struct Index;
19
+
20
+ namespace pipnn {
21
+
22
+ enum class Ranking : uint8_t;
23
+
24
+ /// Randomized Ball Carving settings (paper Alg. 5).
25
+ struct PartitionParams {
26
+ int c_max = 1024; // largest leaf
27
+ int c_min = 128; // smaller children are merged
28
+ int leader_cap = 1000; // most leaders per split
29
+ float leader_fraction = 0.005f; // leaders per split, as a fraction of it
30
+ std::vector<int> fanout = {10, 3}; // leaders joined per depth, then 1
31
+ MetricType metric = METRIC_L2;
32
+ bool ip_partition_by_angle = false;
33
+ size_t wave_budget = size_t(1) << 30; // point instances per wave
34
+ int max_depth = 32; // children at this depth are cut into id slices
35
+ int fanout_at(int depth) const;
36
+ };
37
+
38
+ /// Each partition thread holds a c_max x c_max float matrix.
39
+ inline constexpr int kMaxCMax = 4096;
40
+ /// Leader indices are stored as uint16_t.
41
+ inline constexpr int kMaxLeaderCap = 65535;
42
+
43
+ struct PartitionStats {
44
+ size_t n_leaves = 0;
45
+ size_t n_point_instances = 0; // sum of leaf sizes
46
+ size_t max_leaf = 0;
47
+ size_t n_slice_fallbacks = 0, n_l2_repartitions = 0;
48
+ int max_depth_seen = 0;
49
+ /// leaf_size_histogram[i] counts leaves of size in [64 i + 1, 64 (i + 1)].
50
+ std::vector<size_t> leaf_size_histogram;
51
+ size_t n_waves = 0;
52
+ };
53
+
54
+ /// Called once per leaf with its sorted, unique ids (s <= c_max), concurrently
55
+ /// from partition_and_visit's OpenMP region: omp_get_thread_num() identifies
56
+ /// the calling thread. `ids` is valid only during the call.
57
+ using LeafVisitor = std::function<void(const int32_t* ids, size_t s)>;
58
+
59
+ /// Randomized Ball Carving with multi-level fanout over the sorted `ids` (all
60
+ /// of storage if empty). Calls `on_leaf` exactly once per leaf and fills
61
+ /// `*stats` (if not null). On an exception or an interrupt it rethrows with
62
+ /// some leaves unvisited and `*stats` unchanged.
63
+ void partition_and_visit(
64
+ const Index& storage,
65
+ const PartitionParams& p,
66
+ uint64_t root_seed,
67
+ const LeafVisitor& on_leaf,
68
+ PartitionStats* stats,
69
+ const std::vector<int32_t>& ids = {});
70
+
71
+ /// Deterministic hash of (a, b) for derived seeds. For a fixed a, distinct b
72
+ /// give distinct results.
73
+ uint64_t mix_seed(uint64_t a, uint64_t b);
74
+
75
+ /// Building blocks of partition_and_visit, exposed for unit tests.
76
+ namespace detail {
77
+
78
+ /// min(max(ceil(leader_fraction * size), 4 * fanout(depth)), leader_cap, size)
79
+ size_t leader_count(size_t size, const PartitionParams& p, int depth);
80
+
81
+ /// k distinct positions of [0, N), ascending (Floyd's algorithm).
82
+ void sample_positions(
83
+ size_t N,
84
+ size_t k,
85
+ uint64_t seed,
86
+ std::vector<size_t>& out);
87
+
88
+ Ranking ranking_for(const PartitionParams& p, bool force_l2);
89
+
90
+ } // namespace detail
91
+
92
+ } // namespace pipnn
93
+ } // namespace faiss