faiss 0.6.2 → 0.6.4

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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -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/Clustering.h +8 -0
  7. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  8. data/vendor/faiss/faiss/Index.h +2 -2
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  10. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  11. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +24 -4
  12. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +16 -145
  13. data/vendor/faiss/faiss/IndexBinaryHNSW.h +0 -6
  14. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  15. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +8 -18
  16. data/vendor/faiss/faiss/IndexBinaryIVF.h +8 -1
  17. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  18. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  19. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  20. data/vendor/faiss/faiss/IndexFlat.cpp +21 -54
  21. data/vendor/faiss/faiss/IndexFlat.h +2 -2
  22. data/vendor/faiss/faiss/IndexHNSW.cpp +311 -102
  23. data/vendor/faiss/faiss/IndexHNSW.h +31 -7
  24. data/vendor/faiss/faiss/IndexIDMap.cpp +26 -8
  25. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  26. data/vendor/faiss/faiss/IndexIVF.cpp +36 -10
  27. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  28. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  29. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  30. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  31. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  32. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  33. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  34. data/vendor/faiss/faiss/IndexIVFPQ.cpp +49 -23
  35. data/vendor/faiss/faiss/IndexIVFPQ.h +11 -0
  36. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  37. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  38. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  39. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  40. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  41. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  42. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  43. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  44. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  45. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  46. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  47. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  48. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  49. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  50. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  51. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  52. data/vendor/faiss/faiss/IndexShards.cpp +5 -5
  53. data/vendor/faiss/faiss/IndexShardsIVF.cpp +6 -5
  54. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  55. data/vendor/faiss/faiss/SuperKMeans.cpp +286 -247
  56. data/vendor/faiss/faiss/SuperKMeans.h +33 -2
  57. data/vendor/faiss/faiss/VectorTransform.cpp +71 -2
  58. data/vendor/faiss/faiss/VectorTransform.h +3 -0
  59. data/vendor/faiss/faiss/clone_index.cpp +8 -0
  60. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  61. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  62. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +1 -5
  63. data/vendor/faiss/faiss/gpu/GpuDistance.h +2 -5
  64. data/vendor/faiss/faiss/gpu/GpuIndex.h +38 -16
  65. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +71 -1
  66. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  67. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  68. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  69. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  70. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  71. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  72. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  73. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  74. data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
  75. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +7 -4
  76. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  77. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  78. data/vendor/faiss/faiss/impl/HNSW.cpp +528 -267
  79. data/vendor/faiss/faiss/impl/HNSW.h +46 -7
  80. data/vendor/faiss/faiss/impl/IDSelector.h +44 -0
  81. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  82. data/vendor/faiss/faiss/impl/NNDescent.cpp +10 -3
  83. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  84. data/vendor/faiss/faiss/impl/Panorama.h +20 -9
  85. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  86. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +38 -26
  87. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +45 -37
  88. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  89. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +239 -72
  90. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +66 -4
  91. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +4 -13
  92. data/vendor/faiss/faiss/impl/ResultHandler.h +34 -34
  93. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +287 -84
  94. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +26 -10
  95. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  96. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  97. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  98. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +90 -14
  99. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  100. data/vendor/faiss/faiss/impl/expanded_scanners.h +5 -1
  101. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +1 -0
  102. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  103. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  104. data/vendor/faiss/faiss/impl/index_read.cpp +491 -50
  105. data/vendor/faiss/faiss/impl/index_write.cpp +86 -30
  106. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  107. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  108. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  109. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  110. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +73 -39
  111. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  112. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  113. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  114. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +4 -4
  115. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  116. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  117. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  118. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  119. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  120. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +887 -0
  121. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +2 -2
  122. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  123. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +90 -24
  124. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  125. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  126. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  127. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +169 -13
  128. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +125 -26
  129. data/vendor/faiss/faiss/impl/simd_dispatch.h +70 -31
  130. data/vendor/faiss/faiss/index_factory.cpp +40 -7
  131. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  132. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +9 -6
  133. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +29 -8
  134. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  135. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  136. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  137. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  138. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  139. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  140. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +47 -5
  141. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +23 -3
  142. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +4 -2
  143. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
  144. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +10 -4
  145. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +2 -1
  146. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  147. data/vendor/faiss/faiss/utils/distances.cpp +30 -11
  148. data/vendor/faiss/faiss/utils/distances_dispatch.h +30 -24
  149. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +1 -1
  150. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  151. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  152. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  153. data/vendor/faiss/faiss/utils/hamming.cpp +16 -10
  154. data/vendor/faiss/faiss/utils/hamming.h +10 -1
  155. data/vendor/faiss/faiss/utils/hamming_distance/common.h +14 -3
  156. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +24 -0
  157. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +1 -1
  158. data/vendor/faiss/faiss/utils/hamming_distance/{hamming_computer-avx512_spr.h → hamming_computer-avx512_vpopcnt.h} +85 -24
  159. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +141 -0
  160. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  161. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  162. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +194 -30
  163. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  164. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  165. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +198 -18
  166. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  167. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +330 -40
  168. data/vendor/faiss/faiss/utils/simd_impl/{rabitq_avx512_spr.cpp → rabitq_avx512_vpopcnt.cpp} +112 -23
  169. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  170. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  171. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_dispatch.h +2 -7
  172. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +6 -1
  173. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +34 -0
  174. data/vendor/faiss/faiss/utils/simd_levels.cpp +196 -47
  175. data/vendor/faiss/faiss/utils/simd_levels.h +33 -8
  176. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  177. metadata +21 -5
  178. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +0 -15
