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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f9b24e9b85c954d799a4c19009626bcd90b5071a0b05136a182030920150b5a
4
- data.tar.gz: 1fe65e3ee60d8f77471b8dc6dc77354d7d7f9e7a81a4c12be909ca3ac75fcea1
3
+ metadata.gz: 6a22f4ad8f3c626f1d6571c4cefcb8292965e52eb3b0b77c354f39ed117570c5
4
+ data.tar.gz: b848af534bebc2ecfdfa5fe7eaa270b530d17d5fe5381aea350451aa03c22d9f
5
5
  SHA512:
6
- metadata.gz: 277f548905897091a697479e256a79d2e2f9c56647be54921ea5fd6caec6381966dd859b58cdc0d09fb9da5670ef9a7248dd5ba487e50eef58f315e96d952986
7
- data.tar.gz: 3a837ec44d583d6256f3490da8c6e17eec7e2a3854f4721826f6d03c4949e95163f6d67c70d7626d1b520a4b406fee9a3449b3d8fc7fbdd29fe5ff78dffd2e99
6
+ metadata.gz: 4c5083b9733f589fc30c93f429ed4da74fbb4bbdbe2bde17787506cafb178e2715d114e04ed17bd21a1161a096df66a0c502ae6dc245455c1e221737e80a1f3e
7
+ data.tar.gz: 41eb8ce7434c4408fccf238893468e8c808b1961966a9fc495f960d27ff950d5d6a9cafff18a11e14287f5fedc77f83db9118c47c60e06fdc9f86d9987592c7c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.6.4 (2026-09-17)
2
+
3
+ - Updated Faiss to 1.15.1
4
+
5
+ ## 0.6.3 (2026-08-03)
6
+
7
+ - Updated Faiss to 1.15.0
8
+
1
9
  ## 0.6.2 (2026-06-13)
2
10
 
3
11
  - Updated Faiss to 1.14.3
data/lib/faiss/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Faiss
2
- VERSION = "0.6.2"
2
+ VERSION = "0.6.4"
3
3
  end
@@ -45,6 +45,8 @@ void AutoTuneCriterion::set_groundtruth(
45
45
  gt_D.resize(nq * gt_nnn_in);
46
46
  memcpy(gt_D.data(), gt_D_in, sizeof(gt_D[0]) * nq * gt_nnn_in);
47
47
  }
48
+ FAISS_THROW_IF_NOT_MSG(
49
+ gt_I_in != nullptr, "set_groundtruth: gt_I must be non-null");
48
50
  gt_I.resize(nq * gt_nnn_in);
49
51
  memcpy(gt_I.data(), gt_I_in, sizeof(gt_I[0]) * nq * gt_nnn_in);
50
52
  }
@@ -148,7 +150,7 @@ bool OperatingPoints::add(
148
150
  }
149
151
  // remove non-optimal points from array
