faiss 0.4.3 → 0.5.1

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +2 -0
  4. data/ext/faiss/index.cpp +33 -6
  5. data/ext/faiss/index_binary.cpp +17 -4
  6. data/ext/faiss/kmeans.cpp +6 -6
  7. data/lib/faiss/version.rb +1 -1
  8. data/vendor/faiss/faiss/AutoTune.cpp +2 -3
  9. data/vendor/faiss/faiss/AutoTune.h +1 -1
  10. data/vendor/faiss/faiss/Clustering.cpp +2 -2
  11. data/vendor/faiss/faiss/Clustering.h +2 -2
  12. data/vendor/faiss/faiss/IVFlib.cpp +26 -51
  13. data/vendor/faiss/faiss/IVFlib.h +1 -1
  14. data/vendor/faiss/faiss/Index.cpp +11 -0
  15. data/vendor/faiss/faiss/Index.h +34 -11
  16. data/vendor/faiss/faiss/Index2Layer.cpp +1 -1
  17. data/vendor/faiss/faiss/Index2Layer.h +2 -2
  18. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +1 -0
  19. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +9 -4
  20. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.h +5 -1
  21. data/vendor/faiss/faiss/IndexBinary.h +7 -7
  22. data/vendor/faiss/faiss/IndexBinaryFromFloat.h +1 -1
  23. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +8 -2
  24. data/vendor/faiss/faiss/IndexBinaryHNSW.h +1 -1
  25. data/vendor/faiss/faiss/IndexBinaryHash.cpp +3 -3
  26. data/vendor/faiss/faiss/IndexBinaryHash.h +5 -5
  27. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +7 -6
  28. data/vendor/faiss/faiss/IndexFastScan.cpp +125 -49
  29. data/vendor/faiss/faiss/IndexFastScan.h +102 -7
  30. data/vendor/faiss/faiss/IndexFlat.cpp +374 -4
  31. data/vendor/faiss/faiss/IndexFlat.h +81 -1
  32. data/vendor/faiss/faiss/IndexHNSW.cpp +93 -2
  33. data/vendor/faiss/faiss/IndexHNSW.h +58 -2
  34. data/vendor/faiss/faiss/IndexIDMap.cpp +14 -13
  35. data/vendor/faiss/faiss/IndexIDMap.h +6 -6
  36. data/vendor/faiss/faiss/IndexIVF.cpp +1 -1
  37. data/vendor/faiss/faiss/IndexIVF.h +5 -5
  38. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  39. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +9 -3
  40. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.h +3 -1
  41. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +176 -90
  42. data/vendor/faiss/faiss/IndexIVFFastScan.h +173 -18
  43. data/vendor/faiss/faiss/IndexIVFFlat.cpp +1 -0
  44. data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +251 -0
  45. data/vendor/faiss/faiss/IndexIVFFlatPanorama.h +64 -0
  46. data/vendor/faiss/faiss/IndexIVFPQ.cpp +3 -1
  47. data/vendor/faiss/faiss/IndexIVFPQ.h +1 -1
  48. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +134 -2
  49. data/vendor/faiss/faiss/IndexIVFPQFastScan.h +7 -1
  50. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +99 -8
  51. data/vendor/faiss/faiss/IndexIVFRaBitQ.h +4 -1
  52. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +828 -0
  53. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +252 -0
  54. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +1 -1
  55. data/vendor/faiss/faiss/IndexIVFSpectralHash.h +1 -1
  56. data/vendor/faiss/faiss/IndexNNDescent.cpp +1 -1
  57. data/vendor/faiss/faiss/IndexNSG.cpp +1 -1
  58. data/vendor/faiss/faiss/IndexNeuralNetCodec.h +1 -1
  59. data/vendor/faiss/faiss/IndexPQ.cpp +4 -1
  60. data/vendor/faiss/faiss/IndexPQ.h +1 -1
  61. data/vendor/faiss/faiss/IndexPQFastScan.cpp +6 -2
  62. data/vendor/faiss/faiss/IndexPQFastScan.h +5 -1
  63. data/vendor/faiss/faiss/IndexPreTransform.cpp +14 -0
  64. data/vendor/faiss/faiss/IndexPreTransform.h +9 -0
  65. data/vendor/faiss/faiss/IndexRaBitQ.cpp +96 -13
  66. data/vendor/faiss/faiss/IndexRaBitQ.h +11 -2
  67. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +731 -0
  68. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +175 -0
  69. data/vendor/faiss/faiss/IndexRefine.cpp +49 -0
  70. data/vendor/faiss/faiss/IndexRefine.h +17 -0
  71. data/vendor/faiss/faiss/IndexShards.cpp +1 -1
  72. data/vendor/faiss/faiss/MatrixStats.cpp +3 -3
  73. data/vendor/faiss/faiss/MetricType.h +1 -1
  74. data/vendor/faiss/faiss/VectorTransform.h +2 -2
  75. data/vendor/faiss/faiss/clone_index.cpp +5 -1
  76. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +1 -1
  77. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +3 -1
  78. data/vendor/faiss/faiss/gpu/GpuIndex.h +11 -11
  79. data/vendor/faiss/faiss/gpu/GpuIndexBinaryCagra.h +1 -1
  80. data/vendor/faiss/faiss/gpu/GpuIndexBinaryFlat.h +1 -1
  81. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +11 -7
  82. data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +1 -1
  83. data/vendor/faiss/faiss/gpu/perf/IndexWrapper-inl.h +2 -0
  84. data/vendor/faiss/faiss/gpu/test/TestGpuIcmEncoder.cpp +7 -0
  85. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +1 -1
  86. data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
  87. data/vendor/faiss/faiss/impl/AdditiveQuantizer.h +1 -1
  88. data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +2 -2
  89. data/vendor/faiss/faiss/impl/AuxIndexStructures.h +1 -1
  90. data/vendor/faiss/faiss/impl/CodePacker.h +2 -2
  91. data/vendor/faiss/faiss/impl/DistanceComputer.h +77 -6
  92. data/vendor/faiss/faiss/impl/FastScanDistancePostProcessing.h +53 -0
  93. data/vendor/faiss/faiss/impl/HNSW.cpp +295 -16
  94. data/vendor/faiss/faiss/impl/HNSW.h +35 -6
  95. data/vendor/faiss/faiss/impl/IDSelector.cpp +2 -2
  96. data/vendor/faiss/faiss/impl/IDSelector.h +4 -4
  97. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +4 -4
  98. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.h +1 -1
  99. data/vendor/faiss/faiss/impl/LookupTableScaler.h +1 -1
  100. data/vendor/faiss/faiss/impl/NNDescent.cpp +1 -1
  101. data/vendor/faiss/faiss/impl/NNDescent.h +2 -2
  102. data/vendor/faiss/faiss/impl/NSG.cpp +1 -1
  103. data/vendor/faiss/faiss/impl/Panorama.cpp +193 -0
  104. data/vendor/faiss/faiss/impl/Panorama.h +204 -0
  105. data/vendor/faiss/faiss/impl/PanoramaStats.cpp +33 -0
  106. data/vendor/faiss/faiss/impl/PanoramaStats.h +38 -0
  107. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +5 -5
  108. data/vendor/faiss/faiss/impl/ProductAdditiveQuantizer.cpp +1 -1
  109. data/vendor/faiss/faiss/impl/ProductAdditiveQuantizer.h +1 -1
  110. data/vendor/faiss/faiss/impl/ProductQuantizer-inl.h +2 -0
  111. data/vendor/faiss/faiss/impl/ProductQuantizer.h +1 -1
  112. data/vendor/faiss/faiss/impl/RaBitQStats.cpp +29 -0
  113. data/vendor/faiss/faiss/impl/RaBitQStats.h +56 -0
  114. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +294 -0
  115. data/vendor/faiss/faiss/impl/RaBitQUtils.h +330 -0
  116. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +304 -223
  117. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +72 -4
  118. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +362 -0
  119. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.h +112 -0
  120. data/vendor/faiss/faiss/impl/ResidualQuantizer.h +1 -1
  121. data/vendor/faiss/faiss/impl/ResultHandler.h +4 -4
  122. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +7 -10
  123. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +2 -4
  124. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +7 -4
  125. data/vendor/faiss/faiss/impl/index_read.cpp +238 -10
  126. data/vendor/faiss/faiss/impl/index_write.cpp +212 -19
  127. data/vendor/faiss/faiss/impl/io.cpp +2 -2
  128. data/vendor/faiss/faiss/impl/io.h +4 -4
  129. data/vendor/faiss/faiss/impl/kmeans1d.cpp +1 -1
  130. data/vendor/faiss/faiss/impl/kmeans1d.h +1 -1
  131. data/vendor/faiss/faiss/impl/lattice_Zn.h +2 -2
  132. data/vendor/faiss/faiss/impl/mapped_io.cpp +2 -2
  133. data/vendor/faiss/faiss/impl/mapped_io.h +4 -3
  134. data/vendor/faiss/faiss/impl/maybe_owned_vector.h +8 -1
  135. data/vendor/faiss/faiss/impl/platform_macros.h +12 -0
  136. data/vendor/faiss/faiss/impl/pq4_fast_scan.cpp +30 -4
  137. data/vendor/faiss/faiss/impl/pq4_fast_scan.h +14 -8
  138. data/vendor/faiss/faiss/impl/pq4_fast_scan_search_qbs.cpp +5 -6
  139. data/vendor/faiss/faiss/impl/simd_result_handlers.h +55 -11
  140. data/vendor/faiss/faiss/impl/svs_io.cpp +86 -0
  141. data/vendor/faiss/faiss/impl/svs_io.h +67 -0
  142. data/vendor/faiss/faiss/impl/zerocopy_io.h +1 -1
  143. data/vendor/faiss/faiss/index_factory.cpp +217 -8
  144. data/vendor/faiss/faiss/index_factory.h +1 -1
  145. data/vendor/faiss/faiss/index_io.h +1 -1
  146. data/vendor/faiss/faiss/invlists/BlockInvertedLists.h +1 -1
  147. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  148. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +115 -1
  149. data/vendor/faiss/faiss/invlists/InvertedLists.h +46 -0
  150. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +1 -1
  151. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.h +1 -1
  152. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +261 -0
  153. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +117 -0
  154. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +66 -0
  155. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +245 -0
  156. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +137 -0
  157. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +39 -0
  158. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +42 -0
  159. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +149 -0
  160. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +58 -0
  161. data/vendor/faiss/faiss/utils/AlignedTable.h +1 -1
  162. data/vendor/faiss/faiss/utils/Heap.cpp +2 -2
  163. data/vendor/faiss/faiss/utils/Heap.h +3 -3
  164. data/vendor/faiss/faiss/utils/NeuralNet.cpp +1 -1
  165. data/vendor/faiss/faiss/utils/NeuralNet.h +3 -3
  166. data/vendor/faiss/faiss/utils/approx_topk/approx_topk.h +2 -2
  167. data/vendor/faiss/faiss/utils/approx_topk/avx2-inl.h +2 -2
  168. data/vendor/faiss/faiss/utils/approx_topk/mode.h +1 -1
  169. data/vendor/faiss/faiss/utils/distances.cpp +0 -3
  170. data/vendor/faiss/faiss/utils/distances.h +2 -2
  171. data/vendor/faiss/faiss/utils/extra_distances-inl.h +3 -1
  172. data/vendor/faiss/faiss/utils/hamming-inl.h +2 -0
  173. data/vendor/faiss/faiss/utils/hamming.cpp +7 -6
  174. data/vendor/faiss/faiss/utils/hamming.h +1 -1
  175. data/vendor/faiss/faiss/utils/hamming_distance/common.h +1 -2
  176. data/vendor/faiss/faiss/utils/partitioning.cpp +5 -5
  177. data/vendor/faiss/faiss/utils/partitioning.h +2 -2
  178. data/vendor/faiss/faiss/utils/rabitq_simd.h +222 -336
  179. data/vendor/faiss/faiss/utils/random.cpp +1 -1
  180. data/vendor/faiss/faiss/utils/simdlib_avx2.h +1 -1
  181. data/vendor/faiss/faiss/utils/simdlib_avx512.h +1 -1
  182. data/vendor/faiss/faiss/utils/simdlib_neon.h +2 -2
  183. data/vendor/faiss/faiss/utils/transpose/transpose-avx512-inl.h +1 -1
  184. data/vendor/faiss/faiss/utils/utils.cpp +9 -2
  185. data/vendor/faiss/faiss/utils/utils.h +2 -2
  186. metadata +29 -1