@@ -13,8 +13,9 @@
13
13
  // "A Super Fast K-means for Indexing Vector Embeddings."
14
14
  // arXiv preprint arXiv:2603.20009.
15
15
  //
16
- // Use when: L2 metric, k >= 1024, d >= 128, dense float embeddings.
17
- // Do not use for: IP/cosine (use Clustering with cp.spherical=true), small k,
16
+ // Use when: L2 metric, or spherical inner-product clustering with
17
+ // cp.spherical=true, k >= 1024, d >= 128, dense float embeddings.
18
+ // Do not use for: small k,
18
19
  // binary data (use IndexBinaryIVF), or near-unit-sphere embeddings with
19
20
  // k < 4096 (chi-squared assumption breaks down).
20
21
  //
@@ -23,6 +24,7 @@
23
24
 
24
25
  #pragma once
25
26
 
27
+ #include <cstdint>
26
28
  #include <vector>
27
29
 
28
30
  #include <faiss/Clustering.h>
@@ -94,4 +96,33 @@ struct SuperKMeans {
94
96
  void train(idx_t n, const float* x);
95
97
  };
96
98
 
99
+ /// Reusable scratch for super_kmeans_assign_iteration; pass one instance across
100
+ /// a loop of calls to avoid reallocating (buffers grow only as needed).
101
+ struct SuperKMeansAssignScratch {
102
+ std::vector<float> Y_trail; // (k, d_trail) row-major, input to pdxify
103
+ std::vector<float> Y_pdx; // PDX-laid-out trailing block
104
+ std::vector<float> x_norms_partial; // ||X[i, 0:d_prime]||^2
105
+ std::vector<float> y_norms_partial; // ||Y[j, 0:d_prime]||^2
106
+ std::vector<float> partial_ip; // partial-GEMM tile buffer
107
+ };
108
+
109
+ /// One SuperKMeans iter-1+ assignment pass: partial GEMM over the front
110
+ /// `d_prime` dims + ADSampling progressive pruning over the PDX-laid-out
111
+ /// trailing block. Updates `tau` and `assignments` in place.
112
+ void super_kmeans_assign_iteration(
113
+ const float* X_tilde, // (n, d) points, row-major, rotated space
114
+ int n,
115
+ int d,
116
+ const float* Y_tilde, // (k, d) centroids, row-major, rotated space
117
+ int k,
118
+ float* tau, // in/out, len n; entry = exact full-d L2 to assignment
119
+ int32_t* assignments, // in/out, len n
120
+ int d_prime, // GEMM/pruning split, 1 <= d_prime < d
121
+ const float* ad_coeff, // ADSampling threshold table, len d+1
122
+ const SuperKMeansParameters& cp,
123
+ int64_t* total_pairs = nullptr, // optional out-counter
124
+ int64_t* pruned_at_gemm = nullptr, // optional out-counter
125
+ SuperKMeansAssignScratch* scratch =
126
+ nullptr); // optional, reuse across calls
127
+
97
128
  } // namespace faiss
