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
@@ -9,8 +9,10 @@
9
9
 
10
10
  #include <faiss/IndexRaBitQ.h>
11
11
 
12
+ #include <algorithm>
12
13
  #include <atomic>
13
14
  #include <cinttypes>
15
+ #include <cmath>
14
16
  #include <cstdio>
15
17
  #include <cstdlib>
16
18
  #include <cstring>
@@ -32,8 +34,10 @@
32
34
  #include <faiss/impl/VisitedTable.h>
33
35
  #include <faiss/impl/hnsw/LockVector.h>
34
36
  #include <faiss/impl/hnsw/MinimaxHeap.h>
37
+ #include <faiss/impl/platform_macros.h>
35
38
  #include <faiss/utils/random.h>
36
39
  #include <faiss/utils/sorting.h>
40
+ #include <faiss/utils/utils.h>
37
41
 
38
42
  namespace faiss {
39
43
 
@@ -42,6 +46,13 @@ using NodeDistFarther = HNSW::NodeDistFarther;
42
46
 
43
47
  HNSWStats hnsw_stats;
44
48
 
49
+ void HNSWStats::combine_atomic(const HNSWStats& other) {
50
+ detail::atomic_fetch_add_relaxed(n1, other.n1);
51
+ detail::atomic_fetch_add_relaxed(n2, other.n2);
52
+ detail::atomic_fetch_add_relaxed(ndis, other.ndis);
53
+ detail::atomic_fetch_add_relaxed(nhops, other.nhops);
54
+ }
55
+
45
56
  /**************************************************************
46
57
  * add / search blocks of descriptors
47
58
  **************************************************************/
@@ -429,6 +440,7 @@ void hnsw_search(
429
440
 
430
441
  idx_t check_period = InterruptCallback::get_period_hint(
431
442
  hnsw.max_level * index->d * efSearch);
443
+ check_period *= omp_get_max_threads();
432
444
 
433
445
  for (idx_t i0 = 0; i0 < n; i0 += check_period) {
434
446
  idx_t i1 = std::min(i0 + check_period, n);
@@ -466,6 +478,9 @@ void hnsw_search(
466
478
  rq->stats.reset();
467
479
  }
468
480
 
481
+ // HNSW::search owns the per-query generation advance. The
482
+ // reusable-table acquisition above only clears stale TLS
483
+ // state before this thread starts processing queries.
469
484
  HNSWStats stats =
470
485
  hnsw.search(*dis, index, *res, *vt, params);
471
486
  n1 += stats.n1;
@@ -477,7 +492,6 @@ void hnsw_search(
477
492
  n_rabitq_refine += rq->stats.n_refine;
478
493
  }
479
494
  res->end();
480
- vt->advance();
481
495
  } catch (...) {
482
496
  omp_capture_exception(ex, [&] { interrupt = true; });
483
497
  }
@@ -487,8 +501,10 @@ void hnsw_search(
487
501
  InterruptCallback::check();
488
502
  }
489
503
 
490
- hnsw_stats.combine({n1, n2, ndis, nhops});
491
- rabitq_stats.add({n_rabitq_1bit, n_rabitq_refine});
504
+ if (get_search_stats_enabled()) {
505
+ hnsw_stats.combine_atomic({n1, n2, ndis, nhops});
506
+ rabitq_stats.add_atomic({n_rabitq_1bit, n_rabitq_refine});
507
+ }
492
508
  }
493
509
 
494
510
  } // anonymous namespace
@@ -699,10 +715,9 @@ void IndexHNSW::search_level_0(
699
715
  omp_capture_exception(ex, [&] { interrupt = true; });
700
716
  }
701
717
  }
702
- #pragma omp critical
703
- {
704
- hnsw_stats.combine(search_stats);
705
- rabitq_stats.add(rq_search_stats);
718
+ if (get_search_stats_enabled()) {
719
+ hnsw_stats.combine_atomic(search_stats);
720
+ rabitq_stats.add_atomic(rq_search_stats);
706
721
  }
707
722
  }
