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
@@ -223,18 +223,18 @@ struct QuantizerTemplate<
223
223
  };
224
224
 
225
225
  /**********************************************************
226
- * TurboQuant MSE quantizer
226
+ * Lloyd-Max scalar quantizer
227
227
  **********************************************************/
228
228
 
229
- // NEON TurboQuantMSE: decode via gather, encode stays scalar.
229
+ // NEON Lloyd-Max: decode via gather, encode stays scalar.
230
230
  // NEON doesn't have movemask so 1-bit encode is also scalar.
231
- #define DEFINE_TQMSE_NEON_SPECIALIZATION(NBITS, UNPACK_FN) \
231
+ #define DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(NBITS, UNPACK_FN) \
232
232
  template <> \
233
- struct QuantizerTurboQuantMSE<NBITS, SIMDLevel::ARM_NEON> \
234
- : QuantizerTurboQuantMSE<NBITS, SIMDLevel::NONE> { \
235
- using Base = QuantizerTurboQuantMSE<NBITS, SIMDLevel::NONE>; \
233
+ struct QuantizerLloydMax<NBITS, SIMDLevel::ARM_NEON> \
234
+ : QuantizerLloydMax<NBITS, SIMDLevel::NONE> { \
235
+ using Base = QuantizerLloydMax<NBITS, SIMDLevel::NONE>; \
236
236
  \
237
- QuantizerTurboQuantMSE(size_t d, const std::vector<float>& trained) \
237
+ QuantizerLloydMax(size_t d, const std::vector<float>& trained) \
238
238
  : Base(d, trained) { \
239
239
  assert(d % 8 == 0); \
240
240
  } \
@@ -256,19 +256,19 @@ struct QuantizerTemplate<
256
256
  } \
257
257
  }
258
258
 
259
- DEFINE_TQMSE_NEON_SPECIALIZATION(1, unpack_8x1bit_to_u8);
260
- DEFINE_TQMSE_NEON_SPECIALIZATION(2, unpack_8x2bit_to_u8);
261
- DEFINE_TQMSE_NEON_SPECIALIZATION(3, unpack_8x3bit_to_u8);
262
- DEFINE_TQMSE_NEON_SPECIALIZATION(4, unpack_8x4bit_to_u8);
259
+ DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(1, unpack_8x1bit_to_u8);
260
+ DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(2, unpack_8x2bit_to_u8);
261
+ DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(3, unpack_8x3bit_to_u8);
262
+ DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(4, unpack_8x4bit_to_u8);
263
263
 
264
- #undef DEFINE_TQMSE_NEON_SPECIALIZATION
264
+ #undef DEFINE_LLOYD_MAX_NEON_SPECIALIZATION
265
265
 
266
266
  template <>
