faiss 0.5.0 → 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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/README.md +2 -0
  4. data/ext/faiss/index.cpp +8 -0
  5. data/lib/faiss/version.rb +1 -1
  6. data/vendor/faiss/faiss/IVFlib.cpp +25 -49
  7. data/vendor/faiss/faiss/Index.cpp +11 -0
  8. data/vendor/faiss/faiss/Index.h +24 -1
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +1 -0
  10. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +5 -1
  11. data/vendor/faiss/faiss/IndexFastScan.cpp +1 -1
  12. data/vendor/faiss/faiss/IndexFastScan.h +3 -8
  13. data/vendor/faiss/faiss/IndexFlat.cpp +374 -4
  14. data/vendor/faiss/faiss/IndexFlat.h +80 -0
  15. data/vendor/faiss/faiss/IndexHNSW.cpp +90 -1
  16. data/vendor/faiss/faiss/IndexHNSW.h +57 -1
  17. data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +34 -149
  18. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +86 -2
  19. data/vendor/faiss/faiss/IndexIVFRaBitQ.h +3 -1
  20. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +293 -115
  21. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +52 -16
  22. data/vendor/faiss/faiss/IndexPQ.cpp +4 -1
  23. data/vendor/faiss/faiss/IndexPreTransform.cpp +14 -0
  24. data/vendor/faiss/faiss/IndexPreTransform.h +9 -0
  25. data/vendor/faiss/faiss/IndexRaBitQ.cpp +96 -16
  26. data/vendor/faiss/faiss/IndexRaBitQ.h +5 -1
  27. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +238 -93
  28. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +35 -9
  29. data/vendor/faiss/faiss/IndexRefine.cpp +49 -0
  30. data/vendor/faiss/faiss/IndexRefine.h +17 -0
  31. data/vendor/faiss/faiss/clone_index.cpp +2 -0
  32. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +3 -1
  33. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +1 -1
  34. data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +1 -1
  35. data/vendor/faiss/faiss/impl/DistanceComputer.h +74 -3
  36. data/vendor/faiss/faiss/impl/HNSW.cpp +294 -15
  37. data/vendor/faiss/faiss/impl/HNSW.h +31 -2
  38. data/vendor/faiss/faiss/impl/IDSelector.h +3 -3
  39. data/vendor/faiss/faiss/impl/Panorama.cpp +193 -0
  40. data/vendor/faiss/faiss/impl/Panorama.h +204 -0
  41. data/vendor/faiss/faiss/impl/RaBitQStats.cpp +29 -0
  42. data/vendor/faiss/faiss/impl/RaBitQStats.h +56 -0
  43. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +54 -6
  44. data/vendor/faiss/faiss/impl/RaBitQUtils.h +183 -6
  45. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +269 -84
  46. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +71 -4
  47. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +362 -0
  48. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.h +112 -0
  49. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +6 -9
  50. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +1 -3
  51. data/vendor/faiss/faiss/impl/index_read.cpp +156 -12
  52. data/vendor/faiss/faiss/impl/index_write.cpp +142 -19
  53. data/vendor/faiss/faiss/impl/platform_macros.h +12 -0
  54. data/vendor/faiss/faiss/impl/svs_io.cpp +86 -0
  55. data/vendor/faiss/faiss/impl/svs_io.h +67 -0
  56. data/vendor/faiss/faiss/index_factory.cpp +182 -15
  57. data/vendor/faiss/faiss/invlists/BlockInvertedLists.h +1 -1
  58. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  59. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +18 -109
  60. data/vendor/faiss/faiss/invlists/InvertedLists.h +2 -18
  61. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +1 -1
  62. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.h +1 -1
  63. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +261 -0
  64. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +117 -0
  65. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +66 -0
  66. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +245 -0
  67. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +137 -0
  68. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +39 -0
  69. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +42 -0
  70. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +149 -0
  71. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +58 -0
  72. data/vendor/faiss/faiss/utils/distances.cpp +0 -3
  73. data/vendor/faiss/faiss/utils/utils.cpp +4 -0
  74. metadata +18 -1
