faiss 0.6.2 → 0.6.3

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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/AutoTune.cpp +3 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +9 -1
  6. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  7. data/vendor/faiss/faiss/Index.h +2 -2
  8. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  10. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
  11. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +4 -5
  12. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  13. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +2 -4
  14. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  15. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  16. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  17. data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
  18. data/vendor/faiss/faiss/IndexHNSW.cpp +10 -11
  19. data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
  20. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  21. data/vendor/faiss/faiss/IndexIVF.cpp +17 -6
  22. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  23. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  24. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  25. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  26. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  27. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  28. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  29. data/vendor/faiss/faiss/IndexIVFPQ.cpp +40 -22
  30. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  31. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  32. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  33. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  34. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  35. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  36. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  37. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  38. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  39. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  40. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  41. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  43. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  44. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  45. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  46. data/vendor/faiss/faiss/IndexShards.cpp +2 -2
  47. data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
  48. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
  50. data/vendor/faiss/faiss/SuperKMeans.h +30 -0
  51. data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +5 -0
  53. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  55. data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
  56. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  58. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  59. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  60. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  61. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  62. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  63. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  64. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  65. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
  66. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  67. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  68. data/vendor/faiss/faiss/impl/HNSW.cpp +109 -152
  69. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  70. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  71. data/vendor/faiss/faiss/impl/Panorama.h +9 -7
  72. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  73. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +34 -22
  74. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +44 -36
  75. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  76. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +168 -67
  77. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
  78. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
  79. data/vendor/faiss/faiss/impl/ResultHandler.h +25 -31
  80. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +258 -57
  81. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +20 -0
  82. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  83. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  84. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  85. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
  86. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  87. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  88. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  89. data/vendor/faiss/faiss/impl/index_read.cpp +376 -36
  90. data/vendor/faiss/faiss/impl/index_write.cpp +55 -4
  91. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  92. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  93. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  94. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  95. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  96. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  97. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  98. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  99. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  100. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  101. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  102. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  103. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
  104. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  105. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +85 -23
  106. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  107. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  108. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  109. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +136 -0
  110. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +16 -16
  111. data/vendor/faiss/faiss/impl/simd_dispatch.h +30 -9
  112. data/vendor/faiss/faiss/index_factory.cpp +32 -6
  113. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  114. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +2 -2
  115. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
  116. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  117. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  118. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  119. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  120. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  121. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  122. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +31 -1
  123. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +15 -2
  124. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +1 -2
  125. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  126. data/vendor/faiss/faiss/utils/distances.cpp +14 -2
  127. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  128. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  129. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  130. data/vendor/faiss/faiss/utils/hamming.cpp +1 -1
  131. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  132. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  133. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  134. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  135. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
  136. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  137. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
  138. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +92 -0
  139. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  140. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  141. data/vendor/faiss/faiss/utils/simd_levels.cpp +44 -0
  142. data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
  143. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  144. metadata +16 -1
@@ -47,6 +47,19 @@ int sgemm_(
47
47
  float* c,
48
48
  FINTEGER* ldc);
49
49
 