708
723
  omp_rethrow_if_exception(ex);
@@ -772,7 +787,7 @@ void IndexHNSW::init_level_0_from_entry_points(
772
787
  storage_distance_computer(storage));
773
788
  std::vector<float> vec(storage->d);
774
789
 
775
- #pragma omp for schedule(dynamic)
790
+ #pragma omp for schedule(FAISS_OMP_DYNAMIC)
776
791
  for (int i = 0; i < n; i++) {
777
792
  storage_idx_t pt_id = points[i];
778
793
  storage_idx_t nearest = nearests[i];
@@ -895,6 +910,433 @@ IndexHNSWFlat::IndexHNSWFlat(int d_in, int M, MetricType metric)
895
910
  is_trained = true;
896
911
  }
897
912
 
913
+ /**************************************************************
914
+ * IndexHNSW::add_level_0_links_from_sample
915
+ **************************************************************/
916
+
917
+ namespace {
918
+
919
+ // Relative difference below which two distances are equal: float rounding,
920
+ // or near-duplicate nodes. Exact duplicates give equal distances already.
921
+ constexpr float kSinkDistanceTolerance = 1e-5f;
922
+
923
+ // True when distance (or similarity) a is better than b by more than the
924
+ // tolerance.
925
+ bool is_better(float a, float b, bool similarity) {
926
+ const float tolerance =
927
+ kSinkDistanceTolerance * std::max(std::fabs(a), std::fabs(b));
928
+ return similarity ? a > b + tolerance : a < b - tolerance;
929
+ }
930
+
931
+ constexpr size_t kSinkChunk = 16384;
932
+
933
+ struct SinkScan {
934
+ size_t n_exact_searches = 0;
935
+ std::vector<idx_t> sinks; // sorted
936
+ // For each sink, the nearest nodes of its wrong vectors with their
937
+ // counts, most frequent first. Ties go to the smaller node ID.
938
+ std::vector<std::vector<std::pair<idx_t, idx_t>>> targets;
939
+ };
940
+
941
+ // Finds the sinks from the HNSW labels of the sample vectors. Searches the
942
+ // vectors that arrive at candidate sinks also with the exact search, unless
943
+ // their exact result is already known. exact[i] is the nearer node that the
944
+ // exact search found for labels[i], or labels[i] if none (-1: not computed).
945
+ SinkScan find_sinks(
946
+ size_t ntotal,
947
+ const HNSWLinkSampleSearch& sample,
948
+ const HNSWLevel0LinkParameters& params,
949
+ const std::vector<idx_t>& labels,
950
+ std::vector<idx_t>& exact) {
951
+ const idx_t n = sample.n;
952
+ std::vector<idx_t> counts(ntotal, 0);
953
+ for (idx_t i = 0; i < n; i++) {
954
+ if (labels[i] >= 0) {
955
+ counts[labels[i]]++;
956
+ }
957
+ }
958
+
959
+ const double min_count =
960
+ std::max(2.0, params.min_mean_factor * double(n) / ntotal);
961
+ // candidates: vectors that arrive at candidate sinks
962
+ std::vector<idx_t> candidates, unknown, found;
963
+ candidates.reserve(n);
964
+ for (idx_t i = 0; i < n; i++) {
965
+ if (labels[i] >= 0 && counts[labels[i]] >= min_count) {
966
+ candidates.push_back(i);
967
+ if (exact[i] < 0) {
968
+ unknown.push_back(i);
969
+ found.push_back(labels[i]);
970
+ }
971
+ }
972
+ }
973
+ std::vector<idx_t> exact_labels(unknown.size());
974
+ std::vector<uint8_t> nearer(unknown.size(), 0);
975
+ sample.search_exact(
976
+ unknown.size(),
977
+ unknown.data(),
978
+ found.data(),
979
+ exact_labels.data(),
980
+ nearer.data());
981
+ for (size_t j = 0; j < unknown.size(); j++) {
982
+ exact[unknown[j]] = nearer[j] ? exact_labels[j] : found[j];
983
+ }
984
+
985
+ // (sink candidate, nearest node) for each vector with a nearer node
986
+ std::vector<std::pair<idx_t, idx_t>> wrong;
987
+ wrong.reserve(candidates.size());
988
+ std::vector<idx_t> closer(ntotal, 0);
989
+ for (size_t j = 0; j < candidates.size(); j++) {
990
+ const idx_t i = candidates[j];
991
+ if (exact[i] != labels[i]) {
992
+ closer[labels[i]]++;
993
+ wrong.emplace_back(labels[i], exact[i]);
994
+ }
995
+ }
996
+ std::sort(wrong.begin(), wrong.end());
997
+
998
+ SinkScan scan;
999
+ scan.n_exact_searches = unknown.size();
1000
+ for (size_t a = 0; a < wrong.size();) {
1001
+ const idx_t sink = wrong[a].first;
1002
+ size_t b = a;
1003
+ while (b < wrong.size() && wrong[b].first == sink) {
1004
+ b++;
1005
+ }
1006
+ if (closer[sink] >= params.min_closer_fraction * counts[sink]) {
1007
+ std::vector<std::pair<idx_t, idx_t>> targets;
1008
+ for (size_t c = a; c < b;) {
1009
+ size_t e = c;
1010
+ while (e < b && wrong[e].second == wrong[c].second) {
1011
+ e++;
1012
+ }
1013
+ targets.emplace_back(wrong[c].second, e - c);
1014
+ c = e;
1015
+ }
1016
+ std::sort(
1017
+ targets.begin(),
1018
+ targets.end(),
1019
+ [](const auto& p, const auto& q) {
1020
+ return p.second != q.second ? p.second > q.second
1021
+ : p.first < q.first;
1022
+ });
1023
+ scan.sinks.push_back(sink);
1024
+ scan.targets.push_back(std::move(targets));
1025
+ }
1026
+ a = b;
1027
+ }
1028
+ return scan;
1029
+ }
1030
+
1031
+ // Gives `sink` level-0 links to up to `max_links` targets that it does not
1032
+ // link to yet: first in free slots, then in place of its farthest links. It
1033
+ // keeps at least half of its links. Returns (links added, links replaced).
1034
+ std::pair<size_t, size_t> link_sink(
1035
+ HNSW& hnsw,
1036
+ DistanceComputer& dis,
1037
+ HNSW::storage_idx_t sink,
1038
+ const std::vector<std::pair<idx_t, idx_t>>& targets,
1039
+ int max_links,
1040
+ bool similarity) {
1041
+ size_t begin, end;
1042
+ hnsw.neighbor_range(sink, 0, &begin, &end);
1043
+ size_t degree = 0;
1044
+ while (begin + degree < end && hnsw.neighbors[begin + degree] >= 0) {
1045
+ degree++;
1046
+ }
1047
+
1048
+ std::vector<HNSW::storage_idx_t> fresh;
1049
+ for (const auto& [target, count] : targets) {
1050
+ if (static_cast<int>(fresh.size()) >= max_links) {
1051
+ break;
1052
+ }
1053
+ bool linked = target == sink;
1054
+ for (size_t k = begin; k < begin + degree && !linked; k++) {
1055
+ linked = hnsw.neighbors[k] == target;
1056
+ }
1057
+ if (!linked) {
1058
+ fresh.push_back(static_cast<HNSW::storage_idx_t>(target));
1059
+ }
1060
+ }
1061
+
1062
+ const size_t added = std::min(fresh.size(), (end - begin) - degree);
1063
+ for (size_t k = 0; k < added; k++) {
1064
+ hnsw.neighbors[begin + degree + k] = fresh[k];
1065
+ }
1066
+
1067
+ size_t replaced = 0;
1068
+ if (added < fresh.size()) {
1069
+ // (distance from the sink, slot), farthest first
1070
+ std::vector<std::pair<float, size_t>> links;
1071
+ for (size_t k = begin; k < begin + degree; k++) {
1072
+ links.emplace_back(dis.symmetric_dis(sink, hnsw.neighbors[k]), k);
1073
+ }
1074
+ std::sort(
1075
+ links.begin(), links.end(), [&](const auto& p, const auto& q) {
1076
+ if (p.first != q.first) {
1077
+ return similarity ? p.first < q.first
1078
+ : p.first > q.first;
1079
+ }
1080
+ return p.second < q.second;
1081
+ });
1082
+ const size_t replaceable = degree / 2;
1083
+ for (size_t k = added; k < fresh.size() && replaced < replaceable;
1084
+ k++, replaced++) {
1085
+ hnsw.neighbors[links[replaced].second] = fresh[k];
1086
+ }
1087
+ }
1088
+ return {added, replaced};
1089
+ }
1090
+
1091
+ // The sample of an IndexHNSW: fp32 or packed fp16 rows, or rows encoded by a
1092
+ // codec.
1093
+ struct IndexHNSWLinkSample : HNSWLinkSampleSearch {
1094
+ const IndexHNSW& index;
1095
+ const uint8_t* x;
1096
+ NumericType numeric_type;
1097
+ const Index* codec;
1098
+
1099
+ IndexHNSWLinkSample(
1100
+ const IndexHNSW& index,
1101
+ idx_t n,
1102
+ const uint8_t* x,
1103
+ NumericType numeric_type,
1104
+ const Index* codec)
1105
+ : index(index), x(x), numeric_type(numeric_type), codec(codec) {
1106
+ this->n = n;
1107
+ }
1108
+
1109
+ size_t line_size() const {
1110
+ return codec ? codec->sa_code_size()
1111
+ : get_numeric_type_size(numeric_type) * index.d;
1112
+ }
1113
+
1114
+ // Decodes m contiguous rows to fp32.
1115
+ void decode(size_t m, const uint8_t* rows, float* out) const {
1116
+ if (codec) {
1117
+ codec->sa_decode(m, rows, out);
1118
+ } else if (numeric_type == NumericType::Float16) {
1119
+ fp16_to_fp32(
1120
+ m * index.d, reinterpret_cast<const uint16_t*>(rows), out);
1121
+ } else {
1122
+ memcpy(out, rows, m * index.d * sizeof(float));
1123
+ }
1124
+ }
1125
+
1126
+ void decode_rows(
1127
+ size_t begin,
1128
+ size_t m,
1129
+ const idx_t* ids,
1130
+ std::vector<uint8_t>& rows,
1131
+ float* out) const {
1132
+ const size_t ls = line_size();
1133
+ if (!ids) {
1134
+ decode(m, x + begin * ls, out);
1135
+ return;
1136
+ }
1137
+ if (!codec && numeric_type == NumericType::Float32) {
1138
+ for (size_t j = 0; j < m; j++) {
1139
+ memcpy(out + j * index.d, x + ids[begin + j] * ls, ls);
1140
+ }
1141
+ return;
1142
+ }
1143
+ rows.resize(m * ls);
1144
+ for (size_t j = 0; j < m; j++) {
1145
+ memcpy(rows.data() + j * ls, x + ids[begin + j] * ls, ls);
1146
+ }
1147
+ decode(m, rows.data(), out);
1148
+ }
1149
+
1150
+ // Decodes the sample vectors by blocks of kSinkChunk.
1151
+ void search(
1152
+ const SearchParametersHNSW* params,
1153
+ size_t m_total,
1154
+ const idx_t* ids,
1155
+ idx_t* labels) const override {
1156
+ if (!ids && !codec && numeric_type == NumericType::Float32) {
1157
+ std::vector<float> distances(m_total);
1158
+ index.search(
1159
+ m_total,
1160
+ reinterpret_cast<const float*>(x),
1161
+ 1,
1162
+ distances.data(),
1163
+ labels,
1164
+ params);
1165
+ return;
1166
+ }
1167
+ std::vector<uint8_t> rows;
1168
+ std::vector<float> block, distances(kSinkChunk);
1169
+ for (size_t i0 = 0; i0 < m_total; i0 += kSinkChunk) {
1170
+ const size_t m = std::min(kSinkChunk, m_total - i0);
1171
+ block.resize(m * index.d);
1172
+ decode_rows(i0, m, ids, rows, block.data());
1173
+ index.search(
1174
+ m, block.data(), 1, distances.data(), labels + i0, params);
1175
+ }
1176
+ }
1177
+
1178
+ void search_exact(
1179
+ size_t m_total,
1180
+ const idx_t* ids,
1181
+ const idx_t* found,
1182
+ idx_t* exact,
1183
+ uint8_t* nearer) const override {
1184
+ const size_t d = index.d;
1185
+ const bool similarity = is_similarity_metric(index.metric_type);
1186
+ std::vector<float> exact_distances(kSinkChunk);
1187
+ std::vector<uint8_t> rows;
1188
+ std::vector<float> chunk;
1189
+ for (size_t begin = 0; begin < m_total; begin += kSinkChunk) {
1190
+ const size_t m = std::min(kSinkChunk, m_total - begin);
1191
+ chunk.resize(m * d);
1192
+ decode_rows(begin, m, ids, rows, chunk.data());
1193
+ index.storage->search(
1194
+ m, chunk.data(), 1, exact_distances.data(), exact + begin);
1195
+
1196
+ // Compute both distances again with the same distance function.
1197
+ // The batched exact search and the HNSW search round
1198
+ // differently, which can make a node look nearer when the two
1199
+ // distances are almost equal.
1200
+ #pragma omp parallel
1201
+ {
1202
+ std::unique_ptr<DistanceComputer> dis(
1203
+ storage_distance_computer(index.storage));
1204
+ #pragma omp for schedule(static)
1205
+ for (int64_t j = 0; j < static_cast<int64_t>(m); j++) {
1206
+ const idx_t e = exact[begin + j], f = found[begin + j];
1207
+ if (e < 0 || e == f) {
1208
+ continue;
1209
+ }
1210
+ dis->set_query(chunk.data() + j * d);
1211
+ nearer[begin + j] =
1212
+ is_better((*dis)(e), (*dis)(f), similarity);
1213
+ }
1214
+ }
1215
+ }
1216
+ }
1217
+ };
1218
+
1219
+ HNSWLevel0LinkStats add_level_0_links_impl(
1220
+ IndexHNSW& index,
1221
+ const IndexHNSWLinkSample& sample,
1222
+ const HNSWLevel0LinkParameters& params) {
1223
+ FAISS_THROW_IF_NOT_MSG(
1224
+ index.storage, "add_level_0_links_from_sample needs storage");
1225
+ std::unique_ptr<DistanceComputer> dis(
1226
+ storage_distance_computer(index.storage));
1227
+ return hnsw_add_level_0_links_from_sample(
1228
+ index.hnsw,
1229
+ index.ntotal,
1230
+ *dis,
1231
+ is_similarity_metric(index.metric_type),
1232
+ sample,
1233
+ params);
1234
+ }
1235
+
1236
+ } // namespace
1237
+
1238
+ HNSWLevel0LinkStats hnsw_add_level_0_links_from_sample(
1239
+ HNSW& hnsw,
1240
+ size_t ntotal,
1241
+ DistanceComputer& dis,
1242
+ bool similarity,
1243
+ const HNSWLinkSampleSearch& sample,
1244
+ const HNSWLevel0LinkParameters& params) {
1245
+ FAISS_THROW_IF_NOT(params.max_rounds >= 0 && params.max_links_per_sink > 0);
1246
+ HNSWLevel0LinkStats stats;
1247
+ if (ntotal == 0 || sample.n == 0) {
1248
+ return stats;
1249
+ }
1250
+ std::vector<idx_t> labels(sample.n), exact(sample.n, -1);
1251
+ sample.search(params.search_params, sample.n, nullptr, labels.data());
1252
+ for (int round = 0;; round++) {
1253
+ const SinkScan scan = find_sinks(ntotal, sample, params, labels, exact);
1254
+ stats.n_exact_searches += scan.n_exact_searches;
1255
+ if (round == 0) {
1256
+ stats.n_sinks_found = scan.sinks.size();
1257
+ }
1258
+ stats.n_sinks_remaining = scan.sinks.size();
1259
+ if (scan.sinks.empty() || round == params.max_rounds) {
1260
+ break;
1261
+ }
1262
+ size_t changed = 0;
1263
+ for (size_t i = 0; i < scan.sinks.size(); i++) {
1264
+ const auto [added, replaced] = link_sink(
1265
+ hnsw,
1266
+ dis,
1267
+ static_cast<HNSW::storage_idx_t>(scan.sinks[i]),
1268
+ scan.targets[i],
1269
+ params.max_links_per_sink,
1270
+ similarity);
1271
+ stats.n_links_added += added;
1272
+ stats.n_links_replaced += replaced;
1273
+ changed += added + replaced;
1274
+ }
1275
+ if (changed == 0) {
1276
+ break;
1277
+ }
1278
+ stats.n_rounds++;
1279
+
1280
+ // The new links leave from the sinks, so search again only the
1281
+ // vectors that arrived at a sink.
1282
+ // Approximate: searches that only pass through a sink can change too.
1283
+ std::vector<idx_t> ids;
1284
+ for (idx_t i = 0; i < sample.n; i++) {
1285
+ if (std::binary_search(
1286
+ scan.sinks.begin(), scan.sinks.end(), labels[i])) {
1287
+ ids.push_back(i);
1288
+ }
1289
+ }
1290
+ std::vector<idx_t> found(ids.size());
1291
+ sample.search(
1292
+ params.search_params, ids.size(), ids.data(), found.data());
1293
+ for (size_t j = 0; j < ids.size(); j++) {
1294
+ if (labels[ids[j]] != found[j]) {
1295
+ labels[ids[j]] = found[j];
1296
+ exact[ids[j]] = -1;
1297
+ }
1298
+ }
1299
+ }
1300
+ return stats;
1301
+ }
1302
+
1303
+ HNSWLevel0LinkStats IndexHNSW::add_level_0_links_from_sample(
1304
+ idx_t n,
1305
+ const float* x,
1306
+ const HNSWLevel0LinkParameters& params) {
1307
+ return add_level_0_links_from_sample_ex(n, x, NumericType::Float32, params);
1308
+ }
1309
+
1310
+ HNSWLevel0LinkStats IndexHNSW::add_level_0_links_from_sample_ex(
1311
+ idx_t n,
1312
+ const void* x,
1313
+ NumericType numeric_type,
1314
+ const HNSWLevel0LinkParameters& params) {
1315
+ FAISS_THROW_IF_NOT_MSG(
1316
+ numeric_type == NumericType::Float32 ||
1317
+ numeric_type == NumericType::Float16,
1318
+ "add_level_0_links_from_sample_ex supports Float32 and Float16");
1319
+ const IndexHNSWLinkSample sample(
1320
+ *this, n, static_cast<const uint8_t*>(x), numeric_type, nullptr);
1321
+ return add_level_0_links_impl(*this, sample, params);
1322
+ }
1323
+
1324
+ HNSWLevel0LinkStats IndexHNSW::add_level_0_links_from_sample_encoded(
1325
+ idx_t n,
1326
+ const uint8_t* x,
1327
+ const Index* codec,
1328
+ const HNSWLevel0LinkParameters& params) {
1329
+ FAISS_THROW_IF_NOT_MSG(
1330
+ codec, "add_level_0_links_from_sample_encoded needs a codec");
1331
+ FAISS_THROW_IF_NOT_FMT(
1332
+ codec->d == d,
1333
+ "codec dimension %d is not the index dimension %d",
1334
+ int(codec->d),
1335
+ int(d));
1336
+ const IndexHNSWLinkSample sample(*this, n, x, NumericType::Float32, codec);
1337
+ return add_level_0_links_impl(*this, sample, params);
1338
+ }
1339
+
898
1340
  /**************************************************************
899
1341
  * IndexHNSWFlatPanorama implementation
900
1342
  **************************************************************/