@@ -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,
@@ -481,6 +513,44 @@ void HadamardRotation::apply_noalloc(idx_t n, const float* x, float* xt) const {
481
513
  }
482
514
  }
483
515
 
516
+ void HadamardRotation::reverse_transform(idx_t n, const float* xt, float* x)
517
+ const {
518
+ FAISS_THROW_IF_NOT_MSG(is_trained, "Transformation not trained yet");
519
+ FAISS_THROW_IF_NOT_MSG(
520
+ d_in == d_out,
521
+ "HadamardRotation inverse requires equal input/output dimensions");
522
+
523
+ const size_t p = d_out;
524
+ // Reverse of apply_noalloc: three unnormalized FWHT rounds scale norms
525
+ // by (sqrt(p))^3 = p*sqrt(p); the forward pass cancels this with
526
+ // total_scale = 1/(p*sqrt(p)), so the inverse applies the same factor.
527
+ const float inverse_scale = 1.0f / (p * std::sqrt(static_cast<float>(p)));
528
+
529
+ #pragma omp parallel for schedule(dynamic)
530
+ for (idx_t i = 0; i < n; i++) {
531
+ const float* xi = xt + i * p;
532
+ float* xo = x + i * p;
533
+
534
+ // The inverse reverses the three sign-flip/Hadamard factors.
535
+ std::memcpy(xo, xi, p * sizeof(float));
536
+ fwht_inplace(xo, p);
537
+
538
+ for (size_t j = 0; j < p; j++) {
539
+ xo[j] *= signs3[j];
540
+ }
541
+ fwht_inplace(xo, p);
542
+
543
+ for (size_t j = 0; j < p; j++) {
544
+ xo[j] *= signs2[j];
545
+ }
546
+ fwht_inplace(xo, p);
547
+
548
+ for (size_t j = 0; j < p; j++) {
549
+ xo[j] *= signs1[j] * inverse_scale;
550
+ }
551
+ }
552
+ }
553
+
484
554
  void HadamardRotation::check_identical(const VectorTransform& other) const {
485
555
  auto* hr = dynamic_cast<const HadamardRotation*>(&other);
486
556
  FAISS_THROW_IF_NOT_MSG(hr, "failed to cast to HadamardRotation");
@@ -1064,8 +1134,7 @@ ITQTransform::ITQTransform(int din, int dout, bool do_pca_in)
1064
1134
  }
1065
1135
 