150
152
  for (size_t i = a.size() - 1; i > 0; --i) {
151
- if (a[i].t < a[i - 1].t) {
153
+ if (a[i].t <= a[i - 1].t) {
152
154
  a.erase(a.begin() + (i - 1));
153
155
  }
154
156
  }
@@ -367,7 +367,7 @@ void Clustering::train_encoded(
367
367
  ? std::numeric_limits<double>::max()
368
368
  : std::abs(prev_obj - obj) / std::abs(prev_obj);
369
369
 
370
- if (change >= 0 && change <= early_stop_threshold) {
370
+ if (change <= early_stop_threshold) {
371
371
  if (verbose) {
372
372
  printf("\n Converged at iteration %d: "
373
373
  "objective did not change\n",
@@ -439,6 +439,14 @@ float kmeans_clustering(
439
439
  size_t k,
440
440
  const float* x,
441
441
  float* centroids) {
442
+ FAISS_THROW_IF_NOT_FMT(
443
+ d <= static_cast<size_t>(std::numeric_limits<int>::max()),
444
+ "kmeans_clustering: d=%zu exceeds INT_MAX",
445
+ d);
446
+ FAISS_THROW_IF_NOT_FMT(
447
+ k <= static_cast<size_t>(std::numeric_limits<int>::max()),
448
+ "kmeans_clustering: k=%zu exceeds INT_MAX",
449
+ k);
442
450
  Clustering clus(static_cast<int>(d), static_cast<int>(k));
443
451
  clus.verbose = d * n * k > (size_t(1) << 30);
444
452
  // display logs if > 1Gflop per iteration
@@ -75,6 +75,14 @@ struct ClusteringParameters {
75
75
  /// so the training process stops only if an error
76
76
  /// is unchanged from the previous iteration.
77
77
  double early_stop_threshold = 0.0;
78
+
79
+ /// Whether to use the SuperKMeans (super fast k-means) variant instead of
80
+ /// the vanilla Clustering implementation. Only honored by callers that
81
+ /// explicitly support it (e.g. IVF level-1 quantizer training). Cannot be
82
+ /// combined with a user-provided clustering index: SuperKMeans assigns
83
+ /// with its own index, so callers that supply both get an exception rather
84
+ /// than a silent downgrade to vanilla Clustering.
85
+ bool use_super_kmeans = false;
78
86
  };
79
87
 
80
88
  struct ClusteringIterationStats {
@@ -592,8 +592,12 @@ void handle_ivf(
592
592
  static_cast<faiss::IndexIVF*>(faiss::clone_index(clone));
593
593
  if (generate_ids) {
594
594
  // Assume the quantizer does not natively support add_with_ids.
595
- sharded_index->quantizer =
596
- new IndexIDMap2(sharded_index->quantizer);
595
+ // The IDMap wraps the cloned quantizer; own_fields makes it delete
596
+ // that quantizer when sharded_index (which owns the IDMap) is
597
+ // freed.
598
+ auto* id_quantizer = new IndexIDMap2(sharded_index->quantizer);
599
+ id_quantizer->own_fields = true;
600
+ sharded_index->quantizer = id_quantizer;
597
601
  sharded_index->quantizer->add_with_ids(
598
602
  sharded_centroids[i].size() / index->quantizer->d,
599
603
  sharded_centroids[i].data(),
@@ -615,6 +619,7 @@ void handle_ivf(
615
619
  faiss::write_index(sharded_index, fname);
616
620
  delete sharded_index;
617
621
  }
622
+ delete clone;
618
623
  }
619
624
 
620
625
  void handle_binary_ivf(
@@ -650,8 +655,13 @@ void handle_binary_ivf(
650
655
  faiss::clone_binary_index(clone));
651
656
  if (generate_ids) {
652
657
  // Assume the quantizer does not natively support add_with_ids.
653
- sharded_index->quantizer =
658
+ // The IDMap wraps the cloned quantizer; own_fields makes it delete
659
+ // that quantizer when sharded_index (which owns the IDMap) is
660
+ // freed.
661
+ auto* id_quantizer =
654
662
  new IndexBinaryIDMap2(sharded_index->quantizer);
663
+ id_quantizer->own_fields = true;
664
+ sharded_index->quantizer = id_quantizer;
655
665
  sharded_index->quantizer->add_with_ids(
656
666
  sharded_centroids[i].size() / reconstruction_size,
657
667
  sharded_centroids[i].data(),
@@ -673,6 +683,7 @@ void handle_binary_ivf(
673
683
  faiss::write_index_binary(sharded_index, fname);
674
684
  delete sharded_index;
675
685
  }
686
+ delete clone;
676
687
  }
677
688
 
678
689
  template <typename IndexType>
@@ -16,8 +16,8 @@
16
16
  #include <cstdio>
17
17
 
18
18
  #define FAISS_VERSION_MAJOR 1
19
- #define FAISS_VERSION_MINOR 14
20
- #define FAISS_VERSION_PATCH 3
19
+ #define FAISS_VERSION_MINOR 15
20
+ #define FAISS_VERSION_PATCH 1
21
21
 
22
22
  // Macro to combine the version components into a single string
23
23
  #ifndef FAISS_STRINGIFY
@@ -58,8 +58,8 @@ struct AQDistanceComputerDecompress : FlatCodesDistanceComputer {
58
58
  }
59
59
 
60
60
  float symmetric_dis(idx_t i, idx_t j) final {
61
- aq.decode(codes + i * d, tmp.data(), 1);
62
- aq.decode(codes + j * d, tmp.data() + d, 1);
61
+ aq.decode(codes + i * code_size, tmp.data(), 1);
62
+ aq.decode(codes + j * code_size, tmp.data() + d, 1);
63
63
  return vd(tmp.data(), tmp.data() + d);
64
64
  }
65
65
 
@@ -79,7 +79,8 @@ struct AQDistanceComputerLUT : FlatCodesDistanceComputer {
79
79
 
80
80
  explicit AQDistanceComputerLUT(const IndexAdditiveQuantizer& iaq)
81
81
  : FlatCodesDistanceComputer(iaq.codes.data(), iaq.code_size),
82
- LUT(iaq.aq->total_codebook_size + iaq.d * 2),
82
+ LUT(iaq.aq->total_codebook_size // Storage for LUT.
83
+ + size_t(iaq.d) * 2), // tmp storage for symmetric distance.
83
84
  aq(*iaq.aq),
84
85
  d(iaq.d) {}
85
86
 
@@ -96,9 +97,9 @@ struct AQDistanceComputerLUT : FlatCodesDistanceComputer {
96
97
  }
97
98
 
98
99
  float symmetric_dis(idx_t i, idx_t j) final {
99
- float* tmp = LUT.data();
100
- aq.decode(codes + i * d, tmp, 1);
101
- aq.decode(codes + j * d, tmp + d, 1);
100
+ float* tmp = LUT.data() + aq.total_codebook_size;
101
+ aq.decode(codes + i * code_size, tmp, 1);
102
+ aq.decode(codes + j * code_size, tmp + d, 1);
102
103
  return fvec_L2sqr(tmp, tmp + d, d);
103
104
  }
104
105
 
@@ -234,8 +235,7 @@ void IndexAdditiveQuantizer::search(
234
235
  float* distances,
235
236
  idx_t* labels,
236
237
  const SearchParameters* params) const {
237
- FAISS_THROW_IF_NOT_MSG(
238
- !params, "search params not supported for this index");
238
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
239
239
 
240
240
  if (aq->search_type == AdditiveQuantizer::ST_decompress) {
241
241
  with_VectorDistance(d, metric_type, metric_arg, [&](auto vd) {
@@ -458,8 +458,7 @@ void AdditiveCoarseQuantizer::search(
458
458
  float* distances,
459
459
  idx_t* labels,
460
460
  const SearchParameters* params) const {
461
- FAISS_THROW_IF_NOT_MSG(
462
- !params, "search params not supported for this index");
461
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
463
462
 
464
463
  if (metric_type == METRIC_INNER_PRODUCT) {
465
464
  aq->knn_centroids_inner_product(n, x, k, distances, labels);
@@ -36,7 +36,7 @@ void IndexAdditiveQuantizerFastScan::init(
36
36
  MetricType metric,
37
37
  int bbs_) {
38
38
  FAISS_THROW_IF_NOT(aq_init != nullptr);
39
- FAISS_THROW_IF_NOT(!aq_init->nbits.empty());
39
+ FAISS_THROW_IF_MSG(aq_init->nbits.empty(), "nbits must not be empty");
40
40
  FAISS_THROW_IF_NOT(aq_init->nbits[0] == 4);
41
41
  if (metric == METRIC_INNER_PRODUCT) {
42
42
  FAISS_THROW_IF_NOT_MSG(
@@ -192,8 +192,7 @@ void IndexAdditiveQuantizerFastScan::search(
192
192
  float* distances,
193
193
  idx_t* labels,
194
194
  const SearchParameters* params) const {
195
- FAISS_THROW_IF_NOT_MSG(
196
- !params, "search params not supported for this index");
195
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
197
196
  FAISS_THROW_IF_NOT(k > 0);
198
197
  bool rescale = (rescale_norm && norm_scale > 1 && metric_type == METRIC_L2);
199
198
  if (!rescale) {
@@ -9,9 +9,11 @@
9
9
 
10
10
  #include <faiss/IndexBinaryFromFloat.h>
11
11
 
12
+ #include <faiss/MetricType.h>
12
13
  #include <faiss/impl/FaissAssert.h>
13
14
  #include <faiss/utils/utils.h>
14
15
  #include <algorithm>
16
+ #include <cmath>
15
17
  #include <memory>
16
18
 
17
19
  namespace faiss {
@@ -55,8 +57,7 @@ void IndexBinaryFromFloat::search(
55
57
  int32_t* distances,
56
58
  idx_t* labels,
57
59
  const SearchParameters* params) const {
58
- FAISS_THROW_IF_NOT_MSG(
59
- !params, "search params not supported for this index");
60
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
60
61
  FAISS_THROW_IF_NOT(k > 0);
61
62
 
62
63
  constexpr idx_t bs = 32768;
@@ -68,8 +69,27 @@ void IndexBinaryFromFloat::search(
68
69
  binary_to_real(bn * d, x + b * code_size, xf.get());
69
70
 
70
71
  index->search(bn, xf.get(), k, df.get(), labels + b * k);
71
- for (int i = 0; i < bn * k; ++i) {
72
- distances[b * k + i] = int32_t(std::round(df[i] / 4.0));
72
+ // binary_to_real maps bits to {-1, +1}, so the float distance relates
73
+ // to the Hamming distance differently for each metric. In all cases the
74
+ // mapping is monotonic, so the result order needs no adjustment.
75
+ switch (index->metric_type) {
76
+ case METRIC_INNER_PRODUCT:
77
+ for (int i = 0; i < bn * k; ++i) {
78
+ distances[b * k + i] =
79
+ int32_t(std::round((d - df[i]) / 2.0));
80
+ }
81
+ break;
82
+ case METRIC_L1:
83
+ for (int i = 0; i < bn * k; ++i) {
84
+ distances[b * k + i] = int32_t(std::round(df[i] / 2.0));
85
+ }
86
+ break;
87
+ case METRIC_L2:
88
+ default:
89
+ for (int i = 0; i < bn * k; ++i) {
90
+ distances[b * k + i] = int32_t(std::round(df[i] / 4.0));
91
+ }
92
+ break;
73
93
  }
74
94
  }
75
95
  }
@@ -7,7 +7,6 @@
7
7
 
8
8
  #include <faiss/IndexBinaryHNSW.h>
9
9
 
10
- #include <omp.h>
11
10
  #include <cassert>
12
11
  #include <cmath>
13
12
  #include <cstdio>
@@ -24,7 +23,6 @@
24
23
  #include <faiss/impl/ResultHandler.h>
25
24
  #include <faiss/impl/VisitedTable.h>
26
25
  #include <faiss/utils/Heap.h>
27
- #include <faiss/utils/hamming.h>
28
26
  #include <faiss/utils/random.h>
29
27
 
30
28
  #include <faiss/impl/simd_dispatch.h>
@@ -40,140 +38,6 @@
40
38
 
41
39
  namespace faiss {
42
40
 
43
- /**************************************************************
44
- * add / search blocks of descriptors
45
- **************************************************************/
46
-
47
- namespace {
48
-
49
- void hnsw_add_vertices(
50
- IndexBinaryHNSW& index_hnsw,
51
- size_t n0,
52
- size_t n,
53
- const uint8_t* x,
54
- bool verbose,
55
- bool preset_levels = false) {
56
- HNSW& hnsw = index_hnsw.hnsw;
57
- size_t ntotal = n0 + n;
58
- double t0 = getmillisecs();
59
- if (verbose) {
60
- printf("hnsw_add_vertices: adding %zd elements on top of %zd "
61
- "(preset_levels=%d)\n",
62
- n,
63
- n0,
64
- int(preset_levels));
65
- }
66
-
67
- int max_level = hnsw.prepare_level_tab(n, preset_levels);
68
-
69
- if (verbose) {
70
- printf(" max_level = %d\n", max_level);
71
- }
72
-
73
- auto& locks = index_hnsw.locks;
74
- locks.prepare(ntotal);
75
-
76
- // add vectors from highest to lowest level
77
- std::vector<int> hist;
78
- std::vector<int> order(n);
79
-
80
- { // make buckets with vectors of the same level
81
-
82
- // build histogram
83
- for (size_t i = 0; i < n; i++) {
84
- HNSW::storage_idx_t pt_id =
85
- static_cast<HNSW::storage_idx_t>(i + n0);
86
- int pt_level = hnsw.levels[pt_id] - 1;
87
- while (pt_level >= static_cast<int>(hist.size())) {
88
- hist.push_back(0);
89
- }
90
- hist[pt_level]++;
91
- }
92
-
93
- // accumulate
94
- std::vector<int> offsets(hist.size() + 1, 0);
95
- for (size_t i = 0; i < hist.size() - 1; i++) {
96
- offsets[i + 1] = offsets[i] + hist[i];
97
- }
98
-
99
- // bucket sort
100
- for (size_t i = 0; i < n; i++) {
101
- HNSW::storage_idx_t pt_id =
102
- static_cast<HNSW::storage_idx_t>(i + n0);
103
- int pt_level = hnsw.levels[pt_id] - 1;
104
- order[offsets[pt_level]++] = pt_id;
105
- }
106
- }
107
-
108
- { // perform add
109
- RandomGenerator rng2(789);
110
-
111
- size_t i1 = static_cast<int>(n);
112
-
113
- for (int pt_level = static_cast<int>(hist.size()) - 1;
114
- pt_level >= int(!index_hnsw.init_level0);
115
- pt_level--) {
116
- size_t i0 = i1 - hist[pt_level];
117
-
118
- if (verbose) {
119
- printf("Adding %zu elements at level %d\n", i1 - i0, pt_level);
120
- }
121
-
122
- // random permutation to get rid of dataset order bias
123
- for (size_t j = i0; j < i1; j++) {
124
- std::swap(
125
- order[j],
126
- order[j + rng2.rand_int(static_cast<int>(i1 - j))]);
127
- }
128
-
129
- #pragma omp parallel
130
- {
131
- std::unique_ptr<VisitedTable> vt = VisitedTable::create(ntotal);
132
-
133
- std::unique_ptr<DistanceComputer> dis(
134
- index_hnsw.get_distance_computer());
135
- bool do_display = verbose && omp_get_thread_num() == 0;
136
- size_t prev_display = 0;
137
-
138
- #pragma omp for schedule(dynamic)
139
- for (int64_t i = i0; i < i1; i++) {
140
- HNSW::storage_idx_t pt_id = order[i];
141
- dis->set_query(
142
- (float*)(x + (pt_id - n0) * index_hnsw.code_size));
143
-
144
- hnsw.add_with_locks(
145
- *dis,
146
- pt_level,
147
- pt_id,
148
- locks,
149
- *vt,
150
- index_hnsw.keep_max_size_level0 && (pt_level == 0));
151
-
152
- if (do_display && i - i0 > prev_display + 10000) {
153
- prev_display = i - i0;
154
- printf(" %zu / %zu\r", i - i0, i1 - i0);
155
- fflush(stdout);
156
- }
157
- }
158
- }
159
- i1 = i0;
160
- }
161
- if (index_hnsw.init_level0) {
162
- FAISS_ASSERT(i1 == 0);
163
- } else {
164
- FAISS_ASSERT((i1 - hist[0]) == 0);
165
- }
166
- }
167
- if (verbose) {
168
- printf("Done in %.3f ms\n", getmillisecs() - t0);
169
- }
170
- if (!index_hnsw.retain_locks) {
171
- locks.clear();
172
- }
173
- }
174
-
175
- } // anonymous namespace
176
-
177
41
  /**************************************************************
178
42
  * IndexBinaryHNSW implementation
179
43
  **************************************************************/
@@ -271,18 +135,25 @@ void IndexBinaryHNSW::add(idx_t n, const uint8_t* x) {
271
135
  storage->add(n, x);
272
136
  ntotal = storage->ntotal;
273
137
 
274
- hnsw_add_vertices(
275
- *this,
138
+ bool preset_levels = hnsw.levels.size() == static_cast<size_t>(ntotal);
139
+
140
+ hnsw_add_vertices_deterministic(
141
+ hnsw,
276
142
  n0,
277
143
  n,
278
- x,
144
+ d,
145
+ init_level0,
146
+ keep_max_size_level0,
147
+ preset_levels,
279
148
  verbose,
280
- hnsw.levels.size() == static_cast<size_t>(ntotal));
149
+ [this] { return get_distance_computer(); },
150
+ [this, x, n0](DistanceComputer& dc, HNSW::storage_idx_t pt_id) {
151
+ dc.set_query((const float*)(x + (pt_id - n0) * code_size));
152
+ });
281
153
  }
282
154
 
283
155
  void IndexBinaryHNSW::reset() {
284
156
  hnsw.reset();
285
- locks.clear();
286
157
  storage->reset();
287
158
  ntotal = 0;
288
159
  }
@@ -293,8 +164,8 @@ void IndexBinaryHNSW::reconstruct(idx_t key, uint8_t* recons) const {
293
164
 
294
165
  DistanceComputer* IndexBinaryHNSW::get_distance_computer() const {
295
166
  IndexBinaryFlat* flat_storage = dynamic_cast<IndexBinaryFlat*>(storage);
296
- FAISS_THROW_IF_NOT_MSG(
297
- flat_storage != nullptr,
167
+ FAISS_THROW_IF_MSG(
168
+ flat_storage == nullptr,
298
169
  "IndexBinaryHNSW requires IndexBinaryFlat storage");
299
170
  return with_simd_level([&]<SIMDLevel SL>() {
300
171
  return make_binary_hnsw_distance_computer_fixSL<SL>(
@@ -317,8 +188,8 @@ IndexBinaryHNSWCagra::IndexBinaryHNSWCagra(int d_, int M)
317
188
  }
318
189
 
319
190
  void IndexBinaryHNSWCagra::add(idx_t n, const uint8_t* x) {
320
- FAISS_THROW_IF_NOT_MSG(
321
- !base_level_only,
191
+ FAISS_THROW_IF_MSG(
192
+ base_level_only,
322
193
  "Cannot add vectors when base_level_only is set to True");
323
194
 
324
195
  IndexBinaryHNSW::add(n, x);
@@ -11,7 +11,6 @@
11
11
 
12
12
  #include <faiss/IndexBinaryFlat.h>
13
13
  #include <faiss/impl/HNSW.h>
14
- #include <faiss/impl/hnsw/LockVector.h>
15
14
  #include <faiss/utils/utils.h>
16
15
 
17
16
  namespace faiss {
@@ -41,11 +40,6 @@ struct IndexBinaryHNSW : IndexBinary {
41
40
  // used when GpuIndexBinaryCagra::copyFrom(IndexBinaryHNSW*) is called.
42
41
  bool keep_max_size_level0 = false;
43
42
 
44
- // Per-node locks for HNSW graph construction.
45
- LockVector locks;
46
- // locks are freed after each call to add() unless this flag is set.
47
- bool retain_locks = false;
48
-
49
43
  explicit IndexBinaryHNSW();
50
44
  explicit IndexBinaryHNSW(int d, int M = 32);
51
45
  explicit IndexBinaryHNSW(IndexBinary* storage, int M = 32);
@@ -27,7 +27,7 @@
27
27
  // NOLINTNEXTLINE(facebook-hte-InlineHeader)
28
28
  // NOLINTNEXTLINE(facebook-hte-InlineHeader)
29
29
  #include <faiss/impl/binary_hamming/IndexBinaryHash_impl.h>
30
- #include <faiss/utils/hamming_distance/hamming_computer-generic.h>
30
+ #include <faiss/utils/hamming_distance/hamming_computer-generic.h> // IWYU pragma: keep
31
31
  #undef THE_SIMD_LEVEL
32
32
 
33
33
  namespace faiss {
@@ -83,8 +83,7 @@ void IndexBinaryHash::range_search(
83
83
  int radius,
84
84
  RangeSearchResult* result,
85
85
  const SearchParameters* params) const {
86
- FAISS_THROW_IF_NOT_MSG(
87
- !params, "search params not supported for this index");
86
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
88
87
  size_t nlist = 0, ndis = 0, n0 = 0;
89
88
 
90
89
  #pragma omp parallel if (n > 100) reduction(+ : ndis, n0, nlist)
@@ -116,8 +115,7 @@ void IndexBinaryHash::search(
116
115
  int32_t* distances,
117
116
  idx_t* labels,
118
117
  const SearchParameters* params) const {
119
- FAISS_THROW_IF_NOT_MSG(
120
- !params, "search params not supported for this index");
118
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
121
119
  FAISS_THROW_IF_NOT(k > 0);
122
120
 
123
121
  using HeapForL2 = CMax<int32_t, idx_t>;
@@ -216,8 +214,7 @@ void IndexBinaryMultiHash::range_search(
216
214
  int radius,
217
215
  RangeSearchResult* result,
218
216
  const SearchParameters* params) const {
219
- FAISS_THROW_IF_NOT_MSG(
220
- !params, "search params not supported for this index");
217
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
221
218
  size_t nlist = 0, ndis = 0, n0 = 0;
222
219
 
223
220
  #pragma omp parallel if (n > 100) reduction(+ : ndis, n0, nlist)
@@ -249,8 +246,7 @@ void IndexBinaryMultiHash::search(
249
246
  int32_t* distances,
250
247
  idx_t* labels,
251
248
  const SearchParameters* params) const {
252
- FAISS_THROW_IF_NOT_MSG(
253
- !params, "search params not supported for this index");
249
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
254
250
  FAISS_THROW_IF_NOT(k > 0);
255
251
 
256
252
  using HeapForL2 = CMax<int32_t, idx_t>;
@@ -192,7 +192,7 @@ void IndexBinaryIVF::reconstruct_n(idx_t i0, idx_t ni, uint8_t* recons) const {
192
192
  continue;
193
193
  }
194
194
 
195
- uint8_t* reconstructed = recons + (id - i0) * d;
195
+ uint8_t* reconstructed = recons + (id - i0) * code_size;
196
196
  reconstruct_from_offset(list_no, offset, reconstructed);
197
197
  }
198
198
  }
@@ -206,8 +206,7 @@ void IndexBinaryIVF::search_and_reconstruct(
206
206
  idx_t* __restrict labels,
207
207
  uint8_t* __restrict recons,
208
208
  const SearchParameters* params) const {
209
- FAISS_THROW_IF_NOT_MSG(
210
- !params, "search params not supported for this index");
209
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
211
210
  const size_t nprobe_2 = std::min(nlist, this->nprobe);
212
211
  FAISS_THROW_IF_NOT(k > 0);
213
212
  FAISS_THROW_IF_NOT(nprobe_2 > 0);
@@ -234,10 +233,10 @@ void IndexBinaryIVF::search_and_reconstruct(
234
233
  for (idx_t j = 0; j < k; ++j) {
235
234
  idx_t ij = i * k + j;
236
235
  idx_t key = labels[ij];
237
- uint8_t* reconstructed = recons + ij * d;
236
+ uint8_t* reconstructed = recons + ij * code_size;
238
237
  if (key < 0) {
239
238
  // Fill with NaNs
240
- memset(reconstructed, -1, sizeof(*reconstructed) * d);
239
+ memset(reconstructed, -1, code_size);
241
240
  } else {
242
241
  int list_no = key >> 32;
243
242
  int offset = key & 0xffffffff;
@@ -349,10 +348,6 @@ void IndexBinaryIVF::replace_invlists(InvertedLists* il, bool own) {
349
348
  own_invlists = own;
350
349
  }
351
350
 
352
- // IVFBinaryScannerL2, search_knn_hamming_count, BlockSearch,
353
- // BlockSearchVariableK, search_knn_hamming_per_invlist are now in
354
- // impl/binary_hamming/IndexBinaryIVF_impl.h (compiled per-ISA)
355
-
356
351
  namespace {
357
352
 
358
353
  void search_knn_hamming_heap(
@@ -462,13 +457,9 @@ void search_knn_hamming_heap(
462
457
 
463
458
  } // anonymous namespace
464
459
 
465
- // The remaining template code (search_knn_hamming_count,
466
- // search_knn_hamming_per_invlist, etc.) has been moved to
467
- // impl/binary_hamming/IndexBinaryIVF_impl.h
468
-
469
460
  BinaryInvertedListScanner* IndexBinaryIVF::get_InvertedListScanner(
470
461
  bool store_pairs) const {
471
- return with_simd_level([&]<SIMDLevel SL>() {
462
+ return with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
472
463
  return make_binary_ivf_scanner_fixSL<SL>(code_size, store_pairs);
473
464
  });
474
465
  }
@@ -484,7 +475,7 @@ void IndexBinaryIVF::search_preassigned(
484
475
  bool store_pairs,
485
476
  const IVFSearchParameters* params) const {
486
477
  if (per_invlist_search) {
487
- with_simd_level([&]<SIMDLevel SL>() {
478
+ with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
488
479
  search_knn_hamming_per_invlist_fixSL<SL>(
489
480
  code_size,
490
481
  this,
@@ -502,7 +493,7 @@ void IndexBinaryIVF::search_preassigned(
502
493
  search_knn_hamming_heap(
503
494
  this, n, x, k, cidx, cdis, dis, idx, store_pairs, params);
504
495
  } else {
505
- with_simd_level([&]<SIMDLevel SL>() {
496
+ with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
506
497
  search_knn_hamming_count_fixSL<SL>(
507
498
  code_size,
508
499
  store_pairs,
@@ -524,8 +515,7 @@ void IndexBinaryIVF::range_search(
524
515
  int radius,
525
516
  RangeSearchResult* __restrict res,
526
517
  const SearchParameters* params) const {
527
- FAISS_THROW_IF_NOT_MSG(
528
- !params, "search params not supported for this index");
518
+ FAISS_THROW_IF_MSG(params, "search params not supported for this index");
529
519
  const size_t nprobe_2 = std::min(nlist, this->nprobe);
530
520
  std::unique_ptr<idx_t[]> idx(new idx_t[n * nprobe_2]);
531
521
  std::unique_ptr<int32_t[]> coarse_dis(new int32_t[n * nprobe_2]);
@@ -232,7 +232,14 @@ struct BinaryInvertedListScanner {
232
232
  virtual uint32_t distance_to_code(const uint8_t* code) const = 0;
233
233
 
234
234
  /** compute the distances to codes. (distances, labels) should be
235
- * organized as a min- or max-heap
235
+ * organized as a max-heap: the scan accepts a code whose distance is
236
+ * below distances[0] and replaces the top. A min-heap caller reads its
237
+ * neutral value as a huge unsigned bound and gets wrong results.
238
+
239
+ *
240
+ * The heap top is the only bound. To keep the k nearest codes inside a
241
+ * radius, seed every heap slot with that radius instead of the neutral
242
+ * value; an unfilled slot keeps its label of -1.
236
243
  *
237
244
  * @param n number of codes to scan
238
245
  * @param codes codes to scan (n * code_size)