@@ -53,8 +53,8 @@ void kernel_accumulate_block(
53
53
 
54
54
  // _mm_prefetch(codes + 768, 0);
55
55
  for (int sq = 0; sq < nsq - scaler.nscale; sq += 2) {
56
- // prefetch
57
- simd32uint8 c(codes);
56
+ simd32uint8 c;
57
+ c.loadu(codes);
58
58
  codes += 32;
59
59
 
60
60
  simd32uint8 mask(0xf);
@@ -79,8 +79,8 @@ void kernel_accumulate_block(
79
79
  }
80
80
 
81
81
  for (int sq = 0; sq < scaler.nscale; sq += 2) {
82
- // prefetch
83
- simd32uint8 c(codes);
82
+ simd32uint8 c;
83
+ c.loadu(codes);
84
84
  codes += 32;
85
85
 
86
86
  simd32uint8 mask(0xf);
@@ -623,7 +623,6 @@ void accumulate(
623
623
  ResultHandler& res,
624
624
  const Scaler& scaler) {
625
625
  assert(nsq % 2 == 0);
626
- assert(is_aligned_pointer(codes));
627
626
  assert(is_aligned_pointer(LUT));
628
627
 
629
628
  #define DISPATCH(NQ) \
@@ -782,7 +781,7 @@ void accumulate_to_mem(
782
781
  }
783
782
 
784
783
  int pq4_preferred_qbs(int n) {
785
- // from timmings in P141901742, P141902828
784
+ // from timings in P141901742, P141902828
786
785
  static int map[12] = {
787
786
  0, 1, 2, 3, 0x13, 0x23, 0x33, 0x223, 0x233, 0x333, 0x2233, 0x2333};
788
787
  if (n <= 11) {
@@ -46,7 +46,7 @@ struct SIMDResultHandler {
46
46
  virtual ~SIMDResultHandler() {}
47
47
  };
48
48
 
49
- /* Result handler that will return float resutls eventually */
49
+ /* Result handler that will return float results eventually */
50
50
  struct SIMDResultHandlerToFloat : SIMDResultHandler {
51
51
  size_t nq; // number of queries
52
52
  size_t ntotal; // ignore excess elements after ntotal
@@ -70,13 +70,35 @@ struct SIMDResultHandlerToFloat : SIMDResultHandler {
70
70
  virtual void end() {
71
71
  normalizers = nullptr;
72
72
  }
73
+
74
+ // Number of updates made to the underlying data structure.
75
+ // For example: number of heap updates.
76
+ virtual size_t num_updates() {
77
+ return 0;
78
+ }
79
+
80
+ /** Set context information for handlers that need additional data
81
+ *
82
+ * This method can be overridden by handlers that need list numbers
83
+ * and probe mappings (e.g., RaBitQ handlers). Base implementation
84
+ * does nothing since most handlers don't need this context.
85
+ *
86
+ * @param list_no current inverted list number being processed
87
+ * @param probe_map mapping from local query index to probe index
88
+ */
89
+ virtual void set_list_context(
90
+ size_t /* list_no */,
91
+ const std::vector<int>& /* probe_map */) {
92
+ // Default implementation does nothing
93
+ // Derived handlers can override if they need this context
94
+ }
73
95
  };
74
96
 
75
97
  FAISS_API extern bool simd_result_handlers_accept_virtual;
76
98
 
77
99
  namespace simd_result_handlers {
78
100
 
79
- /** Dummy structure that just computes a chqecksum on results
101
+ /** Dummy structure that just computes a checksum on results
80
102
  * (to avoid the computation to be optimized away) */
81
103
  struct DummyResultHandler : SIMDResultHandler {
82
104
  size_t cs = 0;
@@ -318,8 +340,8 @@ struct HeapHandler : ResultHandlerCompare<C, with_id_map> {
318
340
  std::vector<TI> iids;
319
341
  float* dis;
320
342
  int64_t* ids;
321
-
322
- int64_t k; // number of results to keep
343
+ size_t k; // number of results to keep
344
+ size_t nup = 0; // number of heap updates
323
345
 
324
346
  HeapHandler(
325
347
  size_t nq,
@@ -327,14 +349,30 @@ struct HeapHandler : ResultHandlerCompare<C, with_id_map> {
327
349
  int64_t k,
328
350
  float* dis,
329
351
  int64_t* ids,
330
- const IDSelector* sel_in)
352
+ const IDSelector* sel_in,
353
+ const float* normalizers = nullptr)
331
354
  : RHC(nq, ntotal, sel_in),
332
- idis(nq * k),
333
- iids(nq * k),
355
+ idis(nq * k, threshold_idis(dis, normalizers)),
356
+ iids(nq * k, -1),
334
357
  dis(dis),
335
358
  ids(ids),
336
- k(k) {
337
- heap_heapify<C>(k * nq, idis.data(), iids.data());
359
+ k(k) {}
360
+
361
+ static uint16_t threshold_idis(float* dis_in, const float* normalizers) {
362
+ if (dis_in[0] == std::numeric_limits<float>::max()) {
363
+ return std::numeric_limits<uint16_t>::max();
364
+ }
365
+ if (dis_in[0] == std::numeric_limits<float>::lowest()) {
366
+ return 0;
367
+ }
368
+ if (normalizers) {
369
+ float one_a = 1 / normalizers[0], b = normalizers[1];
370
+ float f = (dis_in[0] - b) / one_a;
371
+ f = C::is_max ? std::ceil(f) : std::floor(f);
372
+ return std::clamp<float>(
373
+ f, 0, std::numeric_limits<uint16_t>::max());
374
+ }
375
+ return C::neutral();
338
376
  }
339
377
 
340
378
  void handle(size_t q, size_t b, simd16uint16 d0, simd16uint16 d1) final {
@@ -372,6 +410,7 @@ struct HeapHandler : ResultHandlerCompare<C, with_id_map> {
372
410
  if (C::cmp(heap_dis[0], dis_2)) {
373
411
  heap_replace_top<C>(
374
412
  k, heap_dis, heap_ids, dis_2, real_idx);
413
+ nup++;
375
414
  }
376
415
  }
377
416
  }
@@ -384,6 +423,7 @@ struct HeapHandler : ResultHandlerCompare<C, with_id_map> {
384
423
  if (C::cmp(heap_dis[0], dis_2)) {
385
424
  int64_t idx = this->adjust_id(b, j);
386
425
  heap_replace_top<C>(k, heap_dis, heap_ids, dis_2, idx);
426
+ nup++;
387
427
  }
388
428
  }
389
429
  }
@@ -408,6 +448,10 @@ struct HeapHandler : ResultHandlerCompare<C, with_id_map> {
408
448
  }
409
449
  }
410
450
  }
451
+
452
+ size_t num_updates() override {
453
+ return nup;
454
+ }
411
455
  };
412
456
 
413
457
  /** Simple top-N implementation using a reservoir.
@@ -726,7 +770,7 @@ void dispatch_SIMDResultHandler_fixedCW(
726
770
  } else { // generic handler -- will not be inlined
727
771
  FAISS_THROW_IF_NOT_FMT(
728
772
  simd_result_handlers_accept_virtual,
729
- "Running vitrual handler for %s",
773
+ "Running virtual handler for %s",
730
774
  typeid(res).name());
731
775
  consumer.template f<SIMDResultHandler>(res, args...);
732
776
  }
@@ -757,7 +801,7 @@ void dispatch_SIMDResultHandler(
757
801
  } else { // generic path
758
802
  FAISS_THROW_IF_NOT_FMT(
759
803
  simd_result_handlers_accept_virtual,
760
- "Running vitrual handler for %s",
804
+ "Running virtual handler for %s",
761
805
  typeid(res).name());
762
806
  consumer.template f<SIMDResultHandler>(res, args...);
763
807
  }
@@ -0,0 +1,86 @@
1
+ /*
2
+ * Portions 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
+ /*
9
+ * Portions Copyright 2025 Intel Corporation
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+
24
+ #include <faiss/impl/svs_io.h>
25
+
26
+ #include <algorithm>
27
+ #include <cstdlib>
28
+
29
+ #include <faiss/impl/FaissAssert.h>
30
+
31
+ namespace faiss {
32
+ namespace svs_io {
33
+
34
+ WriterStreambuf::WriterStreambuf(IOWriter* w_) : w(w_) {}
35
+
36
+ WriterStreambuf::~WriterStreambuf() = default;
37
+
38
+ std::streamsize WriterStreambuf::xsputn(const char* s, std::streamsize n) {
39
+ if (n <= 0)
40
+ return 0;
41
+ size_t wrote = (*w)(s, 1, static_cast<size_t>(n));
42
+ return static_cast<std::streamsize>(wrote);
43
+ }
44
+
45
+ int WriterStreambuf::overflow(int ch) {
46
+ if (ch == traits_type::eof())
47
+ return 0;
48
+ char c = static_cast<char>(ch);
49
+ size_t wrote = (*w)(&c, 1, 1);
50
+ return wrote == 1 ? ch : traits_type::eof();
51
+ }
52
+
53
+ ReaderStreambuf::ReaderStreambuf(IOReader* rr) : r(rr), single_char_buffer(0) {
54
+ // Initialize with empty get area
55
+ setg(nullptr, nullptr, nullptr);
56
+ }
57
+
58
+ ReaderStreambuf::~ReaderStreambuf() = default;
59
+
60
+ std::streambuf::int_type ReaderStreambuf::underflow() {
61
+ // Called by std::istream for single-character operations (get, peek, etc.)
62
+ // when the get area is exhausted. Reads one byte from IOReader.
63
+ size_t got = (*r)(&single_char_buffer, 1, 1);
64
+ if (got == 0) {
65
+ return traits_type::eof();
66
+ }
67
+
68
+ // Configure get area to expose the single buffered character
69
+ setg(&single_char_buffer, &single_char_buffer, &single_char_buffer + 1);
70
+ return traits_type::to_int_type(single_char_buffer);
71
+ }
72
+
73
+ std::streamsize ReaderStreambuf::xsgetn(char* s, std::streamsize n) {
74
+ // Called by std::istream for bulk reads (read, readsome, etc.).
75
+ // Forwards directly to IOReader without intermediate buffering to avoid
76
+ // advancing IOReader beyond what the stream consumer requested.
77
+ if (n <= 0) {
78
+ return 0;
79
+ }
80
+
81
+ size_t got = (*r)(s, 1, n);
82
+ return static_cast<std::streamsize>(got);
83
+ }
84
+
85
+ } // namespace svs_io
86
+ } // namespace faiss
@@ -0,0 +1,67 @@
1
+ /*
2
+ * Portions 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
+ /*
9
+ * Portions Copyright 2025 Intel Corporation
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+
24
+ #pragma once
25
+
26
+ #include <iostream>
27
+ #include <streambuf>
28
+ #include <vector>
29
+
30
+ #include <faiss/impl/io.h>
31
+
32
+ namespace faiss {
33
+ namespace svs_io {
34
+
35
+ // Bridges IOWriter to std::ostream for streaming serialization.
36
+ // No buffering concerns since consumer is expected to write everything
37
+ // he receives.
38
+ struct WriterStreambuf : std::streambuf {
39
+ IOWriter* w;
40
+ explicit WriterStreambuf(IOWriter* w_);
41
+ ~WriterStreambuf() override;
42
+
43
+ protected:
44
+ std::streamsize xsputn(const char* s, std::streamsize n) override;
45
+ int overflow(int ch) override;
46
+ };
47
+
48
+ // Bridges IOReader to std::istream for streaming deserialization.
49
+ // Uses minimal buffering (single byte) to avoid over-reading from IOReader,
50
+ // which would advance its position beyond what the stream consumer actually
51
+ // read. This ensures subsequent direct reads from IOReader continue at the
52
+ // correct position. Bulk reads via xsgetn() forward directly to IOReader
53
+ // without intermediate buffering.
54
+ struct ReaderStreambuf : std::streambuf {
55
+ IOReader* r;
56
+ char single_char_buffer; // Single-byte buffer for underflow() operations
57
+
58
+ explicit ReaderStreambuf(IOReader* rr);
59
+ ~ReaderStreambuf() override;
60
+
61
+ protected:
62
+ int_type underflow() override;
63
+ std::streamsize xsgetn(char* s, std::streamsize n) override;
64
+ };
65
+
66
+ } // namespace svs_io
67
+ } // namespace faiss
@@ -20,7 +20,7 @@ struct ZeroCopyIOReader : public faiss::IOReader {
20
20
  size_t total_ = 0;
21
21
 
22
22
  ZeroCopyIOReader(const uint8_t* data, size_t size);
23
- ~ZeroCopyIOReader();
23
+ ~ZeroCopyIOReader() override;
24
24
 
25
25
  void reset();
26
26
  size_t get_data_view(void** ptr, size_t size, size_t nitems);
@@ -27,10 +27,12 @@
27
27
  #include <faiss/IndexIVFAdditiveQuantizer.h>
28
28
  #include <faiss/IndexIVFAdditiveQuantizerFastScan.h>
29
29
  #include <faiss/IndexIVFFlat.h>
30
+ #include <faiss/IndexIVFFlatPanorama.h>
30
31
  #include <faiss/IndexIVFPQ.h>
31
32
  #include <faiss/IndexIVFPQFastScan.h>
32
33
  #include <faiss/IndexIVFPQR.h>
33
34
  #include <faiss/IndexIVFRaBitQ.h>
35
+ #include <faiss/IndexIVFRaBitQFastScan.h>
34
36
  #include <faiss/IndexIVFSpectralHash.h>
35
37
  #include <faiss/IndexLSH.h>
36
38
  #include <faiss/IndexLattice.h>
@@ -39,6 +41,7 @@
39
41
  #include <faiss/IndexPQFastScan.h>
40
42
  #include <faiss/IndexPreTransform.h>
41
43
  #include <faiss/IndexRaBitQ.h>
44
+ #include <faiss/IndexRaBitQFastScan.h>
42
45
  #include <faiss/IndexRefine.h>
43
46
  #include <faiss/IndexRowwiseMinMax.h>
44
47
  #include <faiss/IndexScalarQuantizer.h>
@@ -49,6 +52,16 @@
49
52
  #include <faiss/IndexBinaryHNSW.h>
50
53
  #include <faiss/IndexBinaryHash.h>
51
54
  #include <faiss/IndexBinaryIVF.h>
55
+
56
+ #ifdef FAISS_ENABLE_SVS
57
+ #include <faiss/svs/IndexSVSFlat.h>
58
+ #include <faiss/svs/IndexSVSVamana.h>
59
+ #include <faiss/svs/IndexSVSVamanaLVQ.h>
60
+ #include <faiss/svs/IndexSVSVamanaLeanVec.h>
61
+ #endif
62
+ #include <faiss/IndexIDMap.h>
63
+ #include <algorithm>
64
+ #include <cctype>
52
65
  #include <string>
53
66
 
54
67
  namespace faiss {
@@ -187,8 +200,6 @@ std::vector<size_t> aq_parse_nbits(std::string stok) {
187
200
  return nbits;
188
201
  }
189
202
 
190
- const std::string rabitq_pattern = "(RaBitQ)";
191
-
192
203
  /***************************************************************
193
204
  * Parse VectorTransform
194
205
  */
@@ -326,6 +337,10 @@ IndexIVF* parse_IndexIVF(
326
337
  if (match("FlatDedup")) {
327
338
  return new IndexIVFFlatDedup(get_q(), d, nlist, mt, own_il);
328
339
  }
340
+ if (match("FlatPanorama([0-9]+)?")) {
341
+ int nlevels = mres_to_int(sm[1], 8); // default to 8 levels
342
+ return new IndexIVFFlatPanorama(get_q(), d, nlist, nlevels, mt, own_il);
343
+ }
329
344
  if (match(sq_pattern)) {
330
345
  return new IndexIVFScalarQuantizer(
331
346
  get_q(),
@@ -447,8 +462,21 @@ IndexIVF* parse_IndexIVF(
447
462
  }
448
463
  return index_ivf;
449
464
  }
450
- if (match(rabitq_pattern)) {
451
- return new IndexIVFRaBitQ(get_q(), d, nlist, mt, own_il);
465
+ // IndexIVFRaBitQ with optional nb_bits (1-9)
466
+ // Accepts: "RaBitQ" (default 1-bit) or "RaBitQ{nb_bits}" (e.g., "RaBitQ4")
467
+ if (match("RaBitQ([1-9])?")) {
468
+ uint8_t nb_bits = sm[1].length() > 0 ? std::stoi(sm[1].str()) : 1;
469
+ return new IndexIVFRaBitQ(get_q(), d, nlist, mt, own_il, nb_bits);
470
+ }
471
+ // Accepts: "RaBitQfs" (default 1-bit, batch size 32)
472
+ // "RaBitQfs{nb_bits}" (e.g., "RaBitQfs4")
473
+ // "RaBitQfs_64" (1-bit, batch size 64)
474
+ // "RaBitQfs{nb_bits}_{bbs}" (e.g., "RaBitQfs4_64")
475
+ if (match("RaBitQfs([1-9])?(_[0-9]+)?")) {
476
+ uint8_t nb_bits = sm[1].length() > 0 ? std::stoi(sm[1].str()) : 1;
477
+ int bbs = mres_to_int(sm[2], 32, 1);
478
+ return new IndexIVFRaBitQFastScan(
479
+ get_q(), d, nlist, mt, bbs, own_il, nb_bits);
452
480
  }
453
481
  return nullptr;
454
482
  }
@@ -471,6 +499,11 @@ IndexHNSW* parse_IndexHNSW(
471
499
  return new IndexHNSWFlat(d, hnsw_M, mt);
472
500
  }
473
501
 
502
+ if (match("FlatPanorama([0-9]+)?")) {
503
+ int nlevels = mres_to_int(sm[1], 8); // default to 8 levels
504
+ return new IndexHNSWFlatPanorama(d, hnsw_M, nlevels, mt);
505
+ }
506
+
474
507
  if (match("PQ([0-9]+)(x[0-9]+)?(np)?")) {
475
508
  int M = std::stoi(sm[1].str());
476
509
  int nbit = mres_to_int(sm[2], 8, 1);
@@ -537,6 +570,109 @@ IndexNSG* parse_IndexNSG(
537
570
  return nullptr;
538
571
  }
539
572
 
573
+ #ifdef FAISS_ENABLE_SVS
574
+ /***************************************************************
575
+ * Parse IndexSVS
576
+ */
577
+
578
+ SVSStorageKind parse_lvq(const std::string& lvq_string) {
579
+ if (lvq_string == "LVQ4x0") {
580
+ return SVSStorageKind::SVS_LVQ4x0;
581
+ }
582
+ if (lvq_string == "LVQ4x4") {
583
+ return SVSStorageKind::SVS_LVQ4x4;
584
+ }
585
+ if (lvq_string == "LVQ4x8") {
586
+ return SVSStorageKind::SVS_LVQ4x8;
587
+ }
588
+ FAISS_ASSERT(!"not supported SVS LVQ level");
589
+ }
590
+
591
+ SVSStorageKind parse_leanvec(const std::string& leanvec_string) {
592
+ if (leanvec_string == "LeanVec4x4") {
593
+ return SVSStorageKind::SVS_LeanVec4x4;
594
+ }
595
+ if (leanvec_string == "LeanVec4x8") {
596
+ return SVSStorageKind::SVS_LeanVec4x8;
597
+ }
598
+ if (leanvec_string == "LeanVec8x8") {
599
+ return SVSStorageKind::SVS_LeanVec8x8;
600
+ }
601
+ FAISS_ASSERT(!"not supported SVS Leanvec level");
602
+ }
603
+
604
+ Index* parse_svs_datatype(
605
+ const std::string& index_type,
606
+ const std::string& arg_string,
607
+ const std::string& datatype_string,
608
+ int d,
609
+ MetricType mt) {
610
+ std::smatch sm;
611
+
612
+ if (datatype_string.empty()) {
613
+ if (index_type == "Vamana")
614
+ return new IndexSVSVamana(d, std::stoul(arg_string), mt);
615
+ if (index_type == "Flat")
616
+ return new IndexSVSFlat(d, mt);
617
+ FAISS_ASSERT(!"Unspported SVS index type");
618
+ }
619
+ if (re_match(datatype_string, "FP16", sm)) {
620
+ if (index_type == "Vamana")
621
+ return new IndexSVSVamana(
622
+ d, std::stoul(arg_string), mt, SVSStorageKind::SVS_FP16);
623
+ FAISS_ASSERT(!"Unspported SVS index type for Float16");
624
+ }
625
+ if (re_match(datatype_string, "SQI8", sm)) {
626
+ if (index_type == "Vamana")
627
+ return new IndexSVSVamana(
628
+ d, std::stoul(arg_string), mt, SVSStorageKind::SVS_SQI8);
629
+ FAISS_ASSERT(!"Unspported SVS index type for SQI8");
630
+ }
631
+ if (re_match(datatype_string, "(LVQ[0-9]+x[0-9]+)", sm)) {
632
+ if (index_type == "Vamana")
633
+ return new IndexSVSVamanaLVQ(
634
+ d, std::stoul(arg_string), mt, parse_lvq(sm[0].str()));
635
+ FAISS_ASSERT(!"Unspported SVS index type for LVQ");
636
+ }
637
+ if (re_match(datatype_string, "(LeanVec[0-9]+x[0-9]+)(_[0-9]+)?", sm)) {
638
+ std::string leanvec_d_string =
639
+ sm[2].length() > 0 ? sm[2].str().substr(1) : "0";
640
+ int leanvec_d = std::stoul(leanvec_d_string);
641
+
642
+ if (index_type == "Vamana")
643
+ return new IndexSVSVamanaLeanVec(
644
+ d,
645
+ std::stoul(arg_string),
646
+ mt,
647
+ leanvec_d,
648
+ parse_leanvec(sm[1].str()));
649
+ FAISS_ASSERT(!"Unspported SVS index type for LeanVec");
650
+ }
651
+ return nullptr;
652
+ }
653
+
654
+ Index* parse_IndexSVS(const std::string& code_string, int d, MetricType mt) {
655
+ std::smatch sm;
656
+ if (re_match(code_string, "Flat(,.+)?", sm)) {
657
+ std::string datatype_string =
658
+ sm[1].length() > 0 ? sm[1].str().substr(1) : "";
659
+ return parse_svs_datatype("Flat", "", datatype_string, d, mt);
660
+ }
661
+ if (re_match(code_string, "Vamana([0-9]+)(,.+)?", sm)) {
662
+ Index* index{nullptr};
663
+ std::string degree_string = sm[1].str();
664
+ std::string datatype_string =
665
+ sm[2].length() > 0 ? sm[2].str().substr(1) : "";
666
+ return parse_svs_datatype(
667
+ "Vamana", degree_string, datatype_string, d, mt);
668
+ }
669
+ if (re_match(code_string, "IVF([0-9]+)(,.+)?", sm)) {
670
+ FAISS_ASSERT(!"Unspported SVS index type");
671
+ }
672
+ return nullptr;
673
+ }
674
+ #endif // FAISS_ENABLE_SVS
675
+
540
676
  /***************************************************************
541
677
  * Parse basic indexes
542
678
  */
@@ -555,6 +691,18 @@ Index* parse_other_indexes(
555
691
  return new IndexFlat(d, metric);
556
692
  }
557
693
 
694
+ // IndexFlatL2Panorama
695
+ if (match("FlatL2Panorama([0-9]+)(_[0-9]+)?")) {
696
+ FAISS_THROW_IF_NOT(metric == METRIC_L2);
697
+ int nlevels = std::stoi(sm[1].str());
698
+ if (sm[2].length() > 0) {
699
+ int batch_size = std::stoi(sm[2].str().substr(1));
700
+ return new IndexFlatL2Panorama(d, nlevels, (size_t)batch_size);
701
+ } else {
702
+ return new IndexFlatL2Panorama(d, nlevels);
703
+ }
704
+ }
705
+
558
706
  // IndexLSH
559
707
  if (match("LSH([0-9]*)(r?)(t?)")) {
560
708
  int nbits = sm[1].length() > 0 ? std::stoi(sm[1].str()) : d;
@@ -671,9 +819,17 @@ Index* parse_other_indexes(
671
819
  }
672
820
  }
673
821
 
674
- // IndexRaBitQ
675
- if (match(rabitq_pattern)) {
676
- return new IndexRaBitQ(d, metric);
822
+ // IndexRaBitQ with optional nb_bits (1-9)
823
+ // Accepts: "RaBitQ" (default 1-bit) or "RaBitQ{nb_bits}" (e.g., "RaBitQ4")
824
+ if (match("RaBitQ([1-9])?")) {
825
+ uint8_t nb_bits = sm[1].length() > 0 ? std::stoi(sm[1].str()) : 1;
826
+ return new IndexRaBitQ(d, metric, nb_bits);
827
+ }
828
+
829
+ if (match("RaBitQfs([1-9])?(_[0-9]+)?")) {
830
+ uint8_t nb_bits = sm[1].length() > 0 ? std::stoi(sm[1].str()) : 1;
831
+ int bbs = mres_to_int(sm[2], 32, 1);
832
+ return new IndexRaBitQFastScan(d, metric, bbs, nb_bits);
677
833
  }
678
834
 
679
835
  return nullptr;
@@ -716,6 +872,18 @@ std::unique_ptr<Index> index_factory_sub(
716
872
  return std::unique_ptr<Index>(idmap);
717
873
  }
718
874
 
875
+ // handle refine Panorama
876
+ // TODO(aknayar): Add tests to test_factory.py
877
+ if (re_match(description, "(.+),RefinePanorama\\((.+)\\)", sm)) {
878
+ std::unique_ptr<Index> filter_index =
879
+ index_factory_sub(d, sm[1].str(), metric);
880
+ std::unique_ptr<Index> refine_index =
881
+ index_factory_sub(d, sm[2].str(), metric);
882
+ auto* index_rf = new IndexRefinePanorama(
883
+ filter_index.release(), refine_index.release());
884
+ return std::unique_ptr<Index>(index_rf);
885
+ }
886
+
719
887
  // handle refines
720
888
  if (re_match(description, "(.+),RFlat", sm) ||
721
889
  re_match(description, "(.+),Refine\\((.+)\\)", sm)) {
@@ -822,6 +990,25 @@ std::unique_ptr<Index> index_factory_sub(
822
990
  return std::unique_ptr<Index>(index);
823
991
  }
824
992
 
993
+ #ifdef FAISS_ENABLE_SVS
994
+ if (re_match(description, "SVS((?:Flat|Vamana|IVF).*)", sm)) {
995
+ std::string code_string = sm[1].str();
996
+ if (verbose) {
997
+ printf("parsing SVS string %s code_string=%s",
998
+ description.c_str(),
999
+ code_string.c_str());
1000
+ }
1001
+
1002
+ Index* index = parse_IndexSVS(code_string, d, metric);
1003
+ FAISS_THROW_IF_NOT_FMT(
1004
+ index,
1005
+ "could not parse SVS code description %s in %s",
1006
+ code_string.c_str(),
1007
+ description.c_str());
1008
+ return std::unique_ptr<Index>(index);
1009
+ }
1010
+ #endif // FAISS_ENABLE_SVS
1011
+
825
1012
  // NSG variants (it was unclear in the old version that the separator was a
826
1013
  // "," so we support both "_" and ",")
827
1014
  if (re_match(description, "NSG([0-9]*)([,_].*)?", sm)) {
@@ -934,6 +1121,28 @@ IndexBinary* index_binary_factory(
934
1121
  bool own_invlists) {
935
1122
  IndexBinary* index = nullptr;
936
1123
 
1124
+ std::smatch sm;
1125
+ std::string desc_str(description);
1126
+
1127
+ // Handle IDMap2 and IDMap wrappers (prefix or suffix)
1128
+ if (re_match(desc_str, "(.+),IDMap2", sm) ||
1129
+ re_match(desc_str, "IDMap2,(.+)", sm)) {
1130
+ IndexBinary* sub_index =
1131
+ index_binary_factory(d, sm[1].str().c_str(), own_invlists);
1132
+ IndexBinaryIDMap2* idmap2 = new IndexBinaryIDMap2(sub_index);
1133
+ idmap2->own_fields = true;
1134
+ return idmap2;
1135
+ }
1136
+
1137
+ if (re_match(desc_str, "(.+),IDMap", sm) ||
1138
+ re_match(desc_str, "IDMap,(.+)", sm)) {
1139
+ IndexBinary* sub_index =
1140
+ index_binary_factory(d, sm[1].str().c_str(), own_invlists);
1141
+ IndexBinaryIDMap* idmap = new IndexBinaryIDMap(sub_index);
1142
+ idmap->own_fields = true;
1143
+ return idmap;
1144
+ }
1145
+
937
1146
  int ncentroids = -1;
938
1147
  int M, nhash, b;
939
1148
 
@@ -959,7 +1168,7 @@ IndexBinary* index_binary_factory(
959
1168
  } else if (sscanf(description, "BHash%d", &b) == 1) {
960
1169
  index = new IndexBinaryHash(d, b);
961
1170
 
962
- } else if (std::string(description) == "BFlat") {
1171
+ } else if (desc_str == "BFlat") {
963
1172
  index = new IndexBinaryFlat(d);
964
1173
 
965
1174
  } else {
@@ -12,7 +12,7 @@
12
12
 
13
13
  namespace faiss {
14
14
 
15
- /** Build and index with the sequence of processing steps described in
15
+ /** Build an index with the sequence of processing steps described in
16
16
  * the string. */
17
17
  Index* index_factory(
18
18
  int d,
@@ -16,7 +16,7 @@
16
16
  * object that abstracts the medium.
17
17
  *
18
18
  * The read functions return objects that should be deallocated with
19
- * delete. All references within these objectes are owned by the
19
+ * delete. All references within these objects are owned by the
20
20
  * object.
21
21
  */
22
22
 
@@ -20,7 +20,7 @@ struct IDSelector;
20
20
  /** Inverted Lists that are organized by blocks.
21
21
  *
22
22
  * Different from the regular inverted lists, the codes are organized by blocks
23
- * of size block_size bytes that reprsent a set of n_per_block. Therefore, code
23
+ * of size block_size bytes that represent a set of n_per_block. Therefore, code
24
24
  * allocations are always rounded up to block_size bytes. The codes are also
25
25
  * aligned on 32-byte boundaries for use with SIMD.
26
26
  *