@@ -1230,7 +1672,9 @@ void IndexHNSW2Level::search(
1230
1672
  }
1231
1673
  omp_rethrow_if_exception(ex);
1232
1674
 
1233
- hnsw_stats.combine({n1, n2, ndis, nhops});
1675
+ if (get_search_stats_enabled()) {
1676
+ hnsw_stats.combine_atomic({n1, n2, ndis, nhops});
1677
+ }
1234
1678
  }
1235
1679
  }
1236
1680
 
@@ -1432,7 +1876,9 @@ void IndexHNSWCagra::range_search(
1432
1876
  result->do_allocation();
1433
1877
  pres.copy_result();
1434
1878
 
1435
- hnsw_stats.combine({n1, n2, ndis, nhops});
1879
+ if (get_search_stats_enabled()) {
1880
+ hnsw_stats.combine_atomic({n1, n2, ndis, nhops});
1881
+ }
1436
1882
  };
1437
1883
 
1438
1884
  if (is_similarity_metric(metric_type)) {
@@ -49,6 +49,15 @@ struct IndexHNSW : Index {
49
49
  // used when GpuIndexCagra::copyFrom(IndexHNSWCagra*) is invoked.
50
50
  bool keep_max_size_level0 = false;
51
51
 
52
+ // When this index is the coarse quantizer of an IndexIVF, IndexIVF::train()
53
+ // calls add_level_0_links_from_sample() after it adds the centroids. The
54
+ // sample is the first min(n, level_0_link_samples_per_node * ntotal)
55
+ // training vectors, in the numeric type or encoding of the training data.
56
+ // 0 disables the call. Training does not call it when
57
+ // quantizer_trains_alone == 1 or when the quantizer is already trained.
58
+ // The index format does not store this value.
59
+ int level_0_link_samples_per_node = 3;
60
+
52
61
  // See impl/VisitedTable.h.
53
62
  std::optional<bool> use_visited_hashset;
54
63
 
@@ -123,6 +132,35 @@ struct IndexHNSW : Index {
123
132
 
124
133
  void link_singletons();
125
134
 
135
+ /** Adds level-0 links so that the search no longer stops at "sinks":
136
+ * nodes where it stops for many vectors whose nearest node is elsewhere.
137
+ * With a small efSearch, HNSW as a coarse quantizer will overfill the
138
+ * IVF lists. Call this function after adding all centroids to this
139
+ * quantizer. Called automatically through IndexIVF::train.
140
+ */
141
+ HNSWLevel0LinkStats add_level_0_links_from_sample(
142
+ idx_t n,
143
+ const float* x,
144
+ const HNSWLevel0LinkParameters& params =
145
+ HNSWLevel0LinkParameters());
146
+
147
+ /// add_level_0_links_from_sample() for Float32 or packed Float16 vectors
148
+ HNSWLevel0LinkStats add_level_0_links_from_sample_ex(
149
+ idx_t n,
150
+ const void* x,
151
+ NumericType numeric_type,
152
+ const HNSWLevel0LinkParameters& params =
153
+ HNSWLevel0LinkParameters());
154
+
155
+ /// add_level_0_links_from_sample() for n vectors that `codec` encoded with
156
+ /// sa_encode()
157
+ HNSWLevel0LinkStats add_level_0_links_from_sample_encoded(
158
+ idx_t n,
159
+ const uint8_t* x,
160
+ const Index* codec,
161
+ const HNSWLevel0LinkParameters& params =
162
+ HNSWLevel0LinkParameters());
163
+
126
164
  virtual void permute_entries(const idx_t* perm);
127
165
 
128
166
  DistanceComputer* get_distance_computer() const override;
@@ -96,11 +96,18 @@ void IndexIDMapTemplate<IndexT>::train(
96
96
  component_t_to_numeric<typename IndexT::component_t>());
97
97
  }
98
98
 
99
+ template <typename IndexT>
100
+ void IndexIDMapTemplate<IndexT>::train(const gpu::Dataset& dataset) {
101
+ index->train(dataset);
102
+ this->is_trained = index->is_trained;
103
+ }
104
+
99
105
  template <typename IndexT>
100
106
  void IndexIDMapTemplate<IndexT>::reset() {
101
107
  index->reset();
102
108
  id_map.clear();
103
109
  this->ntotal = 0;
110
+ this->is_trained = index->is_trained;
104
111
  }
105
112
 
106
113
  template <typename IndexT>
@@ -128,6 +135,20 @@ void IndexIDMapTemplate<IndexT>::add_with_ids(
128
135
  xids);
129
136
  }
130
137
 
138
+ template <typename IndexT>
139
+ void IndexIDMapTemplate<IndexT>::add_with_ids(
140
+ const gpu::Dataset& dataset,
141
+ const idx_t* xids) {
142
+ index->add(dataset);
143
+ FAISS_THROW_IF_NOT(index->ntotal >= this->ntotal);
144
+ // CAGRA may already have built the graph during train(dataset).
145
+ for (idx_t i = 0; i < index->ntotal - this->ntotal; i++) {
146
+ id_map.push_back(xids[i]);
147
+ }
148
+ this->ntotal = index->ntotal;
149
+ this->is_trained = index->is_trained;
150
+ }
151
+
131
152
  template <typename IndexT>
132
153
  size_t IndexIDMapTemplate<IndexT>::sa_code_size() const {
133
154
  return index->sa_code_size();
@@ -335,6 +356,17 @@ void IndexIDMap2Template<IndexT>::add_with_ids(
335
356
  xids);
336
357
  }
337
358
 
359
+ template <typename IndexT>
360
+ void IndexIDMap2Template<IndexT>::add_with_ids(
361
+ const gpu::Dataset& dataset,
362
+ const idx_t* xids) {
363
+ idx_t prev_ntotal = this->ntotal;
364
+ IndexIDMapTemplate<IndexT>::add_with_ids(dataset, xids);
365
+ for (idx_t i = prev_ntotal; i < this->ntotal; i++) {
366
+ rev_map[this->id_map[i]] = i;
367
+ }
368
+ }
369
+
338
370
  template <typename IndexT>
339
371
  void IndexIDMap2Template<IndexT>::add_sa_codes(
340
372
  idx_t n,
@@ -37,6 +37,10 @@ struct IndexIDMapTemplate : IndexT {
37
37
  NumericType numeric_type,
38
38
  const idx_t* xids) override;
39
39
 
40
+ /// Add a Dataset and assign one xids entry per unmapped vector, including
41
+ /// vectors already built by train(dataset).
42
+ virtual void add_with_ids(const gpu::Dataset& dataset, const idx_t* xids);
43
+
40
44
  /// this will fail. Use add_with_ids
41
45
  void add(idx_t n, const component_t* x) override;
42
46
  void add_ex(idx_t n, const void* x, NumericType numeric_type) override;
@@ -58,6 +62,7 @@ struct IndexIDMapTemplate : IndexT {
58
62
  const SearchParameters* params = nullptr) const override;
59
63
 
60
64
  void train(idx_t n, const component_t* x) override;
65
+ void train(const gpu::Dataset& dataset) override;
61
66
  void train_ex(idx_t n, const void* x, NumericType numeric_type) override;
62
67
 
63
68
  void reset() override;
@@ -102,6 +107,8 @@ struct IndexIDMap2Template : IndexIDMapTemplate<IndexT> {
102
107
  /// make the rev_map from scratch
103
108
  void construct_rev_map();
104
109
 
110
+ void add_with_ids(const gpu::Dataset& dataset, const idx_t* xids) override;
111
+
105
112
  void add_with_ids(idx_t n, const component_t* x, const idx_t* xids)
106
113
  override;
107
114
  void add_with_ids_ex(