faiss 0.6.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/Clustering.h +8 -0
- 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 +24 -4
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +16 -145
- data/vendor/faiss/faiss/IndexBinaryHNSW.h +0 -6
- data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +8 -18
- data/vendor/faiss/faiss/IndexBinaryIVF.h +8 -1
- 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 +21 -54
- data/vendor/faiss/faiss/IndexFlat.h +2 -2
- data/vendor/faiss/faiss/IndexHNSW.cpp +311 -102
- data/vendor/faiss/faiss/IndexHNSW.h +31 -7
- data/vendor/faiss/faiss/IndexIDMap.cpp +26 -8
- data/vendor/faiss/faiss/IndexIDMap.h +2 -0
- data/vendor/faiss/faiss/IndexIVF.cpp +36 -10
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
- 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 +3 -4
- data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +49 -23
- data/vendor/faiss/faiss/IndexIVFPQ.h +11 -0
- data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
- 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 +9 -2
- data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
- 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/IndexShards.cpp +5 -5
- data/vendor/faiss/faiss/IndexShardsIVF.cpp +6 -5
- data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
- data/vendor/faiss/faiss/SuperKMeans.cpp +286 -247
- data/vendor/faiss/faiss/SuperKMeans.h +33 -2
- data/vendor/faiss/faiss/VectorTransform.cpp +71 -2
- data/vendor/faiss/faiss/VectorTransform.h +3 -0
- data/vendor/faiss/faiss/clone_index.cpp +8 -0
- data/vendor/faiss/faiss/factory_tools.cpp +47 -4
- data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
- data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +1 -5
- data/vendor/faiss/faiss/gpu/GpuDistance.h +2 -5
- data/vendor/faiss/faiss/gpu/GpuIndex.h +38 -16
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +71 -1
- data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
- data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
- 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/MetalIndexIVFFlat.h +1 -5
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
- data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
- data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +7 -4
- 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 +528 -267
- data/vendor/faiss/faiss/impl/HNSW.h +46 -7
- data/vendor/faiss/faiss/impl/IDSelector.h +44 -0
- data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
- data/vendor/faiss/faiss/impl/NNDescent.cpp +10 -3
- data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
- data/vendor/faiss/faiss/impl/Panorama.h +20 -9
- data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +38 -26
- 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 +239 -72
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +66 -4
- data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +4 -13
- data/vendor/faiss/faiss/impl/ResultHandler.h +34 -34
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +287 -84
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +26 -10
- data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
- data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
- data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +90 -14
- data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
- data/vendor/faiss/faiss/impl/expanded_scanners.h +5 -1
- data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +1 -0
- data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
- data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
- data/vendor/faiss/faiss/impl/index_read.cpp +491 -50
- data/vendor/faiss/faiss/impl/index_write.cpp +86 -30
- data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
- data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
- 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/IVFPQScanner_impl.h +73 -39
- 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 +4 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +4 -4
- 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 +887 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +2 -2
- data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +90 -24
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +169 -13
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +125 -26
- data/vendor/faiss/faiss/impl/simd_dispatch.h +70 -31
- data/vendor/faiss/faiss/index_factory.cpp +40 -7
- data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +9 -6
- data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +29 -8
- 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 +47 -5
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +23 -3
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +4 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +10 -4
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +2 -1
- data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
- data/vendor/faiss/faiss/utils/distances.cpp +30 -11
- data/vendor/faiss/faiss/utils/distances_dispatch.h +30 -24
- data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +1 -1
- data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
- 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 +16 -10
- data/vendor/faiss/faiss/utils/hamming.h +10 -1
- data/vendor/faiss/faiss/utils/hamming_distance/common.h +14 -3
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +24 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +1 -1
- data/vendor/faiss/faiss/utils/hamming_distance/{hamming_computer-avx512_spr.h → hamming_computer-avx512_vpopcnt.h} +85 -24
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +141 -0
- 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_arm_sve.cpp +194 -30
- 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 +198 -18
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +330 -40
- data/vendor/faiss/faiss/utils/simd_impl/{rabitq_avx512_spr.cpp → rabitq_avx512_vpopcnt.cpp} +112 -23
- 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_impl/super_kmeans_dispatch.h +2 -7
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +6 -1
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +34 -0
- data/vendor/faiss/faiss/utils/simd_levels.cpp +196 -47
- data/vendor/faiss/faiss/utils/simd_levels.h +33 -8
- data/vendor/faiss/faiss/utils/utils.cpp +9 -27
- metadata +21 -5
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +0 -15
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
+
#include <functional>
|
|
10
11
|
#include <optional>
|
|
11
12
|
#include <queue>
|
|
13
|
+
#include <utility>
|
|
12
14
|
#include <vector>
|
|
13
15
|
|
|
14
16
|
#include <omp.h>
|
|
@@ -152,8 +154,14 @@ struct HNSW {
|
|
|
152
154
|
/// use bounded queue during exploration
|
|
153
155
|
bool search_bounded_queue = true;
|
|
154
156
|
|
|
155
|
-
///
|
|
156
|
-
|
|
157
|
+
/// Specialized level-0 search implementations. This state is derived from
|
|
158
|
+
/// the owning IndexHNSW subtype after construction or deserialization.
|
|
159
|
+
enum Search_method_t : uint8_t {
|
|
160
|
+
SM_DEFAULT,
|
|
161
|
+
SM_PANORAMA,
|
|
162
|
+
SM_RABITQ,
|
|
163
|
+
};
|
|
164
|
+
Search_method_t search_method = SM_DEFAULT;
|
|
157
165
|
|
|
158
166
|
/// distance comparison semantics: when true, distances are treated as
|
|
159
167
|
/// similarity scores (larger is better). Default false matches the
|
|
@@ -204,14 +212,27 @@ struct HNSW {
|
|
|
204
212
|
VisitedTable& vt,
|
|
205
213
|
bool keep_max_size_level0 = false);
|
|
206
214
|
|
|
207
|
-
/**
|
|
208
|
-
*
|
|
209
|
-
|
|
215
|
+
/** Deterministic build, phase A: write pt_id's forward links against an
|
|
216
|
+
* immutable snapshot, touching only pt_id's own slots. Reciprocal edges
|
|
217
|
+
* are collected in `pt_reverse_edges` for phase B, not applied. Requires
|
|
218
|
+
* entry_point set. */
|
|
219
|
+
void compute_forward_links_deterministic(
|
|
210
220
|
DistanceComputer& ptdis,
|
|
211
221
|
int pt_level,
|
|
212
|
-
|
|
213
|
-
LockVector& locks,
|
|
222
|
+
storage_idx_t pt_id,
|
|
214
223
|
VisitedTable& vt,
|
|
224
|
+
std::vector<std::pair<storage_idx_t, int>>& pt_reverse_edges,
|
|
225
|
+
bool keep_max_size_level0 = false);
|
|
226
|
+
|
|
227
|
+
/** Deterministic build, phase B: merge `incoming` into `node` and
|
|
228
|
+
* re-prune in a total order (distance, ties by id) so the result is
|
|
229
|
+
* order-independent. Touches only `node`'s slots; `incoming` is sorted
|
|
230
|
+
* and deduplicated in place. */
|
|
231
|
+
void merge_reverse_links_deterministic(
|
|
232
|
+
DistanceComputer& dis,
|
|
233
|
+
storage_idx_t node,
|
|
234
|
+
int level,
|
|
235
|
+
std::vector<storage_idx_t>& incoming,
|
|
215
236
|
bool keep_max_size_level0 = false);
|
|
216
237
|
|
|
217
238
|
/// Search interface for 1 point, single thread
|
|
@@ -257,6 +278,24 @@ struct HNSW {
|
|
|
257
278
|
void permute_entries(const idx_t* map);
|
|
258
279
|
};
|
|
259
280
|
|
|
281
|
+
/** Deterministic, lock-free HNSW graph build. This is the only graph build
|
|
282
|
+
* that add() uses, and it is shared by IndexHNSW and
|
|
283
|
+
* IndexBinaryHNSW. The callbacks let both share the algorithm:
|
|
284
|
+
* `make_distance_computer()` returns a fresh DistanceComputer per thread
|
|
285
|
+
* (caller-owned) and `set_query(dc, pt_id)` points it at pt_id's vector. */
|
|
286
|
+
void hnsw_add_vertices_deterministic(
|
|
287
|
+
HNSW& hnsw,
|
|
288
|
+
size_t n0,
|
|
289
|
+
size_t n,
|
|
290
|
+
int d,
|
|
291
|
+
bool init_level0,
|
|
292
|
+
bool keep_max_size_level0,
|
|
293
|
+
bool preset_levels,
|
|
294
|
+
bool verbose,
|
|
295
|
+
const std::function<DistanceComputer*()>& make_distance_computer,
|
|
296
|
+
const std::function<void(DistanceComputer&, HNSW::storage_idx_t)>&
|
|
297
|
+
set_query);
|
|
298
|
+
|
|
260
299
|
struct HNSWStats {
|
|
261
300
|
size_t n1 = 0; /// number of vectors searched
|
|
262
301
|
size_t n2 =
|
|
@@ -23,6 +23,50 @@ struct IDSelector {
|
|
|
23
23
|
virtual ~IDSelector() {}
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
/** Scan context handed to IDSelectorWithContext::is_member_with_context: the
|
|
27
|
+
* contiguous id block being scanned and the position of the id under test. */
|
|
28
|
+
struct IDScanContext {
|
|
29
|
+
/// the contiguous block of ids being scanned
|
|
30
|
+
const idx_t* ids;
|
|
31
|
+
/// number of entries in `ids`
|
|
32
|
+
size_t list_size;
|
|
33
|
+
/// index of the tested id within `ids` (i.e. ids[j] == id)
|
|
34
|
+
size_t j;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** IDSelector that also receives the surrounding scan context on each
|
|
38
|
+
* membership test, letting an implementation exploit locality across a scan
|
|
39
|
+
* (for example, prefetching data for an entry it will be asked about soon).
|
|
40
|
+
* The policy is entirely up to the implementation. */
|
|
41
|
+
struct IDSelectorWithContext : IDSelector {
|
|
42
|
+
virtual bool is_member_with_context(idx_t id, const IDScanContext& ctx)
|
|
43
|
+
const = 0;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Routes each per-candidate membership test to is_member_with_context() when
|
|
47
|
+
* the selector implements IDSelectorWithContext, else to plain is_member().
|
|
48
|
+
* Construct one per inverted-list scan: the dynamic_cast is the only RTTI cost
|
|
49
|
+
* and the per-candidate cost is a single predicted branch (cf. the
|
|
50
|
+
* IDSelectorRange dynamic_cast in IndexIVF.cpp). The scan context is only
|
|
51
|
+
* meaningful when the scan exposes a real id array (i.e. !store_pairs); when
|
|
52
|
+
* store_pairs the context path is disabled and every test falls back to
|
|
53
|
+
* is_member. */
|
|
54
|
+
struct IDSelectorContextDispatch {
|
|
55
|
+
const IDSelector* sel;
|
|
56
|
+
const IDSelectorWithContext* ctx_sel;
|
|
57
|
+
|
|
58
|
+
IDSelectorContextDispatch(const IDSelector* sel, bool store_pairs)
|
|
59
|
+
: sel(sel),
|
|
60
|
+
ctx_sel((sel != nullptr && !store_pairs)
|
|
61
|
+
? dynamic_cast<const IDSelectorWithContext*>(sel)
|
|
62
|
+
: nullptr) {}
|
|
63
|
+
|
|
64
|
+
bool is_member(idx_t id, const IDScanContext& ctx) const {
|
|
65
|
+
return ctx_sel ? ctx_sel->is_member_with_context(id, ctx)
|
|
66
|
+
: sel->is_member(id);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
26
70
|
/** ids between [imin, imax) */
|
|
27
71
|
struct IDSelectorRange : IDSelector {
|
|
28
72
|
idx_t imin, imax;
|
|
@@ -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
|
-
|
|
601
|
-
FAISS_THROW_IF_NOT(binaries
|
|
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>() {
|
|
@@ -264,6 +264,8 @@ void NNDescent::update() {
|
|
|
264
264
|
auto& nn_new = node.nn_new;
|
|
265
265
|
auto& nn_old = node.nn_old;
|
|
266
266
|
|
|
267
|
+
// other.pool.back() below is read without other.lock: in this
|
|
268
|
+
// phase no thread reorders or resizes a pool. Lock guards rnn_*.
|
|
267
269
|
for (int l = 0; l < node.M; ++l) {
|
|
268
270
|
auto& nn = node.pool[l];
|
|
269
271
|
auto& other = graph[nn.id]; // the other side of the edge
|
|
@@ -283,7 +285,6 @@ void NNDescent::update() {
|
|
|
283
285
|
}
|
|
284
286
|
}
|
|
285
287
|
nn.flag = false;
|
|
286
|
-
|
|
287
288
|
} else { // the node is old
|
|
288
289
|
// push the neighbor into nn_old
|
|
289
290
|
nn_old.push_back(nn.id);
|
|
@@ -300,11 +301,17 @@ void NNDescent::update() {
|
|
|
300
301
|
}
|
|
301
302
|
}
|
|
302
303
|
}
|
|
303
|
-
// make heap to join later (in join() function)
|
|
304
|
-
std::make_heap(node.pool.begin(), node.pool.end());
|
|
305
304
|
}
|
|
306
305
|
}
|
|
307
306
|
|
|
307
|
+
// Restore the max-heap invariant for the next join()'s insert(). Must
|
|
308
|
+
// stay a separate region: re-heapifying during Step 3 would race with
|
|
309
|
+
// its unlocked other.pool.back() reads.
|
|
310
|
+
#pragma omp parallel for
|
|
311
|
+
for (int n = 0; n < ntotal; ++n) {
|
|
312
|
+
std::make_heap(graph[n].pool.begin(), graph[n].pool.end());
|
|
313
|
+
}
|
|
314
|
+
|
|
308
315
|
// Step 4.
|
|
309
316
|
// Combine the forward and the reverse links
|
|
310
317
|
// R = 0 means no reverse links are used.
|
|
@@ -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
|
-
|
|
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]) *
|
|
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]) *
|
|
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]) *
|
|
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]) *
|
|
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
|
-
|
|
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++) {
|
|
@@ -233,7 +235,8 @@ inline auto with_bool(bool value, Lambda&& fn) {
|
|
|
233
235
|
* accelerating the refinement stage.
|
|
234
236
|
*/
|
|
235
237
|
struct Panorama {
|
|
236
|
-
static constexpr size_t kDefaultBatchSize =
|
|
238
|
+
static constexpr size_t kDefaultBatchSize = 1024;
|
|
239
|
+
static constexpr size_t kLegacyBatchSize = 128;
|
|
237
240
|
|
|
238
241
|
size_t d = 0;
|
|
239
242
|
size_t code_size = 0;
|
|
@@ -320,12 +323,20 @@ struct Panorama {
|
|
|
320
323
|
size_t batch_offset = batch_no * batch_size * code_size;
|
|
321
324
|
const uint8_t* storage_base = codes_base + batch_offset;
|
|
322
325
|
|
|
326
|
+
// Honor IDSelectorWithContext: the scan-order position within the whole
|
|
327
|
+
// list is global_idx, so a lookahead crosses batch boundaries correctly
|
|
328
|
+
// (ids is the full list, length list_size).
|
|
329
|
+
IDSelectorContextDispatch sel_dispatch(
|
|
330
|
+
sel, /*store_pairs=*/ids == nullptr);
|
|
331
|
+
|
|
323
332
|
// Initialize active set with ID-filtered vectors.
|
|
324
333
|
size_t num_active = 0;
|
|
325
334
|
for (size_t i = 0; i < curr_batch_size; i++) {
|
|
326
335
|
size_t global_idx = batch_start + i;
|
|
327
336
|
idx_t id = (ids == nullptr) ? global_idx : ids[global_idx];
|
|
328
|
-
bool include = !use_sel ||
|
|
337
|
+
bool include = !use_sel ||
|
|
338
|
+
sel_dispatch.is_member(
|
|
339
|
+
id, IDScanContext{ids, list_size, global_idx});
|
|
329
340
|
|
|
330
341
|
active_indices[num_active] = i;
|
|
331
342
|
float cum_sum = batch_cum_sums[i];
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
|
|
27
27
|
#include <faiss/impl/FaissAssert.h>
|
|
28
28
|
|
|
29
|
+
#include <faiss/impl/polysemous_training/dispatch.h>
|
|
30
|
+
|
|
29
31
|
/*****************************************
|
|
30
32
|
* Mixed PQ / Hamming
|
|
31
33
|
******************************************/
|
|
@@ -168,6 +170,94 @@ static inline int hamming_dis(uint64_t a, uint64_t b) {
|
|
|
168
170
|
return popcount64(a ^ b);
|
|
169
171
|
}
|
|
170
172
|
|
|
173
|
+
static inline double sqr(double x) {
|
|
174
|
+
return x * x;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Scalar (NONE) kernels for the two objectives. The dispatch boundary lives in
|
|
178
|
+
// the objective methods below, which route to these or to the AVX-512
|
|
179
|
+
// specializations (in polysemous_training/avx512.cpp) via
|
|
180
|
+
// with_selected_simd_levels.
|
|
181
|
+
namespace polysemous_training {
|
|
182
|
+
|
|
183
|
+
template <>
|
|
184
|
+
double hamming_compute_cost<SIMDLevel::NONE>(
|
|
185
|
+
int n,
|
|
186
|
+
const int* perm,
|
|
187
|
+
const double* target_dis,
|
|
188
|
+
const double* weights) {
|
|
189
|
+
double cost = 0;
|
|
190
|
+
for (int i = 0; i < n; i++) {
|
|
191
|
+
for (int j = 0; j < n; j++) {
|
|
192
|
+
double wanted = target_dis[i * n + j];
|
|
193
|
+
double w = weights[i * n + j];
|
|
194
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
195
|
+
cost += w * sqr(wanted - actual);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return cost;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
template <>
|
|
202
|
+
double hamming_cost_update<SIMDLevel::NONE>(
|
|
203
|
+
int n,
|
|
204
|
+
const int* perm,
|
|
205
|
+
int iw,
|
|
206
|
+
int jw,
|
|
207
|
+
const double* target_dis,
|
|
208
|
+
const double* weights) {
|
|
209
|
+
double delta_cost = 0;
|
|
210
|
+
|
|
211
|
+
for (int i = 0; i < n; i++) {
|
|
212
|
+
if (i == iw) {
|
|
213
|
+
for (int j = 0; j < n; j++) {
|
|
214
|
+
double wanted = target_dis[i * n + j], w = weights[i * n + j];
|
|
215
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
216
|
+
delta_cost -= w * sqr(wanted - actual);
|
|
217
|
+
double new_actual = hamming_dis(
|
|
218
|
+
perm[jw],
|
|
219
|
+
perm[j == iw ? jw
|
|
220
|
+
: j == jw ? iw
|
|
221
|
+
: j]);
|
|
222
|
+
delta_cost += w * sqr(wanted - new_actual);
|
|
223
|
+
}
|
|
224
|
+
} else if (i == jw) {
|
|
225
|
+
for (int j = 0; j < n; j++) {
|
|
226
|
+
double wanted = target_dis[i * n + j], w = weights[i * n + j];
|
|
227
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
228
|
+
delta_cost -= w * sqr(wanted - actual);
|
|
229
|
+
double new_actual = hamming_dis(
|
|
230
|
+
perm[iw],
|
|
231
|
+
perm[j == iw ? jw
|
|
232
|
+
: j == jw ? iw
|
|
233
|
+
: j]);
|
|
234
|
+
delta_cost += w * sqr(wanted - new_actual);
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
int j = iw;
|
|
238
|
+
{
|
|
239
|
+
double wanted = target_dis[i * n + j], w = weights[i * n + j];
|
|
240
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
241
|
+
delta_cost -= w * sqr(wanted - actual);
|
|
242
|
+
double new_actual = hamming_dis(perm[i], perm[jw]);
|
|
243
|
+
delta_cost += w * sqr(wanted - new_actual);
|
|
244
|
+
}
|
|
245
|
+
j = jw;
|
|
246
|
+
{
|
|
247
|
+
double wanted = target_dis[i * n + j], w = weights[i * n + j];
|
|
248
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
249
|
+
delta_cost -= w * sqr(wanted - actual);
|
|
250
|
+
double new_actual = hamming_dis(perm[i], perm[iw]);
|
|
251
|
+
delta_cost += w * sqr(wanted - new_actual);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return delta_cost;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
} // namespace polysemous_training
|
|
260
|
+
|
|
171
261
|
namespace {
|
|
172
262
|
|
|
173
263
|
/// optimize permutation to reproduce a distance table with Hamming distances
|
|
@@ -175,10 +265,6 @@ struct ReproduceWithHammingObjective : PermutationObjective {
|
|
|
175
265
|
int nbits;
|
|
176
266
|
double dis_weight_factor;
|
|
177
267
|
|
|
178
|
-
static double sqr(double x) {
|
|
179
|
-
return x * x;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
268
|
// weighting of distances: it is more important to reproduce small
|
|
183
269
|
// distances well
|
|
184
270
|
double dis_weight(double x) const {
|
|
@@ -190,73 +276,21 @@ struct ReproduceWithHammingObjective : PermutationObjective {
|
|
|
190
276
|
|
|
191
277
|
// cost = quadratic difference between actual distance and Hamming distance
|
|
192
278
|
double compute_cost(const int* perm) const override {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
199
|
-
cost += w * sqr(wanted - actual);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return cost;
|
|
279
|
+
return with_selected_simd_levels<polysemous_training::SIMD_LEVELS>(
|
|
280
|
+
[&]<SIMDLevel SL>() {
|
|
281
|
+
return polysemous_training::hamming_compute_cost<SL>(
|
|
282
|
+
n, perm, target_dis.data(), weights.data());
|
|
283
|
+
});
|
|
203
284
|
}
|
|
204
285
|
|
|
205
286
|
// what would the cost update be if iw and jw were swapped?
|
|
206
287
|
// computed in O(n) instead of O(n^2) for the full re-computation
|
|
207
288
|
double cost_update(const int* perm, int iw, int jw) const override {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
double wanted = target_dis[i * n + j],
|
|
214
|
-
w = weights[i * n + j];
|
|
215
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
216
|
-
delta_cost -= w * sqr(wanted - actual);
|
|
217
|
-
double new_actual = hamming_dis(
|
|
218
|
-
perm[jw],
|
|
219
|
-
perm[j == iw ? jw
|
|
220
|
-
: j == jw ? iw
|
|
221
|
-
: j]);
|
|
222
|
-
delta_cost += w * sqr(wanted - new_actual);
|
|
223
|
-
}
|
|
224
|
-
} else if (i == jw) {
|
|
225
|
-
for (int j = 0; j < n; j++) {
|
|
226
|
-
double wanted = target_dis[i * n + j],
|
|
227
|
-
w = weights[i * n + j];
|
|
228
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
229
|
-
delta_cost -= w * sqr(wanted - actual);
|
|
230
|
-
double new_actual = hamming_dis(
|
|
231
|
-
perm[iw],
|
|
232
|
-
perm[j == iw ? jw
|
|
233
|
-
: j == jw ? iw
|
|
234
|
-
: j]);
|
|
235
|
-
delta_cost += w * sqr(wanted - new_actual);
|
|
236
|
-
}
|
|
237
|
-
} else {
|
|
238
|
-
int j = iw;
|
|
239
|
-
{
|
|
240
|
-
double wanted = target_dis[i * n + j],
|
|
241
|
-
w = weights[i * n + j];
|
|
242
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
243
|
-
delta_cost -= w * sqr(wanted - actual);
|
|
244
|
-
double new_actual = hamming_dis(perm[i], perm[jw]);
|
|
245
|
-
delta_cost += w * sqr(wanted - new_actual);
|
|
246
|
-
}
|
|
247
|
-
j = jw;
|
|
248
|
-
{
|
|
249
|
-
double wanted = target_dis[i * n + j],
|
|
250
|
-
w = weights[i * n + j];
|
|
251
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
252
|
-
delta_cost -= w * sqr(wanted - actual);
|
|
253
|
-
double new_actual = hamming_dis(perm[i], perm[iw]);
|
|
254
|
-
delta_cost += w * sqr(wanted - new_actual);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return delta_cost;
|
|
289
|
+
return with_selected_simd_levels<polysemous_training::SIMD_LEVELS>(
|
|
290
|
+
[&]<SIMDLevel SL>() {
|
|
291
|
+
return polysemous_training::hamming_cost_update<SL>(
|
|
292
|
+
n, perm, iw, jw, target_dis.data(), weights.data());
|
|
293
|
+
});
|
|
260
294
|
}
|
|
261
295
|
|
|
262
296
|
ReproduceWithHammingObjective(
|
|
@@ -306,14 +340,20 @@ double ReproduceDistancesObjective::get_source_dis(int i, int j) const {
|
|
|
306
340
|
return source_dis[i * n + j];
|
|
307
341
|
}
|
|
308
342
|
|
|
343
|
+
namespace polysemous_training {
|
|
344
|
+
|
|
309
345
|
// cost = quadratic difference between actual distance and Hamming distance
|
|
310
|
-
|
|
346
|
+
template <>
|
|
347
|
+
double distances_compute_cost<SIMDLevel::NONE>(
|
|
348
|
+
const ReproduceDistancesObjective& obj,
|
|
349
|
+
const int* perm) {
|
|
350
|
+
const int n = obj.n;
|
|
311
351
|
double cost = 0;
|
|
312
352
|
for (int i = 0; i < n; i++) {
|
|
313
353
|
for (int j = 0; j < n; j++) {
|
|
314
|
-
double wanted = target_dis[i * n + j];
|
|
315
|
-
double w = weights[i * n + j];
|
|
316
|
-
double actual = get_source_dis(perm[i], perm[j]);
|
|
354
|
+
double wanted = obj.target_dis[i * n + j];
|
|
355
|
+
double w = obj.weights[i * n + j];
|
|
356
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
317
357
|
cost += w * sqr(wanted - actual);
|
|
318
358
|
}
|
|
319
359
|
}
|
|
@@ -322,16 +362,22 @@ double ReproduceDistancesObjective::compute_cost(const int* perm) const {
|
|
|
322
362
|
|
|
323
363
|
// what would the cost update be if iw and jw were swapped?
|
|
324
364
|
// computed in O(n) instead of O(n^2) for the full re-computation
|
|
325
|
-
|
|
326
|
-
|
|
365
|
+
template <>
|
|
366
|
+
double distances_cost_update<SIMDLevel::NONE>(
|
|
367
|
+
const ReproduceDistancesObjective& obj,
|
|
368
|
+
const int* perm,
|
|
369
|
+
int iw,
|
|
370
|
+
int jw) {
|
|
371
|
+
const int n = obj.n;
|
|
327
372
|
double delta_cost = 0;
|
|
328
373
|
for (int i = 0; i < n; i++) {
|
|
329
374
|
if (i == iw) {
|
|
330
375
|
for (int j = 0; j < n; j++) {
|
|
331
|
-
double wanted = target_dis[i * n + j],
|
|
332
|
-
|
|
376
|
+
double wanted = obj.target_dis[i * n + j],
|
|
377
|
+
w = obj.weights[i * n + j];
|
|
378
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
333
379
|
delta_cost -= w * sqr(wanted - actual);
|
|
334
|
-
double new_actual = get_source_dis(
|
|
380
|
+
double new_actual = obj.get_source_dis(
|
|
335
381
|
perm[jw],
|
|
336
382
|
perm[j == iw ? jw
|
|
337
383
|
: j == jw ? iw
|
|
@@ -340,10 +386,11 @@ double ReproduceDistancesObjective::cost_update(const int* perm, int iw, int jw)
|
|
|
340
386
|
}
|
|
341
387
|
} else if (i == jw) {
|
|
342
388
|
for (int j = 0; j < n; j++) {
|
|
343
|
-
double wanted = target_dis[i * n + j],
|
|
344
|
-
|
|
389
|
+
double wanted = obj.target_dis[i * n + j],
|
|
390
|
+
w = obj.weights[i * n + j];
|
|
391
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
345
392
|
delta_cost -= w * sqr(wanted - actual);
|
|
346
|
-
double new_actual = get_source_dis(
|
|
393
|
+
double new_actual = obj.get_source_dis(
|
|
347
394
|
perm[iw],
|
|
348
395
|
perm[j == iw ? jw
|
|
349
396
|
: j == jw ? iw
|
|
@@ -353,18 +400,20 @@ double ReproduceDistancesObjective::cost_update(const int* perm, int iw, int jw)
|
|
|
353
400
|
} else {
|
|
354
401
|
int j = iw;
|
|
355
402
|
{
|
|
356
|
-
double wanted = target_dis[i * n + j],
|
|
357
|
-
|
|
403
|
+
double wanted = obj.target_dis[i * n + j],
|
|
404
|
+
w = obj.weights[i * n + j];
|
|
405
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
358
406
|
delta_cost -= w * sqr(wanted - actual);
|
|
359
|
-
double new_actual = get_source_dis(perm[i], perm[jw]);
|
|
407
|
+
double new_actual = obj.get_source_dis(perm[i], perm[jw]);
|
|
360
408
|
delta_cost += w * sqr(wanted - new_actual);
|
|
361
409
|
}
|
|
362
410
|
j = jw;
|
|
363
411
|
{
|
|
364
|
-
double wanted = target_dis[i * n + j],
|
|
365
|
-
|
|
412
|
+
double wanted = obj.target_dis[i * n + j],
|
|
413
|
+
w = obj.weights[i * n + j];
|
|
414
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
366
415
|
delta_cost -= w * sqr(wanted - actual);
|
|
367
|
-
double new_actual = get_source_dis(perm[i], perm[iw]);
|
|
416
|
+
double new_actual = obj.get_source_dis(perm[i], perm[iw]);
|
|
368
417
|
delta_cost += w * sqr(wanted - new_actual);
|
|
369
418
|
}
|
|
370
419
|
}
|
|
@@ -372,6 +421,25 @@ double ReproduceDistancesObjective::cost_update(const int* perm, int iw, int jw)
|
|
|
372
421
|
return delta_cost;
|
|
373
422
|
}
|
|
374
423
|
|
|
424
|
+
} // namespace polysemous_training
|
|
425
|
+
|
|
426
|
+
double ReproduceDistancesObjective::compute_cost(const int* perm) const {
|
|
427
|
+
return with_selected_simd_levels<polysemous_training::SIMD_LEVELS>(
|
|
428
|
+
[&]<SIMDLevel SL>() {
|
|
429
|
+
return polysemous_training::distances_compute_cost<SL>(
|
|
430
|
+
*this, perm);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
double ReproduceDistancesObjective::cost_update(const int* perm, int iw, int jw)
|
|
435
|
+
const {
|
|
436
|
+
return with_selected_simd_levels<polysemous_training::SIMD_LEVELS>(
|
|
437
|
+
[&]<SIMDLevel SL>() {
|
|
438
|
+
return polysemous_training::distances_cost_update<SL>(
|
|
439
|
+
*this, perm, iw, jw);
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
375
443
|
ReproduceDistancesObjective::ReproduceDistancesObjective(
|
|
376
444
|
int n_in,
|
|
377
445
|
const double* source_dis_in,
|