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
@@ -18,7 +18,6 @@
18
18
  #include <faiss/IndexScalarQuantizer.h>
19
19
  #include <faiss/impl/HNSW.h>
20
20
  #include <faiss/impl/Panorama.h>
21
- #include <faiss/impl/hnsw/LockVector.h>
22
21
  #include <faiss/utils/utils.h>
23
22
 
24
23
  namespace faiss {
@@ -53,11 +52,6 @@ struct IndexHNSW : Index {
53
52
  // See impl/VisitedTable.h.
54
53
  std::optional<bool> use_visited_hashset;
55
54
 
56
- // Per-node locks for HNSW graph construction.
57
- LockVector locks;
58
- // locks are freed after each call to add() unless this flag is set.
59
- bool retain_locks = false;
60
-
61
55
  explicit IndexHNSW(int d = 0, int M = 32, MetricType metric = METRIC_L2);
62
56
  explicit IndexHNSW(Index* storage, int M = 32);
63
57
 
@@ -216,6 +210,36 @@ struct IndexHNSWSQ : IndexHNSW {
216
210
  MetricType metric = METRIC_L2);
217
211
  };
218
212
 
213
+ /** HNSW index whose storage is RaBitQ-compressed.
214
+ *
215
+ * The graph is built directly from the compressed codes. Neighbor-diversity
216
+ * pruning uses a fast 1-bit symmetric L2 estimator, including when the storage
217
+ * contains multi-bit codes.
218
+ *
219
+ * With nb_bits >= 2 the codes carry a per-vector error factor, so search uses
220
+ * the staged search method: a 1-bit estimate for every neighbor and the full
221
+ * multi-bit distance only for candidates the error bound cannot rule out.
222
+ * nb_bits = 1 has no error factor and uses ordinary HNSW search.
223
+ */
224
+ struct IndexHNSWRaBitQ : IndexHNSW {
225
+ IndexHNSWRaBitQ();
226
+ IndexHNSWRaBitQ(
227
+ int d,
228
+ int M,
229
+ uint8_t nb_bits = 1,
230
+ MetricType metric = METRIC_L2);
231
+
232
+ IndexHNSWRaBitQ& operator=(const IndexHNSWRaBitQ&) = delete;
233
+
234
+ private:
235
+ // clone_index() replaces the shallow-copied storage with a deep copy
236
+ // before returning it. Keep ordinary C++ copies from sharing ownership.
237
+ IndexHNSWRaBitQ(const IndexHNSWRaBitQ&) = default;
238
+ #ifndef SWIG
239
+ friend IndexHNSW* clone_IndexHNSW(const IndexHNSW* index);
240
+ #endif
241
+ };
242
+
219
243
  /** 2-level code structure with fast random access
220
244
  */
221
245
  struct IndexHNSW2Level : IndexHNSW {
@@ -253,7 +277,7 @@ struct IndexHNSWCagra : IndexHNSW {
253
277
  /// searches only the base level knn graph of the HNSW index.
254
278
  /// This parameter selects the entry point by randomly selecting
255
279
  /// some points and using the best one.
256
- int num_base_level_search_entrypoints = 32;
280
+ int num_base_level_search_entrypoints = 256;
257
281
 
258
282
  void add(idx_t n, const float* x) override;
259
283
 
@@ -138,7 +138,8 @@ void IndexIDMapTemplate<IndexT>::add_sa_codes(
138
138
  idx_t n,
139
139
  const uint8_t* codes,
140
140
  const idx_t* xids) {
141
- index->add_sa_codes(n, codes, xids);
141
+ // don't pass the ids to the sub-index, they are kept in id_map
142
+ index->add_sa_codes(n, codes, nullptr);
142
143
  for (idx_t i = 0; i < n; i++) {
143
144
  id_map.push_back(xids[i]);
144
145
  }
@@ -228,15 +229,20 @@ void IndexIDMapTemplate<IndexT>::range_search(
228
229
  typename IndexT::distance_t radius,
229
230
  RangeSearchResult* result,
230
231
  const SearchParameters* params) const {
231
- if (params) {
232
- SearchParameters internal_search_parameters;
233
- IDSelectorTranslated id_selector_translated(id_map, params->sel);
234
- internal_search_parameters.sel = &id_selector_translated;
232
+ IDSelectorTranslated this_idtrans(this->id_map, nullptr);
233
+ ScopedSelChange sel_change;
235
234
 
236
- index->range_search(n, x, radius, result, &internal_search_parameters);
237
- } else {
238
- index->range_search(n, x, radius, result);
235
+ if (params && params->sel) {
236
+ auto idtrans = dynamic_cast<const IDSelectorTranslated*>(params->sel);
237
+
238
+ if (!idtrans) {
239
+ // Preserve the concrete params type and its index-specific fields.
240
+ auto params_non_const = const_cast<SearchParameters*>(params);
241
+ this_idtrans.sel = params->sel;
242
+ sel_change.set(params_non_const, &this_idtrans);
243
+ }
239
244
  }
245
+ index->range_search(n, x, radius, result, params);
240
246
 
241
247
  const idx_t id_map_size = static_cast<idx_t>(id_map.size());
242
248
  #pragma omp parallel for
@@ -329,6 +335,18 @@ void IndexIDMap2Template<IndexT>::add_with_ids(
329
335
  xids);
330
336
  }
331
337
 
338
+ template <typename IndexT>
339
+ void IndexIDMap2Template<IndexT>::add_sa_codes(
340
+ idx_t n,
341
+ const uint8_t* codes,
342
+ const idx_t* xids) {
343
+ idx_t prev_ntotal = this->ntotal;
344
+ IndexIDMapTemplate<IndexT>::add_sa_codes(n, codes, xids);
345
+ for (idx_t i = prev_ntotal; i < this->ntotal; i++) {
346
+ rev_map[this->id_map[i]] = i;
347
+ }
348
+ }
349
+
332
350
  template <typename IndexT>
333
351
  void IndexIDMap2Template<IndexT>::check_consistency() const {
334
352
  FAISS_THROW_IF_NOT(rev_map.size() == this->id_map.size());
@@ -110,6 +110,8 @@ struct IndexIDMap2Template : IndexIDMapTemplate<IndexT> {
110
110
  NumericType numeric_type,
111
111
  const idx_t* xids) override;
112
112
 
113
+ void add_sa_codes(idx_t n, const uint8_t* x, const idx_t* xids) override;
114
+
113
115
  size_t remove_ids(const IDSelector& sel) override;
114
116
 
115
117
  void reconstruct(idx_t key, component_t* recons) const override;
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  #include <faiss/IndexIVF.h>
9
+ #include <faiss/SuperKMeans.h>
9
10
 
10
11
  #include <omp.h>
11
12
  #include <atomic>
@@ -17,7 +18,6 @@
17
18
  #include <cstdio>
18
19
  #include <limits>
19
20
 
20
- #include <faiss/utils/hamming.h>
21
21
  #include <faiss/utils/utils.h>
22
22
 
23
23
  #include <faiss/IndexFlat.h>
@@ -79,13 +79,27 @@ void Level1Quantizer::train_q1(
79
79
  printf("Training level-1 quantizer on %zd vectors in %zdD\n", n, d);
80
80
  }
81
81
 
82
- Clustering clus(static_cast<int>(d), static_cast<int>(nlist), cp);
82
+ FAISS_THROW_IF_MSG(
83
+ cp.use_super_kmeans && clustering_index,
84
+ "cp.use_super_kmeans is incompatible with a user-provided "
85
+ "clustering_index: SuperKMeans assigns with its own index");
86
+
83
87
  quantizer->reset();
84
- if (clustering_index) {
85
- clus.train(n, x, *clustering_index);
88
+ if (cp.use_super_kmeans) {
89
+ SuperKMeansParameters super_cp;
90
+ static_cast<ClusteringParameters&>(super_cp) = cp;
91
+ SuperKMeans clus(
92
+ static_cast<int>(d), static_cast<int>(nlist), super_cp);
93
+ clus.train(n, x);
86
94
  quantizer->add(nlist, clus.centroids.data());
87
95
  } else {
88
- clus.train(n, x, *quantizer);
96
+ Clustering clus(static_cast<int>(d), static_cast<int>(nlist), cp);
97
+ if (clustering_index) {
98
+ clus.train(n, x, *clustering_index);
99
+ quantizer->add(nlist, clus.centroids.data());
100
+ } else {
101
+ clus.train(n, x, *quantizer);
102
+ }
89
103
  }
90
104
  quantizer->is_trained = true;
91
105
  } else if (quantizer_trains_alone == 2) {
@@ -501,7 +515,7 @@ void IndexIVF::search_preassigned(
501
515
  if (!do_heap_init) {
502
516
  return;
503
517
  }
504
- if (metric_type == METRIC_INNER_PRODUCT) {
518
+ if (is_similarity_metric(metric_type)) {
505
519
  heap_heapify<HeapForIP>(k, simi, idxi);
506
520
  } else {
507
521
  heap_heapify<HeapForL2>(k, simi, idxi);
@@ -512,7 +526,7 @@ void IndexIVF::search_preassigned(
512
526
  const idx_t* local_idx,
513
527
  float* simi,
514
528
  idx_t* idxi) {
515
- if (metric_type == METRIC_INNER_PRODUCT) {
529
+ if (is_similarity_metric(metric_type)) {
516
530
  heap_addn<HeapForIP>(
517
531
  k, simi, idxi, local_dis, local_idx, k);
518
532
  } else {
@@ -525,7 +539,7 @@ void IndexIVF::search_preassigned(
525
539
  if (!do_heap_init) {
526
540
  return;
527
541
  }
528
- if (metric_type == METRIC_INNER_PRODUCT) {
542
+ if (is_similarity_metric(metric_type)) {
529
543
  heap_reorder<HeapForIP>(k, simi, idxi);
530
544
  } else {
531
545
  heap_reorder<HeapForL2>(k, simi, idxi);
@@ -599,7 +613,7 @@ void IndexIVF::search_preassigned(
599
613
 
600
614
  size_t old_scan_cnt = 0;
601
615
  size_t old_heap_updates = 0;
602
- if (metric_type == METRIC_INNER_PRODUCT) {
616
+ if (is_similarity_metric(metric_type)) {
603
617
  HeapResultHandler<HeapForIP, false> handler(
604
618
  k, simi, idxi);
605
619
  old_scan_cnt = handler.stats.scan_cnt;
@@ -1056,7 +1070,19 @@ InvertedListScanner* IndexIVF::get_InvertedListScanner(
1056
1070
 
1057
1071
  void IndexIVF::reconstruct(idx_t key, float* recons) const {
1058
1072
  idx_t lo = direct_map.get(key);
1059
- reconstruct_from_offset(lo_listno(lo), lo_offset(lo), recons);
1073
+ const size_t list_no = lo_listno(lo);
1074
+ const size_t offset = lo_offset(lo);
1075
+ FAISS_THROW_IF_NOT_FMT(
1076
+ list_no < nlist,
1077
+ "IndexIVF::reconstruct: list_no %zd out of range (nlist=%zd)",
1078
+ list_no,
1079
+ nlist);
1080
+ FAISS_THROW_IF_NOT_FMT(
1081
+ offset < invlists->list_size(list_no),
1082
+ "IndexIVF::reconstruct: offset %zd out of range (list_size=%zd)",
1083
+ offset,
1084
+ invlists->list_size(list_no));
1085
+ reconstruct_from_offset(list_no, offset, recons);
1060
1086
  }
1061
1087
 
1062
1088
  void IndexIVF::reconstruct_n(idx_t i0, idx_t ni, float* recons) const {
@@ -283,7 +283,7 @@ InvertedListScanner* IndexIVFAdditiveQuantizer::get_InvertedListScanner(
283
283
  bool store_pairs,
284
284
  const IDSelector* sel,
285
285
  const IVFSearchParameters*) const {
286
- FAISS_THROW_IF_NOT(!sel);
286
+ FAISS_THROW_IF_MSG(sel, "id selector not supported for this index");
287
287
  if (metric_type == METRIC_INNER_PRODUCT) {
288
288
  if (aq->search_type == AdditiveQuantizer::ST_decompress) {
289
289
  return new AQInvertedListScannerDecompress<true>(
@@ -48,8 +48,8 @@ void IndexIVFAdditiveQuantizerFastScan::init(
48
48
  MetricType metric,
49
49
  int bbs_,
50
50
  bool own_invlists_) {
51
- FAISS_THROW_IF_NOT(aq_ != nullptr);
52
- FAISS_THROW_IF_NOT(!aq_->nbits.empty());
51
+ FAISS_THROW_IF_NOT(aq_);
52
+ FAISS_THROW_IF_MSG(aq_->nbits.empty(), "quantizer nbits must not be empty");
53
53
  FAISS_THROW_IF_NOT(aq_->nbits[0] == 4);
54
54
  if (metric == METRIC_INNER_PRODUCT) {
55
55
  FAISS_THROW_IF_NOT_MSG(
@@ -310,8 +310,7 @@ void IndexIVFAdditiveQuantizerFastScan::search(
310
310
  float* distances,
311
311
  idx_t* labels,
312
312
  const SearchParameters* params) const {
313
- FAISS_THROW_IF_NOT_MSG(
314
- !params, "search params not supported for this index");
313
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
315
314
 
316
315
  FAISS_THROW_IF_NOT(k > 0);
317
316
  bool rescale = (rescale_norm && norm_scale > 1 && metric_type == METRIC_L2);
@@ -0,0 +1,302 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #include <faiss/IndexIVFEDEN.h>
9
+
10
+ #include <omp.h>
11
+
12
+ #include <cstddef>
13
+ #include <cstdint>
14
+ #include <cstring>
15
+ #include <memory>
16
+ #include <vector>
17
+
18
+ #include <faiss/impl/EDENQuantizer.h>
19
+ #include <faiss/impl/FaissAssert.h>
20
+ #include <faiss/impl/expanded_scanners.h>
21
+
22
+ namespace faiss {
23
+
24
+ IndexIVFEDEN::IndexIVFEDEN(
25
+ Index* quantizer_in,
26
+ const size_t d_in,
27
+ const size_t nlist_in,
28
+ MetricType metric,
29
+ bool own_invlists_in,
30
+ uint8_t nb_bits_in,
31
+ EDENScaleType scale_type_in)
32
+ : IndexIVF(quantizer_in, d_in, nlist_in, 0, metric, own_invlists_in),
33
+ sq(d_in, eden_utils::quantizer_type_for_bits(nb_bits_in)),
34
+ scale_type(scale_type_in) {
35
+ FAISS_THROW_IF_NOT_MSG(
36
+ scale_type == EDENScaleType_UNBIASED ||
37
+ scale_type == EDENScaleType_BIASED,
38
+ "invalid EDEN scale type");
39
+ sq.train(0, nullptr);
40
+ code_size = eden_utils::code_size(d, sq.bits);
41
+ if (own_invlists_in) {
42
+ invlists->code_size = code_size;
43
+ }
44
+ is_trained = false;
45
+ by_residual = true;
46
+ }
47
+
48
+ IndexIVFEDEN::IndexIVFEDEN() {
49
+ by_residual = true;
50
+ }
51
+
52
+ void IndexIVFEDEN::train_encoder(
53
+ idx_t n,
54
+ const float* x,
55
+ const idx_t* /*assign*/) {
56
+ sq.train(n, x);
57
+ }
58
+
59
+ void IndexIVFEDEN::encode_vectors(
60
+ idx_t n,
61
+ const float* x,
62
+ const idx_t* list_nos,
63
+ uint8_t* codes,
64
+ bool include_listnos) const {
65
+ const size_t coarse_size = include_listnos ? coarse_code_size() : 0;
66
+ memset(codes, 0, (code_size + coarse_size) * n);
67
+
68
+ #pragma omp parallel if (n > 1000)
69
+ {
70
+ std::vector<float> centroid(d);
71
+
72
+ #pragma omp for
73
+ for (idx_t i = 0; i < n; i++) {
74
+ const int64_t list_no = list_nos[i];
75
+ if (list_no >= 0) {
76
+ const float* xi = x + i * d;
77
+ uint8_t* code = codes + i * (code_size + coarse_size);
78
+
79
+ quantizer->reconstruct(list_no, centroid.data());
80
+ eden_utils::compute_codes(
81
+ sq,
82
+ metric_type,
83
+ scale_type,
84
+ xi,
85
+ code + coarse_size,
86
+ 1,
87
+ centroid.data());
88
+
89
+ if (coarse_size) {
90
+ encode_listno(list_no, code);
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ void IndexIVFEDEN::decode_vectors(
98
+ idx_t n,
99
+ const uint8_t* codes,
100
+ const idx_t* listnos,
101
+ float* x) const {
102
+ #pragma omp parallel
103
+ {
104
+ std::vector<float> centroid(d);
105
+
106
+ #pragma omp for
107
+ for (idx_t i = 0; i < n; i++) {
108
+ const uint8_t* code = codes + i * code_size;
109
+ const int64_t list_no = listnos[i];
110
+ float* xi = x + i * d;
111
+
112
+ quantizer->reconstruct(list_no, centroid.data());
113
+ eden_utils::decode(sq, code, xi, 1, centroid.data());
114
+ }
115
+ }
116
+ }
117
+
118
+ void IndexIVFEDEN::add_core(
119
+ idx_t n,
120
+ const float* x,
121
+ const idx_t* xids,
122
+ const idx_t* precomputed_idx,
123
+ void* inverted_list_context) {
124
+ FAISS_THROW_IF_NOT(is_trained);
125
+
126
+ DirectMapAdd dm_add(direct_map, n, xids);
127
+
128
+ #pragma omp parallel
129
+ {
130
+ std::vector<uint8_t> one_code(code_size);
131
+ std::vector<float> centroid(d);
132
+
133
+ const int nt = omp_get_num_threads();
134
+ const int rank = omp_get_thread_num();
135
+
136
+ for (idx_t i = 0; i < n; i++) {
137
+ const int64_t list_no = precomputed_idx[i];
138
+ if (list_no >= 0 && list_no % nt == rank) {
139
+ const int64_t id = xids ? xids[i] : ntotal + i;
140
+ const float* xi = x + i * d;
141
+
142
+ quantizer->reconstruct(list_no, centroid.data());
143
+ eden_utils::compute_codes(
144
+ sq,
145
+ metric_type,
146
+ scale_type,
147
+ xi,
148
+ one_code.data(),
149
+ 1,
150
+ centroid.data());
151
+
152
+ const size_t ofs = invlists->add_entry(
153
+ list_no, id, one_code.data(), inverted_list_context);
154
+ dm_add.add(i, list_no, ofs);
155
+ } else if (rank == 0 && list_no == -1) {
156
+ dm_add.add(i, -1, 0);
157
+ }
158
+ }
159
+ }
160
+
161
+ ntotal += n;
162
+ }
163
+
164
+ namespace {
165
+
166
+ struct EDENInvertedListScanner : InvertedListScanner {
167
+ using InvertedListScanner::scan_codes;
168
+
169
+ const IndexIVFEDEN& ivf_eden;
170
+ std::vector<float> reconstructed_centroid;
171
+ std::vector<float> query_vector;
172
+ std::unique_ptr<FlatCodesDistanceComputer> dc;
173
+
174
+ explicit EDENInvertedListScanner(
175
+ const IndexIVFEDEN& ivf_eden_in,
176
+ bool store_pairs_in = false,
177
+ const IDSelector* sel_in = nullptr)
178
+ : InvertedListScanner(store_pairs_in, sel_in),
179
+ ivf_eden(ivf_eden_in) {
180
+ keep_max = is_similarity_metric(ivf_eden.metric_type);
181
+ code_size = ivf_eden.code_size;
182
+ }
183
+
184
+ void set_query(const float* query_vector_in) override {
185
+ query_vector.assign(query_vector_in, query_vector_in + ivf_eden.d);
186
+ internal_try_setup_dc();
187
+ }
188
+
189
+ void set_list(idx_t list_no_in, float /*coarse_dis*/) override {
190
+ list_no = list_no_in;
191
+
192
+ reconstructed_centroid.resize(ivf_eden.d);
193
+ ivf_eden.quantizer->reconstruct(
194
+ list_no_in, reconstructed_centroid.data());
195
+ internal_try_setup_dc();
196
+ }
197
+
198
+ float distance_to_code(const uint8_t* code) const final {
199
+ return dc->distance_to_code(code);
200
+ }
201
+
202
+ size_t scan_codes(
203
+ size_t list_size,
204
+ const uint8_t* codes,
205
+ const idx_t* ids,
206
+ ResultHandler& handler) const override {
207
+ return run_scan_codes(*this, list_size, codes, ids, handler);
208
+ }
209
+
210
+ void internal_try_setup_dc() {
211
+ if (!query_vector.empty() && !reconstructed_centroid.empty()) {
212
+ dc.reset(
213
+ eden_utils::get_distance_computer(
214
+ ivf_eden.sq,
215
+ ivf_eden.metric_type,
216
+ reconstructed_centroid.data()));
217
+ dc->set_query(query_vector.data());
218
+ }
219
+ }
220
+ };
221
+
222
+ } // namespace
223
+
224
+ InvertedListScanner* IndexIVFEDEN::get_InvertedListScanner(
225
+ bool store_pairs,
226
+ const IDSelector* sel,
227
+ const IVFSearchParameters* /*params*/) const {
228
+ return new EDENInvertedListScanner(*this, store_pairs, sel);
229
+ }
230
+
231
+ void IndexIVFEDEN::reconstruct_from_offset(
232
+ int64_t list_no,
233
+ int64_t offset,
234
+ float* recons) const {
235
+ const uint8_t* code = invlists->get_single_code(list_no, offset);
236
+
237
+ std::vector<float> centroid(d);
238
+ quantizer->reconstruct(list_no, centroid.data());
239
+ eden_utils::decode(sq, code, recons, 1, centroid.data());
240
+
241
+ invlists->release_codes(list_no, code);
242
+ }
243
+
244
+ void IndexIVFEDEN::sa_decode(idx_t n, const uint8_t* bytes, float* x) const {
245
+ const size_t coarse_size = coarse_code_size();
246
+
247
+ #pragma omp parallel
248
+ {
249
+ std::vector<float> centroid(d);
250
+
251
+ #pragma omp for
252
+ for (idx_t i = 0; i < n; i++) {
253
+ const uint8_t* code = bytes + i * (code_size + coarse_size);
254
+ const int64_t list_no = decode_listno(code);
255
+ float* xi = x + i * d;
256
+
257
+ quantizer->reconstruct(list_no, centroid.data());
258
+ eden_utils::decode(sq, code + coarse_size, xi, 1, centroid.data());
259
+ }
260
+ }
261
+ }
262
+
263
+ struct IVFEDENDistanceComputer : DistanceComputer {
264
+ const float* q = nullptr;
265
+ const IndexIVFEDEN* parent = nullptr;
266
+
267
+ void set_query(const float* x) override {
268
+ q = x;
269
+ }
270
+
271
+ float operator()(idx_t i) override {
272
+ const idx_t lo = parent->direct_map.get(i);
273
+ const uint64_t list_no = lo_listno(lo);
274
+ const uint64_t offset = lo_offset(lo);
275
+ const uint8_t* code =
276
+ parent->invlists->get_single_code(list_no, offset);
277
+
278
+ std::vector<float> centroid(parent->d);
279
+ parent->quantizer->reconstruct(list_no, centroid.data());
280
+
281
+ std::unique_ptr<FlatCodesDistanceComputer> dc(
282
+ eden_utils::get_distance_computer(
283
+ parent->sq, parent->metric_type, centroid.data()));
284
+ dc->set_query(q);
285
+ const float distance = dc->distance_to_code(code);
286
+
287
+ parent->invlists->release_codes(list_no, code);
288
+ return distance;
289
+ }
290
+
291
+ float symmetric_dis(idx_t /*i*/, idx_t /*j*/) override {
292
+ FAISS_THROW_MSG("Not implemented");
293
+ }
294
+ };
295
+
296
+ DistanceComputer* IndexIVFEDEN::get_distance_computer() const {
297
+ IVFEDENDistanceComputer* dc = new IVFEDENDistanceComputer;
298
+ dc->parent = this;
299
+ return dc;
300
+ }
301
+
302
+ } // namespace faiss
@@ -0,0 +1,70 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #pragma once
9
+
10
+ #include <cstddef>
11
+ #include <cstdint>
12
+
13
+ #include <faiss/IndexIVF.h>
14
+ #include <faiss/impl/EDENQuantizer.h>
15
+
16
+ namespace faiss {
17
+
18
+ struct IndexIVFEDEN : IndexIVF {
19
+ ScalarQuantizer sq;
20
+ EDENScaleType scale_type = EDENScaleType_UNBIASED;
21
+
22
+ // Factory strings: IVF<nlist>,EDEN, IVF<nlist>,EDEN<n>, and the same
23
+ // forms with the BIASED suffix for EDEN's MSE-minimizing scale.
24
+ IndexIVFEDEN(
25
+ Index* quantizer,
26
+ const size_t d,
27
+ const size_t nlist,
28
+ MetricType metric = METRIC_L2,
29
+ bool own_invlists = true,
30
+ uint8_t nb_bits = 1,
31
+ EDENScaleType scale_type = EDENScaleType_UNBIASED);
32
+
33
+ IndexIVFEDEN();
34
+
35
+ void train_encoder(idx_t n, const float* x, const idx_t* assign) override;
36
+
37
+ void encode_vectors(
38
+ idx_t n,
39
+ const float* x,
40
+ const idx_t* list_nos,
41
+ uint8_t* codes,
42
+ bool include_listnos = false) const override;
43
+
44
+ void decode_vectors(
45
+ idx_t n,
46
+ const uint8_t* codes,
47
+ const idx_t* list_nos,
48
+ float* x) const override;
49
+
50
+ void add_core(
51
+ idx_t n,
52
+ const float* x,
53
+ const idx_t* xids,
54
+ const idx_t* precomputed_idx,
55
+ void* inverted_list_context = nullptr) override;
56
+
57
+ InvertedListScanner* get_InvertedListScanner(
58
+ bool store_pairs,
59
+ const IDSelector* sel,
60
+ const IVFSearchParameters* params) const override;
61
+
62
+ void reconstruct_from_offset(int64_t list_no, int64_t offset, float* recons)
63
+ const override;
64
+
65
+ void sa_decode(idx_t n, const uint8_t* bytes, float* x) const override;
66
+
67
+ DistanceComputer* get_distance_computer() const override;
68
+ };
69
+
70
+ } // namespace faiss
@@ -376,9 +376,8 @@ void IndexIVFFastScan::search_preassigned(
376
376
  cur_nprobe = params->nprobe;
377
377
  }
378
378
 
379
- FAISS_THROW_IF_NOT_MSG(
380
- !store_pairs, "store_pairs not supported for this index");
381
- FAISS_THROW_IF_NOT_MSG(!stats, "stats not supported for this index");
379
+ FAISS_THROW_IF_MSG(store_pairs, "store_pairs not supported for this index");
380
+ FAISS_THROW_IF_MSG(stats, "stats not supported for this index");
382
381
  FAISS_THROW_IF_NOT(k > 0);
383
382
  FastScanDistancePostProcessing empty_context{};
384
383
 
@@ -404,8 +403,8 @@ void IndexIVFFastScan::range_search(
404
403
  params->max_lists_num == 0,
405
404
  "max_lists_num is a knn knob and is not honored by "
406
405
  "fastscan range search");
407
- FAISS_THROW_IF_NOT_MSG(
408
- !params->ensure_topk_full,
406
+ FAISS_THROW_IF_MSG(
407
+ params->ensure_topk_full,
409
408
  "ensure_topk_full is a knn knob and is not honored by "
410
409
  "fastscan range search");
411
410
  FAISS_THROW_IF_NOT_MSG(
@@ -1592,7 +1591,7 @@ void IndexIVFFastScan::reconstruct_from_offset(
1592
1591
  }
1593
1592
 
1594
1593
  void IndexIVFFastScan::reconstruct_orig_invlists() {
1595
- FAISS_THROW_IF_NOT(orig_invlists != nullptr);
1594
+ FAISS_THROW_IF_NOT(orig_invlists);
1596
1595
  FAISS_THROW_IF_NOT(orig_invlists->list_size(0) == 0);
1597
1596
 
1598
1597
  #pragma omp parallel for if (nlist > 100)