faiss 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/AutoTune.cpp +3 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +9 -1
  6. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  7. data/vendor/faiss/faiss/Index.h +2 -2
  8. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  10. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
  11. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +4 -5
  12. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  13. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +2 -4
  14. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  15. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  16. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  17. data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
  18. data/vendor/faiss/faiss/IndexHNSW.cpp +10 -11
  19. data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
  20. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  21. data/vendor/faiss/faiss/IndexIVF.cpp +17 -6
  22. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  23. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  24. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  25. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  26. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  27. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  28. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  29. data/vendor/faiss/faiss/IndexIVFPQ.cpp +40 -22
  30. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  31. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  32. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  33. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  34. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  35. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  36. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  37. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  38. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  39. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  40. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  41. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  43. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  44. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  45. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  46. data/vendor/faiss/faiss/IndexShards.cpp +2 -2
  47. data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
  48. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
  50. data/vendor/faiss/faiss/SuperKMeans.h +30 -0
  51. data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +5 -0
  53. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  55. data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
  56. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  58. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  59. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  60. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  61. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  62. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  63. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  64. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  65. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
  66. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  67. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  68. data/vendor/faiss/faiss/impl/HNSW.cpp +109 -152
  69. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  70. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  71. data/vendor/faiss/faiss/impl/Panorama.h +9 -7
  72. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  73. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +34 -22
  74. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +44 -36
  75. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  76. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +168 -67
  77. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
  78. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
  79. data/vendor/faiss/faiss/impl/ResultHandler.h +25 -31
  80. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +258 -57
  81. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +20 -0
  82. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  83. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  84. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  85. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
  86. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  87. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  88. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  89. data/vendor/faiss/faiss/impl/index_read.cpp +376 -36
  90. data/vendor/faiss/faiss/impl/index_write.cpp +55 -4
  91. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  92. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  93. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  94. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  95. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  96. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  97. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  98. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  99. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  100. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  101. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  102. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  103. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
  104. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  105. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +85 -23
  106. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  107. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  108. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  109. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +136 -0
  110. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +16 -16
  111. data/vendor/faiss/faiss/impl/simd_dispatch.h +30 -9
  112. data/vendor/faiss/faiss/index_factory.cpp +32 -6
  113. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  114. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +2 -2
  115. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
  116. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  117. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  118. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  119. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  120. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  121. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  122. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +31 -1
  123. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +15 -2
  124. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +1 -2
  125. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  126. data/vendor/faiss/faiss/utils/distances.cpp +14 -2
  127. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  128. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  129. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  130. data/vendor/faiss/faiss/utils/hamming.cpp +1 -1
  131. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  132. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  133. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  134. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  135. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
  136. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  137. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
  138. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +92 -0
  139. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  140. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  141. data/vendor/faiss/faiss/utils/simd_levels.cpp +44 -0
  142. data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
  143. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  144. metadata +16 -1
@@ -7,6 +7,7 @@
7
7
 
8
8
  #include <faiss/impl/HNSW.h>
9
9
 
10
+ #include <array>
10
11
  #include <cinttypes>
11
12
  #include <cstddef>
12
13
  #include <cstdlib>
@@ -172,17 +173,21 @@ void HNSW::print_neighbor_stats(int level) const {
172
173
  }
173
174
 
