faiss 0.6.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/faiss/version.rb +1 -1
- data/vendor/faiss/faiss/AutoTune.cpp +3 -1
- data/vendor/faiss/faiss/Clustering.cpp +9 -1
- data/vendor/faiss/faiss/IVFlib.cpp +14 -3
- data/vendor/faiss/faiss/Index.h +2 -2
- data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
- data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
- data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +10 -12
- data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +5 -7
- data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
- data/vendor/faiss/faiss/IndexEDEN.h +57 -0
- data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
- data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
- data/vendor/faiss/faiss/IndexHNSW.cpp +177 -148
- data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
- data/vendor/faiss/faiss/IndexIDMap.h +2 -0
- data/vendor/faiss/faiss/IndexIVF.cpp +19 -8
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +3 -3
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
- data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
- data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
- data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
- data/vendor/faiss/faiss/IndexIVFFlat.cpp +6 -5
- data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +3 -3
- data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +42 -25
- data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
- data/vendor/faiss/faiss/IndexIVFPQR.cpp +2 -3
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +23 -62
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
- data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
- data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
- data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
- data/vendor/faiss/faiss/IndexNNDescent.cpp +10 -3
- data/vendor/faiss/faiss/IndexNSG.cpp +8 -4
- data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
- data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
- data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
- data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
- data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
- data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
- data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
- data/vendor/faiss/faiss/IndexScalarQuantizer.cpp +68 -6
- data/vendor/faiss/faiss/IndexScalarQuantizer.h +10 -0
- data/vendor/faiss/faiss/IndexShards.cpp +2 -2
- data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
- data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
- data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
- data/vendor/faiss/faiss/SuperKMeans.h +30 -0
- data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
- data/vendor/faiss/faiss/clone_index.cpp +5 -0
- data/vendor/faiss/faiss/cppcontrib/SaDecodeKernels.h +1 -1
- data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-neon-inl.h +902 -12
- data/vendor/faiss/faiss/cppcontrib/sa_decode/PQ-neon-inl.h +702 -10
- data/vendor/faiss/faiss/factory_tools.cpp +51 -4
- data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
- data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
- data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
- data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
- data/vendor/faiss/faiss/gpu/GpuResources.h +3 -2
- data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +11 -12
- data/vendor/faiss/faiss/gpu/StandardGpuResources.h +3 -3
- data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
- data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
- data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +87 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +7 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +177 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
- data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +48 -3
- data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +45 -0
- data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFFlat.h +193 -0
- data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
- data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
- data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
- data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
- data/vendor/faiss/faiss/impl/HNSW.cpp +658 -344
- data/vendor/faiss/faiss/impl/HNSW.h +51 -13
- data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
- data/vendor/faiss/faiss/impl/NSG.cpp +18 -12
- data/vendor/faiss/faiss/impl/Panorama.h +20 -7
- data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +59 -24
- data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +45 -37
- data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +175 -68
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
- data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
- data/vendor/faiss/faiss/impl/ResultHandler.h +26 -31
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +522 -58
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +70 -0
- data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
- data/vendor/faiss/faiss/impl/VisitedTable.cpp +33 -13
- data/vendor/faiss/faiss/impl/VisitedTable.h +88 -33
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
- data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
- data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +38 -3
- data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
- data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.cpp +35 -43
- data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.h +64 -15
- data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +86 -40
- data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +81 -50
- data/vendor/faiss/faiss/impl/index_read.cpp +476 -75
- data/vendor/faiss/faiss/impl/index_write.cpp +56 -4
- data/vendor/faiss/faiss/impl/io_macros.h +25 -0
- data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
- data/vendor/faiss/faiss/impl/platform_macros.h +15 -9
- data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
- data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
- data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
- data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +6 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +2 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +2 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +20 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +36 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +5 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +105 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +2 -0
- data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
- data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
- data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +6 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +336 -26
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +331 -32
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +553 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +558 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +284 -45
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +502 -3
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +157 -32
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +26 -0
- data/vendor/faiss/faiss/impl/simd_dispatch.h +86 -8
- data/vendor/faiss/faiss/index_factory.cpp +37 -7
- data/vendor/faiss/faiss/index_io.h +16 -0
- data/vendor/faiss/faiss/invlists/DirectMap.cpp +5 -2
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +15 -15
- data/vendor/faiss/faiss/invlists/InvertedLists.h +2 -2
- data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
- data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
- data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
- data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
- data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
- data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +150 -23
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +30 -7
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +3 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +65 -25
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +3 -2
- data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
- data/vendor/faiss/faiss/utils/bf16.h +34 -0
- data/vendor/faiss/faiss/utils/distances.cpp +14 -2
- data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -4
- data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
- data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
- data/vendor/faiss/faiss/utils/hamming.cpp +9 -9
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +2 -1
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +15 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +6 -30
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_spr.h +171 -0
- data/vendor/faiss/faiss/utils/partitioning.cpp +0 -2
- data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
- data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
- data/vendor/faiss/faiss/utils/simd_impl/partitioning_simdlib256.h +14 -68
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +435 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
- data/vendor/faiss/faiss/utils/simd_levels.cpp +56 -2
- data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
- data/vendor/faiss/faiss/utils/utils.cpp +9 -27
- metadata +27 -2
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
+
#include <cstring>
|
|
11
|
+
|
|
10
12
|
#include <faiss/impl/AuxIndexStructures.h>
|
|
11
13
|
#include <faiss/impl/DistanceComputer.h>
|
|
12
14
|
#include <faiss/impl/Quantizer.h>
|
|
@@ -39,6 +41,18 @@ struct ScalarQuantizer : Quantizer {
|
|
|
39
41
|
QT_3bit_tqmse, ///< TurboQuant MSE-optimized, 3 bits per component
|
|
40
42
|
QT_4bit_tqmse, ///< TurboQuant MSE-optimized, 4 bits per component
|
|
41
43
|
QT_8bit_tqmse, ///< TurboQuant MSE-optimized, 8 bits per component
|
|
44
|
+
QT_2bit_tq, ///< Full TurboQuant (1-bit MSE + 1-bit QJL + factors)
|
|
45
|
+
QT_3bit_tq, ///< Full TurboQuant (2-bit MSE + 1-bit QJL + factors)
|
|
46
|
+
QT_4bit_tq, ///< Full TurboQuant (3-bit MSE + 1-bit QJL + factors)
|
|
47
|
+
QT_5bit_tq, ///< Full TurboQuant (4-bit MSE + 1-bit QJL + factors)
|
|
48
|
+
QT_1bit_eden, ///< EDEN Lloyd-Max scalar code, 1 bit per component
|
|
49
|
+
QT_2bit_eden, ///< EDEN Lloyd-Max scalar code, 2 bits per component
|
|
50
|
+
QT_3bit_eden, ///< EDEN Lloyd-Max scalar code, 3 bits per component
|
|
51
|
+
QT_4bit_eden, ///< EDEN Lloyd-Max scalar code, 4 bits per component
|
|
52
|
+
QT_5bit_eden, ///< EDEN Lloyd-Max scalar code, 5 bits per component
|
|
53
|
+
QT_6bit_eden, ///< EDEN Lloyd-Max scalar code, 6 bits per component
|
|
54
|
+
QT_7bit_eden, ///< EDEN Lloyd-Max scalar code, 7 bits per component
|
|
55
|
+
QT_8bit_eden, ///< EDEN Lloyd-Max scalar code, 8 bits per component
|
|
42
56
|
QT_count
|
|
43
57
|
};
|
|
44
58
|
|
|
@@ -129,8 +143,64 @@ struct ScalarQuantizer : Quantizer {
|
|
|
129
143
|
float distance_to_code(const uint8_t* code) final {
|
|
130
144
|
return query_to_code(code);
|
|
131
145
|
}
|
|
146
|
+
|
|
147
|
+
void distance_to_code_batch_4(
|
|
148
|
+
const uint8_t* c1,
|
|
149
|
+
const uint8_t* c2,
|
|
150
|
+
const uint8_t* c3,
|
|
151
|
+
const uint8_t* c4,
|
|
152
|
+
float& d1,
|
|
153
|
+
float& d2,
|
|
154
|
+
float& d3,
|
|
155
|
+
float& d4) override {
|
|
156
|
+
query_to_codes_batch_4(c1, c2, c3, c4, d1, d2, d3, d4);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/// TurboQuant full (QT_*_tq) refinement state, isolated from the
|
|
161
|
+
/// main ScalarQuantizer to avoid polluting it with TQ-specific data.
|
|
162
|
+
struct TurboQuantRefine {
|
|
163
|
+
static bool is_turboq_full(QuantizerType qt) {
|
|
164
|
+
return qt >= QT_2bit_tq && qt <= QT_5bit_tq;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static void pack_seed(uint64_t seed, float out[2]) {
|
|
168
|
+
static_assert(sizeof(uint64_t) == 2 * sizeof(float));
|
|
169
|
+
std::memcpy(out, &seed, sizeof(uint64_t));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
static uint64_t unpack_seed(float lo, float hi) {
|
|
173
|
+
float tmp[2] = {lo, hi};
|
|
174
|
+
uint64_t s;
|
|
175
|
+
static_assert(sizeof(uint64_t) == 2 * sizeof(float));
|
|
176
|
+
std::memcpy(&s, tmp, sizeof(uint64_t));
|
|
177
|
+
return s;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
uint8_t qjl_type = 0;
|
|
181
|
+
uint64_t seed = 42;
|
|
182
|
+
size_t padded_d = 0;
|
|
183
|
+
std::vector<float> fwht_signs;
|
|
184
|
+
std::vector<float> rr_matrix;
|
|
185
|
+
size_t nb_bits_lo = 0;
|
|
186
|
+
size_t n_hi_dims = 0;
|
|
187
|
+
|
|
188
|
+
void init_projection(size_t d);
|
|
189
|
+
bool use_fwht() const {
|
|
190
|
+
return qjl_type == 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
struct DistanceComputer : SQDistanceComputer {
|
|
194
|
+
virtual void configure(uint8_t qb, bool int_qjl) = 0;
|
|
195
|
+
virtual void set_prescreen_threshold(
|
|
196
|
+
const float* t,
|
|
197
|
+
bool minimize) = 0;
|
|
198
|
+
virtual void clear_prescreen_threshold() = 0;
|
|
199
|
+
};
|
|
132
200
|
};
|
|
133
201
|
|
|
202
|
+
TurboQuantRefine turboq_refine;
|
|
203
|
+
|
|
134
204
|
SQDistanceComputer* get_distance_computer(
|
|
135
205
|
MetricType metric = METRIC_L2) const;
|
|
136
206
|
|
|
@@ -70,8 +70,8 @@ void ThreadedIndex<IndexT>::addIndex(IndexT* index) {
|
|
|
70
70
|
|
|
71
71
|
// Make sure this index is not duplicated
|
|
72
72
|
for (auto& p : indices_) {
|
|
73
|
-
|
|
74
|
-
p.first
|
|
73
|
+
FAISS_THROW_IF_MSG(
|
|
74
|
+
p.first == index,
|
|
75
75
|
"addIndex: attempting to add index "
|
|
76
76
|
"that is already in the collection");
|
|
77
77
|
}
|
|
@@ -15,22 +15,42 @@ namespace faiss {
|
|
|
15
15
|
// advance() is O(1) except every 250 calls, which are O(size).
|
|
16
16
|
// The hash set strategy is a constant factor slower for get()/set(),
|
|
17
17
|
// but O(1) to construct and O(visits) to advance.
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
// 10M is only a current estimated threshold, not a proven crossover: we are not
|
|
19
|
+
// sure the array still wins at 10M. The point where the array stops paying off
|
|
20
|
+
// varies by dataset (it shifts with dimension, working-set / cache pressure,
|
|
21
|
+
// etc.), so this is a coarse default that should eventually be replaced by
|
|
22
|
+
// smarter per-index tuning.
|
|
23
|
+
size_t visited_table_hashset_threshold = 10000000;
|
|
24
|
+
|
|
25
|
+
std::unique_ptr<VisitedTable> VisitedTable::create(
|
|
26
|
+
size_t size,
|
|
27
|
+
std::optional<bool> use_hashset) {
|
|
28
|
+
bool use_set =
|
|
29
|
+
use_hashset.value_or(size >= visited_table_hashset_threshold);
|
|
30
|
+
if (use_set) {
|
|
31
|
+
return std::make_unique<VisitedTableSet>();
|
|
32
|
+
}
|
|
33
|
+
return std::make_unique<VisitedTableVector>(size);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
VisitedTable& VisitedTable::get_reusable(
|
|
37
|
+
size_t size,
|
|
38
|
+
std::optional<bool> use_hashset) {
|
|
39
|
+
bool use_set =
|
|
40
|
+
use_hashset.value_or(size >= visited_table_hashset_threshold);
|
|
41
|
+
if (use_set) {
|
|
42
|
+
thread_local VisitedTableSet tls_set;
|
|
43
|
+
tls_set.advance();
|
|
44
|
+
return tls_set;
|
|
27
45
|
}
|
|
46
|
+
thread_local VisitedTableVector tls_vec(0);
|
|
47
|
+
tls_vec.ensure_size(size);
|
|
48
|
+
tls_vec.advance();
|
|
49
|
+
return tls_vec;
|
|
28
50
|
}
|
|
29
51
|
|
|
30
|
-
void
|
|
31
|
-
if (visno
|
|
32
|
-
visited_set.clear();
|
|
33
|
-
} else if (visno < 254) {
|
|
52
|
+
void VisitedTableVector::advance() {
|
|
53
|
+
if (visno < 254) {
|
|
34
54
|
// 254 rather than 255 because sometimes we use visno and visno+1
|
|
35
55
|
++visno;
|
|
36
56
|
} else {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
#include <stdint.h>
|
|
12
12
|
|
|
13
|
+
#include <memory>
|
|
13
14
|
#include <optional>
|
|
14
15
|
#include <unordered_set>
|
|
15
16
|
#include <vector>
|
|
@@ -21,54 +22,108 @@ namespace faiss {
|
|
|
21
22
|
|
|
22
23
|
FAISS_API extern size_t visited_table_hashset_threshold;
|
|
23
24
|
|
|
24
|
-
///
|
|
25
|
+
/// Abstract base class for a fast, reusable Visited Set for graph search
|
|
26
|
+
/// algorithms.
|
|
25
27
|
struct VisitedTable {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
virtual ~VisitedTable() = default;
|
|
29
|
+
|
|
30
|
+
/// set flag #no to true, return whether this changed it.
|
|
31
|
+
virtual bool set(size_t no) = 0;
|
|
32
|
+
|
|
33
|
+
/// get flag #no
|
|
34
|
+
virtual bool get(size_t no) const = 0;
|
|
35
|
+
|
|
36
|
+
/// prefetch flag #no
|
|
37
|
+
virtual void prefetch(size_t no) const = 0;
|
|
38
|
+
|
|
39
|
+
/// pre-allocate bucket space to avoid rehashing during repeated set() calls
|
|
40
|
+
virtual void reserve(size_t /*n*/) {}
|
|
41
|
+
|
|
42
|
+
/// reset all flags to false
|
|
43
|
+
virtual void advance() = 0;
|
|
29
44
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
45
|
+
/// Factory method to create appropriate implementation.
|
|
46
|
+
/// If use_hashset is nullopt, the use of a hashset will be determined by
|
|
47
|
+
/// size >= visited_table_hashset_threshold.
|
|
48
|
+
static std::unique_ptr<VisitedTable> create(
|
|
33
49
|
size_t size,
|
|
34
50
|
std::optional<bool> use_hashset = std::nullopt);
|
|
35
51
|
|
|
36
|
-
///
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
/// Returns a thread-local, reusable table sized for at least `size` and
|
|
53
|
+
/// reset to a clean state. Unlike create(), it does not allocate on each
|
|
54
|
+
/// call: the O(size) versioned array is allocated once per thread and
|
|
55
|
+
/// reused across searches, avoiding a per-search alloc+zero of the whole
|
|
56
|
+
/// array when a static index is searched repeatedly.
|
|
57
|
+
///
|
|
58
|
+
/// The returned reference is owned by thread-local storage: do not delete
|
|
59
|
+
/// it and do not use it beyond the current search on the calling thread.
|
|
60
|
+
static VisitedTable& get_reusable(
|
|
61
|
+
size_t size,
|
|
62
|
+
std::optional<bool> use_hashset = std::nullopt);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/// Set-based implementation using unordered_set.
|
|
66
|
+
/// O(1) to construct and O(visits) to advance.
|
|
67
|
+
struct VisitedTableSet FAISS_FINAL : VisitedTable {
|
|
68
|
+
std::unordered_set<size_t> visited_set;
|
|
69
|
+
|
|
70
|
+
VisitedTableSet() = default;
|
|
71
|
+
|
|
72
|
+
bool set(size_t no) final {
|
|
73
|
+
return visited_set.insert(no).second;
|
|
46
74
|
}
|
|
47
75
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (visno == 0) {
|
|
51
|
-
visited_set.reserve(n);
|
|
52
|
-
}
|
|
76
|
+
bool get(size_t no) const final {
|
|
77
|
+
return visited_set.count(no) != 0;
|
|
53
78
|
}
|
|
54
79
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
80
|
+
void prefetch(size_t /*no*/) const final {
|
|
81
|
+
// No-op for set-based implementation
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
void reserve(size_t n) final {
|
|
85
|
+
visited_set.reserve(n);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void advance() final {
|
|
89
|
+
visited_set.clear();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/// Vector-based implementation using a versioned byte array.
|
|
94
|
+
/// Faster for get()/set(), but O(size) to initialize.
|
|
95
|
+
/// advance() is O(1) except every 250 calls, which are O(size).
|
|
96
|
+
struct VisitedTableVector FAISS_FINAL : VisitedTable {
|
|
97
|
+
std::vector<uint8_t> visited;
|
|
98
|
+
uint8_t visno{1}; // Version number, 1..254
|
|
99
|
+
|
|
100
|
+
explicit VisitedTableVector(size_t size) : visited(size, 0) {}
|
|
101
|
+
|
|
102
|
+
/// Grow so indices in [0, size) are valid; new slots read as unvisited.
|
|
103
|
+
/// Never shrinks, so capacity is retained when the table is reused.
|
|
104
|
+
void ensure_size(size_t size) {
|
|
105
|
+
if (visited.size() < size) {
|
|
106
|
+
visited.resize(size, 0);
|
|
61
107
|
}
|
|
62
108
|
}
|
|
63
109
|
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
110
|
+
bool set(size_t no) final {
|
|
111
|
+
if (visited[no] == visno) {
|
|
112
|
+
return false;
|
|
67
113
|
}
|
|
114
|
+
visited[no] = visno;
|
|
115
|
+
return true;
|
|
68
116
|
}
|
|
69
117
|
|
|
70
|
-
|
|
71
|
-
|
|
118
|
+
bool get(size_t no) const final {
|
|
119
|
+
return visited[no] == visno;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
void prefetch(size_t no) const final {
|
|
123
|
+
prefetch_L2(&visited[no]);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
void advance() final;
|
|
72
127
|
};
|
|
73
128
|
|
|
74
129
|
} // namespace faiss
|
|
@@ -292,7 +292,7 @@ void search_knn_hamming_per_invlist(
|
|
|
292
292
|
nprobe = std::min((idx_t)ivf->nlist, nprobe);
|
|
293
293
|
idx_t max_codes = params ? params->max_codes : ivf->max_codes;
|
|
294
294
|
FAISS_THROW_IF_NOT(max_codes == 0);
|
|
295
|
-
|
|
295
|
+
FAISS_THROW_IF_MSG(store_pairs, "store_pairs is not supported here");
|
|
296
296
|
|
|
297
297
|
// reorder buckets
|
|
298
298
|
std::vector<int64_t> lims(n + 1);
|
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
|
|
10
10
|
#define THE_SIMD_LEVEL SIMDLevel::AVX2
|
|
11
11
|
|
|
12
|
-
// NOLINTNEXTLINE(facebook-hte-InlineHeader)
|
|
12
|
+
// NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
|
|
13
13
|
#include <faiss/utils/hamming_distance/hamming_computer-avx2.h>
|
|
14
14
|
|
|
15
15
|
// NOLINTNEXTLINE(facebook-hte-InlineHeader)
|
|
16
|
-
#include <faiss/impl/binary_hamming/IndexBinaryHNSW_impl.h>
|
|
16
|
+
#include <faiss/impl/binary_hamming/IndexBinaryHNSW_impl.h> // IWYU pragma: keep
|
|
17
17
|
// NOLINTNEXTLINE(facebook-hte-InlineHeader)
|
|
18
|
-
#include <faiss/impl/binary_hamming/IndexBinaryHash_impl.h>
|
|
18
|
+
#include <faiss/impl/binary_hamming/IndexBinaryHash_impl.h> // IWYU pragma: keep
|
|
19
19
|
// NOLINTNEXTLINE(facebook-hte-InlineHeader)
|
|
20
20
|
#include <faiss/impl/binary_hamming/IndexBinaryIVF_impl.h>
|
|
21
21
|
// NOLINTNEXTLINE(facebook-hte-InlineHeader)
|
|
22
|
-
#include <faiss/impl/binary_hamming/IndexIVFSpectralHash_impl.h>
|
|
22
|
+
#include <faiss/impl/binary_hamming/IndexIVFSpectralHash_impl.h> // IWYU pragma: keep
|
|
23
23
|
// NOLINTNEXTLINE(facebook-hte-InlineHeader)
|
|
24
24
|
#include <faiss/impl/binary_hamming/IndexPQ_impl.h>
|
|
25
25
|
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
#include <faiss/impl/fast_scan/accumulate_loops.h>
|
|
35
35
|
#include <faiss/impl/fast_scan/fast_scan.h>
|
|
36
|
+
#include <faiss/utils/simd_levels.h>
|
|
36
37
|
|
|
37
38
|
#if defined(COMPILE_SIMD_AVX512) && defined(__AVX512F__)
|
|
38
39
|
#include <faiss/impl/fast_scan/accumulate_loops_512.h>
|
|
@@ -48,7 +49,9 @@ using namespace simd_result_handlers;
|
|
|
48
49
|
* so callers don't need to know the handler type.
|
|
49
50
|
***************************************************************/
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
// SIMDLevel SL = THE_LEVEL_TO_DISPATCH added to make the mangled
|
|
53
|
+
// symbol name unique per translation unit.
|
|
54
|
+
template <class Handler, SIMDLevel SL = THE_LEVEL_TO_DISPATCH>
|
|
52
55
|
struct ScannerMixIn : FastScanCodeScanner {
|
|
53
56
|
Handler handler_;
|
|
54
57
|
|
|
@@ -112,8 +115,40 @@ struct ScannerMixIn : FastScanCodeScanner {
|
|
|
112
115
|
constexpr bool use_avx512_qbs = false;
|
|
113
116
|
#endif
|
|
114
117
|
if constexpr (use_avx512_qbs) {
|
|
115
|
-
//
|
|
116
|
-
|
|
118
|
+
// AMD Zen 4 / Zen 4c ("Bergamo", family 0x19) split 512-bit
|
|
119
|
+
// ops over a 256-bit datapath, so the 512-bit QBS kernel yields no
|
|
120
|
+
// throughput gain but pays extra per-block LUT-assembly and
|
|
121
|
+
// cross-lane reduction overhead (measured ~14% search regression
|
|
122
|
+
// for PQ8x4fs / PQ16x4fs). Route those CPUs to the 256-bit (AVX2)
|
|
123
|
+
// QBS kernel instead -- same output, no downside on Zen 4. This is
|
|
124
|
+
// a process-constant runtime branch, hoisted out of the inner
|
|
125
|
+
// accumulate loop. Intel AVX-512 keeps the 512-bit kernel.
|
|
126
|
+
if (SIMDConfig::avx512_split) {
|
|
127
|
+
if (pq2x4_scale) {
|
|
128
|
+
NormTableScaler<SIMDLevel::AVX2> scaler(pq2x4_scale);
|
|
129
|
+
pq4_accumulate_loop_qbs_fixed_scaler_256<SIMDLevel::AVX2>(
|
|
130
|
+
qbs,
|
|
131
|
+
nb,
|
|
132
|
+
nsq,
|
|
133
|
+
codes,
|
|
134
|
+
LUT,
|
|
135
|
+
handler_,
|
|
136
|
+
scaler,
|
|
137
|
+
block_stride);
|
|
138
|
+
} else {
|
|
139
|
+
DummyScaler<SIMDLevel::AVX2> dummy;
|
|
140
|
+
pq4_accumulate_loop_qbs_fixed_scaler_256<SIMDLevel::AVX2>(
|
|
141
|
+
qbs,
|
|
142
|
+
nb,
|
|
143
|
+
nsq,
|
|
144
|
+
codes,
|
|
145
|
+
LUT,
|
|
146
|
+
handler_,
|
|
147
|
+
dummy,
|
|
148
|
+
block_stride);
|
|
149
|
+
}
|
|
150
|
+
} else if (pq2x4_scale) {
|
|
151
|
+
// Use 512-bit QBS kernels with properly-leveled scalers.
|
|
117
152
|
NormTableScaler<THE_LEVEL_TO_DISPATCH> scaler(pq2x4_scale);
|
|
118
153
|
pq4_accumulate_loop_qbs_fixed_scaler_512(
|
|
119
154
|
qbs,
|
|
@@ -30,7 +30,7 @@ void LockVector::prepare(size_t new_size) {
|
|
|
30
30
|
// Just destroy old and init fresh; omp_lock_t is not copyable.
|
|
31
31
|
clear();
|
|
32
32
|
data_ = static_cast<omp_lock_t*>(malloc(new_cap * sizeof(omp_lock_t)));
|
|
33
|
-
FAISS_THROW_IF_NOT(data_
|
|
33
|
+
FAISS_THROW_IF_NOT(data_);
|
|
34
34
|
capacity_ = new_cap;
|
|
35
35
|
}
|
|
36
36
|
for (size_t i = size_; i < new_size; i++) {
|
|
@@ -5,39 +5,32 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
#include <cmath>
|
|
9
|
-
|
|
10
8
|
#include <faiss/impl/hnsw/MinimaxHeap.h>
|
|
11
9
|
|
|
12
|
-
#include <cassert>
|
|
13
|
-
|
|
14
10
|
#include <faiss/impl/simd_dispatch.h>
|
|
15
11
|
|
|
16
12
|
namespace faiss {
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
--nvalid;
|
|
29
|
-
}
|
|
30
|
-
faiss::heap_pop<HC>(k--, dis.data(), ids.data());
|
|
31
|
-
}
|
|
32
|
-
faiss::heap_push<HC>(++k, dis.data(), ids.data(), v, i);
|
|
33
|
-
++nvalid;
|
|
14
|
+
// Runtime-dispatched pop_min (NONE + AVX2 + AVX512 only).
|
|
15
|
+
constexpr int MINIMAX_HEAP_SIMD_LEVELS = (1 << int(SIMDLevel::NONE)) |
|
|
16
|
+
(1 << int(SIMDLevel::AVX2)) | (1 << int(SIMDLevel::AVX512));
|
|
17
|
+
|
|
18
|
+
template <class HC_>
|
|
19
|
+
int MinimaxHeapT<HC_>::pop_min(float* vmin_out) {
|
|
20
|
+
return with_selected_simd_levels<MINIMAX_HEAP_SIMD_LEVELS>(
|
|
21
|
+
[&]<SIMDLevel SL>() {
|
|
22
|
+
return pop_min_tpl<HC_, SL>(this, vmin_out);
|
|
23
|
+
});
|
|
34
24
|
}
|
|
35
25
|
|
|
36
|
-
//
|
|
37
|
-
template
|
|
38
|
-
int
|
|
26
|
+
// Primary-template scalar implementation. Used directly when SL==NONE
|
|
27
|
+
template <class HC>
|
|
28
|
+
int pop_min_simd_none(MinimaxHeapT<HC>* heap, float* vmin_out) {
|
|
29
|
+
int k = heap->k;
|
|
30
|
+
int* ids = heap->ids.data();
|
|
31
|
+
float* dis = heap->dis.data();
|
|
39
32
|
assert(k > 0);
|
|
40
|
-
//
|
|
33
|
+
// Returns the "best" entry. This is an O(n) operation.
|
|
41
34
|
int i = k - 1;
|
|
42
35
|
while (i >= 0) {
|
|
43
36
|
if (ids[i] != -1) {
|
|
@@ -52,7 +45,8 @@ int MinimaxHeap::pop_min_tpl<SIMDLevel::NONE>(float* vmin_out) {
|
|
|
52
45
|
float vmin = dis[i];
|
|
53
46
|
i--;
|
|
54
47
|
while (i >= 0) {
|
|
55
|
-
|
|
48
|
+
// HC::cmp(vmin, dis[i]) → "dis[i] is better than vmin".
|
|
49
|
+
if (ids[i] != -1 && HC::cmp(vmin, dis[i])) {
|
|
56
50
|
vmin = dis[i];
|
|
57
51
|
imin = i;
|
|
58
52
|
}
|
|
@@ -63,29 +57,27 @@ int MinimaxHeap::pop_min_tpl<SIMDLevel::NONE>(float* vmin_out) {
|
|
|
63
57
|
}
|
|
64
58
|
int ret = ids[imin];
|
|
65
59
|
ids[imin] = -1;
|
|
66
|
-
--nvalid;
|
|
67
|
-
|
|
60
|
+
--heap->nvalid;
|
|
68
61
|
return ret;
|
|
69
62
|
}
|
|
70
63
|
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return
|
|
77
|
-
[&]<SIMDLevel SL>() { return pop_min_tpl<SL>(vmin_out); });
|
|
64
|
+
// declare for min and max heap at simd level NONE
|
|
65
|
+
template <>
|
|
66
|
+
int pop_min_tpl<CMin<float, int32_t>, SIMDLevel::NONE>(
|
|
67
|
+
MinimaxHeapT<CMin<float, int32_t>>* heap,
|
|
68
|
+
float* vmin_out) {
|
|
69
|
+
return pop_min_simd_none(heap, vmin_out);
|
|
78
70
|
}
|
|
79
71
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return n_below;
|
|
72
|
+
template <>
|
|
73
|
+
int pop_min_tpl<CMax<float, int32_t>, SIMDLevel::NONE>(
|
|
74
|
+
MinimaxHeapT<CMax<float, int32_t>>* heap,
|
|
75
|
+
float* vmin_out) {
|
|
76
|
+
return pop_min_simd_none(heap, vmin_out);
|
|
89
77
|
}
|
|
90
78
|
|
|
79
|
+
// Explicit instantiations of pop_min for the two HC variants
|
|
80
|
+
template int MinimaxHeapT<CMax<float, int32_t>>::pop_min(float*);
|
|
81
|
+
template int MinimaxHeapT<CMin<float, int32_t>>::pop_min(float*);
|
|
82
|
+
|
|
91
83
|
} // namespace faiss
|
|
@@ -7,21 +7,30 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
+
#include <cassert>
|
|
11
|
+
#include <cmath>
|
|
10
12
|
#include <cstdint>
|
|
11
13
|
#include <vector>
|
|
12
14
|
|
|
13
15
|
#include <faiss/utils/Heap.h>
|
|
16
|
+
#include <faiss/utils/ordered_key_value.h>
|
|
14
17
|
#include <faiss/utils/simd_levels.h>
|
|
15
18
|
|
|
16
19
|
namespace faiss {
|
|
17
20
|
|
|
18
21
|
/** Heap structure that allows fast access and updates.
|
|
19
22
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
+
* Templated on the comparator HC_ so that the same data structure can
|
|
24
|
+
* service both distance-style searches (HC_ = CMax<float, int32_t>, smaller
|
|
25
|
+
* is better) and similarity-style searches (HC_ = CMin<float, int32_t>,
|
|
26
|
+
* larger is better). For the distance variant the underlying heap is a
|
|
27
|
+
* max-heap and "pop_min" returns the closest element; for similarity the
|
|
28
|
+
* underlying heap is a min-heap and "pop_min" returns the most similar
|
|
29
|
+
* element.
|
|
23
30
|
*/
|
|
24
|
-
|
|
31
|
+
template <class HC_ = CMax<float, int32_t>>
|
|
32
|
+
struct MinimaxHeapT {
|
|
33
|
+
using HC = HC_;
|
|
25
34
|
using storage_idx_t = int32_t;
|
|
26
35
|
|
|
27
36
|
int n;
|
|
@@ -30,12 +39,34 @@ struct MinimaxHeap {
|
|
|
30
39
|
|
|
31
40
|
std::vector<storage_idx_t> ids;
|
|
32
41
|
std::vector<float> dis;
|
|
33
|
-
using HC = faiss::CMax<float, storage_idx_t>;
|
|
34
42
|
|
|
35
|
-
explicit
|
|
43
|
+
explicit MinimaxHeapT(int n_in)
|
|
36
44
|
: n(n_in), k(0), nvalid(0), ids(n_in), dis(n_in) {}
|
|
37
45
|
|
|
38
|
-
void push(storage_idx_t i, float v)
|
|
46
|
+
void push(storage_idx_t i, float v) {
|
|
47
|
+
// Treat NaN distances as the "worst" value so heap ordering is
|
|
48
|
+
// preserved (insertion is then guaranteed to fall through the
|
|
49
|
+
// not-better-than-top early-reject branch when the heap is full).
|
|
50
|
+
if (std::isnan(v)) {
|
|
51
|
+
v = HC::neutral();
|
|
52
|
+
}
|
|
53
|
+
if (k == n) {
|
|
54
|
+
// top of the heap is the "worst" entry under HC. If the new
|
|
55
|
+
// value is not strictly better than the worst, drop it.
|
|
56
|
+
// HC::cmp(top, v) means "v is better than top" for both CMax
|
|
57
|
+
// (cmp = a > b → top > v → v < top) and CMin (cmp = a < b →
|
|
58
|
+
// top < v → v > top).
|
|
59
|
+
if (!HC::cmp(dis[0], v)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (ids[0] != -1) {
|
|
63
|
+
--nvalid;
|
|
64
|
+
}
|
|
65
|
+
faiss::heap_pop<HC>(k--, dis.data(), ids.data());
|
|
66
|
+
}
|
|
67
|
+
faiss::heap_push<HC>(++k, dis.data(), ids.data(), v, i);
|
|
68
|
+
++nvalid;
|
|
69
|
+
}
|
|
39
70
|
|
|
40
71
|
float max() const {
|
|
41
72
|
return dis[0];
|
|
@@ -49,16 +80,34 @@ struct MinimaxHeap {
|
|
|
49
80
|
nvalid = k = 0;
|
|
50
81
|
}
|
|
51
82
|
|
|
52
|
-
///
|
|
53
|
-
/// Specializations exist for NONE, AVX2, and AVX512.
|
|
54
|
-
template <SIMDLevel SL>
|
|
55
|
-
int pop_min_tpl(float* vmin_out = nullptr);
|
|
56
|
-
|
|
57
|
-
/// Runtime-dispatched pop_min (calls pop_min_tpl with best available
|
|
58
|
-
/// SIMD level).
|
|
83
|
+
/// Runtime-dispatched best-element extraction (NONE + AVX2 + AVX512).
|
|
59
84
|
int pop_min(float* vmin_out = nullptr);
|
|
60
85
|
|
|
61
|
-
int count_below(float thresh)
|
|
86
|
+
int count_below(float thresh) {
|
|
87
|
+
int n_below = 0;
|
|
88
|
+
for (int i = 0; i < k; i++) {
|
|
89
|
+
// Count entries that are strictly "better than" thresh.
|
|
90
|
+
// HC::cmp(thresh, dis[i]) → for CMax: thresh > dis[i]
|
|
91
|
+
// (i.e., dis[i] < thresh, the historical L2 semantics);
|
|
92
|
+
// for CMin: thresh < dis[i] (similarity above threshold).
|
|
93
|
+
if (HC::cmp(thresh, dis[i])) {
|
|
94
|
+
n_below++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return n_below;
|
|
98
|
+
}
|
|
62
99
|
};
|
|
63
100
|
|
|
101
|
+
// Default `MinimaxHeap` keeps the historical max-heap semantics (smaller
|
|
102
|
+
// distance is better). The CMin instantiation is used when the owning
|
|
103
|
+
// HNSW has `is_similarity = true`. The alias itself is declared once,
|
|
104
|
+
// alongside the forward declaration in HNSW.h, to avoid duplicate
|
|
105
|
+
// `using` declarations that SWIG treats as redundant.
|
|
106
|
+
|
|
107
|
+
// Forward declarations of the SIMD specializations. The actual bodies live
|
|
108
|
+
// in the SIMD-specific translation units (avx2.cpp, avx512.cpp) and are
|
|
109
|
+
// resolved at link time.
|
|
110
|
+
template <class HC_, SIMDLevel SL>
|
|
111
|
+
int pop_min_tpl(MinimaxHeapT<HC_>* heap, float* vmin_out);
|
|
112
|
+
|
|
64
113
|
} // namespace faiss
|