50
+ int sgemv_(
51
+ const char* trans,
52
+ FINTEGER* m,
53
+ FINTEGER* n,
54
+ float* alpha,
55
+ const float* a,
56
+ FINTEGER* lda,
57
+ const float* x,
58
+ FINTEGER* incx,
59
+ float* beta,
60
+ float* y,
61
+ FINTEGER* incy);
62
+
50
63
  int dgemm_(
51
64
  const char* transa,
52
65
  const char* transb,
@@ -192,6 +205,25 @@ void LinearTransform::apply_noalloc(idx_t n, const float* x, float* xt) const {
192
205
 
193
206
  float one = 1;
194
207
  FINTEGER nbiti = d_out, ni = static_cast<FINTEGER>(n), di = d_in;
208
+ if (n == 1) {
209
+ FINTEGER onei = 1;
210
+ // Avoid GEMM packing overhead for single-vector transforms. GEMV is
211
+ // mathematically equivalent but may not be bit-exact with GEMM because
212
+ // BLAS implementations can use different accumulation orders.
213
+ sgemv_("Transposed",
214
+ &di,
215
+ &nbiti,
216
+ &one,
217
+ A.data(),
218
+ &di,
219
+ x,
220
+ &onei,
221
+ &c_factor,
222
+ xt,
223
+ &onei);
224
+ return;
225
+ }
226
+
195
227
  sgemm_("Transposed",
196
228
  "Not transposed",
197
229
  &nbiti,
@@ -1064,8 +1096,7 @@ ITQTransform::ITQTransform(int din, int dout, bool do_pca_in)
1064
1096
  }
1065
1097
 
1066
1098
  void ITQTransform::train(idx_t n, const float* x_in) {
1067
- FAISS_THROW_IF_NOT_MSG(
1068
- !is_trained, "ITQTransform has already been trained");
1099
+ FAISS_THROW_IF_MSG(is_trained, "ITQTransform has already been trained");
1069
1100
 
1070
1101
  size_t max_train_points = std::max(d_in * max_train_per_dim, 32768);
1071
1102
  const float* x =
@@ -18,10 +18,12 @@
18
18
  #include <faiss/IndexBinaryFlat.h>
19
19
  #include <faiss/IndexBinaryHNSW.h>
20
20
  #include <faiss/IndexBinaryIVF.h>
21
+ #include <faiss/IndexEDEN.h>
21
22
  #include <faiss/IndexFlat.h>
22
23
  #include <faiss/IndexHNSW.h>
23
24
  #include <faiss/IndexIVF.h>
24
25
  #include <faiss/IndexIVFAdditiveQuantizerFastScan.h>
26
+ #include <faiss/IndexIVFEDEN.h>
25
27
  #include <faiss/IndexIVFFlat.h>
26
28
  #include <faiss/IndexIVFFlatPanorama.h>
27
29
  #include <faiss/IndexIVFPQ.h>
@@ -77,6 +79,7 @@ VectorTransform* Cloner::clone_VectorTransform(const VectorTransform* vt) {
77
79
  TRYCLONE(PCAMatrix, vt)
78
80
  TRYCLONE(ITQMatrix, vt)
79
81
  TRYCLONE(RandomRotationMatrix, vt)
82
+ TRYCLONE(HadamardRotation, vt)
80
83
  TRYCLONE(LinearTransform, vt) {
81
84
  FAISS_THROW_MSG("clone not supported for this type of VectorTransform");
82
85
  }
@@ -98,6 +101,7 @@ IndexIVF* Cloner::clone_IndexIVF(const IndexIVF* ivf) {
98
101
  TRYCLONE(IndexIVFResidualQuantizerFastScan, ivf)
99
102
  TRYCLONE(IndexIVFPQFastScan, ivf)
100
103
 
104
+ TRYCLONE(IndexIVFEDEN, ivf)
101
105
  TRYCLONE(IndexIVFRaBitQFastScan, ivf)
102
106
  TRYCLONE(IndexIVFRaBitQ, ivf)
103
107
 
@@ -287,6 +291,7 @@ Index* Cloner::clone_Index(const Index* index) {
287
291
  TRYCLONE(IndexLattice, index)
288
292
  TRYCLONE(IndexRandom, index)
289
293
  TRYCLONE(IndexPQFastScan, index)
294
+ TRYCLONE(IndexEDEN, index)
290
295
 
291
296
  TRYCLONE(IndexScalarQuantizer, index)
292
297
  TRYCLONE(MultiIndexQuantizer, index)
@@ -14,8 +14,10 @@
14
14
  #include <faiss/IndexBinaryFlat.h>
15
15
  #include <faiss/IndexBinaryHNSW.h>
16
16
  #include <faiss/IndexBinaryIVF.h>
17
+ #include <faiss/IndexEDEN.h>
17
18
  #include <faiss/IndexHNSW.h>
18
19
  #include <faiss/IndexIDMap.h>
20
+ #include <faiss/IndexIVFEDEN.h>
19
21
  #include <faiss/IndexIVFFlat.h>
20
22
  #include <faiss/IndexIVFPQFastScan.h>
21
23
  #include <faiss/IndexIVFRaBitQ.h>
@@ -33,11 +35,17 @@ namespace {
33
35
  const std::map<faiss::ScalarQuantizer::QuantizerType, std::string> sq_types = {
34
36
  {faiss::ScalarQuantizer::QT_8bit, "SQ8"},
35
37
  {faiss::ScalarQuantizer::QT_4bit, "SQ4"},
38
+ // QT_8bit_uniform and QT_4bit_uniform have no round-trippable
39
+ // index_factory string; the names below are synthetic identifiers used
40
+ // for telemetry logging only.
41
+ {faiss::ScalarQuantizer::QT_8bit_uniform, "SQ8u"},
42
+ {faiss::ScalarQuantizer::QT_4bit_uniform, "SQ4u"},
36
43
  {faiss::ScalarQuantizer::QT_6bit, "SQ6"},
37
44
  {faiss::ScalarQuantizer::QT_fp16, "SQfp16"},
38
45
  {faiss::ScalarQuantizer::QT_bf16, "SQbf16"},
39
46
  {faiss::ScalarQuantizer::QT_8bit_direct_signed, "SQ8_direct_signed"},
40
47
  {faiss::ScalarQuantizer::QT_8bit_direct, "SQ8_direct"},
48
+ {faiss::ScalarQuantizer::QT_0bit, "SQ0"},
41
49
  {faiss::ScalarQuantizer::QT_1bit_tqmse, "SQtqmse1"},
42
50
  {faiss::ScalarQuantizer::QT_2bit_tqmse, "SQtqmse2"},
43
51
  {faiss::ScalarQuantizer::QT_3bit_tqmse, "SQtqmse3"},
@@ -65,6 +73,19 @@ int get_hnsw_M(const faiss::IndexBinaryHNSW* index) {
65
73
  return 0;
66
74
  }
67
75
 
76
+ std::string eden_factory_string(
77
+ const faiss::ScalarQuantizer& sq,
78
+ faiss::EDENScaleType scale_type) {
79
+ std::string result = "EDEN";
80
+ if (sq.bits != 1) {
81
+ result += std::to_string(sq.bits);
82
+ }
83
+ if (scale_type == faiss::EDENScaleType_BIASED) {
84
+ result += "BIASED";
85
+ }
86
+ return result;
87
+ }
88
+
68
89
  } // namespace
69
90
 
70
91
  // Reference for reverse_index_factory:
@@ -114,8 +135,17 @@ std::string reverse_index_factory(const faiss::Index* index) {
114
135
  ivf_index)) {
115
136
  return prefix + ",PQ" + std::to_string(ivfpqfs_index->pq.M) + "x" +
116
137
  std::to_string(ivfpqfs_index->pq.nbits) + "fs";
117
- } else if (dynamic_cast<const faiss::IndexIVFRaBitQ*>(ivf_index)) {
118
- return prefix + ",RaBitQ";
138
+ } else if (
139
+ const faiss::IndexIVFRaBitQ* ivfrabitq_index =
140
+ dynamic_cast<const faiss::IndexIVFRaBitQ*>(ivf_index)) {
141
+ size_t nb_bits = ivfrabitq_index->rabitq.nb_bits;
142
+ return prefix + ",RaBitQ" +
143
+ (nb_bits == 1 ? "" : std::to_string(nb_bits));
144
+ } else if (
145
+ const faiss::IndexIVFEDEN* ivf_eden =
146
+ dynamic_cast<const faiss::IndexIVFEDEN*>(ivf_index)) {
147
+ return prefix + "," +
148
+ eden_factory_string(ivf_eden->sq, ivf_eden->scale_type);
119
149
  }
120
150
  } else if (
121
151
  const faiss::IndexPreTransform* pretransform_index =
@@ -184,12 +214,25 @@ std::string reverse_index_factory(const faiss::Index* index) {
184
214
  const faiss::IndexScalarQuantizer* sq_index =
185
215
  dynamic_cast<const faiss::IndexScalarQuantizer*>(index)) {
186
216
  return sq_types.at(sq_index->sq.qtype);
217
+ } else if (
218
+ // IndexIDMap2 inherits IndexIDMap — check subclass first.
219
+ const faiss::IndexIDMap2* idmap2 =
220
+ dynamic_cast<const faiss::IndexIDMap2*>(index)) {
221
+ return std::string("IDMap2,") + reverse_index_factory(idmap2->index);
187
222
  } else if (
188
223
  const faiss::IndexIDMap* idmap =
189
224
  dynamic_cast<const faiss::IndexIDMap*>(index)) {
190
225
  return std::string("IDMap,") + reverse_index_factory(idmap->index);
191
- } else if (dynamic_cast<const faiss::IndexRaBitQ*>(index)) {
192
- return "RaBitQ";
226
+ } else if (
227
+ const faiss::IndexRaBitQ* rabitq_index =
228
+ dynamic_cast<const faiss::IndexRaBitQ*>(index)) {
229
+ size_t nb_bits = rabitq_index->rabitq.nb_bits;
230
+ return "RaBitQ" +
231
+ (nb_bits == 1 ? std::string() : std::to_string(nb_bits));
232
+ } else if (
233
+ const faiss::IndexEDEN* eden =
234
+ dynamic_cast<const faiss::IndexEDEN*>(index)) {
235
+ return eden_factory_string(eden->sq, eden->scale_type);
193
236
  }
194
237
  // Avoid runtime error, just return empty string for logging.
195
238
  return "";
@@ -14,7 +14,7 @@
14
14
 
15
15
  #include <faiss/IndexBinaryFlat.h>
16
16
  #include <faiss/IndexFlat.h>
17
- #if defined USE_NVIDIA_CUVS
17
+ #if defined(USE_NVIDIA_CUVS) && !defined(FAISS_CUVS_NO_CAGRA)
18
18
  #include <faiss/IndexBinaryHNSW.h>
19
19
  #include <faiss/IndexHNSW.h>
20
20
  #endif
@@ -28,7 +28,7 @@
28
28
  #include <faiss/MetaIndexes.h>
29
29
  #include <faiss/gpu/GpuIndex.h>
30
30
  #include <faiss/gpu/GpuIndexBinaryFlat.h>
31
- #if defined USE_NVIDIA_CUVS
31
+ #if defined(USE_NVIDIA_CUVS) && !defined(FAISS_CUVS_NO_CAGRA)
32
32
  #include <faiss/gpu/GpuIndexBinaryCagra.h>
33
33
  #include <faiss/gpu/GpuIndexCagra.h>
34
34
  #endif
@@ -92,7 +92,7 @@ Index* ToCPUCloner::clone_Index(const Index* index) {
92
92
  // (inverse op of ToGpuClonerMultiple)
93
93
 
94
94
  }
95
- #if defined USE_NVIDIA_CUVS
95
+ #if defined(USE_NVIDIA_CUVS) && !defined(FAISS_CUVS_NO_CAGRA)
96
96
  else if (auto icg = dynamic_cast<const GpuIndexCagra*>(index)) {
97
97
  IndexHNSWCagra* res = new IndexHNSWCagra();
98
98
  if (icg->get_numeric_type() != faiss::NumericType::Float32) {
@@ -150,8 +150,8 @@ Index* ToGpuCloner::clone_Index(const Index* index) {
150
150
  GpuIndexFlatConfig config;
151
151
  config.device = device;
152
152
  config.useFloat16 = true;
153
- FAISS_THROW_IF_NOT_MSG(
154
- !use_cuvs, "this type of index is not implemented for cuVS");
153
+ FAISS_THROW_IF_MSG(
154
+ use_cuvs, "this type of index is not implemented for cuVS");
155
155
  GpuIndexFlat* gif = new GpuIndexFlat(
156
156
  provider, index->d, index->metric_type, config);
157
157
  // transfer data by blocks
@@ -187,8 +187,8 @@ Index* ToGpuCloner::clone_Index(const Index* index) {
187
187
  config.device = device;
188
188
  config.indicesOptions = indicesOptions;
189
189
  config.flatConfig.useFloat16 = useFloat16CoarseQuantizer;
190
- FAISS_THROW_IF_NOT_MSG(
191
- !use_cuvs, "this type of index is not implemented for cuVS");
190
+ config.use_cuvs = use_cuvs;
191
+ config.allowCpuCoarseQuantizer = allowCpuCoarseQuantizer;
192
192
 
193
193
  GpuIndexIVFScalarQuantizer* res = new GpuIndexIVFScalarQuantizer(
194
194
  provider,
@@ -233,7 +233,7 @@ Index* ToGpuCloner::clone_Index(const Index* index) {
233
233
 
234
234
  return res;
235
235
  }
236
- #if defined USE_NVIDIA_CUVS
236
+ #if defined(USE_NVIDIA_CUVS) && !defined(FAISS_CUVS_NO_CAGRA)
237
237
  else if (auto icg = dynamic_cast<const faiss::IndexHNSWCagra*>(index)) {
238
238
  GpuIndexCagraConfig config;
239
239
  config.device = device;
@@ -533,7 +533,7 @@ faiss::IndexBinary* index_binary_gpu_to_cpu(
533
533
  ii->copyTo(ret);
534
534
  return ret;
535
535
  }
536
- #if defined USE_NVIDIA_CUVS
536
+ #if defined(USE_NVIDIA_CUVS) && !defined(FAISS_CUVS_NO_CAGRA)
537
537
  else if (auto ii = dynamic_cast<const GpuIndexBinaryCagra*>(gpu_index)) {
538
538
  IndexBinaryHNSWCagra* ret = new IndexBinaryHNSWCagra();
539
539
  ii->copyTo(ret);
@@ -549,13 +549,13 @@ faiss::IndexBinary* index_binary_cpu_to_gpu(
549
549
  GpuResourcesProvider* provider,
550
550
  int device,
551
551
  const faiss::IndexBinary* index,
552
- const GpuClonerOptions* options) {
552
+ const GpuClonerOptions* /*options*/) {
553
553
  if (auto ii = dynamic_cast<const IndexBinaryFlat*>(index)) {
554
554
  GpuIndexBinaryFlatConfig config;
555
555
  config.device = device;
556
556
  return new GpuIndexBinaryFlat(provider, ii, config);
557
557
  }
558
- #if defined USE_NVIDIA_CUVS
558
+ #if defined(USE_NVIDIA_CUVS) && !defined(FAISS_CUVS_NO_CAGRA)
559
559
  else if (
560
560
  auto ii = dynamic_cast<const faiss::IndexBinaryHNSWCagra*>(index)) {
561
561
  GpuIndexCagraConfig config;
@@ -177,6 +177,33 @@ class GpuIndex : public faiss::Index {
177
177
  }
178
178
  };
179
179
 
180
+ virtual void addImplPrecomputed_(
181
+ idx_t n,
182
+ const float* x,
183
+ const idx_t* ids,
184
+ const idx_t* precomputed_idx);
185
+
186
+ virtual void addImplPrecomputed_ex_(
187
+ idx_t n,
188
+ const void* x,
189
+ NumericType numeric_type,
190
+ const idx_t* ids,
191
+ const idx_t* precomputed_idx);
192
+
193
+ /// Handles paged adds if the add set is too large, passes to
194
+ /// addImpl_ to actually perform the add for the current page
195
+ void addPaged_(
196
+ idx_t n,
197
+ const float* x,
198
+ const idx_t* ids,
199
+ const idx_t* precomputed_idx = nullptr);
200
+ void addPaged_ex_(
201
+ idx_t n,
202
+ const void* x,
203
+ NumericType numeric_type,
204
+ const idx_t* ids,
205
+ const idx_t* precomputed_idx = nullptr);
206
+
180
207
  /// Overridden to actually perform the search
181
208
  /// All data is guaranteed to be resident on our device
182
209
  virtual void searchImpl_(
@@ -209,22 +236,18 @@ class GpuIndex : public faiss::Index {
209
236
  }
210
237
 
211
238
  private:
212
- /// Handles paged adds if the add set is too large, passes to
213
- /// addImpl_ to actually perform the add for the current page
214
- void addPaged_(idx_t n, const float* x, const idx_t* ids);
215
- void addPaged_ex_(
216
- idx_t n,
217
- const void* x,
218
- NumericType numeric_type,
219
- const idx_t* ids);
220
-
221
239
  /// Calls addImpl_ for a single page of GPU-resident data
222
- void addPage_(idx_t n, const float* x, const idx_t* ids);
240
+ void addPage_(
241
+ idx_t n,
242
+ const float* x,
243
+ const idx_t* ids,
244
+ const idx_t* precomputed_idx = nullptr);
223
245
  void addPage_ex_(
224
246
  idx_t n,
225
247
  const void* x,
226
248
  NumericType numeric_type,
227
- const idx_t* ids);
249
+ const idx_t* ids,
250
+ const idx_t* precomputed_idx = nullptr);
228
251
 
229
252
  /// Calls searchImpl_ for a single page of GPU-resident data
230
253
  void searchNonPaged_(
@@ -284,6 +284,46 @@ struct GpuIndexCagra : public GpuIndex {
284
284
  /// in the index instance
285
285
  void copyTo(faiss::IndexHNSWCagra* index) const;
286
286
 
287
+ /// Train CAGRA using multiple GPUs by sharding the dataset.
288
+ /// Uses cuVS native SNMG (single-node multi-GPU) CAGRA build.
289
+ /// Each device builds one shard in parallel via OpenMP.
290
+ /// Float32 only. After training, call copyTo() to produce a CPU
291
+ /// IndexHNSWCagra with full HNSW upper levels.
292
+ /// The training data pointer must remain valid until copyTo() completes.
293
+ /// stitch_mode: 0=CPU HNSW (Approach C), 1=GPU brute-force (Approach B)
294
+ void trainMultiGpu(
295
+ idx_t n,
296
+ const float* x,
297
+ std::vector<GpuResourcesProvider*>& providers,
298
+ std::vector<int>& devices,
299
+ idx_t stitch_per_shard = 0,
300
+ int stitch_k = 2,
301
+ int stitch_mode = 0);
302
+
303
+ /// Build a unified CAGRA graph using cuVS all_neighbors
304
+ /// (multi-GPU kNN graph construction with overlapping clusters) followed
305
+ /// by cagra::optimize (graph pruning). Produces a single unified graph
306
+ /// without stitching. The training data pointer must remain valid until
307
+ /// copyTo() completes.
308
+ /// build_algo: 0=NN-descent (default), 1=brute-force, 2=IVF-PQ
309
+ /// refinement_rate: IVF-PQ refinement multiplier (only used when
310
+ /// build_algo==2). Higher values trade build time for recall; the cuVS
311
+ /// default is 2.0.
312
+ /// ivfpq_search_batch: cap the IVF-PQ search `max_internal_batch_size` used
313
+ /// during the all_neighbors kNN build (build_algo==2). 0 = cuVS default
314
+ /// (128*1024), which can OOM at 100M; a smaller value (e.g. 8192) bounds
315
+ /// the GPU search workspace with no effect on results (recall-neutral).
316
+ void trainAllNeighbors(
317
+ idx_t n,
318
+ const float* x,
319
+ std::vector<int>& devices,
320
+ int n_clusters = 0,
321
+ int overlap_factor = 0,
322
+ bool multi_gpu_optimize = false,
323
+ int build_algo = 0,
324
+ float refinement_rate = 2.0f,
325
+ int ivfpq_search_batch = 0);
326
+
287
327
  void reset() override;
288
328
 
289
329
  std::vector<idx_t> get_knngraph() const;
@@ -317,6 +357,8 @@ struct GpuIndexCagra : public GpuIndex {
317
357
  idx_t* labels,
318
358
  const SearchParameters* search_params) const override;
319
359
 
360
+ void copyToMultiGpu_(faiss::IndexHNSWCagra* index) const;
361
+
320
362
  /// Our configuration options
321
363
  const GpuIndexCagraConfig cagraConfig_;
322
364
 
@@ -329,6 +371,11 @@ struct GpuIndexCagra : public GpuIndex {
329
371
  std::shared_ptr<CuvsCagra<half>>,
330
372
  std::shared_ptr<CuvsCagra<int8_t>>>
331
373
  index_;
374
+
375
+ /// Multi-GPU state: populated by trainMultiGpu(), used by copyTo()
376
+ std::vector<idx_t> merged_knngraph_;
377
+ idx_t merged_knngraph_degree_ = 0;
378
+ const float* multi_gpu_dataset_ = nullptr;
332
379
  };
333
380
 
334
381
  } // namespace gpu
@@ -98,6 +98,17 @@ class GpuIndexIVF : public GpuIndex, public IndexIVFInterface {
98
98
  /// debugging purposes.
99
99
  virtual std::vector<idx_t> getListIndices(idx_t listId) const;
100
100
 
101
+ /// Add vectors with precomputed IVF list assignments.
102
+ /// Same naming and argument order as IndexIVF::add_core.
103
+ /// `x`, `xids`, and `precomputed_idx` can be resident on the CPU or any
104
+ /// GPU; copies are performed as needed.
105
+ void add_core(
106
+ idx_t n,
107
+ const float* x,
108
+ const idx_t* xids,
109
+ const idx_t* precomputed_idx,
110
+ void* inverted_list_context = nullptr);
111
+
101
112
  void search_preassigned(
102
113
  idx_t n,
103
114
  const float* x,
@@ -133,6 +144,12 @@ class GpuIndexIVF : public GpuIndex, public IndexIVFInterface {
133
144
  /// Called from GpuIndex for add/add_with_ids
134
145
  void addImpl_(idx_t n, const float* x, const idx_t* ids) override;
135
146
 
147
+ void addImplPrecomputed_(
148
+ idx_t n,
149
+ const float* x,
150
+ const idx_t* xids,
151
+ const idx_t* precomputed_idx) override;
152
+
136
153
  /// Called from GpuIndex for search
137
154
  void searchImpl_(
138
155
  idx_t n,
@@ -93,6 +93,22 @@ class GpuIndexIVFScalarQuantizer : public GpuIndexIVF {
93
93
  /// Validates index SQ parameters
94
94
  void verifySQSettings_() const;
95
95
 
96
+ /// Whether this index configuration can use cuVS IVF-SQ
97
+ bool shouldUseCuvs_() const;
98
+
99
+ /// Construct the backing IVF implementation
100
+ void setIndex_(
101
+ GpuResources* resources,
102
+ int dim,
103
+ idx_t nlist,
104
+ faiss::MetricType metric,
105
+ float metricArg,
106
+ bool useResidual,
107
+ faiss::ScalarQuantizer* scalarQ,
108
+ bool interleavedLayout,
109
+ IndicesOptions indicesOptions,
110
+ MemorySpace space);
111
+
96
112
  /// Called from train to handle SQ residual training
97
113
  void trainResiduals_(idx_t n, const float* x);
98
114
 
@@ -42,7 +42,7 @@ using namespace faiss::gpu;
42
42
  int main(int argc, char** argv) {
43
43
  gflags::ParseCommandLineFlags(&argc, &argv, true);
44
44
 
45
- cudaProfilerStop();
45
+ CUDA_VERIFY(cudaProfilerStop());
46
46
 
47
47
  auto seed = FLAGS_seed != -1 ? FLAGS_seed : time(nullptr);
48
48
  printf("using seed %ld\n", seed);
@@ -31,7 +31,7 @@ DEFINE_bool(reserve_memory, false, "whether or not to pre-reserve memory");
31
31
  int main(int argc, char** argv) {
32
32
  gflags::ParseCommandLineFlags(&argc, &argv, true);
33
33
 
34
- cudaProfilerStop();
34
+ CUDA_VERIFY(cudaProfilerStop());
35
35
 
36
36
  int dim = FLAGS_dim;
37
37
  int numCentroids = FLAGS_centroids;
@@ -72,7 +72,7 @@ int main(int argc, char** argv) {
72
72
  }
73
73
  }
74
74
 
75
- cudaDeviceSynchronize();
75
+ CUDA_VERIFY(cudaDeviceSynchronize());
76
76
  CUDA_VERIFY(cudaProfilerStart());
77
77
 
78
78
  float totalGpuTime = 0.0f;