267
- struct QuantizerTurboQuantMSE<8, SIMDLevel::ARM_NEON>
268
- : QuantizerTurboQuantMSE<8, SIMDLevel::NONE> {
269
- using Base = QuantizerTurboQuantMSE<8, SIMDLevel::NONE>;
267
+ struct QuantizerLloydMax<8, SIMDLevel::ARM_NEON>
268
+ : QuantizerLloydMax<8, SIMDLevel::NONE> {
269
+ using Base = QuantizerLloydMax<8, SIMDLevel::NONE>;
270
270
 
271
- QuantizerTurboQuantMSE(size_t d, const std::vector<float>& trained)
271
+ QuantizerLloydMax(size_t d, const std::vector<float>& trained)
272
272
  : Base(d, trained) {
273
273
  assert(d % 8 == 0);
274
274
  }
@@ -84,6 +84,24 @@ inline auto dispatch_with_fallback(LambdaType&& action) {
84
84
  }
85
85
  }
86
86
 
87
+ /** Run action at current_level; on a null result, retry the next-lower level,
88
+ * down to NONE (terminal). action is called per level tried, so never moved. */
89
+ template <int available_levels, SIMDLevel current_level, typename LambdaType>
90
+ inline auto dispatch_simd_level_or_lower(LambdaType& action) {
91
+ if constexpr (current_level == SIMDLevel::NONE) {
92
+ return action.template operator()<SIMDLevel::NONE>();
93
+ } else {
94
+ if constexpr (available_levels & (1 << int(current_level))) {
95
+ if (auto result = action.template operator()<current_level>()) {
96
+ return result;
97
+ }
98
+ }
99
+ return dispatch_simd_level_or_lower<
100
+ available_levels,
101
+ get_simd_fallback(current_level)>(action);
102
+ }
103
+ }
104
+
87
105
  /** The complete dispatching function. It takes into account:
88
106
  * - the currently selected SIMD level
89
107
  * - the compiled in SIMD levels (given by COMPILE_SIMD_XXX)
@@ -163,6 +181,18 @@ inline auto with_selected_simd_levels(LambdaType&& action) {
163
181
  #endif
164
182
  }
165
183
 
184
+ /** Like with_selected_simd_levels, but for factory actions that return null to
185
+ * decline a level (e.g. AVX-512 needing d % 16 == 0). Falls back to the next
186
+ * lower level, down to NONE. */
187
+ template <int available_levels, typename LambdaType>
188
+ inline auto with_simd_level_fallback(const LambdaType& action) {
189
+ return with_selected_simd_levels<available_levels>(
190
+ [&action]<SIMDLevel SL>() {
191
+ return dispatch_simd_level_or_lower<available_levels, SL>(
192
+ action);
193
+ });
194
+ }
195
+
166
196
  /**
167
197
  * Dispatch to a lambda with SIMDLevel as a compile-time constant.
168
198
  *
@@ -198,15 +228,6 @@ inline auto with_simd_level(LambdaType&& action) {
198
228
  std::forward<LambdaType>(action));
199
229
  }
200
230
 
201
- /**
202
- * Use for functions with AVX512_SPR-specific implementations.
203
- */
204
- template <typename LambdaType>
205
- inline auto with_simd_level_spr(LambdaType&& action) {
206
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0_SPR>(
207
- std::forward<LambdaType>(action));
208
- }
209
-
210
231
  /**
211
232
  * Use for functions implemented with simdXintY (256-bit) operations
212
233
  * that don't have dedicated AVX512 or SVE implementations.
@@ -21,11 +21,13 @@
21
21
  #include <faiss/Index2Layer.h>
22
22
  #include <faiss/IndexAdditiveQuantizer.h>
23
23
  #include <faiss/IndexAdditiveQuantizerFastScan.h>
24
+ #include <faiss/IndexEDEN.h>
24
25
  #include <faiss/IndexFlat.h>
25
26
  #include <faiss/IndexHNSW.h>
26
27
  #include <faiss/IndexIVF.h>
27
28
  #include <faiss/IndexIVFAdditiveQuantizer.h>
28
29
  #include <faiss/IndexIVFAdditiveQuantizerFastScan.h>
30
+ #include <faiss/IndexIVFEDEN.h>
29
31
  #include <faiss/IndexIVFFlat.h>
30
32
  #include <faiss/IndexIVFFlatPanorama.h>
31
33
  #include <faiss/IndexIVFPQ.h>
@@ -315,8 +317,8 @@ Index* parse_coarse_quantizer(
315
317
  SVSStorageKind storage = SVSStorageKind::SVS_FP32;
316
318
  if (sm[3].matched) {
317
319
  std::string s = sm[3].str().substr(1);
318
- if (s == "SQI8") {
319
- storage = SVSStorageKind::SVS_SQI8;
320
+ if (s == "SQ8") {
321
+ storage = SVSStorageKind::SVS_SQ8;
320
322
  } else if (s == "FP16") {
321
323
  storage = SVSStorageKind::SVS_FP16;
322
324
  } else if (s == "FP32") {
@@ -514,6 +516,18 @@ IndexIVF* parse_IndexIVF(
514
516
  uint8_t nb_bits = sm[1].length() > 0 ? std::stoi(sm[1].str()) : 1;
515
517
  return new IndexIVFRaBitQ(get_q(), d, nlist, mt, own_il, nb_bits);
516
518
  }
519
+ // IndexIVFEDEN with optional nb_bits (1-8) and scale type.
520
+ // Accepts: "EDEN" (default 1-bit), "EDEN{nb_bits}" (e.g., "EDEN4"),
521
+ // or "EDEN{nb_bits}BIASED" for the MSE-minimizing scale.
522
+ if (match("EDEN([1-8])?(BIASED|BIAS)?")) {
523
+ uint8_t nb_bits = sm[1].length() > 0 ? std::stoi(sm[1].str()) : 1;
524
+ EDENScaleType scale_type =
525
+ sm[2].str() == "BIASED" || sm[2].str() == "BIAS"
526
+ ? EDENScaleType_BIASED
527
+ : EDENScaleType_UNBIASED;
528
+ return new IndexIVFEDEN(
529
+ get_q(), d, nlist, mt, own_il, nb_bits, scale_type);
530
+ }
517
531
  // Accepts: "RaBitQfs" (default 1-bit, batch size 32)
518
532
  // "RaBitQfs{nb_bits}" (e.g., "RaBitQfs4")
519
533
  // "RaBitQfs_64" (1-bit, batch size 64)
@@ -681,16 +695,16 @@ Index* parse_svs_datatype(
681
695
  }
682
696
  FAISS_ASSERT(false && "Unsupported SVS index type for Float16");
683
697
  }
684
- if (re_match(datatype_string, "SQI8", sm)) {
698
+ if (re_match(datatype_string, "SQ8", sm)) {
685
699
  if (index_type == "Vamana") {
686
700
  return new IndexSVSVamana(
687
- d, std::stoul(arg_string), mt, SVSStorageKind::SVS_SQI8);
701
+ d, std::stoul(arg_string), mt, SVSStorageKind::SVS_SQ8);
688
702
  }
689
703
  if (index_type == "IVF") {
690
704
  return new IndexSVSIVF(
691
- d, std::stoul(arg_string), mt, SVSStorageKind::SVS_SQI8);
705
+ d, std::stoul(arg_string), mt, SVSStorageKind::SVS_SQ8);
692
706
  }
693
- FAISS_ASSERT(false && "Unsupported SVS index type for SQI8");
707
+ FAISS_ASSERT(false && "Unsupported SVS index type for SQ8");
694
708
  }
695
709
  if (re_match(datatype_string, "(LVQ[0-9]+x[0-9]+)", sm)) {
696
710
  if (index_type == "Vamana") {
@@ -917,6 +931,18 @@ Index* parse_other_indexes(
917
931
  return new IndexRaBitQ(d, metric, nb_bits);
918
932
  }
919
933
 
934
+ // IndexEDEN with optional nb_bits (1-8) and scale type.
935
+ // Accepts: "EDEN" (default 1-bit), "EDEN{nb_bits}" (e.g., "EDEN4"),
936
+ // or "EDEN{nb_bits}BIASED" for the MSE-minimizing scale.
937
+ if (match("EDEN([1-8])?(BIASED|BIAS)?")) {
938
+ uint8_t nb_bits = sm[1].length() > 0 ? std::stoi(sm[1].str()) : 1;
939
+ EDENScaleType scale_type =
940
+ sm[2].str() == "BIASED" || sm[2].str() == "BIAS"
941
+ ? EDENScaleType_BIASED
942
+ : EDENScaleType_UNBIASED;
943
+ return new IndexEDEN(d, metric, nb_bits, scale_type);
944
+ }
945
+
920
946
  if (match("RaBitQfs([1-9])?(_[0-9]+)?")) {
921
947
  uint8_t nb_bits = sm[1].length() > 0 ? std::stoi(sm[1].str()) : 1;
922
948
  int bbs = mres_to_int(sm[2], 32, 1);
@@ -80,7 +80,7 @@ idx_t DirectMap::get(idx_t key) const {
80
80
  return lo;
81
81
  } else if (type == Hashtable) {
82
82
  auto res = hashtable.find(key);
83
- FAISS_THROW_IF_NOT_MSG(res != hashtable.end(), "key not found");
83
+ FAISS_THROW_IF_MSG(res == hashtable.end(), "key not found");
84
84
  return res->second;
85
85
  } else {
86
86
  FAISS_THROW_MSG("direct map not initialized");
@@ -371,8 +371,8 @@ ArrayInvertedListsPanorama::ArrayInvertedListsPanorama(
371
371
  pano(code_size_in, n_levels_in, batch_size) {
372
372
  FAISS_THROW_IF_NOT(n_levels_in > 0);
373
373
  FAISS_THROW_IF_NOT(code_size_in % sizeof(float) == 0);
374
- FAISS_THROW_IF_NOT_MSG(
375
- !use_iterator,
374
+ FAISS_THROW_IF_MSG(
375
+ use_iterator,
376
376
  "IndexIVFFlatPanorama does not support iterators, use vanilla IndexIVFFlat instead");
377
377
  FAISS_ASSERT(level_width % sizeof(float) == 0);
378
378
 
@@ -405,7 +405,8 @@ void OnDiskInvertedLists::update_entries(
405
405
  size_t n_entry,
406
406
  const idx_t* ids_in,
407
407
  const uint8_t* codes_in) {
408
- FAISS_THROW_IF_NOT(!read_only);
408
+ FAISS_THROW_IF_MSG(
409
+ read_only, "cannot modify a read-only OnDiskInvertedLists");
409
410
  if (n_entry == 0) {
410
411
  return;
411
412
  }
@@ -422,7 +423,8 @@ size_t OnDiskInvertedLists::add_entries(
422
423
  size_t n_entry,
423
424
  const idx_t* ids,
424
425
  const uint8_t* code) {
425
- FAISS_THROW_IF_NOT(!read_only);
426
+ FAISS_THROW_IF_MSG(
427
+ read_only, "cannot modify a read-only OnDiskInvertedLists");
426
428
  locks->lock_1(list_no);
427
429
  size_t o = list_size(list_no);
428
430
  resize_locked(list_no, n_entry + o);
@@ -432,7 +434,8 @@ size_t OnDiskInvertedLists::add_entries(
432
434
  }
433
435
 
434
436
  void OnDiskInvertedLists::resize(size_t list_no, size_t new_size) {
435
- FAISS_THROW_IF_NOT(!read_only);
437
+ FAISS_THROW_IF_MSG(
438
+ read_only, "cannot modify a read-only OnDiskInvertedLists");
436
439
  locks->lock_1(list_no);
437
440
  resize_locked(list_no, new_size);
438
441
  locks->unlock_1(list_no);
@@ -799,7 +802,19 @@ InvertedLists* OnDiskInvertedListsIOHook::read_ArrayInvertedLists(
799
802
  OnDiskInvertedLists::List& l = ails->lists[i];
800
803
  l.size = l.capacity = sizes[i];
801
804
  l.offset = o;
802
- o += l.size * (sizeof(idx_t) + ails->code_size);
805
+ size_t elem_size = add_no_overflow(
806
+ sizeof(idx_t), ails->code_size, "OnDisk inverted list element");
807
+ size_t list_bytes =
808
+ mul_no_overflow(l.size, elem_size, "OnDisk inverted list");
809
+ o = add_no_overflow(o, list_bytes, "OnDisk inverted list offset");
810
+ FAISS_THROW_IF_NOT_FMT(
811
+ o <= ails->totsize,
812
+ "inverted list %zu at offset %zu with %zu bytes exceeds "
813
+ "mapped file size %zu",
814
+ i,
815
+ l.offset,
816
+ list_bytes,
817
+ ails->totsize);
803
818
  }
804
819
  // resume normal reading of file
805
820
  fseek(fdesc, o, SEEK_SET);
@@ -151,7 +151,9 @@ int64_t PyCallbackShardingFunction::operator()(int64_t i, int64_t shard_count) {
151
151
  if (shard_id == nullptr) {
152
152
  FAISS_THROW_MSG("propagate py error");
153
153
  }
154
- return PyLong_AsLongLong(shard_id);
154
+ int64_t rv = PyLong_AsLongLong(shard_id);
155
+ Py_DECREF(shard_id);
156
+ return rv;
155
157
  }
156
158
 
157
159
  PyCallbackShardingFunction::~PyCallbackShardingFunction() {
@@ -30,6 +30,7 @@
30
30
  #include <faiss/impl/AuxIndexStructures.h>
31
31
  #include <faiss/impl/FaissAssert.h>
32
32
  #include <faiss/impl/IDSelector.h>
33
+ #include <faiss/impl/mapped_io.h>
33
34
 
34
35
  #include <algorithm>
35
36
  #include <concepts>
@@ -248,4 +249,63 @@ struct FaissResultsAllocator : public svs_runtime::ResultsAllocator {
248
249
  using LabelsConverter = OutputBufferConverter<size_t, faiss::idx_t>;
249
250
  mutable LabelsConverter labels_converter;
250
251
  };
252
+
253
+ // Helper for memory-mapped SVS index loading.
254
+ // Acquires a pointer to the remaining mapped region and returns the data
255
+ // pointer, size in bytes, and starting position. Validates that mmap returns
256
+ // expected byte count. MappedFileIOReader::mmap returns number of items (not
257
+ // bytes); we always request size=1 so items == bytes, but we name variables
258
+ // explicitly to avoid confusion.
259
+ struct MmapSpan {
260
+ void* data = nullptr;
261
+ size_t size_bytes = 0;
262
+ size_t start_pos = 0;
263
+ };
264
+
265
+ inline MmapSpan acquire_mmap_span(MappedFileIOReader* mf) {
266
+ FAISS_THROW_IF_NOT(mf);
267
+ FAISS_THROW_IF_NOT(mf->mmap_owner);
268
+ size_t pos = mf->pos;
269
+ size_t size_to_end = mf->mmap_owner->size() - pos;
270
+ // Reject an empty span: MappedFileIOReader::mmap() returns 0 without
271
+ // writing *ptr when no bytes remain, which would otherwise pass the
272
+ // size check below and hand a nullptr / zero size to the SVS runtime
273
+ // (e.g. for a truncated or corrupt file).
274
+ FAISS_THROW_IF_NOT_FMT(
275
+ size_to_end > 0,
276
+ "acquire_mmap_span: no mapped bytes remain at reader position %zu",
277
+ pos);
278
+ void* data = nullptr;
279
+ // mmap returns actual_nitems; with size=1 this equals bytes
280
+ size_t actual_nitems = mf->mmap(&data, 1, size_to_end);
281
+ FAISS_THROW_IF_NOT_FMT(
282
+ actual_nitems == size_to_end,
283
+ "mmap() returned unexpected size: %zu items (expected %zu bytes)",
284
+ actual_nitems,
285
+ size_to_end);
286
+ return {data, size_to_end, pos};
287
+ }
288
+
289
+ // Adjusts MappedFileIOReader position after SVS consumes read_bytes,
290
+ // storing mmap_owner reference to keep mapping alive.
291
+ inline void finalize_mmap_span(
292
+ MappedFileIOReader* mf,
293
+ const MmapSpan& span,
294
+ size_t read_bytes,
295
+ std::shared_ptr<MmappedFileMappingOwner>& mmap_owner_out) {
296
+ // Take ownership of the mapping BEFORE validating read_bytes. By this point
297
+ // the caller's impl already holds pointers into the mapped region, so the
298
+ // index must keep the mapping alive even on the throw path. Otherwise the
299
+ // index would be left with a live impl but no mmap_owner, and once the
300
+ // reader's mapping reference is released the index destructor (which calls
301
+ // destroy(impl)) would touch unmapped memory.
302
+ mmap_owner_out = mf->mmap_owner;
303
+ FAISS_THROW_IF_NOT_FMT(
304
+ read_bytes > 0 && read_bytes <= span.size_bytes,
305
+ "map_to_memory returned invalid read_bytes: %zu (span size %zu)",
306
+ read_bytes,
307
+ span.size_bytes);
308
+ mf->pos = span.start_pos + read_bytes;
309
+ }
310
+
251
311
  } // namespace faiss
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  #include <faiss/Index.h>
25
+ #include <faiss/impl/mapped_io.h>
25
26
  #include <faiss/svs/IndexSVSFaissUtils.h>
26
27
  #include <faiss/svs/IndexSVSFlat.h>
27
28
 
@@ -55,11 +56,16 @@ void IndexSVSFlat::add(idx_t n, const float* x) {
55
56
 
56
57
  void IndexSVSFlat::reset() {
57
58
  if (impl) {
58
- auto status = impl->reset();
59
+ // Destroy impl to ensure no dangling pointers to mmap region remain.
60
+ // FlatIndex::reset may retain views; destroy guarantees clean state
61
+ // matching Vamana static behavior.
62
+ auto status = svs_runtime::FlatIndex::destroy(impl);
59
63
  if (!status.ok()) {
60
64
  FAISS_THROW_MSG(status.message());
61
65
  }
66
+ impl = nullptr;
62
67
  }
68
+ mmap_owner.reset(); // Release the memory mapping
63
69
  ntotal = 0;
64
70
  }
65
71
 
@@ -114,4 +120,23 @@ void IndexSVSFlat::deserialize_impl(std::istream& in) {
114
120
  FAISS_THROW_IF_NOT_MSG(impl, "Failed to load SVS Flat index.");
115
121
  }
116
122
 
123
+ void IndexSVSFlat::map_to(MappedFileIOReader* mf) {
124
+ FAISS_THROW_IF_MSG(impl, "Cannot map_to: SVS index already loaded.");
125
+ FAISS_THROW_IF_NOT(mf);
126
+
127
+ MmapSpan span = acquire_mmap_span(mf);
128
+
129
+ auto svs_metric = to_svs_metric(metric_type);
130
+
131
+ size_t read_bytes = 0;
132
+ auto status = svs_runtime::FlatIndex::map_to_memory(
133
+ &impl, span.data, span.size_bytes, svs_metric, &read_bytes);
134
+
135
+ if (!status.ok()) {
136
+ FAISS_THROW_MSG(status.message());
137
+ }
138
+
139
+ finalize_mmap_span(mf, span, read_bytes, mmap_owner);
140
+ }
141
+
117
142
  } // namespace faiss
@@ -29,9 +29,14 @@
29
29
  #include <svs/runtime/flat_index.h>
30
30
 
31
31
  #include <iostream>
32
+ #include <memory>
32
33
 
33
34
  namespace faiss {
34
35
 
36
+ // Forward declarations
37
+ struct MappedFileIOReader;
38
+ struct MmappedFileMappingOwner;
39
+
35
40
  struct IndexSVSFlat : Index {
36
41
  // sequential labels
37
42
  size_t nlabels{0};
@@ -56,10 +61,18 @@ struct IndexSVSFlat : Index {
56
61
  /* The actual SVS implementation */
57
62
  svs_runtime::FlatIndex* impl{nullptr};
58
63
 
64
+ // Holds a reference to the memory-mapped file owner to keep the memory
65
+ // mapping alive for the lifetime of this index. Only used when index is
66
+ // loaded via map_to() with memory-mapped I/O.
67
+ std::shared_ptr<MmappedFileMappingOwner> mmap_owner{nullptr};
68
+
59
69
  /* Serialization */
60
70
  void serialize_impl(std::ostream& out) const;
61
71
  void deserialize_impl(std::istream& in);
62
72
 
73
+ /* Memory-mapped deserialization */
74
+ void map_to(MappedFileIOReader* mf);
75
+
63
76
  protected:
64
77
  /* Initializes the implementation*/
65
78
  virtual void create_impl();
@@ -222,7 +222,7 @@ size_t IndexSVSIVF::remove_ids(const IDSelector& sel) {
222
222
  }
223
223
 
224
224
  void IndexSVSIVF::create_impl(idx_t n, const float* x) {
225
- FAISS_THROW_IF_NOT(!impl);
225
+ FAISS_THROW_IF_MSG(impl, "impl already created");
226
226
  ntotal = 0;
227
227
  auto svs_metric = to_svs_metric(metric_type);
228
228
  auto svs_storage_kind = to_svs_storage_kind(storage_kind);
@@ -119,7 +119,7 @@ void IndexSVSIVFLeanVec::deserialize_training_data(std::istream& in) {
119
119
  }
120
120
 
121
121
  void IndexSVSIVFLeanVec::create_impl(idx_t n, const float* x) {
122
- FAISS_THROW_IF_NOT(!impl);
122
+ FAISS_THROW_IF_MSG(impl, "impl already created");
123
123
  ntotal = 0;
124
124
  auto svs_metric = to_svs_metric(metric_type);
125
125
  auto svs_storage_kind = to_svs_storage_kind(storage_kind);
@@ -25,6 +25,7 @@
25
25
  #include <faiss/svs/IndexSVSVamana.h>
26
26
 
27
27
  #include <faiss/Index.h>
28
+ #include <faiss/impl/mapped_io.h>
28
29
 
29
30
  #include <svs/runtime/api_defs.h>
30
31
  #include <svs/runtime/dynamic_vamana_index.h>
@@ -187,6 +188,7 @@ void IndexSVSVamana::reset() {
187
188
  }
188
189
  stored_vectors.clear();
189
190
  stored_vectors_valid = true;
191
+ mmap_owner.reset(); // Release the memory mapping
190
192
  is_trained = false;
191
193
  ntotal = 0;
192
194
  }
@@ -266,7 +268,7 @@ size_t IndexSVSVamana::remove_ids(const IDSelector& sel) {
266
268
  }
267
269
 
268
270
  void IndexSVSVamana::create_impl(idx_t n, const float* x) {
269
- FAISS_THROW_IF_NOT(!impl);
271
+ FAISS_THROW_IF_MSG(impl, "impl already created");
270
272
  ntotal = 0;
271
273
  auto svs_metric = to_svs_metric(metric_type);
272
274
  auto svs_storage_kind = to_svs_storage_kind(storage_kind);
@@ -363,4 +365,32 @@ svs_runtime::DynamicVamanaIndex* IndexSVSVamana::dynamic_impl() const {
363
365
  return static_cast<svs_runtime::DynamicVamanaIndex*>(impl);
364
366
  }
365
367
 
368
+ void IndexSVSVamana::map_to(MappedFileIOReader* mf) {
369
+ FAISS_THROW_IF_MSG(
370
+ !is_static,
371
+ "map_to() is only supported for static Vamana indices.");
372
+ FAISS_THROW_IF_MSG(impl, "Cannot map_to: SVS index already loaded.");
373
+ FAISS_THROW_IF_NOT(mf);
374
+
375
+ MmapSpan span = acquire_mmap_span(mf);
376
+
377
+ auto svs_metric = to_svs_metric(metric_type);
378
+ auto svs_storage_kind = to_svs_storage_kind(storage_kind);
379
+
380
+ size_t read_bytes = 0;
381
+ auto status = svs_runtime::VamanaIndex::map_to_memory(
382
+ &impl,
383
+ span.data,
384
+ span.size_bytes,
385
+ svs_metric,
386
+ svs_storage_kind,
387
+ &read_bytes);
388
+
389
+ if (!status.ok()) {
390
+ FAISS_THROW_MSG(status.message());
391
+ }
392
+
393
+ finalize_mmap_span(mf, span, read_bytes, mmap_owner);
394
+ }
395
+
366
396
  } // namespace faiss
@@ -30,11 +30,16 @@
30
30
  #include <svs/runtime/dynamic_vamana_index.h>
31
31
 
32
32
  #include <iostream>
33
+ #include <memory>
33
34
  #include <type_traits>
34
35
  #include <vector>
35
36
 
36
37
  namespace faiss {
37
38
 
39
+ // Forward declarations
40
+ struct MappedFileIOReader;
41
+ struct MmappedFileMappingOwner;
42
+
38
43
  struct SearchParametersSVSVamana : public SearchParameters {
39
44
  size_t search_window_size = 0;
40
45
  size_t search_buffer_capacity = 0;
@@ -44,7 +49,7 @@ struct SearchParametersSVSVamana : public SearchParameters {
44
49
  enum SVSStorageKind {
45
50
  SVS_FP32,
46
51
  SVS_FP16,
47
- SVS_SQI8,
52
+ SVS_SQ8,
48
53
  SVS_LVQ4x0,
49
54
  SVS_LVQ4x4,
50
55
  SVS_LVQ4x8,
@@ -61,7 +66,7 @@ inline svs_runtime::StorageKind to_svs_storage_kind(SVSStorageKind kind) {
61
66
  return svs_runtime::StorageKind::FP32;
62
67
  case SVS_FP16:
63
68
  return svs_runtime::StorageKind::FP16;
64
- case SVS_SQI8:
69
+ case SVS_SQ8:
65
70
  return svs_runtime::StorageKind::SQI8;
66
71
  case SVS_LVQ4x0:
67
72
  return svs_runtime::StorageKind::LVQ4x0;
@@ -141,10 +146,18 @@ struct IndexSVSVamana : Index {
141
146
  void serialize_impl(std::ostream& out) const;
142
147
  virtual void deserialize_impl(std::istream& in);
143
148
 
149
+ /* Memory-mapped deserialization for static indices */
150
+ virtual void map_to(MappedFileIOReader* mf);
151
+
144
152
  /* The actual SVS implementation (VamanaIndex is the base for both
145
153
  static and dynamic variants) */
146
154
  svs_runtime::VamanaIndex* impl{nullptr};
147
155
 
156
+ // Holds a reference to the memory-mapped file owner to keep the memory
157
+ // mapping alive for the lifetime of this index. Only used when index is
158
+ // loaded via map_to() with memory-mapped I/O.
159
+ std::shared_ptr<MmappedFileMappingOwner> mmap_owner{nullptr};
160
+
148
161
  // The SVS runtime API does not expose vector retrieval, so we keep a copy
149
162
  // of added vectors to support reconstruct(). When used as a coarse
150
163
  // quantizer this holds only nlist centroids.
@@ -29,7 +29,6 @@
29
29
  #include <svs/runtime/vamana_index.h>
30
30
 
31
31
  #include <memory>
32
- #include <span>
33
32
  #include "faiss/svs/IndexSVSVamana.h"
34
33
 
35
34
  namespace faiss {
@@ -122,7 +121,7 @@ void IndexSVSVamanaLeanVec::deserialize_training_data(std::istream& in) {
122
121
  }
123
122
 
124
123
  void IndexSVSVamanaLeanVec::create_impl(idx_t n, const float* x) {
125
- FAISS_THROW_IF_NOT(!impl);
124
+ FAISS_THROW_IF_MSG(impl, "impl already created");
126
125
  ntotal = 0;
127
126
  auto svs_metric = to_svs_metric(metric_type);
128
127
  auto svs_storage_kind = to_svs_storage_kind(storage_kind);
@@ -118,7 +118,7 @@ struct HeapWithBucketsForHamming32<
118
118
  }
119
119
  }
120
120
 
121
- if (valid_counter == 8) {
121
+ if (valid_counter == 0) {
122
122
  continue; // Skip if all vectors are filtered out
123
123
  }
124
124
 
@@ -291,6 +291,11 @@ void exhaustive_inner_product_seq(
291
291
  BlockResultHandler& res) {
292
292
  using SingleResultHandler =
293
293
  typename BlockResultHandler::SingleResultHandler;
294
+
295
+ if (nx == 0) {
296
+ return;
297
+ }
298
+
294
299
  [[maybe_unused]] int nt = std::min(int(nx), omp_get_max_threads());
295
300
 
296
301
  #pragma omp parallel num_threads(nt)
@@ -327,6 +332,11 @@ void exhaustive_L2sqr_seq(
327
332
  BlockResultHandler& res) {
328
333
  using SingleResultHandler =
329
334
  typename BlockResultHandler::SingleResultHandler;
335
+
336
+ if (nx == 0) {
337
+ return;
338
+ }
339
+
330
340
  [[maybe_unused]] int nt = std::min(int(nx), omp_get_max_threads());
331
341
 
332
342
  #pragma omp parallel num_threads(nt)
@@ -531,8 +541,10 @@ void exhaustive_L2sqr_blas<Top1BlockResultHandler<CMax<float, int64_t>>>(
531
541
  return;
532
542
  }
533
543
 
534
- with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A2>([&]<SIMDLevel SL>() {
535
- if constexpr (SL == SIMDLevel::AVX2 || SL == SIMDLevel::ARM_SVE) {
544
+ with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>([&]<SIMDLevel SL>() {
545
+ if constexpr (
546
+ SL == SIMDLevel::AVX2 || SL == SIMDLevel::AVX512 ||
547
+ SL == SIMDLevel::ARM_SVE) {
536
548
  exhaustive_L2sqr_blas_cmax<SL>(x, y, d, nx, ny, res, y_norms);
537
549
  } else {
538
550
  exhaustive_L2sqr_blas_default_impl<
@@ -10,18 +10,19 @@
10
10
  #include <faiss/utils/distances.h>
11
11
 
12
12
  #include <cmath>
13
- #include <cstdio>
14
13
  #include <cstring>
15
14
 
16
- #include <faiss/impl/FaissAssert.h>
17
15
  #include <faiss/impl/simdlib/simdlib_dispatch.h>
18
16
 
19
17
  #define THE_SIMD_LEVEL SIMDLevel::NONE
20
18
  // NOLINTNEXTLINE(facebook-hte-InlineHeader)
21
19
  #include <faiss/utils/simd_impl/distances_autovec-inl.h>
22
20
 
21
+ // This TU is the SIMDLevel::NONE build of the specializations that
22
+ // distances_simdlib256.h emits, so the include is required despite no symbol
23
+ // being named directly here.
23
24
  // NOLINTNEXTLINE(facebook-hte-InlineHeader)
24
- #include <faiss/utils/simd_impl/distances_simdlib256.h>
25
+ #include <faiss/utils/simd_impl/distances_simdlib256.h> // IWYU pragma: keep
25
26
 
26
27
  namespace faiss {
27
28