faiss 0.4.2 → 0.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/ext/faiss/index.cpp +36 -10
- data/ext/faiss/index_binary.cpp +19 -6
- data/ext/faiss/kmeans.cpp +6 -6
- data/ext/faiss/numo.hpp +273 -123
- data/lib/faiss/version.rb +1 -1
- data/vendor/faiss/faiss/AutoTune.cpp +2 -3
- data/vendor/faiss/faiss/AutoTune.h +1 -1
- data/vendor/faiss/faiss/Clustering.cpp +2 -2
- data/vendor/faiss/faiss/Clustering.h +2 -2
- data/vendor/faiss/faiss/IVFlib.cpp +1 -2
- data/vendor/faiss/faiss/IVFlib.h +1 -1
- data/vendor/faiss/faiss/Index.h +10 -10
- data/vendor/faiss/faiss/Index2Layer.cpp +1 -1
- data/vendor/faiss/faiss/Index2Layer.h +2 -2
- data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +9 -4
- data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.h +5 -1
- data/vendor/faiss/faiss/IndexBinary.h +7 -7
- data/vendor/faiss/faiss/IndexBinaryFromFloat.h +1 -1
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +3 -1
- data/vendor/faiss/faiss/IndexBinaryHNSW.h +1 -1
- data/vendor/faiss/faiss/IndexBinaryHash.cpp +3 -3
- data/vendor/faiss/faiss/IndexBinaryHash.h +5 -5
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +7 -6
- data/vendor/faiss/faiss/IndexFastScan.cpp +125 -49
- data/vendor/faiss/faiss/IndexFastScan.h +107 -7
- data/vendor/faiss/faiss/IndexFlat.h +1 -1
- data/vendor/faiss/faiss/IndexHNSW.cpp +3 -1
- data/vendor/faiss/faiss/IndexHNSW.h +1 -1
- data/vendor/faiss/faiss/IndexIDMap.cpp +14 -13
- data/vendor/faiss/faiss/IndexIDMap.h +6 -6
- data/vendor/faiss/faiss/IndexIVF.cpp +1 -1
- data/vendor/faiss/faiss/IndexIVF.h +5 -5
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +9 -3
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.h +3 -1
- data/vendor/faiss/faiss/IndexIVFFastScan.cpp +176 -90
- data/vendor/faiss/faiss/IndexIVFFastScan.h +173 -18
- data/vendor/faiss/faiss/IndexIVFFlat.cpp +1 -0
- data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +366 -0
- data/vendor/faiss/faiss/IndexIVFFlatPanorama.h +64 -0
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +3 -1
- data/vendor/faiss/faiss/IndexIVFPQ.h +1 -1
- data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +134 -2
- data/vendor/faiss/faiss/IndexIVFPQFastScan.h +7 -1
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +13 -6
- data/vendor/faiss/faiss/IndexIVFRaBitQ.h +1 -0
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +650 -0
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +216 -0
- data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +1 -1
- data/vendor/faiss/faiss/IndexIVFSpectralHash.h +1 -1
- data/vendor/faiss/faiss/IndexNNDescent.cpp +1 -1
- data/vendor/faiss/faiss/IndexNSG.cpp +1 -1
- data/vendor/faiss/faiss/IndexNeuralNetCodec.h +1 -1
- data/vendor/faiss/faiss/IndexPQ.h +1 -1
- data/vendor/faiss/faiss/IndexPQFastScan.cpp +6 -2
- data/vendor/faiss/faiss/IndexPQFastScan.h +5 -1
- data/vendor/faiss/faiss/IndexRaBitQ.cpp +13 -10
- data/vendor/faiss/faiss/IndexRaBitQ.h +7 -2
- data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +586 -0
- data/vendor/faiss/faiss/IndexRaBitQFastScan.h +149 -0
- data/vendor/faiss/faiss/IndexShards.cpp +1 -1
- data/vendor/faiss/faiss/MatrixStats.cpp +3 -3
- data/vendor/faiss/faiss/MetricType.h +1 -1
- data/vendor/faiss/faiss/VectorTransform.h +2 -2
- data/vendor/faiss/faiss/clone_index.cpp +3 -1
- data/vendor/faiss/faiss/gpu/GpuCloner.cpp +1 -1
- data/vendor/faiss/faiss/gpu/GpuIndex.h +11 -11
- data/vendor/faiss/faiss/gpu/GpuIndexBinaryCagra.h +1 -1
- data/vendor/faiss/faiss/gpu/GpuIndexBinaryFlat.h +1 -1
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +10 -6
- data/vendor/faiss/faiss/gpu/perf/IndexWrapper-inl.h +2 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuIcmEncoder.cpp +7 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +1 -1
- data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/impl/AdditiveQuantizer.h +1 -1
- data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +2 -2
- data/vendor/faiss/faiss/impl/AuxIndexStructures.h +1 -1
- data/vendor/faiss/faiss/impl/CodePacker.h +2 -2
- data/vendor/faiss/faiss/impl/DistanceComputer.h +3 -3
- data/vendor/faiss/faiss/impl/FastScanDistancePostProcessing.h +53 -0
- data/vendor/faiss/faiss/impl/HNSW.cpp +1 -1
- data/vendor/faiss/faiss/impl/HNSW.h +4 -4
- data/vendor/faiss/faiss/impl/IDSelector.cpp +2 -2
- data/vendor/faiss/faiss/impl/IDSelector.h +1 -1
- data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +4 -4
- data/vendor/faiss/faiss/impl/LocalSearchQuantizer.h +1 -1
- data/vendor/faiss/faiss/impl/LookupTableScaler.h +1 -1
- data/vendor/faiss/faiss/impl/NNDescent.cpp +1 -1
- data/vendor/faiss/faiss/impl/NNDescent.h +2 -2
- data/vendor/faiss/faiss/impl/NSG.cpp +1 -1
- data/vendor/faiss/faiss/impl/PanoramaStats.cpp +33 -0
- data/vendor/faiss/faiss/impl/PanoramaStats.h +38 -0
- data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +5 -5
- data/vendor/faiss/faiss/impl/ProductAdditiveQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/impl/ProductAdditiveQuantizer.h +1 -1
- data/vendor/faiss/faiss/impl/ProductQuantizer-inl.h +2 -0
- data/vendor/faiss/faiss/impl/ProductQuantizer.h +1 -1
- data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +246 -0
- data/vendor/faiss/faiss/impl/RaBitQUtils.h +153 -0
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +54 -158
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +2 -1
- data/vendor/faiss/faiss/impl/ResidualQuantizer.h +1 -1
- data/vendor/faiss/faiss/impl/ResultHandler.h +4 -4
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +1 -1
- data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +7 -4
- data/vendor/faiss/faiss/impl/index_read.cpp +87 -3
- data/vendor/faiss/faiss/impl/index_write.cpp +73 -3
- data/vendor/faiss/faiss/impl/io.cpp +2 -2
- data/vendor/faiss/faiss/impl/io.h +4 -4
- data/vendor/faiss/faiss/impl/kmeans1d.cpp +1 -1
- data/vendor/faiss/faiss/impl/kmeans1d.h +1 -1
- data/vendor/faiss/faiss/impl/lattice_Zn.h +2 -2
- data/vendor/faiss/faiss/impl/mapped_io.cpp +2 -2
- data/vendor/faiss/faiss/impl/mapped_io.h +4 -3
- data/vendor/faiss/faiss/impl/maybe_owned_vector.h +8 -1
- data/vendor/faiss/faiss/impl/pq4_fast_scan.cpp +30 -4
- data/vendor/faiss/faiss/impl/pq4_fast_scan.h +14 -8
- data/vendor/faiss/faiss/impl/pq4_fast_scan_search_qbs.cpp +5 -6
- data/vendor/faiss/faiss/impl/simd_result_handlers.h +55 -11
- data/vendor/faiss/faiss/impl/zerocopy_io.h +1 -1
- data/vendor/faiss/faiss/index_factory.cpp +43 -1
- data/vendor/faiss/faiss/index_factory.h +1 -1
- data/vendor/faiss/faiss/index_io.h +1 -1
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +205 -0
- data/vendor/faiss/faiss/invlists/InvertedLists.h +62 -0
- data/vendor/faiss/faiss/utils/AlignedTable.h +1 -1
- data/vendor/faiss/faiss/utils/Heap.cpp +2 -2
- data/vendor/faiss/faiss/utils/Heap.h +3 -3
- data/vendor/faiss/faiss/utils/NeuralNet.cpp +1 -1
- data/vendor/faiss/faiss/utils/NeuralNet.h +3 -3
- data/vendor/faiss/faiss/utils/approx_topk/approx_topk.h +2 -2
- data/vendor/faiss/faiss/utils/approx_topk/avx2-inl.h +2 -2
- data/vendor/faiss/faiss/utils/approx_topk/mode.h +1 -1
- data/vendor/faiss/faiss/utils/distances.h +2 -2
- data/vendor/faiss/faiss/utils/extra_distances-inl.h +3 -1
- data/vendor/faiss/faiss/utils/hamming-inl.h +2 -0
- data/vendor/faiss/faiss/utils/hamming.cpp +7 -6
- data/vendor/faiss/faiss/utils/hamming.h +1 -1
- data/vendor/faiss/faiss/utils/hamming_distance/common.h +1 -2
- data/vendor/faiss/faiss/utils/partitioning.cpp +5 -5
- data/vendor/faiss/faiss/utils/partitioning.h +2 -2
- data/vendor/faiss/faiss/utils/rabitq_simd.h +222 -336
- data/vendor/faiss/faiss/utils/random.cpp +1 -1
- data/vendor/faiss/faiss/utils/simdlib_avx2.h +1 -1
- data/vendor/faiss/faiss/utils/simdlib_avx512.h +1 -1
- data/vendor/faiss/faiss/utils/simdlib_neon.h +2 -2
- data/vendor/faiss/faiss/utils/transpose/transpose-avx512-inl.h +1 -1
- data/vendor/faiss/faiss/utils/utils.cpp +5 -2
- data/vendor/faiss/faiss/utils/utils.h +2 -2
- metadata +14 -3
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
-
#include <memory>
|
|
11
|
-
|
|
12
10
|
#include <faiss/IndexIVF.h>
|
|
11
|
+
#include <faiss/impl/FastScanDistancePostProcessing.h>
|
|
13
12
|
#include <faiss/utils/AlignedTable.h>
|
|
14
13
|
|
|
15
14
|
namespace faiss {
|
|
@@ -63,6 +62,15 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
63
62
|
// quantizer used to pack the codes
|
|
64
63
|
Quantizer* fine_quantizer = nullptr;
|
|
65
64
|
|
|
65
|
+
/** Constructor for IndexIVFFastScan
|
|
66
|
+
*
|
|
67
|
+
* @param quantizer coarse quantizer for IVF clustering
|
|
68
|
+
* @param d dimensionality of vectors
|
|
69
|
+
* @param nlist number of inverted lists
|
|
70
|
+
* @param code_size size of each code in bytes
|
|
71
|
+
* @param metric distance metric to use
|
|
72
|
+
* @param own_invlists whether to own the inverted lists
|
|
73
|
+
*/
|
|
66
74
|
IndexIVFFastScan(
|
|
67
75
|
Index* quantizer,
|
|
68
76
|
size_t d,
|
|
@@ -73,7 +81,16 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
73
81
|
|
|
74
82
|
IndexIVFFastScan();
|
|
75
83
|
|
|
76
|
-
|
|
84
|
+
/** Initialize the fast scan functionality (called by implementations)
|
|
85
|
+
*
|
|
86
|
+
* @param fine_quantizer fine quantizer for encoding
|
|
87
|
+
* @param M number of subquantizers
|
|
88
|
+
* @param nbits number of bits per subquantizer
|
|
89
|
+
* @param nlist number of inverted lists
|
|
90
|
+
* @param metric distance metric to use
|
|
91
|
+
* @param bbs block size for SIMD processing
|
|
92
|
+
* @param own_invlists whether to own the inverted lists
|
|
93
|
+
*/
|
|
77
94
|
void init_fastscan(
|
|
78
95
|
Quantizer* fine_quantizer,
|
|
79
96
|
size_t M,
|
|
@@ -91,34 +108,72 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
91
108
|
/// orig's inverted lists (for debugging)
|
|
92
109
|
InvertedLists* orig_invlists = nullptr;
|
|
93
110
|
|
|
111
|
+
/** Add vectors with specific IDs to the index
|
|
112
|
+
*
|
|
113
|
+
* @param n number of vectors to add
|
|
114
|
+
* @param x vectors to add (n * d)
|
|
115
|
+
* @param xids IDs for the vectors (n)
|
|
116
|
+
*/
|
|
94
117
|
void add_with_ids(idx_t n, const float* x, const idx_t* xids) override;
|
|
95
|
-
|
|
96
118
|
// prepare look-up tables
|
|
97
119
|
|
|
98
120
|
virtual bool lookup_table_is_3d() const = 0;
|
|
99
121
|
|
|
100
122
|
// compact way of conveying coarse quantization results
|
|
101
123
|
struct CoarseQuantized {
|
|
102
|
-
size_t nprobe;
|
|
124
|
+
size_t nprobe = 0;
|
|
103
125
|
const float* dis = nullptr;
|
|
104
126
|
const idx_t* ids = nullptr;
|
|
105
127
|
};
|
|
106
128
|
|
|
129
|
+
/* Compute distance table for query set, given a list of coarse
|
|
130
|
+
* quantizers.
|
|
131
|
+
*
|
|
132
|
+
* @param n number of queries
|
|
133
|
+
* @param x query vectors (n, d)
|
|
134
|
+
* @param cq coarse quantization results
|
|
135
|
+
* @param dis_tables output distance tables
|
|
136
|
+
* @param biases output bias values
|
|
137
|
+
* @param context processing context containing query factors
|
|
138
|
+
processor
|
|
139
|
+
*/
|
|
107
140
|
virtual void compute_LUT(
|
|
108
141
|
size_t n,
|
|
109
142
|
const float* x,
|
|
110
143
|
const CoarseQuantized& cq,
|
|
111
144
|
AlignedTable<float>& dis_tables,
|
|
112
|
-
AlignedTable<float>& biases
|
|
145
|
+
AlignedTable<float>& biases,
|
|
146
|
+
const FastScanDistancePostProcessing& context) const = 0;
|
|
113
147
|
|
|
148
|
+
/** Compute quantized lookup tables for distance computation
|
|
149
|
+
*
|
|
150
|
+
* @param n number of query vectors
|
|
151
|
+
* @param x query vectors (n * d)
|
|
152
|
+
* @param cq coarse quantization results
|
|
153
|
+
* @param dis_tables output quantized distance tables
|
|
154
|
+
* @param biases output quantized bias values
|
|
155
|
+
* @param normalizers output normalization factors
|
|
156
|
+
* @param context processing context containing query factors
|
|
157
|
+
* processor
|
|
158
|
+
*/
|
|
114
159
|
void compute_LUT_uint8(
|
|
115
160
|
size_t n,
|
|
116
161
|
const float* x,
|
|
117
162
|
const CoarseQuantized& cq,
|
|
118
163
|
AlignedTable<uint8_t>& dis_tables,
|
|
119
164
|
AlignedTable<uint16_t>& biases,
|
|
120
|
-
float* normalizers
|
|
165
|
+
float* normalizers,
|
|
166
|
+
const FastScanDistancePostProcessing& context) const;
|
|
121
167
|
|
|
168
|
+
/** Search for k nearest neighbors
|
|
169
|
+
*
|
|
170
|
+
* @param n number of query vectors
|
|
171
|
+
* @param x query vectors (n * d)
|
|
172
|
+
* @param k number of nearest neighbors to find
|
|
173
|
+
* @param distances output distances (n * k)
|
|
174
|
+
* @param labels output labels/indices (n * k)
|
|
175
|
+
* @param params optional search parameters
|
|
176
|
+
*/
|
|
122
177
|
void search(
|
|
123
178
|
idx_t n,
|
|
124
179
|
const float* x,
|
|
@@ -127,6 +182,19 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
127
182
|
idx_t* labels,
|
|
128
183
|
const SearchParameters* params = nullptr) const override;
|
|
129
184
|
|
|
185
|
+
/** Search with pre-assigned coarse quantization
|
|
186
|
+
*
|
|
187
|
+
* @param n number of query vectors
|
|
188
|
+
* @param x query vectors (n * d)
|
|
189
|
+
* @param k number of nearest neighbors to find
|
|
190
|
+
* @param assign coarse cluster assignments (n * nprobe)
|
|
191
|
+
* @param centroid_dis distances to centroids (n * nprobe)
|
|
192
|
+
* @param distances output distances (n * k)
|
|
193
|
+
* @param labels output labels/indices (n * k)
|
|
194
|
+
* @param store_pairs whether to store cluster-relative pairs
|
|
195
|
+
* @param params optional IVF search parameters
|
|
196
|
+
* @param stats optional search statistics
|
|
197
|
+
*/
|
|
130
198
|
void search_preassigned(
|
|
131
199
|
idx_t n,
|
|
132
200
|
const float* x,
|
|
@@ -139,6 +207,14 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
139
207
|
const IVFSearchParameters* params = nullptr,
|
|
140
208
|
IndexIVFStats* stats = nullptr) const override;
|
|
141
209
|
|
|
210
|
+
/** Range search for all neighbors within radius
|
|
211
|
+
*
|
|
212
|
+
* @param n number of query vectors
|
|
213
|
+
* @param x query vectors (n * d)
|
|
214
|
+
* @param radius search radius
|
|
215
|
+
* @param result output range search results
|
|
216
|
+
* @param params optional search parameters
|
|
217
|
+
*/
|
|
142
218
|
void range_search(
|
|
143
219
|
idx_t n,
|
|
144
220
|
const float* x,
|
|
@@ -146,7 +222,45 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
146
222
|
RangeSearchResult* result,
|
|
147
223
|
const SearchParameters* params = nullptr) const override;
|
|
148
224
|
|
|
149
|
-
|
|
225
|
+
/** Create a KNN handler for this index type
|
|
226
|
+
*
|
|
227
|
+
* This method can be overridden by derived classes to provide
|
|
228
|
+
* specialized handlers (e.g., IVFRaBitQHeapHandler for RaBitQ indexes).
|
|
229
|
+
* Base implementation creates standard handlers based on k and impl.
|
|
230
|
+
*
|
|
231
|
+
* @param is_max true for max-heap (inner product), false for
|
|
232
|
+
* min-heap (L2 distance)
|
|
233
|
+
* @param impl implementation number:
|
|
234
|
+
* - even (10, 12, 14): use heap for top-k
|
|
235
|
+
* - odd (11, 13, 15): use reservoir sampling
|
|
236
|
+
* @param n number of queries
|
|
237
|
+
* @param k number of neighbors to find per query
|
|
238
|
+
* @param distances output array for distances (n * k), will be
|
|
239
|
+
* populated by handler
|
|
240
|
+
* @param labels output array for result IDs (n * k), will be
|
|
241
|
+
* populated by handler
|
|
242
|
+
* @param sel optional ID selector to filter results (nullptr =
|
|
243
|
+
* no filtering)
|
|
244
|
+
* @param context processing context containing additional data
|
|
245
|
+
* @param normalizers optional array of size 2*n for converting quantized
|
|
246
|
+
* uint16 distances to float.
|
|
247
|
+
*
|
|
248
|
+
* @return Allocated result handler (caller owns and must delete).
|
|
249
|
+
* Handler processes SIMD batches and populates distances/labels.
|
|
250
|
+
*
|
|
251
|
+
* @note The returned handler must be deleted by caller after use.
|
|
252
|
+
* Typical usage: handler->begin() → process batches → handler->end()
|
|
253
|
+
*/
|
|
254
|
+
virtual SIMDResultHandlerToFloat* make_knn_handler(
|
|
255
|
+
bool is_max,
|
|
256
|
+
int impl,
|
|
257
|
+
idx_t n,
|
|
258
|
+
idx_t k,
|
|
259
|
+
float* distances,
|
|
260
|
+
idx_t* labels,
|
|
261
|
+
const IDSelector* sel,
|
|
262
|
+
const FastScanDistancePostProcessing& context,
|
|
263
|
+
const float* normalizers = nullptr) const;
|
|
150
264
|
|
|
151
265
|
// dispatch to implementations and parallelize
|
|
152
266
|
void search_dispatch_implem(
|
|
@@ -156,7 +270,7 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
156
270
|
float* distances,
|
|
157
271
|
idx_t* labels,
|
|
158
272
|
const CoarseQuantized& cq,
|
|
159
|
-
const
|
|
273
|
+
const FastScanDistancePostProcessing& context,
|
|
160
274
|
const IVFSearchParameters* params = nullptr) const;
|
|
161
275
|
|
|
162
276
|
void range_search_dispatch_implem(
|
|
@@ -165,7 +279,7 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
165
279
|
float radius,
|
|
166
280
|
RangeSearchResult& rres,
|
|
167
281
|
const CoarseQuantized& cq_in,
|
|
168
|
-
const
|
|
282
|
+
const FastScanDistancePostProcessing& context,
|
|
169
283
|
const IVFSearchParameters* params = nullptr) const;
|
|
170
284
|
|
|
171
285
|
// impl 1 and 2 are just for verification
|
|
@@ -177,7 +291,7 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
177
291
|
float* distances,
|
|
178
292
|
idx_t* labels,
|
|
179
293
|
const CoarseQuantized& cq,
|
|
180
|
-
const
|
|
294
|
+
const FastScanDistancePostProcessing& context,
|
|
181
295
|
const IVFSearchParameters* params = nullptr) const;
|
|
182
296
|
|
|
183
297
|
template <class C>
|
|
@@ -188,7 +302,7 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
188
302
|
float* distances,
|
|
189
303
|
idx_t* labels,
|
|
190
304
|
const CoarseQuantized& cq,
|
|
191
|
-
const
|
|
305
|
+
const FastScanDistancePostProcessing& context,
|
|
192
306
|
const IVFSearchParameters* params = nullptr) const;
|
|
193
307
|
|
|
194
308
|
// implem 10 and 12 are not multithreaded internally, so
|
|
@@ -200,7 +314,7 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
200
314
|
const CoarseQuantized& cq,
|
|
201
315
|
size_t* ndis_out,
|
|
202
316
|
size_t* nlist_out,
|
|
203
|
-
const
|
|
317
|
+
const FastScanDistancePostProcessing& context,
|
|
204
318
|
const IVFSearchParameters* params = nullptr) const;
|
|
205
319
|
|
|
206
320
|
void search_implem_12(
|
|
@@ -210,7 +324,7 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
210
324
|
const CoarseQuantized& cq,
|
|
211
325
|
size_t* ndis_out,
|
|
212
326
|
size_t* nlist_out,
|
|
213
|
-
const
|
|
327
|
+
const FastScanDistancePostProcessing& context,
|
|
214
328
|
const IVFSearchParameters* params = nullptr) const;
|
|
215
329
|
|
|
216
330
|
// implem 14 is multithreaded internally across nprobes and queries
|
|
@@ -222,7 +336,7 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
222
336
|
idx_t* labels,
|
|
223
337
|
const CoarseQuantized& cq,
|
|
224
338
|
int impl,
|
|
225
|
-
const
|
|
339
|
+
const FastScanDistancePostProcessing& context,
|
|
226
340
|
const IVFSearchParameters* params = nullptr) const;
|
|
227
341
|
|
|
228
342
|
// reconstruct vectors from packed invlists
|
|
@@ -234,16 +348,57 @@ struct IndexIVFFastScan : IndexIVF {
|
|
|
234
348
|
// reconstruct orig invlists (for debugging)
|
|
235
349
|
void reconstruct_orig_invlists();
|
|
236
350
|
|
|
237
|
-
/** Decode a set of vectors
|
|
351
|
+
/** Decode a set of vectors
|
|
238
352
|
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
353
|
+
* NOTE: The codes in the IndexFastScan object are non-contiguous.
|
|
354
|
+
* But this method requires a contiguous representation.
|
|
241
355
|
*
|
|
242
356
|
* @param n number of vectors
|
|
243
357
|
* @param bytes input encoded vectors, size n * code_size
|
|
244
358
|
* @param x output vectors, size n * d
|
|
245
359
|
*/
|
|
246
360
|
void sa_decode(idx_t n, const uint8_t* bytes, float* x) const override;
|
|
361
|
+
|
|
362
|
+
protected:
|
|
363
|
+
/** Preprocess metadata from encoded vectors before packing.
|
|
364
|
+
*
|
|
365
|
+
* Called during add_with_ids after encode_vectors but before codes
|
|
366
|
+
* are packed into SIMD-friendly blocks. Subclasses can override to
|
|
367
|
+
* extract and store metadata embedded in codes or perform other
|
|
368
|
+
* pre-packing operations.
|
|
369
|
+
*
|
|
370
|
+
* Default implementation: no-op
|
|
371
|
+
*
|
|
372
|
+
* Example use case:
|
|
373
|
+
* - IndexIVFRaBitQFastScan extracts factor data from codes for use
|
|
374
|
+
* during search-time distance corrections
|
|
375
|
+
*
|
|
376
|
+
* @param n number of vectors encoded
|
|
377
|
+
* @param flat_codes encoded vectors (n * code_size bytes)
|
|
378
|
+
* @param start_global_idx starting global index (ntotal before add)
|
|
379
|
+
*/
|
|
380
|
+
virtual void preprocess_code_metadata(
|
|
381
|
+
idx_t n,
|
|
382
|
+
const uint8_t* flat_codes,
|
|
383
|
+
idx_t start_global_idx);
|
|
384
|
+
|
|
385
|
+
/** Get stride for interpreting codes during SIMD packing.
|
|
386
|
+
*
|
|
387
|
+
* The stride determines how to read codes when packing them into
|
|
388
|
+
* SIMD-friendly block format. This is needed when codes contain
|
|
389
|
+
* embedded metadata that should be skipped during packing.
|
|
390
|
+
*
|
|
391
|
+
* Default implementation: returns 0 (use standard M-byte stride)
|
|
392
|
+
*
|
|
393
|
+
* Example use case:
|
|
394
|
+
* - IndexIVFRaBitQFastScan returns code_size because codes contain
|
|
395
|
+
* embedded factor data after the quantized bits
|
|
396
|
+
*
|
|
397
|
+
* @return stride in bytes:
|
|
398
|
+
* - 0: use default stride (M bytes, standard PQ/AQ codes)
|
|
399
|
+
* - >0: use custom stride (e.g., code_size for embedded metadata)
|
|
400
|
+
*/
|
|
401
|
+
virtual size_t code_packing_stride() const;
|
|
247
402
|
};
|
|
248
403
|
|
|
249
404
|
struct IVFFastScanStats {
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// -*- c++ -*-
|
|
9
|
+
|
|
10
|
+
#include <faiss/IndexIVFFlatPanorama.h>
|
|
11
|
+
|
|
12
|
+
#include <cstdio>
|
|
13
|
+
|
|
14
|
+
#include <faiss/IndexFlat.h>
|
|
15
|
+
|
|
16
|
+
#include <faiss/impl/AuxIndexStructures.h>
|
|
17
|
+
#include <faiss/impl/IDSelector.h>
|
|
18
|
+
#include <faiss/impl/PanoramaStats.h>
|
|
19
|
+
|
|
20
|
+
#include <faiss/impl/FaissAssert.h>
|
|
21
|
+
#include <faiss/utils/distances.h>
|
|
22
|
+
#include <faiss/utils/extra_distances.h>
|
|
23
|
+
#include <faiss/utils/utils.h>
|
|
24
|
+
|
|
25
|
+
namespace faiss {
|
|
26
|
+
|
|
27
|
+
IndexIVFFlatPanorama::IndexIVFFlatPanorama(
|
|
28
|
+
Index* quantizer,
|
|
29
|
+
size_t d,
|
|
30
|
+
size_t nlist,
|
|
31
|
+
int n_levels,
|
|
32
|
+
MetricType metric,
|
|
33
|
+
bool own_invlists)
|
|
34
|
+
: IndexIVFFlat(quantizer, d, nlist, metric, false), n_levels(n_levels) {
|
|
35
|
+
// For now, we only support L2 distance.
|
|
36
|
+
// Supporting dot product and cosine distance is a trivial addition
|
|
37
|
+
// left for future work.
|
|
38
|
+
FAISS_THROW_IF_NOT(metric == METRIC_L2);
|
|
39
|
+
|
|
40
|
+
// We construct the inverted lists here so that we can use the
|
|
41
|
+
// level-oriented storage. This does not cause a leak as we constructed
|
|
42
|
+
// IndexIVF first, with own_invlists set to false.
|
|
43
|
+
this->invlists = new ArrayInvertedListsPanorama(nlist, code_size, n_levels);
|
|
44
|
+
this->own_invlists = own_invlists;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
IndexIVFFlatPanorama::IndexIVFFlatPanorama() : n_levels(0) {}
|
|
48
|
+
|
|
49
|
+
namespace {
|
|
50
|
+
|
|
51
|
+
template <typename VectorDistance, bool use_sel>
|
|
52
|
+
struct IVFFlatScannerPanorama : InvertedListScanner {
|
|
53
|
+
VectorDistance vd;
|
|
54
|
+
const ArrayInvertedListsPanorama* storage;
|
|
55
|
+
using C = typename VectorDistance::C;
|
|
56
|
+
|
|
57
|
+
IVFFlatScannerPanorama(
|
|
58
|
+
const VectorDistance& vd,
|
|
59
|
+
const ArrayInvertedListsPanorama* storage,
|
|
60
|
+
bool store_pairs,
|
|
61
|
+
const IDSelector* sel)
|
|
62
|
+
: InvertedListScanner(store_pairs, sel), vd(vd), storage(storage) {
|
|
63
|
+
keep_max = vd.is_similarity;
|
|
64
|
+
code_size = vd.d * sizeof(float);
|
|
65
|
+
cum_sums.resize(storage->n_levels + 1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const float* xi = nullptr;
|
|
69
|
+
std::vector<float> cum_sums;
|
|
70
|
+
float q_norm = 0.0f;
|
|
71
|
+
void set_query(const float* query) override {
|
|
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];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
void set_list(idx_t list_no, float /* coarse_dis */) override {
|
|
99
|
+
this->list_no = list_no;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// This function is unreachable as `IndexIVF` only calls this within
|
|
103
|
+
/// iterators, which are not supported by `IndexIVFFlatPanorama`.
|
|
104
|
+
/// To avoid undefined behavior, we throw an error here.
|
|
105
|
+
float distance_to_code(const uint8_t* /* code */) const override {
|
|
106
|
+
FAISS_THROW_MSG(
|
|
107
|
+
"IndexIVFFlatPanorama does not support distance_to_code");
|
|
108
|
+
}
|
|
109
|
+
|
|
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
|
+
size_t scan_codes(
|
|
200
|
+
size_t list_size,
|
|
201
|
+
const uint8_t* codes,
|
|
202
|
+
const idx_t* ids,
|
|
203
|
+
float* simi,
|
|
204
|
+
idx_t* idxi,
|
|
205
|
+
size_t k) const override {
|
|
206
|
+
size_t nup = 0;
|
|
207
|
+
|
|
208
|
+
const size_t n_batches =
|
|
209
|
+
(list_size + storage->kBatchSize - 1) / storage->kBatchSize;
|
|
210
|
+
|
|
211
|
+
const uint8_t* codes_base = codes;
|
|
212
|
+
const float* cum_sums_data = storage->get_cum_sums(list_no);
|
|
213
|
+
|
|
214
|
+
std::vector<float> exact_distances(storage->kBatchSize);
|
|
215
|
+
std::vector<uint32_t> active_indices(storage->kBatchSize);
|
|
216
|
+
|
|
217
|
+
PanoramaStats local_stats;
|
|
218
|
+
local_stats.reset();
|
|
219
|
+
|
|
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
|
+
for (size_t batch_no = 0; batch_no < n_batches; batch_no++) {
|
|
236
|
+
size_t batch_start = batch_no * storage->kBatchSize;
|
|
237
|
+
|
|
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);
|
|
248
|
+
|
|
249
|
+
// Add batch survivors to heap.
|
|
250
|
+
for (size_t i = 0; i < num_active; i++) {
|
|
251
|
+
uint32_t idx = active_indices[i];
|
|
252
|
+
size_t global_idx = batch_start + idx;
|
|
253
|
+
float dis = exact_distances[idx];
|
|
254
|
+
|
|
255
|
+
if (C::cmp(simi[0], dis)) {
|
|
256
|
+
int64_t id = store_pairs ? lo_build(list_no, global_idx)
|
|
257
|
+
: ids[global_idx];
|
|
258
|
+
heap_replace_top<C>(k, simi, idxi, dis, id);
|
|
259
|
+
nup++;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
indexPanorama_stats.add(local_stats);
|
|
265
|
+
return nup;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
void scan_codes_range(
|
|
269
|
+
size_t list_size,
|
|
270
|
+
const uint8_t* codes,
|
|
271
|
+
const idx_t* ids,
|
|
272
|
+
float radius,
|
|
273
|
+
RangeQueryResult& res) const override {
|
|
274
|
+
const size_t n_batches =
|
|
275
|
+
(list_size + storage->kBatchSize - 1) / storage->kBatchSize;
|
|
276
|
+
|
|
277
|
+
const uint8_t* codes_base = codes;
|
|
278
|
+
const float* cum_sums_data = storage->get_cum_sums(list_no);
|
|
279
|
+
|
|
280
|
+
std::vector<float> exact_distances(storage->kBatchSize);
|
|
281
|
+
std::vector<uint32_t> active_indices(storage->kBatchSize);
|
|
282
|
+
|
|
283
|
+
PanoramaStats local_stats;
|
|
284
|
+
local_stats.reset();
|
|
285
|
+
|
|
286
|
+
// Same progressive filtering as scan_codes, but with fixed radius
|
|
287
|
+
// threshold instead of dynamic heap threshold.
|
|
288
|
+
for (size_t batch_no = 0; batch_no < n_batches; batch_no++) {
|
|
289
|
+
size_t batch_start = batch_no * storage->kBatchSize;
|
|
290
|
+
|
|
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);
|
|
301
|
+
|
|
302
|
+
// Add batch survivors to range result.
|
|
303
|
+
for (size_t i = 0; i < num_active; i++) {
|
|
304
|
+
uint32_t idx = active_indices[i];
|
|
305
|
+
size_t global_idx = batch_start + idx;
|
|
306
|
+
float dis = exact_distances[idx];
|
|
307
|
+
|
|
308
|
+
if (C::cmp(radius, dis)) {
|
|
309
|
+
int64_t id = store_pairs ? lo_build(list_no, global_idx)
|
|
310
|
+
: ids[global_idx];
|
|
311
|
+
res.add(dis, id);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
indexPanorama_stats.add(local_stats);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
struct Run_get_InvertedListScanner {
|
|
321
|
+
using T = InvertedListScanner*;
|
|
322
|
+
|
|
323
|
+
template <class VD>
|
|
324
|
+
InvertedListScanner* f(
|
|
325
|
+
VD& vd,
|
|
326
|
+
const IndexIVFFlatPanorama* ivf,
|
|
327
|
+
bool store_pairs,
|
|
328
|
+
const IDSelector* sel) {
|
|
329
|
+
// Safely cast to ArrayInvertedListsPanorama to access cumulative sums.
|
|
330
|
+
const ArrayInvertedListsPanorama* storage =
|
|
331
|
+
dynamic_cast<const ArrayInvertedListsPanorama*>(ivf->invlists);
|
|
332
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
333
|
+
storage,
|
|
334
|
+
"IndexIVFFlatPanorama requires ArrayInvertedListsPanorama");
|
|
335
|
+
|
|
336
|
+
if (sel) {
|
|
337
|
+
return new IVFFlatScannerPanorama<VD, true>(
|
|
338
|
+
vd, storage, store_pairs, sel);
|
|
339
|
+
} else {
|
|
340
|
+
return new IVFFlatScannerPanorama<VD, false>(
|
|
341
|
+
vd, storage, store_pairs, sel);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
} // anonymous namespace
|
|
347
|
+
|
|
348
|
+
InvertedListScanner* IndexIVFFlatPanorama::get_InvertedListScanner(
|
|
349
|
+
bool store_pairs,
|
|
350
|
+
const IDSelector* sel,
|
|
351
|
+
const IVFSearchParameters*) const {
|
|
352
|
+
Run_get_InvertedListScanner run;
|
|
353
|
+
return dispatch_VectorDistance(
|
|
354
|
+
d, metric_type, metric_arg, run, this, store_pairs, sel);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
void IndexIVFFlatPanorama::reconstruct_from_offset(
|
|
358
|
+
int64_t list_no,
|
|
359
|
+
int64_t offset,
|
|
360
|
+
float* recons) const {
|
|
361
|
+
const uint8_t* code = invlists->get_single_code(list_no, offset);
|
|
362
|
+
memcpy(recons, code, code_size);
|
|
363
|
+
invlists->release_codes(list_no, code);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
} // namespace faiss
|