@@ -70,29 +70,8 @@ struct IVFFlatScannerPanorama : InvertedListScanner {
70
70
  float q_norm = 0.0f;
71
71
  void set_query(const float* query) override {
72
72
  this->xi = query;
73
-
74
- const size_t d = vd.d;
75
- const size_t level_width_floats = storage->level_width / sizeof(float);
76
-
77
- std::vector<float> suffix_sums(d + 1);
78
- suffix_sums[d] = 0.0f;
79
-
80
- for (int j = d - 1; j >= 0; j--) {
81
- float squared_val = query[j] * query[j];
82
- suffix_sums[j] = suffix_sums[j + 1] + squared_val;
83
- }
84
-
85
- for (size_t level = 0; level < storage->n_levels; level++) {
86
- size_t start_idx = level * level_width_floats;
87
- if (start_idx < d) {
88
- cum_sums[level] = sqrt(suffix_sums[start_idx]);
89
- } else {
90
- cum_sums[level] = 0.0f;
91
- }
92
- }
93
-
94
- cum_sums[storage->n_levels] = 0.0f;
95
- q_norm = suffix_sums[0];
73
+ this->storage->pano.compute_query_cum_sums(query, cum_sums.data());
74
+ q_norm = cum_sums[0] * cum_sums[0];
96
75
  }
97
76
 
98
77
  void set_list(idx_t list_no, float /* coarse_dis */) override {
@@ -107,95 +86,6 @@ struct IVFFlatScannerPanorama : InvertedListScanner {
107
86
  "IndexIVFFlatPanorama does not support distance_to_code");
108
87
  }
109
88
 
110
- /// Helper function for progressive filtering that both scan_codes and
111
- /// scan_codes_range use. Processes a batch of vectors through all levels,
112
- /// computing exact distances and pruning based on a threshold.
113
- /// Returns the number of active survivors after all levels.
114
- size_t progressive_filter_batch(
115
- size_t batch_no,
116
- size_t list_size,
117
- const uint8_t* codes_base,
118
- const float* cum_sums_data,
119
- float threshold,
120
- std::vector<float>& exact_distances,
121
- std::vector<uint32_t>& active_indices,
122
- const idx_t* ids,
123
- PanoramaStats& local_stats) const {
124
- const size_t d = vd.d;
125
- const size_t level_width_floats = storage->level_width / sizeof(float);
126
-
127
- size_t batch_start = batch_no * storage->kBatchSize;
128
- size_t curr_batch_size =
129
- std::min(list_size - batch_start, storage->kBatchSize);
130
-
131
- size_t cumsum_batch_offset =
132
- batch_no * storage->kBatchSize * (storage->n_levels + 1);
133
- const float* batch_cum_sums = cum_sums_data + cumsum_batch_offset;
134
-
135
- size_t batch_offset = batch_no * storage->kBatchSize * code_size;
136
- const uint8_t* storage_base = codes_base + batch_offset;
137
-
138
- // Initialize active set with ID-filtered vectors.
139
- size_t num_active = 0;
140
- for (size_t i = 0; i < curr_batch_size; i++) {
141
- size_t global_idx = batch_start + i;
142
- bool include = !use_sel || sel->is_member(ids[global_idx]);
143
-
144
- active_indices[num_active] = i;
145
- float cum_sum = batch_cum_sums[i];
146
- exact_distances[i] = cum_sum * cum_sum + q_norm;
147
-
148
- num_active += include;
149
- }
150
-
151
- if (num_active == 0) {
152
- return 0;
153
- }
154
-
155
- size_t total_active = num_active;
156
-
157
- const float* level_cum_sums = batch_cum_sums + storage->kBatchSize;
158
-
159
- // Progressive filtering through levels.
160
- for (size_t level = 0; level < storage->n_levels; level++) {
161
- local_stats.total_dims_scanned += num_active;
162
- local_stats.total_dims += total_active;
163
-
164
- float query_cum_norm = cum_sums[level + 1];
165
-
166
- size_t level_offset =
167
- level * storage->level_width * storage->kBatchSize;
168
- const float* level_storage =
169
- (const float*)(storage_base + level_offset);
170
-
171
- size_t next_active = 0;
172
- for (size_t i = 0; i < num_active; i++) {
173
- uint32_t idx = active_indices[i];
174
- const float* yj = level_storage + idx * level_width_floats;
175
- const float* query_level = xi + level * level_width_floats;
176
-
177
- size_t actual_level_width = std::min(
178
- level_width_floats, d - level * level_width_floats);
179
- float dot_product =
180
- fvec_inner_product(query_level, yj, actual_level_width);
181
-
182
- exact_distances[idx] -= 2.0f * dot_product;
183
-
184
- float cum_sum = level_cum_sums[idx];
185
- float cauchy_schwarz_bound = 2.0f * cum_sum * query_cum_norm;
186
- float lower_bound = exact_distances[idx] - cauchy_schwarz_bound;
187
-
188
- active_indices[next_active] = idx;
189
- next_active += C::cmp(threshold, lower_bound) ? 1 : 0;
190
- }
191
-
192
- num_active = next_active;
193
- level_cum_sums += storage->kBatchSize;
194
- }
195
-
196
- return num_active;
197
- }
198
-
199
89
  size_t scan_codes(
200
90
  size_t list_size,
201
91
  const uint8_t* codes,
@@ -208,7 +98,6 @@ struct IVFFlatScannerPanorama : InvertedListScanner {
208
98
  const size_t n_batches =
209
99
  (list_size + storage->kBatchSize - 1) / storage->kBatchSize;
210
100
 
211
- const uint8_t* codes_base = codes;
212
101
  const float* cum_sums_data = storage->get_cum_sums(list_no);
213
102
 
214
103
  std::vector<float> exact_distances(storage->kBatchSize);
@@ -217,34 +106,25 @@ struct IVFFlatScannerPanorama : InvertedListScanner {
217
106
  PanoramaStats local_stats;
218
107
  local_stats.reset();
219
108
 
220
- // Panorama's IVFFlat core progressive filtering algorithm:
221
- // Process vectors in batches for cache efficiency. For each batch:
222
- // 1. Apply ID selection filter and initialize distances
223
- // (||y||^2 + ||x||^2).
224
- // 2. Maintain an "active set" of candidate indices that haven't been
225
- // pruned yet.
226
- // 3. For each level, refine distances incrementally and compact the
227
- // active set:
228
- // - Compute dot product for current level: exact_dist -= 2*<x,y>.
229
- // - Use Cauchy-Schwarz bound on remaining levels to get lower bound
230
- // - Prune candidates whose lower bound exceeds k-th best distance.
231
- // - Compact active_indices to remove pruned candidates (branchless)
232
- // 4. After all levels, survivors are exact distances; update heap.
233
- // This achieves early termination while maintaining SIMD-friendly
234
- // sequential access patterns in the level-oriented storage layout.
235
109
  for (size_t batch_no = 0; batch_no < n_batches; batch_no++) {
236
110
  size_t batch_start = batch_no * storage->kBatchSize;
237
111
 
238
- size_t num_active = progressive_filter_batch(
239
- batch_no,
240
- list_size,
241
- codes_base,
242
- cum_sums_data,
243
- simi[0],
244
- exact_distances,
245
- active_indices,
246
- ids,
247
- local_stats);
112
+ size_t num_active =
113
+ storage->pano
114
+ .progressive_filter_batch<CMax<float, int64_t>>(
115
+ codes,
116
+ cum_sums_data,
117
+ xi,
118
+ cum_sums.data(),
119
+ batch_no,
120
+ list_size,
121
+ sel,
122
+ ids,
123
+ use_sel,
124
+ active_indices,
125
+ exact_distances,
126
+ simi[0],
127
+ local_stats);
248
128
 
249
129
  // Add batch survivors to heap.
250
130
  for (size_t i = 0; i < num_active; i++) {
@@ -274,7 +154,6 @@ struct IVFFlatScannerPanorama : InvertedListScanner {
274
154
  const size_t n_batches =
275
155
  (list_size + storage->kBatchSize - 1) / storage->kBatchSize;
276
156
 
277
- const uint8_t* codes_base = codes;
278
157
  const float* cum_sums_data = storage->get_cum_sums(list_no);
279
158
 
280
159
  std::vector<float> exact_distances(storage->kBatchSize);
@@ -288,16 +167,22 @@ struct IVFFlatScannerPanorama : InvertedListScanner {
288
167
  for (size_t batch_no = 0; batch_no < n_batches; batch_no++) {
289
168
  size_t batch_start = batch_no * storage->kBatchSize;
290
169
 
291
- size_t num_active = progressive_filter_batch(
292
- batch_no,
293
- list_size,
294
- codes_base,
295
- cum_sums_data,
296
- radius,
297
- exact_distances,
298
- active_indices,
299
- ids,
300
- local_stats);
170
+ size_t num_active =
171
+ storage->pano
172
+ .progressive_filter_batch<CMax<float, int64_t>>(
173
+ codes,
174
+ cum_sums_data,
175
+ xi,
176
+ cum_sums.data(),
177
+ batch_no,
178
+ list_size,
179
+ sel,
180
+ ids,
181
+ use_sel,
182
+ active_indices,
183
+ exact_distances,
184
+ radius,
185
+ local_stats);
301
186
 
302
187
  // Add batch survivors to range result.
303
188
  for (size_t i = 0; i < num_active; i++) {
@@ -24,9 +24,10 @@ IndexIVFRaBitQ::IndexIVFRaBitQ(
24
24
  const size_t d,
25
25
  const size_t nlist,
26
26
  MetricType metric,
27
- bool own_invlists)
27
+ bool own_invlists,
28
+ uint8_t nb_bits_in)
28
29
  : IndexIVF(quantizer, d, nlist, 0, metric, own_invlists),
29
- rabitq(d, metric) {
30
+ rabitq(d, metric, nb_bits_in) {
30
31
  code_size = rabitq.code_size;
31
32
  if (own_invlists) {
32
33
  invlists->code_size = code_size;
@@ -153,6 +154,8 @@ struct RaBitInvertedListScanner : InvertedListScanner {
153
154
  std::vector<float> query_vector;
154
155
 
155
156
  std::unique_ptr<FlatCodesDistanceComputer> dc;
157
+ RaBitQDistanceComputer* rabitq_dc =
158
+ nullptr; // For multi-bit adaptive filtering
156
159
 
157
160
  uint8_t qb = 0;
158
161
  bool centered = false;
@@ -194,6 +197,84 @@ struct RaBitInvertedListScanner : InvertedListScanner {
194
197
  return dc->distance_to_code(code);
195
198
  }
196
199
 
200
+ /// Override scan_codes to implement adaptive filtering for multi-bit codes
201
+ size_t scan_codes(
202
+ size_t list_size,
203
+ const uint8_t* codes,
204
+ const idx_t* ids,
205
+ float* simi,
206
+ idx_t* idxi,
207
+ size_t k) const override {
208
+ size_t ex_bits = ivf_rabitq.rabitq.nb_bits - 1;
209
+
210
+ // For 1-bit codes, use default implementation
211
+ if (ex_bits == 0 || rabitq_dc == nullptr) {
212
+ return InvertedListScanner::scan_codes(
213
+ list_size, codes, ids, simi, idxi, k);
214
+ }
215
+
216
+ // Multi-bit: Two-stage search with adaptive filtering
217
+ size_t nup = 0;
218
+
219
+ // Stats tracking for multi-bit two-stage search
220
+ // n_1bit_evaluations: candidates evaluated using 1-bit lower bound
221
+ // n_multibit_evaluations: candidates requiring full multi-bit distance
222
+ size_t local_1bit_evaluations = 0;
223
+ size_t local_multibit_evaluations = 0;
224
+
225
+ for (size_t j = 0; j < list_size; j++) {
226
+ if (sel != nullptr) {
227
+ int64_t id = store_pairs ? lo_build(list_no, j) : ids[j];
228
+ if (!sel->is_member(id)) {
229
+ codes += code_size;
230
+ continue;
231
+ }
232
+ }
233
+
234
+ local_1bit_evaluations++;
235
+
236
+ // Stage 1: Compute lower bound using 1-bit codes
237
+ float lower_bound = rabitq_dc->lower_bound_distance(codes);
238
+
239
+ // Stage 2: Adaptive filtering
240
+ // L2 (min-heap): filter if lower_bound < simi[0]
241
+ // IP (max-heap): filter if lower_bound > simi[0]
242
+ // Note: Using simi[0] directly (not cached) enables more aggressive
243
+ // filtering as the heap is updated with better candidates
244
+ bool should_refine = keep_max ? (lower_bound > simi[0])
245
+ : (lower_bound < simi[0]);
246
+
247
+ if (should_refine) {
248
+ local_multibit_evaluations++;
249
+ // Lower bound is promising, compute full distance
250
+ float dis = distance_to_code(codes);
251
+
252
+ // Check if distance improves heap
253
+ bool improves_heap =
254
+ keep_max ? (dis > simi[0]) : (dis < simi[0]);
255
+
256
+ if (improves_heap) {
257
+ int64_t id = store_pairs ? lo_build(list_no, j) : ids[j];
258
+ if (keep_max) {
259
+ minheap_replace_top(k, simi, idxi, dis, id);
260
+ } else {
261
+ maxheap_replace_top(k, simi, idxi, dis, id);
262
+ }
263
+ nup++;
264
+ }
265
+ }
266
+ codes += code_size;
267
+ }
268
+
269
+ // Update global stats atomically
270
+ #pragma omp atomic
271
+ rabitq_stats.n_1bit_evaluations += local_1bit_evaluations;
272
+ #pragma omp atomic
273
+ rabitq_stats.n_multibit_evaluations += local_multibit_evaluations;
274
+
275
+ return nup;
276
+ }
277
+
197
278
  void internal_try_setup_dc() {
198
279
  if (!query_vector.empty() && !reconstructed_centroid.empty()) {
199
280
  // both query_vector and centroid are available!
@@ -202,6 +283,9 @@ struct RaBitInvertedListScanner : InvertedListScanner {
202
283
  qb, reconstructed_centroid.data(), centered));
203
284
 
204
285
  dc->set_query(query_vector.data());
286
+
287
+ // Try to cast to RaBitQDistanceComputer for multi-bit support
288
+ rabitq_dc = dynamic_cast<RaBitQDistanceComputer*>(dc.get());
205
289
  }
206
290
  }
207
291
  };
@@ -13,6 +13,7 @@
13
13
  #include <faiss/Index.h>
14
14
  #include <faiss/IndexIVF.h>
15
15
 
16
+ #include <faiss/impl/RaBitQStats.h>
16
17
  #include <faiss/impl/RaBitQuantizer.h>
17
18
 
18
19
  namespace faiss {
@@ -35,7 +36,8 @@ struct IndexIVFRaBitQ : IndexIVF {
35
36
  const size_t d,
36
37
  const size_t nlist,
37
38
  MetricType metric = METRIC_L2,
38
- bool own_invlists = true);
39
+ bool own_invlists = true,
40
+ uint8_t nb_bits = 1);
39
41
 
40
42
  IndexIVFRaBitQ();
41
43