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
@@ -13,6 +13,7 @@
13
13
  #include <vector>
14
14
 
15
15
  #include <faiss/IndexFlatCodes.h>
16
+ #include <faiss/impl/Panorama.h>
16
17
 
17
18
  namespace faiss {
18
19
 
@@ -66,7 +67,7 @@ struct IndexFlat : IndexFlatCodes {
66
67
 
67
68
  FlatCodesDistanceComputer* get_FlatCodesDistanceComputer() const override;
68
69
 
69
- /* The stanadlone codec interface (just memcopies in this case) */
70
+ /* The standalone codec interface (just memcopies in this case) */
70
71
  void sa_encode(idx_t n, const float* x, uint8_t* bytes) const override;
71
72
 
72
73
  void sa_decode(idx_t n, const uint8_t* bytes, float* x) const override;
@@ -99,6 +100,85 @@ struct IndexFlatL2 : IndexFlat {
99
100
  void clear_l2norms();
100
101
  };
101
102
 
103
+ struct IndexFlatPanorama : IndexFlat {
104
+ const size_t batch_size;
105
+ const size_t n_levels;
106
+ std::vector<float> cum_sums;
107
+ Panorama pano;
108
+
109
+ /**
110
+ * @param d dimensionality of the input vectors
111
+ * @param metric metric type
112
+ * @param n_levels number of Panorama levels
113
+ * @param batch_size batch size for Panorama storage
114
+ */
115
+ explicit IndexFlatPanorama(
116
+ idx_t d,
117
+ MetricType metric,
118
+ size_t n_levels,
119
+ size_t batch_size)
120
+ : IndexFlat(d, metric),
121
+ batch_size(batch_size),
122
+ n_levels(n_levels),
123
+ pano(code_size, n_levels, batch_size) {
124
+ FAISS_THROW_IF_NOT(metric == METRIC_L2);
125
+ }
126
+
127
+ void add(idx_t n, const float* x) override;
128
+
129
+ void search(
130
+ idx_t n,
131
+ const float* x,
132
+ idx_t k,
133
+ float* distances,
134
+ idx_t* labels,
135
+ const SearchParameters* params = nullptr) const override;
136
+
137
+ void range_search(
138
+ idx_t n,
139
+ const float* x,
140
+ float radius,
141
+ RangeSearchResult* result,
142
+ const SearchParameters* params = nullptr) const override;
143
+
144
+ void search_subset(
145
+ idx_t n,
146
+ const float* x,
147
+ idx_t k_base,
148
+ const idx_t* base_labels,
149
+ idx_t k,
150
+ float* distances,
151
+ idx_t* labels) const override;
152
+
153
+ void reset() override;
154
+
155
+ void reconstruct(idx_t key, float* recons) const override;
156
+
157
+ void reconstruct_n(idx_t i, idx_t n, float* recons) const override;
158
+
159
+ size_t remove_ids(const IDSelector& sel) override;
160
+
161
+ void merge_from(Index& otherIndex, idx_t add_id) override;
162
+
163
+ void add_sa_codes(idx_t n, const uint8_t* codes_in, const idx_t* xids)
164
+ override;
165
+
166
+ void permute_entries(const idx_t* perm);
167
+ };
168
+
169
+ struct IndexFlatL2Panorama : IndexFlatPanorama {
170
+ /**
171
+ * @param d dimensionality of the input vectors
172
+ * @param n_levels number of Panorama levels
173
+ * @param batch_size batch size for Panorama storage
174
+ */
175
+ explicit IndexFlatL2Panorama(
176
+ idx_t d,
177
+ size_t n_levels,
178
+ size_t batch_size = 512)
179
+ : IndexFlatPanorama(d, METRIC_L2, n_levels, batch_size) {}
180
+ };
181
+
102
182
  /// optimized version for 1D "vectors".
103
183
  struct IndexFlat1D : IndexFlatL2 {
104
184
  bool continuous_update = true; ///< is the permutation updated continuously?
@@ -276,7 +276,7 @@ void hnsw_search(
276
276
  res.begin(i);
277
277
  dis->set_query(x + i * index->d);
278
278
 
279
- HNSWStats stats = hnsw.search(*dis, res, vt, params);
279
+ HNSWStats stats = hnsw.search(*dis, index, res, vt, params);
280
280
  n1 += stats.n1;
281
281
  n2 += stats.n2;
282
282
  ndis += stats.ndis;
@@ -450,7 +450,9 @@ void IndexHNSW::search_level_0(
450
450
  vt.advance();
451
451
  }
452
452
  #pragma omp critical
453
- { hnsw_stats.combine(search_stats); }
453
+ {
454
+ hnsw_stats.combine(search_stats);
455
+ }
454
456
  }
455
457
  if (is_similarity_metric(this->metric_type)) {
456
458
  // we need to revert the negated distances
@@ -647,6 +649,95 @@ IndexHNSWFlat::IndexHNSWFlat(int d, int M, MetricType metric)
647
649
  is_trained = true;
648
650
  }
649
651
 
652
+ /**************************************************************
653
+ * IndexHNSWFlatPanorama implementation
654
+ **************************************************************/
655
+
656
+ void IndexHNSWFlatPanorama::compute_cum_sums(
657
+ const float* x,
658
+ float* dst_cum_sums,
659
+ int d,
660
+ int num_panorama_levels,
661
+ int panorama_level_width) {
662
+ // Iterate backwards through levels, accumulating sum as we go.
663
+ // This avoids computing the suffix sum for each vector, which takes
664
+ // extra memory.
665
+
666
+ float sum = 0.0f;
667
+ dst_cum_sums[num_panorama_levels] = 0.0f;
668
+ for (int level = num_panorama_levels - 1; level >= 0; level--) {
669
+ int start_idx = level * panorama_level_width;
670
+ int end_idx = std::min(start_idx + panorama_level_width, d);
671
+ for (int j = start_idx; j < end_idx; j++) {
672
+ sum += x[j] * x[j];
673
+ }
674
+ dst_cum_sums[level] = std::sqrt(sum);
675
+ }
676
+ }
677
+
678
+ IndexHNSWFlatPanorama::IndexHNSWFlatPanorama()
679
+ : IndexHNSWFlat(),
680
+ cum_sums(),
681
+ panorama_level_width(0),
682
+ num_panorama_levels(0) {}
683
+
684
+ IndexHNSWFlatPanorama::IndexHNSWFlatPanorama(
685
+ int d,
686
+ int M,
687
+ int num_panorama_levels,
688
+ MetricType metric)
689
+ : IndexHNSWFlat(d, M, metric),
690
+ cum_sums(),
691
+ panorama_level_width(
692
+ (d + num_panorama_levels - 1) / num_panorama_levels),
693
+ num_panorama_levels(num_panorama_levels) {
694
+ // For now, we only support L2 distance.
695
+ // Supporting dot product and cosine distance is a trivial addition
696
+ // left for future work.
697
+ FAISS_THROW_IF_NOT(metric == METRIC_L2);
698
+
699
+ // Enable Panorama search mode.
700
+ // This is not ideal, but is still more simple than making a subclass of
701
+ // HNSW and overriding the search logic.
702
+ hnsw.is_panorama = true;
703
+ }
704
+
705
+ void IndexHNSWFlatPanorama::add(idx_t n, const float* x) {
706
+ idx_t n0 = ntotal;
707
+ cum_sums.resize((ntotal + n) * (num_panorama_levels + 1));
708
+
709
+ for (size_t idx = 0; idx < n; idx++) {
710
+ const float* vector = x + idx * d;
711
+ compute_cum_sums(
712
+ vector,
713
+ &cum_sums[(n0 + idx) * (num_panorama_levels + 1)],
714
+ d,
715
+ num_panorama_levels,
716
+ panorama_level_width);
717
+ }
718
+
719
+ IndexHNSWFlat::add(n, x);
720
+ }
721
+
722
+ void IndexHNSWFlatPanorama::reset() {
723
+ cum_sums.clear();
724
+ IndexHNSWFlat::reset();
725
+ }
726
+
727
+ void IndexHNSWFlatPanorama::permute_entries(const idx_t* perm) {
728
+ std::vector<float> new_cum_sums(ntotal * (num_panorama_levels + 1));
729
+
730
+ for (idx_t i = 0; i < ntotal; i++) {
731
+ idx_t src = perm[i];
732
+ memcpy(&new_cum_sums[i * (num_panorama_levels + 1)],
733
+ &cum_sums[src * (num_panorama_levels + 1)],
734
+ (num_panorama_levels + 1) * sizeof(float));
735
+ }
736
+
737
+ std::swap(cum_sums, new_cum_sums);
738
+ IndexHNSWFlat::permute_entries(perm);
739
+ }
740
+
650
741
  /**************************************************************
651
742
  * IndexHNSWPQ implementation
652
743
  **************************************************************/
@@ -43,7 +43,7 @@ struct IndexHNSW : Index {
43
43
 
44
44
  // When set to true, all neighbors in level 0 are filled up
45
45
  // to the maximum size allowed (2 * M). This option is used by
46
- // IndexHHNSWCagra to create a full base layer graph that is
46
+ // IndexHNSWCagra to create a full base layer graph that is
47
47
  // used when GpuIndexCagra::copyFrom(IndexHNSWCagra*) is invoked.
48
48
  bool keep_max_size_level0 = false;
49
49
 
@@ -111,7 +111,7 @@ struct IndexHNSW : Index {
111
111
 
112
112
  void link_singletons();
113
113
 
114
- void permute_entries(const idx_t* perm);
114
+ virtual void permute_entries(const idx_t* perm);
115
115
 
116
116
  DistanceComputer* get_distance_computer() const override;
117
117
  };
@@ -125,6 +125,62 @@ struct IndexHNSWFlat : IndexHNSW {
125
125
  IndexHNSWFlat(int d, int M, MetricType metric = METRIC_L2);
126
126
  };
127
127
 
128
+ /** Panorama implementation of IndexHNSWFlat following
129
+ * https://www.arxiv.org/pdf/2510.00566.
130
+ *
131
+ * Unlike cluster-based Panorama, the vectors have to be higher dimensional
132
+ * (i.e. typically d > 512) and/or be able to compress a lot of their energy in
133
+ * the early dimensions to be effective. This is because HNSW accesses vectors
134
+ * in a random order, which makes cache misses dominate the distance computation
135
+ * time.
136
+ *
137
+ * The `num_panorama_levels` parameter controls the granularity of progressive
138
+ * distance refinement, allowing candidates to be eliminated early using partial
139
+ * distance computations rather than computing full distances.
140
+ *
141
+ * NOTE: This version of HNSW handles search slightly differently than the
142
+ * vanilla HNSW, as it uses partial distance computations with progressive
143
+ * refinement bounds. Instead of computing full distances immediately for all
144
+ * candidates, Panorama maintains lower and upper bounds that are incrementally
145
+ * tightened across refinement levels. Candidates are inserted into the search
146
+ * beam using approximate distance estimates (LB+UB)/2 and are only fully
147
+ * evaluated when they survive pruning and enter the result heap. This allows
148
+ * the algorithm to prune unpromising candidates early using Cauchy-Schwarz
149
+ * bounds on partial inner products. Hence, recall is not guaranteed to be the
150
+ * same as vanilla HNSW due to the heterogeneous precision within the search
151
+ * beam (exact vs. partial distance estimates affecting traversal order).
152
+ */
153
+ struct IndexHNSWFlatPanorama : IndexHNSWFlat {
154
+ IndexHNSWFlatPanorama();
155
+ IndexHNSWFlatPanorama(
156
+ int d,
157
+ int M,
158
+ int num_panorama_levels,
159
+ MetricType metric = METRIC_L2);
160
+
161
+ void add(idx_t n, const float* x) override;
162
+ void reset() override;
163
+ void permute_entries(const idx_t* perm) override;
164
+
165
+ /// Inline for performance - called frequently in search hot path.
166
+ const float* get_cum_sum(idx_t i) const {
167
+ return cum_sums.data() + i * (num_panorama_levels + 1);
168
+ }
169
+
170
+ /// Compute cumulative sums for a vector (used both for database points and
171
+ /// queries).
172
+ static void compute_cum_sums(
173
+ const float* x,
174
+ float* dst_cum_sums,
175
+ int d,
176
+ int num_panorama_levels,
177
+ int panorama_level_width);
178
+
179
+ std::vector<float> cum_sums;
180
+ const size_t panorama_level_width;
181
+ const size_t num_panorama_levels;
182
+ };
183
+
128
184
  /** PQ index topped with with a HNSW structure to access elements
129
185
  * more efficiently.
130
186
  */
@@ -59,7 +59,7 @@ IndexIDMapTemplate<IndexT>::IndexIDMapTemplate(IndexT* index) : index(index) {
59
59
  }
60
60
 
61
61
  template <typename IndexT>
62
- void IndexIDMapTemplate<IndexT>::addEx(
62
+ void IndexIDMapTemplate<IndexT>::add_ex(
63
63
  idx_t,
64
64
  const void*,
65
65
  NumericType numeric_type) {
@@ -78,11 +78,11 @@ void IndexIDMapTemplate<IndexT>::add(
78
78
  }
79
79
 
80
80
  template <typename IndexT>
81
- void IndexIDMapTemplate<IndexT>::trainEx(
81
+ void IndexIDMapTemplate<IndexT>::train_ex(
82
82
  idx_t n,
83
83
  const void* x,
84
84
  NumericType numeric_type) {
85
- index->trainEx(n, x, numeric_type);
85
+ index->train_ex(n, x, numeric_type);
86
86
  this->is_trained = index->is_trained;
87
87
  }
88
88
 
@@ -90,7 +90,8 @@ template <typename IndexT>
90
90
  void IndexIDMapTemplate<IndexT>::train(
91
91
  idx_t n,
92
92
  const typename IndexT::component_t* x) {
93
- trainEx(n,
93
+ train_ex(
94
+ n,
94
95
  static_cast<const void*>(x),
95
96
  component_t_to_numeric<typename IndexT::component_t>());
96
97
  }
@@ -103,12 +104,12 @@ void IndexIDMapTemplate<IndexT>::reset() {
103
104
  }
104
105
 
105
106
  template <typename IndexT>
106
- void IndexIDMapTemplate<IndexT>::add_with_idsEx(
107
+ void IndexIDMapTemplate<IndexT>::add_with_ids_ex(
107
108
  idx_t n,
108
109
  const void* x,
109
110
  NumericType numeric_type,
110
111
  const idx_t* xids) {
111
- index->addEx(n, x, numeric_type);
112
+ index->add_ex(n, x, numeric_type);
112
113
  for (idx_t i = 0; i < n; i++) {
113
114
  id_map.push_back(xids[i]);
114
115
  }
@@ -120,7 +121,7 @@ void IndexIDMapTemplate<IndexT>::add_with_ids(
120
121
  idx_t n,
121
122
  const typename IndexT::component_t* x,
122
123
  const idx_t* xids) {
123
- add_with_idsEx(
124
+ add_with_ids_ex(
124
125
  n,
125
126
  static_cast<const void*>(x),
126
127
  component_t_to_numeric<typename IndexT::component_t>(),
@@ -166,7 +167,7 @@ struct ScopedSelChange {
166
167
  } // namespace
167
168
 
168
169
  template <typename IndexT>
169
- void IndexIDMapTemplate<IndexT>::searchEx(
170
+ void IndexIDMapTemplate<IndexT>::search_ex(
170
171
  idx_t n,
171
172
  const void* x,
172
173
  NumericType numeric_type,
@@ -193,7 +194,7 @@ void IndexIDMapTemplate<IndexT>::searchEx(
193
194
  sel_change.set(params_non_const, &this_idtrans);
194
195
  }
195
196
  }
196
- index->searchEx(n, x, numeric_type, k, distances, labels, params);
197
+ index->search_ex(n, x, numeric_type, k, distances, labels, params);
197
198
  idx_t* li = labels;
198
199
  #pragma omp parallel for
199
200
  for (idx_t i = 0; i < n * k; i++) {
@@ -209,7 +210,7 @@ void IndexIDMapTemplate<IndexT>::search(
209
210
  typename IndexT::distance_t* distances,
210
211
  idx_t* labels,
211
212
  const SearchParameters* params) const {
212
- searchEx(
213
+ search_ex(
213
214
  n,
214
215
  static_cast<const void*>(x),
215
216
  component_t_to_numeric<typename IndexT::component_t>(),
@@ -301,13 +302,13 @@ IndexIDMap2Template<IndexT>::IndexIDMap2Template(IndexT* index)
301
302
  : IndexIDMapTemplate<IndexT>(index) {}
302
303
 
303
304
  template <typename IndexT>
304
- void IndexIDMap2Template<IndexT>::add_with_idsEx(
305
+ void IndexIDMap2Template<IndexT>::add_with_ids_ex(
305
306
  idx_t n,
306
307
  const void* x,
307
308
  NumericType numeric_type,
308
309
  const idx_t* xids) {
309
310
  size_t prev_ntotal = this->ntotal;
310
- IndexIDMapTemplate<IndexT>::add_with_idsEx(n, x, numeric_type, xids);
311
+ IndexIDMapTemplate<IndexT>::add_with_ids_ex(n, x, numeric_type, xids);
311
312
  for (size_t i = prev_ntotal; i < this->ntotal; i++) {
312
313
  rev_map[this->id_map[i]] = i;
313
314
  }
@@ -318,7 +319,7 @@ void IndexIDMap2Template<IndexT>::add_with_ids(
318
319
  idx_t n,
319
320
  const typename IndexT::component_t* x,
320
321
  const idx_t* xids) {
321
- add_with_idsEx(
322
+ add_with_ids_ex(
322
323
  n,
323
324
  static_cast<const void*>(x),
324
325
  component_t_to_numeric<typename IndexT::component_t>(),
@@ -23,7 +23,7 @@ struct IndexIDMapTemplate : IndexT {
23
23
  using distance_t = typename IndexT::distance_t;
24
24
 
25
25
  IndexT* index = nullptr; ///! the sub-index
26
- bool own_fields = false; ///! whether pointers are deleted in destructo
26
+ bool own_fields = false; ///! whether pointers are deleted in destructor
27
27
  std::vector<idx_t> id_map;
28
28
 
29
29
  explicit IndexIDMapTemplate(IndexT* index);
@@ -31,7 +31,7 @@ struct IndexIDMapTemplate : IndexT {
31
31
  /// @param xids if non-null, ids to store for the vectors (size n)
32
32
  void add_with_ids(idx_t n, const component_t* x, const idx_t* xids)
33
33
  override;
34
- void add_with_idsEx(
34
+ void add_with_ids_ex(
35
35
  idx_t n,
36
36
  const void* x,
37
37
  NumericType numeric_type,
@@ -39,7 +39,7 @@ struct IndexIDMapTemplate : IndexT {
39
39
 
40
40
  /// this will fail. Use add_with_ids
41
41
  void add(idx_t n, const component_t* x) override;
42
- void addEx(idx_t n, const void* x, NumericType numeric_type) override;
42
+ void add_ex(idx_t n, const void* x, NumericType numeric_type) override;
43
43
 
44
44
  void search(
45
45
  idx_t n,
@@ -48,7 +48,7 @@ struct IndexIDMapTemplate : IndexT {
48
48
  distance_t* distances,
49
49
  idx_t* labels,
50
50
  const SearchParameters* params = nullptr) const override;
51
- void searchEx(
51
+ void search_ex(
52
52
  idx_t n,
53
53
  const void* x,
54
54
  NumericType numeric_type,
@@ -58,7 +58,7 @@ struct IndexIDMapTemplate : IndexT {
58
58
  const SearchParameters* params = nullptr) const override;
59
59
 
60
60
  void train(idx_t n, const component_t* x) override;
61
- void trainEx(idx_t n, const void* x, NumericType numeric_type) override;
61
+ void train_ex(idx_t n, const void* x, NumericType numeric_type) override;
62
62
 
63
63
  void reset() override;
64
64
 
@@ -104,7 +104,7 @@ struct IndexIDMap2Template : IndexIDMapTemplate<IndexT> {
104
104
 
105
105
  void add_with_ids(idx_t n, const component_t* x, const idx_t* xids)
106
106
  override;
107
- void add_with_idsEx(
107
+ void add_with_ids_ex(
108
108
  idx_t n,
109
109
  const void* x,
110
110
  NumericType numeric_type,
@@ -506,7 +506,7 @@ void IndexIVF::search_preassigned(
506
506
  };
507
507
 
508
508
  // single list scan using the current scanner (with query
509
- // set porperly) and storing results in simi and idxi
509
+ // set properly) and storing results in simi and idxi
510
510
  auto scan_one_list = [&](idx_t key,
511
511
  float coarse_dis_i,
512
512
  float* simi,
@@ -160,7 +160,7 @@ struct IndexIVFInterface : Level1Quantizer {
160
160
  * index maps to a list (aka inverted list or posting list), where the
161
161
  * id of the vector is stored.
162
162
  *
163
- * The inverted list object is required only after trainng. If none is
163
+ * The inverted list object is required only after training. If none is
164
164
  * set externally, an ArrayInvertedLists is used automatically.
165
165
  *
166
166
  * At search time, the vector to be searched is also quantized, and
@@ -171,7 +171,7 @@ struct IndexIVFInterface : Level1Quantizer {
171
171
  * lists are visited.
172
172
  *
173
173
  * Sub-classes implement a post-filtering of the index that refines
174
- * the distance estimation from the query to databse vectors.
174
+ * the distance estimation from the query to database vectors.
175
175
  */
176
176
  struct IndexIVF : Index, IndexIVFInterface {
177
177
  /// Access to the actual data
@@ -497,12 +497,12 @@ struct InvertedListScanner {
497
497
  /// compute a single query-to-code distance
498
498
  virtual float distance_to_code(const uint8_t* code) const = 0;
499
499
 
500
- /** scan a set of codes, compute distances to current query and
500
+ /** scan a set of codes, compute distances to current query, and
501
501
  * update heap of results if necessary. Default implementation
502
502
  * calls distance_to_code.
503
503
  *
504
- * @param n number of codes to scan
505
- * @param codes codes to scan (n * code_size)
504
+ * @param n number of codes to scan
505
+ * @param codes codes to scan (n * code_size)
506
506
  * @param ids corresponding ids (ignored if store_pairs)
507
507
  * @param distances heap distances (size k)
508
508
  * @param labels heap labels (size k)
@@ -198,7 +198,7 @@ struct AQInvertedListScanner : InvertedListScanner {
198
198
  }
199
199
  }
200
200
 
201
- ~AQInvertedListScanner() = default;
201
+ ~AQInvertedListScanner() override = default;
202
202
  };
203
203
 
204
204
  template <bool is_IP>
@@ -14,6 +14,7 @@
14
14
 
15
15
  #include <faiss/impl/AuxIndexStructures.h>
16
16
  #include <faiss/impl/FaissAssert.h>
17
+ #include <faiss/impl/FastScanDistancePostProcessing.h>
17
18
  #include <faiss/impl/LookupTableScaler.h>
18
19
  #include <faiss/impl/pq4_fast_scan.h>
19
20
  #include <faiss/invlists/BlockInvertedLists.h>
@@ -212,7 +213,9 @@ void IndexIVFAdditiveQuantizerFastScan::estimate_norm_scale(
212
213
  size_t index_nprobe = nprobe;
213
214
  nprobe = 1;
214
215
  CoarseQuantized cq{index_nprobe, coarse_dis.data(), coarse_ids.data()};
215
- compute_LUT(n, x, cq, dis_tables, biases);
216
+ FastScanDistancePostProcessing empty_context{};
217
+
218
+ compute_LUT(n, x, cq, dis_tables, biases, empty_context);
216
219
  nprobe = index_nprobe;
217
220
 
218
221
  float scale = 0;
@@ -314,8 +317,10 @@ void IndexIVFAdditiveQuantizerFastScan::search(
314
317
  }
315
318
 
316
319
  NormTableScaler scaler(norm_scale);
320
+ FastScanDistancePostProcessing context;
321
+ context.norm_scaler = &scaler;
317
322
  IndexIVFFastScan::CoarseQuantized cq{nprobe};
318
- search_dispatch_implem(n, x, k, distances, labels, cq, &scaler);
323
+ search_dispatch_implem(n, x, k, distances, labels, cq, context);
319
324
  }
320
325
 
321
326
  /*********************************************************
@@ -383,7 +388,8 @@ void IndexIVFAdditiveQuantizerFastScan::compute_LUT(
383
388
  const float* x,
384
389
  const CoarseQuantized& cq,
385
390
  AlignedTable<float>& dis_tables,
386
- AlignedTable<float>& biases) const {
391
+ AlignedTable<float>& biases,
392
+ const FastScanDistancePostProcessing&) const {
387
393
  const size_t dim12 = ksub * M;
388
394
  const size_t ip_dim12 = aq->M * ksub;
389
395
  const size_t nprobe = cq.nprobe;
@@ -12,6 +12,7 @@
12
12
  #include <faiss/IndexIVFAdditiveQuantizer.h>
13
13
  #include <faiss/IndexIVFFastScan.h>
14
14
  #include <faiss/impl/AdditiveQuantizer.h>
15
+ #include <faiss/impl/FastScanDistancePostProcessing.h>
15
16
  #include <faiss/impl/ProductAdditiveQuantizer.h>
16
17
  #include <faiss/utils/AlignedTable.h>
17
18
 
@@ -101,7 +102,8 @@ struct IndexIVFAdditiveQuantizerFastScan : IndexIVFFastScan {
101
102
  const float* x,
102
103
  const CoarseQuantized& cq,
103
104
  AlignedTable<float>& dis_tables,
104
- AlignedTable<float>& biases) const override;
105
+ AlignedTable<float>& biases,
106
+ const FastScanDistancePostProcessing& context) const override;
105
107
  };
106
108
 
107
109
  struct IndexIVFLocalSearchQuantizerFastScan