174
175
  void HNSW::fill_with_random_links(size_t n) {
175
- int max_level_2 = prepare_level_tab(n);
176
+ if (n == 0) {
177
+ return;
178
+ }
179
+ max_level = prepare_level_tab(n);
180
+ entry_point = 0;
181
+
176
182
  RandomGenerator rng2(456);
177
183
 
178
- for (int level = max_level_2 - 1; level >= 0; --level) {
184
+ for (int level = max_level - 1; level >= 0; --level) {
179
185
  std::vector<int> elts;
180
186
  for (size_t i = 0; i < n; i++) {
181
187
  if (levels[i] > level) {
182
188
  elts.push_back(i);
183
189
  }
184
190
  }
185
- printf("linking %zd elements in level %d\n", elts.size(), level);
186
191
 
187
192
  if (elts.size() == 1) {
188
193
  continue;
@@ -1092,8 +1097,8 @@ int hnsw_detail::search_from_candidates_panorama(
1092
1097
  const SearchParameters* params) {
1093
1098
  // Panorama's progressive-bound math is L2-specific: refuse to run in
1094
1099
  // similarity mode.
1095
- FAISS_THROW_IF_NOT_MSG(
1096
- !hnsw.is_similarity,
1100
+ FAISS_THROW_IF_MSG(
1101
+ hnsw.is_similarity,
1097
1102
  "search_from_candidates_panorama does not support is_similarity=true");
1098
1103
 
1099
1104
  using C = HNSW::C_distance;
@@ -1132,177 +1137,129 @@ int hnsw_detail::search_from_candidates_panorama(
1132
1137
  flat_codes_qdis,
1133
1138
  "DistanceComputer must be a FlatCodesDistanceComputer");
1134
1139
 
1135
- // Allocate space for the index array and exact distances.
1136
- size_t M = hnsw.nb_neighbors(0);
1137
- std::vector<idx_t> index_array(M);
1138
- std::vector<float> exact_distances(M);
1140
+ const auto& pano = panorama_index->pano;
1141
+ const size_t nb_per_parent = static_cast<size_t>(hnsw.nb_neighbors(level));
1142
+ const size_t num_panorama_levels = pano.n_levels;
1143
+ const size_t level_width_floats = pano.level_width_floats;
1139
1144
 
1140
- const float* query = flat_codes_qdis->q;
1141
- std::vector<float> query_cum_sums(panorama_index->pano.n_levels + 1);
1142
- panorama_index->pano.compute_query_cum_sums(query, query_cum_sums.data());
1143
- float query_norm_sq = query_cum_sums[0] * query_cum_sums[0];
1145
+ constexpr size_t kTargetBatch = 64;
1146
+ const size_t buf_cap = kTargetBatch + nb_per_parent;
1147
+ std::vector<uint32_t> index_array(buf_cap);
1148
+ std::vector<float> exact_distances(buf_cap);
1149
+ std::vector<float> dot_buffer(buf_cap);
1150
+ std::vector<float> query_cum_sums_buf(num_panorama_levels + 1);
1144
1151
 
1145
- int nstep = 0;
1152
+ const float* query = flat_codes_qdis->q;
1146
1153
  const size_t d = static_cast<size_t>(panorama_index->d);
1147
1154
 
1148
- PanoramaStats local_pano_stats;
1149
- local_pano_stats.reset();
1155
+ const float* cum_base = panorama_index->get_cum_sum(0);
1156
+ const size_t cum_stride = num_panorama_levels + 1;
1157
+ const auto* flat_storage =
1158
+ static_cast<const IndexFlat*>(panorama_index->storage);
1159
+ const float* xb_base =
1160
+ reinterpret_cast<const float*>(flat_storage->codes.data());
1161
+ const size_t feat_stride = d;
1150
1162
 
1151
- while (candidates.size() > 0) {
1152
- float d0 = 0;
1153
- int v0 = candidates.pop_min(&d0);
1154
-
1155
- if (do_dis_check) {
1156
- // tricky stopping condition: there are more than ef
1157
- // distances that are processed already that are smaller
1158
- // than d0
1163
+ pano.compute_query_cum_sums(query, query_cum_sums_buf.data());
1164
+ const float* query_cum_sums = query_cum_sums_buf.data();
1165
+ const float query_norm_sq = query_cum_sums[0] * query_cum_sums[0];
1159
1166
 
1160
- int n_dis_below = candidates.count_below(d0);
1161
- if (n_dis_below >= efSearch) {
1162
- break;
1163
- }
1164
- }
1167
+ int nstep = 0;
1165
1168
 
1166
- size_t begin, end;
1167
- hnsw.neighbor_range(v0, level, &begin, &end);
1169
+ PanoramaStats local_pano_stats;
1170
+ local_pano_stats.reset();
1168
1171
 
1169
- // Unlike the vanilla HNSW, we already remove (and compact) the visited
1170
- // nodes from the candidates list at this stage. We also remove nodes
1171
- // that are not selected.
1172
+ bool stop_flag = false;
1173
+ while (candidates.size() > 0 && !stop_flag) {
1172
1174
  size_t initial_size = 0;
1173
- for (size_t j = begin; j < end; j++) {
1174
- int v1 = hnsw.neighbors[j];
1175
- if (v1 < 0) {
1176
- break;
1175
+ size_t k_popped = 0;
1176
+ while (initial_size < kTargetBatch && candidates.size() > 0) {
1177
+ float d0 = 0;
1178
+ int v0 = candidates.pop_min(&d0);
1179
+ if (do_dis_check) {
1180
+ int n_dis_below = candidates.count_below(d0);
1181
+ if (n_dis_below >= efSearch) {
1182
+ if (k_popped == 0) {
1183
+ // Standard early-stop: nothing queued this
1184
+ // iteration, terminate the outer loop entirely
1185
+ // (matches the single-pop path's behavior).
1186
+ stop_flag = true;
1187
+ } else {
1188
+ // We already have parents queued; un-pop this
1189
+ // one so the next outer iteration sees it and
1190
+ // re-applies the stop check from a clean state.
1191
+ candidates.push(v0, d0);
1192
+ }
1193
+ break;
1194
+ }
1177
1195
  }
1196
+ k_popped++;
1178
1197
 
1179
- const float* cum_sums_v1 = panorama_index->get_cum_sum(v1);
1180
- index_array[initial_size] = v1;
1181
- exact_distances[initial_size] =
1182
- query_norm_sq + cum_sums_v1[0] * cum_sums_v1[0];
1198
+ size_t begin, end;
1199
+ hnsw.neighbor_range(v0, level, &begin, &end);
1200
+ for (size_t j = begin; j < end; j++) {
1201
+ int v1 = hnsw.neighbors[j];
1202
+ if (v1 < 0) {
1203
+ break;
1204
+ }
1183
1205
 
1184
- bool is_selected = !sel || sel->is_member(v1);
1185
- initial_size += is_selected && vt.set(v1) ? 1 : 0;
1206
+ bool is_new = vt.set(v1);
1207
+ bool is_selected = !sel || sel->is_member(v1);
1208
+ if (is_new && is_selected) {
1209
+ const float vsum =
1210
+ cum_base[static_cast<size_t>(v1) * cum_stride];
1211
+ index_array[initial_size] = v1;
1212
+ exact_distances[initial_size] = query_norm_sq + vsum * vsum;
1213
+ initial_size++;
1214
+ }
1215
+ }
1186
1216
  }
1187
1217
 
1188
1218
  local_pano_stats.total_dims += initial_size * d;
1219
+
1189
1220
  size_t batch_size = initial_size;
1190
1221
  size_t curr_panorama_level = 0;
1191
- const size_t num_panorama_levels = panorama_index->pano.n_levels;
1192
1222
  while (curr_panorama_level < num_panorama_levels && batch_size > 0) {
1193
- float query_cum_norm = query_cum_sums[curr_panorama_level + 1];
1194
-
1195
- size_t start_dim = curr_panorama_level *
1196
- panorama_index->pano.level_width_floats;
1197
- size_t end_dim = (curr_panorama_level + 1) *
1198
- panorama_index->pano.level_width_floats;
1199
- end_dim = std::min(end_dim, static_cast<size_t>(panorama_index->d));
1223
+ const size_t cs_level_idx = curr_panorama_level + 1;
1224
+ const float query_cum_norm = query_cum_sums[cs_level_idx];
1225
+ const float two_qc = 2.0f * query_cum_norm;
1226
+
1227
+ const size_t start_dim = curr_panorama_level * level_width_floats;
1228
+ size_t end_dim = (curr_panorama_level + 1) * level_width_floats;
1229
+ end_dim = std::min(end_dim, d);
1230
+ const size_t dim_span = end_dim - start_dim;
1231
+
1232
+ const float* level_base = xb_base + start_dim;
1233
+ with_level_width(dim_span, [&]<size_t W>() {
1234
+ compute_level_dot_kernel<false, W>(
1235
+ query + start_dim,
1236
+ level_base,
1237
+ index_array.data(),
1238
+ batch_size,
1239
+ dim_span,
1240
+ dot_buffer.data(),
1241
+ feat_stride);
1242
+ });
1243
+ ndis += batch_size;
1200
1244
 
1201
- size_t i = 0;
1202
1245
  size_t next_batch_size = 0;
1203
- for (; i + 3 < batch_size; i += 4) {
1204
- idx_t idx_0 = index_array[i];
1205
- idx_t idx_1 = index_array[i + 1];
1206
- idx_t idx_2 = index_array[i + 2];
1207
- idx_t idx_3 = index_array[i + 3];
1208
-
1209
- float dp[4];
1210
- flat_codes_qdis->partial_dot_product_batch_4(
1211
- idx_0,
1212
- idx_1,
1213
- idx_2,
1214
- idx_3,
1215
- dp[0],
1216
- dp[1],
1217
- dp[2],
1218
- dp[3],
1219
- start_dim,
1220
- end_dim - start_dim);
1221
- ndis += 4;
1222
1246
 
1223
- float new_exact_0 = exact_distances[i + 0] - 2 * dp[0];
1224
- float new_exact_1 = exact_distances[i + 1] - 2 * dp[1];
1225
- float new_exact_2 = exact_distances[i + 2] - 2 * dp[2];
1226
- float new_exact_3 = exact_distances[i + 3] - 2 * dp[3];
1227
-
1228
- float cum_sum_0 = panorama_index->get_cum_sum(
1229
- idx_0)[curr_panorama_level + 1];
1230
- float cum_sum_1 = panorama_index->get_cum_sum(
1231
- idx_1)[curr_panorama_level + 1];
1232
- float cum_sum_2 = panorama_index->get_cum_sum(
1233
- idx_2)[curr_panorama_level + 1];
1234
- float cum_sum_3 = panorama_index->get_cum_sum(
1235
- idx_3)[curr_panorama_level + 1];
1236
-
1237
- float cs_bound_0 = 2.0f * cum_sum_0 * query_cum_norm;
1238
- float cs_bound_1 = 2.0f * cum_sum_1 * query_cum_norm;
1239
- float cs_bound_2 = 2.0f * cum_sum_2 * query_cum_norm;
1240
- float cs_bound_3 = 2.0f * cum_sum_3 * query_cum_norm;
1241
-
1242
- float lower_bound_0 = new_exact_0 - cs_bound_0;
1243
- float lower_bound_1 = new_exact_1 - cs_bound_1;
1244
- float lower_bound_2 = new_exact_2 - cs_bound_2;
1245
- float lower_bound_3 = new_exact_3 - cs_bound_3;
1246
-
1247
- // The following code is not the most branch friendly (due to
1248
- // the maintenance of the candidate heap), but micro-benchmarks
1249
- // have shown that it is not worth it to write horrible code to
1250
- // squeeze out those cycles.
1251
- if (!C::cmp(lower_bound_0, threshold)) {
1252
- exact_distances[next_batch_size] = new_exact_0;
1253
- index_array[next_batch_size] = idx_0;
1254
- next_batch_size += 1;
1247
+ for (size_t i = 0; i < batch_size; i++) {
1248
+ float ne = exact_distances[i] - 2.0f * dot_buffer[i];
1249
+ float cum = cum_base
1250
+ [static_cast<size_t>(index_array[i]) * cum_stride +
1251
+ cs_level_idx];
1252
+ float lb = ne - two_qc * cum;
1253
+ if (lb <= threshold) {
1254
+ exact_distances[next_batch_size] = ne;
1255
+ index_array[next_batch_size] = index_array[i];
1256
+ next_batch_size++;
1255
1257
  } else {
1256
- candidates.push(idx_0, new_exact_0);
1257
- }
1258
- if (!C::cmp(lower_bound_1, threshold)) {
1259
- exact_distances[next_batch_size] = new_exact_1;
1260
- index_array[next_batch_size] = idx_1;
1261
- next_batch_size += 1;
1262
- } else {
1263
- candidates.push(idx_1, new_exact_1);
1264
- }
1265
- if (!C::cmp(lower_bound_2, threshold)) {
1266
- exact_distances[next_batch_size] = new_exact_2;
1267
- index_array[next_batch_size] = idx_2;
1268
- next_batch_size += 1;
1269
- } else {
1270
- candidates.push(idx_2, new_exact_2);
1271
- }
1272
- if (!C::cmp(lower_bound_3, threshold)) {
1273
- exact_distances[next_batch_size] = new_exact_3;
1274
- index_array[next_batch_size] = idx_3;
1275
- next_batch_size += 1;
1276
- } else {
1277
- candidates.push(idx_3, new_exact_3);
1278
- }
1279
- }
1280
-
1281
- // Process the remaining candidates.
1282
- for (; i < batch_size; i++) {
1283
- idx_t idx = index_array[i];
1284
-
1285
- float dp = flat_codes_qdis->partial_dot_product(
1286
- idx, start_dim, end_dim - start_dim);
1287
- ndis += 1;
1288
- float new_exact = exact_distances[i] - 2.0f * dp;
1289
-
1290
- float cum_sum = panorama_index->get_cum_sum(
1291
- idx)[curr_panorama_level + 1];
1292
- float cs_bound = 2.0f * cum_sum * query_cum_norm;
1293
- float lower_bound = new_exact - cs_bound;
1294
-
1295
- if (!C::cmp(lower_bound, threshold)) {
1296
- exact_distances[next_batch_size] = new_exact;
1297
- index_array[next_batch_size] = idx;
1298
- next_batch_size += 1;
1299
- } else {
1300
- candidates.push(idx, new_exact);
1258
+ candidates.push(index_array[i], ne);
1301
1259
  }
1302
1260
  }
1303
1261
 
1304
- local_pano_stats.total_dims_scanned +=
1305
- batch_size * (end_dim - start_dim);
1262
+ local_pano_stats.total_dims_scanned += batch_size * dim_span;
1306
1263
  batch_size = next_batch_size;
1307
1264
  curr_panorama_level++;
1308
1265
  }
@@ -1317,7 +1274,7 @@ int hnsw_detail::search_from_candidates_panorama(
1317
1274
  candidates.push(idx, exact_distances[i]);
1318
1275
  }
1319
1276
 
1320
- nstep++;
1277
+ nstep += static_cast<int>(k_popped);
1321
1278
  if (!do_dis_check && nstep > efSearch) {
1322
1279
  break;
1323
1280
  }
@@ -597,8 +597,8 @@ void LocalSearchQuantizer::icm_encode_step(
597
597
  const float* binaries,
598
598
  size_t n,
599
599
  size_t n_iters) const {
600
- FAISS_THROW_IF_NOT(M != 0 && K != 0);
601
- FAISS_THROW_IF_NOT(binaries != nullptr);
600
+ FAISS_THROW_IF_MSG(M == 0 || K == 0, "M and K must be nonzero");
601
+ FAISS_THROW_IF_NOT(binaries);
602
602
 
603
603
  // Resolve SIMD level once, not per iteration of the n × n_iters × M loop.
604
604
  with_simd_level_256bit([&]<SIMDLevel SL>() {
@@ -142,7 +142,9 @@ void NSG::build(
142
142
  idx_t n,
143
143
  const nsg::Graph<idx_t>& knn_graph,
144
144
  bool verbose) {
145
- FAISS_THROW_IF_NOT(!is_built && ntotal == 0);
145
+ FAISS_THROW_IF_MSG(
146
+ is_built || ntotal != 0,
147
+ "NSG graph must be empty and not yet built");
146
148
 
147
149
  if (verbose) {
148
150
  printf("NSG::build R=%d, L=%d, C=%d\n", R, L, C);
@@ -49,18 +49,20 @@ static inline void compute_level_dot_kernel(
49
49
  const uint32_t* active_indices,
50
50
  const size_t num_active,
51
51
  const size_t level_width_dims,
52
- float* FAISS_RESTRICT dot_products) {
52
+ float* FAISS_RESTRICT dot_products,
53
+ size_t stride = 0) {
53
54
  const size_t width = LevelWidth > 0 ? LevelWidth : level_width_dims;
55
+ const size_t row_stride = stride == 0 ? width : stride;
54
56
  size_t i = 0;
55
57
  for (; i + 4 <= num_active; i += 4) {
56
58
  const float* y0 = level_storage +
57
- (AllActive ? (i + 0) : active_indices[i + 0]) * width;
59
+ (AllActive ? (i + 0) : active_indices[i + 0]) * row_stride;
58
60
  const float* y1 = level_storage +
59
- (AllActive ? (i + 1) : active_indices[i + 1]) * width;
61
+ (AllActive ? (i + 1) : active_indices[i + 1]) * row_stride;
60
62
  const float* y2 = level_storage +
61
- (AllActive ? (i + 2) : active_indices[i + 2]) * width;
63
+ (AllActive ? (i + 2) : active_indices[i + 2]) * row_stride;
62
64
  const float* y3 = level_storage +
63
- (AllActive ? (i + 3) : active_indices[i + 3]) * width;
65
+ (AllActive ? (i + 3) : active_indices[i + 3]) * row_stride;
64
66
 
65
67
  float dp0 = 0, dp1 = 0, dp2 = 0, dp3 = 0;
66
68
  FAISS_PRAGMA_IMPRECISE_LOOP
@@ -78,8 +80,8 @@ static inline void compute_level_dot_kernel(
78
80
  dot_products[i + 3] = dp3;
79
81
  }
80
82
  for (; i < num_active; i++) {
81
- const float* yj =
82
- level_storage + (AllActive ? i : active_indices[i]) * width;
83
+ const float* yj = level_storage +
84
+ (AllActive ? i : active_indices[i]) * row_stride;
83
85
  float dp = 0;
84
86
  FAISS_PRAGMA_IMPRECISE_LOOP
85
87
  for (size_t j = 0; j < width; j++) {