1066
1136
  void ITQTransform::train(idx_t n, const float* x_in) {
1067
- FAISS_THROW_IF_NOT_MSG(
1068
- !is_trained, "ITQTransform has already been trained");
1137
+ FAISS_THROW_IF_MSG(is_trained, "ITQTransform has already been trained");
1069
1138
 
1070
1139
  size_t max_train_points = std::max(d_in * max_train_per_dim, 32768);
1071
1140
  const float* x =
@@ -144,6 +144,9 @@ struct HadamardRotation : VectorTransform {
144
144
 
145
145
  void apply_noalloc(idx_t n, const float* x, float* xt) const override;
146
146
 
147
+ /// Apply the inverse transform when d_in == d_out.
148
+ void reverse_transform(idx_t n, const float* xt, float* x) const override;
149
+
147
150
  void check_identical(const VectorTransform& other) const override;
148
151
 
149
152
  HadamardRotation() {}
@@ -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>
@@ -36,6 +38,7 @@
36
38
  #include <faiss/IndexPQ.h>
37
39
  #include <faiss/IndexPQFastScan.h>
38
40
  #include <faiss/IndexPreTransform.h>
41
+ #include <faiss/IndexRaBitQ.h>
39
42
  #include <faiss/IndexRefine.h>
40
43
  #include <faiss/IndexRowwiseMinMax.h>
41
44
  #include <faiss/IndexScalarQuantizer.h>
@@ -77,6 +80,7 @@ VectorTransform* Cloner::clone_VectorTransform(const VectorTransform* vt) {
77
80
  TRYCLONE(PCAMatrix, vt)
78
81
  TRYCLONE(ITQMatrix, vt)
79
82
  TRYCLONE(RandomRotationMatrix, vt)
83
+ TRYCLONE(HadamardRotation, vt)
80
84
  TRYCLONE(LinearTransform, vt) {
81
85
  FAISS_THROW_MSG("clone not supported for this type of VectorTransform");
82
86
  }
@@ -98,6 +102,7 @@ IndexIVF* Cloner::clone_IndexIVF(const IndexIVF* ivf) {
98
102
  TRYCLONE(IndexIVFResidualQuantizerFastScan, ivf)
99
103
  TRYCLONE(IndexIVFPQFastScan, ivf)
100
104
 
105
+ TRYCLONE(IndexIVFEDEN, ivf)
101
106
  TRYCLONE(IndexIVFRaBitQFastScan, ivf)
102
107
  TRYCLONE(IndexIVFRaBitQ, ivf)
103
108
 
@@ -135,6 +140,7 @@ IndexIDMap* clone_IndexIDMap(const IndexIDMap* im) {
135
140
  IndexHNSW* clone_IndexHNSW(const IndexHNSW* ihnsw) {
136
141
  TRYCLONE(IndexHNSW2Level, ihnsw)
137
142
  TRYCLONE(IndexHNSWFlatPanorama, ihnsw)
143
+ TRYCLONE(IndexHNSWRaBitQ, ihnsw)
138
144
  TRYCLONE(IndexHNSWFlat, ihnsw)
139
145
  TRYCLONE(IndexHNSWPQ, ihnsw)
140
146
  TRYCLONE(IndexHNSWSQ, ihnsw)
@@ -287,8 +293,10 @@ Index* Cloner::clone_Index(const Index* index) {
287
293
  TRYCLONE(IndexLattice, index)
288
294
  TRYCLONE(IndexRandom, index)
289
295
  TRYCLONE(IndexPQFastScan, index)
296
+ TRYCLONE(IndexEDEN, index)
290
297
 
291
298
  TRYCLONE(IndexScalarQuantizer, index)
299
+ TRYCLONE(IndexRaBitQ, index)
292
300
  TRYCLONE(MultiIndexQuantizer, index)
293
301
 
294
302
  if (const IndexIVF* ivf = dynamic_cast<const IndexIVF*>(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;
@@ -39,12 +39,8 @@ struct GpuClonerOptions {
39
39
  /// Set verbose options on the index
40
40
  bool verbose = false;
41
41
 
42
- /// use the cuVS implementation
43
- #if defined USE_NVIDIA_CUVS
44
- bool use_cuvs = true;
45
- #else
42
+ /// Use the cuVS implementation. Opt-in: see GpuIndexConfig::use_cuvs.
46
43
  bool use_cuvs = false;
47
- #endif
48
44
 
49
45
  /// This flag controls the CPU fallback logic for coarse quantizer
50
46
  /// component of the index. When set to false (default), the cloner will
@@ -107,12 +107,9 @@ struct GpuDistanceParams {
107
107
  /// execution
108
108
  int device = -1;
109
109
 
110
- /// Should the index dispatch down to cuVS?
111
- #if defined USE_NVIDIA_CUVS
112
- bool use_cuvs = true;
113
- #else
110
+ /// Should the search dispatch down to cuVS? Opt-in: see
111
+ /// GpuIndexConfig::use_cuvs.
114
112
  bool use_cuvs = false;
115
- #endif
116
113
  };
117
114
 
118
115
  /// A function that determines whether cuVS should be used based on various
@@ -38,12 +38,11 @@ struct GpuIndexConfig {
38
38
  /// more memory than is available on the GPU.
39
39
  MemorySpace memorySpace = MemorySpace::Device;
40
40
 
41
- /// Should the index dispatch down to cuVS?
42
- #if defined USE_NVIDIA_CUVS
43
- bool use_cuvs = true;
44
- #else
41
+ /// Should the index dispatch down to cuVS? Opt-in: dispatching to cuVS
42
+ /// selects a different implementation with its own numerical behaviour, so
43
+ /// it must be an explicit choice by the caller rather than a consequence of
44
+ /// how the binary happened to be built.
45
45
  bool use_cuvs = false;
46
- #endif
47
46
  };
48
47
 
49
48
  /// A centralized function that determines whether cuVS should
@@ -177,6 +176,33 @@ class GpuIndex : public faiss::Index {
177
176
  }
178
177
  };
179
178
 
179
+ virtual void addImplPrecomputed_(
180
+ idx_t n,
181
+ const float* x,
182
+ const idx_t* ids,
183
+ const idx_t* precomputed_idx);
184
+
185
+ virtual void addImplPrecomputed_ex_(
186
+ idx_t n,
187
+ const void* x,
188
+ NumericType numeric_type,
189
+ const idx_t* ids,
190
+ const idx_t* precomputed_idx);
191
+
192
+ /// Handles paged adds if the add set is too large, passes to
193
+ /// addImpl_ to actually perform the add for the current page
194
+ void addPaged_(
195
+ idx_t n,
196
+ const float* x,
197
+ const idx_t* ids,
198
+ const idx_t* precomputed_idx = nullptr);
199
+ void addPaged_ex_(
200
+ idx_t n,
201
+ const void* x,
202
+ NumericType numeric_type,
203
+ const idx_t* ids,
204
+ const idx_t* precomputed_idx = nullptr);
205
+
180
206
  /// Overridden to actually perform the search
181
207
  /// All data is guaranteed to be resident on our device
182
208
  virtual void searchImpl_(
@@ -209,22 +235,18 @@ class GpuIndex : public faiss::Index {
209
235
  }
210
236
 
211
237
  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
238
  /// Calls addImpl_ for a single page of GPU-resident data
222
- void addPage_(idx_t n, const float* x, const idx_t* ids);
239
+ void addPage_(
240
+ idx_t n,
241
+ const float* x,
242
+ const idx_t* ids,
243
+ const idx_t* precomputed_idx = nullptr);
223
244
  void addPage_ex_(
224
245
  idx_t n,
225
246
  const void* x,
226
247
  NumericType numeric_type,
227
- const idx_t* ids);
248
+ const idx_t* ids,
249
+ const idx_t* precomputed_idx = nullptr);
228
250
 
229
251
  /// Calls searchImpl_ for a single page of GPU-resident data
230
252
  void searchNonPaged_(
@@ -47,7 +47,9 @@ enum class graph_build_algo {
47
47
  /// Use NN-Descent to build all-neighbors knn graph
48
48
  NN_DESCENT,
49
49
  /// Use iterative search to build knn graph
50
- ITERATIVE_SEARCH
50
+ ITERATIVE_SEARCH,
51
+ /// Exact knn graph via tiled brute force
52
+ BRUTE_FORCE
51
53
  };
52
54
 
53
55
  /// A type for specifying how PQ codebooks are created.
@@ -173,6 +175,34 @@ struct IVFPQSearchCagraConfig {
173
175
  uint32_t max_internal_batch_size = 4096;
174
176
  };
175
177
 
178
+ /// Knobs for the multi-GPU build path, selected by listing more than one
179
+ /// device in GpuIndexCagraConfig::devices. Degrees, build_algo and metric
180
+ /// still come from GpuIndexCagraConfig.
181
+ struct AllNeighborsCagraConfig {
182
+ /// Number of overlapping clusters the dataset is partitioned into.
183
+ /// 0 selects max(2 * number of devices, 4).
184
+ size_t n_clusters = 0;
185
+
186
+ /// Clusters each vector is assigned to. Must be >= 2: with 1 there are no
187
+ /// cross-cluster edges and recall collapses. 0 selects 2.
188
+ size_t overlap_factor = 2;
189
+
190
+ /// Bounds IVF-PQ search memory during the knn build. cuVS's default can
191
+ /// exhaust device memory around 100M vectors; 8192 avoids that and does
192
+ /// not change recall. 0 keeps cuVS's default.
193
+ uint32_t ivf_pq_search_batch_size = 0;
194
+
195
+ /// IVF-PQ refinement multiplier. The refine pass runs on the CPU, so cost
196
+ /// scales with this. Raising it above 1.0 measured both slower and less
197
+ /// accurate, so it is separate from the shared refine_rate.
198
+ float refinement_rate = 1.0f;
199
+
200
+ /// Size the IVF-PQ index for one cluster rather than the whole dataset.
201
+ /// cuVS reuses these params for every cluster without rescaling, so
202
+ /// sizing from the full dataset badly over-partitions each one.
203
+ bool ivf_pq_size_from_cluster = true;
204
+ };
205
+
176
206
  struct GpuIndexCagraConfig : public GpuIndexConfig {
177
207
  /// Degree of input graph for pruning.
178
208
  size_t intermediate_graph_degree = 128;
@@ -190,6 +220,27 @@ struct GpuIndexCagraConfig : public GpuIndexConfig {
190
220
 
191
221
  /// Whether to use MST optimization to guarantee graph connectivity.
192
222
  bool guarantee_connectivity = false;
223
+
224
+ /// Devices to build on. More than one selects the multi-GPU build in
225
+ /// train(); see AllNeighborsCagraConfig and train() for its restrictions.
226
+ /// Empty or one device uses GpuIndexConfig::device as usual.
227
+ std::vector<int> devices;
228
+
229
+ AllNeighborsCagraConfig all_neighbors_params;
230
+
231
+ /// Build the HNSW upper levels on the GPU during copyTo() instead of by
232
+ /// CPU insertion. Roughly 10x faster, but measured worse recall than
233
+ /// base_level_only, which is cheaper still. Off by default.
234
+ bool gpu_hnsw_upper_levels = false;
235
+
236
+ /// intermediate_graph_degree for the per-level subgraph builds.
237
+ /// 0 = twice the upper-level degree.
238
+ size_t gpu_hnsw_intermediate_degree = 0;
239
+
240
+ /// MST connectivity pass on the per-level subgraphs. On by default:
241
+ /// upper levels are walked greedily with no backtracking, so a
242
+ /// disconnected component is a trap the descent cannot escape.
243
+ bool gpu_hnsw_guarantee_connectivity = true;
193
244
  };
194
245
 
195
246
  enum class search_algo {
@@ -317,6 +368,20 @@ struct GpuIndexCagra : public GpuIndex {
317
368
  idx_t* labels,
318
369
  const SearchParameters* search_params) const override;
319
370
 
371
+ /// Multi-GPU build path taken by train() when cagraConfig_.devices lists
372
+ /// more than one device: cuVS `all_neighbors` knn graph construction over
373
+ /// overlapping clusters, followed by graph pruning. Leaves the result in
374
+ /// merged_knngraph_ for copyTo(); index_ stays empty.
375
+ void trainAllNeighbors_(idx_t n, const float* x);
376
+
377
+ /// Populate HNSW levels >= 1 of `index` by building a CAGRA graph over
378
+ /// each level's node subset on the GPU. Requires the level table to be
379
+ /// prepared and the storage populated. Returns the max level.
380
+ void buildHnswUpperLevelsGpu_(faiss::IndexHNSWCagra* index, int max_lvl)
381
+ const;
382
+
383
+ void copyToMultiGpu_(faiss::IndexHNSWCagra* index) const;
384
+
320
385
  /// Our configuration options
321
386
  const GpuIndexCagraConfig cagraConfig_;
322
387
 
@@ -329,6 +394,11 @@ struct GpuIndexCagra : public GpuIndex {
329
394
  std::shared_ptr<CuvsCagra<half>>,
330
395
  std::shared_ptr<CuvsCagra<int8_t>>>
331
396
  index_;
397
+
398
+ /// Multi-GPU state: populated by trainAllNeighbors_(), used by copyTo()
399
+ std::vector<idx_t> merged_knngraph_;
400
+ idx_t merged_knngraph_degree_ = 0;
401
+ const float* multi_gpu_dataset_ = nullptr;
332
402
  };
333
403
 
334
404
  } // 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