faiss 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +103 -3
- data/ext/faiss/ext.cpp +99 -32
- data/ext/faiss/extconf.rb +12 -2
- data/lib/faiss/ext.bundle +0 -0
- data/lib/faiss/index.rb +3 -3
- data/lib/faiss/index_binary.rb +3 -3
- data/lib/faiss/kmeans.rb +1 -1
- data/lib/faiss/pca_matrix.rb +2 -2
- data/lib/faiss/product_quantizer.rb +3 -3
- data/lib/faiss/version.rb +1 -1
- data/vendor/faiss/AutoTune.cpp +719 -0
- data/vendor/faiss/AutoTune.h +212 -0
- data/vendor/faiss/Clustering.cpp +261 -0
- data/vendor/faiss/Clustering.h +101 -0
- data/vendor/faiss/IVFlib.cpp +339 -0
- data/vendor/faiss/IVFlib.h +132 -0
- data/vendor/faiss/Index.cpp +171 -0
- data/vendor/faiss/Index.h +261 -0
- data/vendor/faiss/Index2Layer.cpp +437 -0
- data/vendor/faiss/Index2Layer.h +85 -0
- data/vendor/faiss/IndexBinary.cpp +77 -0
- data/vendor/faiss/IndexBinary.h +163 -0
- data/vendor/faiss/IndexBinaryFlat.cpp +83 -0
- data/vendor/faiss/IndexBinaryFlat.h +54 -0
- data/vendor/faiss/IndexBinaryFromFloat.cpp +78 -0
- data/vendor/faiss/IndexBinaryFromFloat.h +52 -0
- data/vendor/faiss/IndexBinaryHNSW.cpp +325 -0
- data/vendor/faiss/IndexBinaryHNSW.h +56 -0
- data/vendor/faiss/IndexBinaryIVF.cpp +671 -0
- data/vendor/faiss/IndexBinaryIVF.h +211 -0
- data/vendor/faiss/IndexFlat.cpp +508 -0
- data/vendor/faiss/IndexFlat.h +175 -0
- data/vendor/faiss/IndexHNSW.cpp +1090 -0
- data/vendor/faiss/IndexHNSW.h +170 -0
- data/vendor/faiss/IndexIVF.cpp +909 -0
- data/vendor/faiss/IndexIVF.h +353 -0
- data/vendor/faiss/IndexIVFFlat.cpp +502 -0
- data/vendor/faiss/IndexIVFFlat.h +118 -0
- data/vendor/faiss/IndexIVFPQ.cpp +1207 -0
- data/vendor/faiss/IndexIVFPQ.h +161 -0
- data/vendor/faiss/IndexIVFPQR.cpp +219 -0
- data/vendor/faiss/IndexIVFPQR.h +65 -0
- data/vendor/faiss/IndexIVFSpectralHash.cpp +331 -0
- data/vendor/faiss/IndexIVFSpectralHash.h +75 -0
- data/vendor/faiss/IndexLSH.cpp +225 -0
- data/vendor/faiss/IndexLSH.h +87 -0
- data/vendor/faiss/IndexLattice.cpp +143 -0
- data/vendor/faiss/IndexLattice.h +68 -0
- data/vendor/faiss/IndexPQ.cpp +1188 -0
- data/vendor/faiss/IndexPQ.h +199 -0
- data/vendor/faiss/IndexPreTransform.cpp +288 -0
- data/vendor/faiss/IndexPreTransform.h +91 -0
- data/vendor/faiss/IndexReplicas.cpp +123 -0
- data/vendor/faiss/IndexReplicas.h +76 -0
- data/vendor/faiss/IndexScalarQuantizer.cpp +317 -0
- data/vendor/faiss/IndexScalarQuantizer.h +127 -0
- data/vendor/faiss/IndexShards.cpp +317 -0
- data/vendor/faiss/IndexShards.h +100 -0
- data/vendor/faiss/InvertedLists.cpp +623 -0
- data/vendor/faiss/InvertedLists.h +334 -0
- data/vendor/faiss/LICENSE +21 -0
- data/vendor/faiss/MatrixStats.cpp +252 -0
- data/vendor/faiss/MatrixStats.h +62 -0
- data/vendor/faiss/MetaIndexes.cpp +351 -0
- data/vendor/faiss/MetaIndexes.h +126 -0
- data/vendor/faiss/OnDiskInvertedLists.cpp +674 -0
- data/vendor/faiss/OnDiskInvertedLists.h +127 -0
- data/vendor/faiss/VectorTransform.cpp +1157 -0
- data/vendor/faiss/VectorTransform.h +322 -0
- data/vendor/faiss/c_api/AutoTune_c.cpp +83 -0
- data/vendor/faiss/c_api/AutoTune_c.h +64 -0
- data/vendor/faiss/c_api/Clustering_c.cpp +139 -0
- data/vendor/faiss/c_api/Clustering_c.h +117 -0
- data/vendor/faiss/c_api/IndexFlat_c.cpp +140 -0
- data/vendor/faiss/c_api/IndexFlat_c.h +115 -0
- data/vendor/faiss/c_api/IndexIVFFlat_c.cpp +64 -0
- data/vendor/faiss/c_api/IndexIVFFlat_c.h +58 -0
- data/vendor/faiss/c_api/IndexIVF_c.cpp +92 -0
- data/vendor/faiss/c_api/IndexIVF_c.h +135 -0
- data/vendor/faiss/c_api/IndexLSH_c.cpp +37 -0
- data/vendor/faiss/c_api/IndexLSH_c.h +40 -0
- data/vendor/faiss/c_api/IndexShards_c.cpp +44 -0
- data/vendor/faiss/c_api/IndexShards_c.h +42 -0
- data/vendor/faiss/c_api/Index_c.cpp +105 -0
- data/vendor/faiss/c_api/Index_c.h +183 -0
- data/vendor/faiss/c_api/MetaIndexes_c.cpp +49 -0
- data/vendor/faiss/c_api/MetaIndexes_c.h +49 -0
- data/vendor/faiss/c_api/clone_index_c.cpp +23 -0
- data/vendor/faiss/c_api/clone_index_c.h +32 -0
- data/vendor/faiss/c_api/error_c.h +42 -0
- data/vendor/faiss/c_api/error_impl.cpp +27 -0
- data/vendor/faiss/c_api/error_impl.h +16 -0
- data/vendor/faiss/c_api/faiss_c.h +58 -0
- data/vendor/faiss/c_api/gpu/GpuAutoTune_c.cpp +96 -0
- data/vendor/faiss/c_api/gpu/GpuAutoTune_c.h +56 -0
- data/vendor/faiss/c_api/gpu/GpuClonerOptions_c.cpp +52 -0
- data/vendor/faiss/c_api/gpu/GpuClonerOptions_c.h +68 -0
- data/vendor/faiss/c_api/gpu/GpuIndex_c.cpp +17 -0
- data/vendor/faiss/c_api/gpu/GpuIndex_c.h +30 -0
- data/vendor/faiss/c_api/gpu/GpuIndicesOptions_c.h +38 -0
- data/vendor/faiss/c_api/gpu/GpuResources_c.cpp +86 -0
- data/vendor/faiss/c_api/gpu/GpuResources_c.h +66 -0
- data/vendor/faiss/c_api/gpu/StandardGpuResources_c.cpp +54 -0
- data/vendor/faiss/c_api/gpu/StandardGpuResources_c.h +53 -0
- data/vendor/faiss/c_api/gpu/macros_impl.h +42 -0
- data/vendor/faiss/c_api/impl/AuxIndexStructures_c.cpp +220 -0
- data/vendor/faiss/c_api/impl/AuxIndexStructures_c.h +149 -0
- data/vendor/faiss/c_api/index_factory_c.cpp +26 -0
- data/vendor/faiss/c_api/index_factory_c.h +30 -0
- data/vendor/faiss/c_api/index_io_c.cpp +42 -0
- data/vendor/faiss/c_api/index_io_c.h +50 -0
- data/vendor/faiss/c_api/macros_impl.h +110 -0
- data/vendor/faiss/clone_index.cpp +147 -0
- data/vendor/faiss/clone_index.h +38 -0
- data/vendor/faiss/demos/demo_imi_flat.cpp +151 -0
- data/vendor/faiss/demos/demo_imi_pq.cpp +199 -0
- data/vendor/faiss/demos/demo_ivfpq_indexing.cpp +146 -0
- data/vendor/faiss/demos/demo_sift1M.cpp +252 -0
- data/vendor/faiss/gpu/GpuAutoTune.cpp +95 -0
- data/vendor/faiss/gpu/GpuAutoTune.h +27 -0
- data/vendor/faiss/gpu/GpuCloner.cpp +403 -0
- data/vendor/faiss/gpu/GpuCloner.h +82 -0
- data/vendor/faiss/gpu/GpuClonerOptions.cpp +28 -0
- data/vendor/faiss/gpu/GpuClonerOptions.h +53 -0
- data/vendor/faiss/gpu/GpuDistance.h +52 -0
- data/vendor/faiss/gpu/GpuFaissAssert.h +29 -0
- data/vendor/faiss/gpu/GpuIndex.h +148 -0
- data/vendor/faiss/gpu/GpuIndexBinaryFlat.h +89 -0
- data/vendor/faiss/gpu/GpuIndexFlat.h +190 -0
- data/vendor/faiss/gpu/GpuIndexIVF.h +89 -0
- data/vendor/faiss/gpu/GpuIndexIVFFlat.h +85 -0
- data/vendor/faiss/gpu/GpuIndexIVFPQ.h +143 -0
- data/vendor/faiss/gpu/GpuIndexIVFScalarQuantizer.h +100 -0
- data/vendor/faiss/gpu/GpuIndicesOptions.h +30 -0
- data/vendor/faiss/gpu/GpuResources.cpp +52 -0
- data/vendor/faiss/gpu/GpuResources.h +73 -0
- data/vendor/faiss/gpu/StandardGpuResources.cpp +295 -0
- data/vendor/faiss/gpu/StandardGpuResources.h +114 -0
- data/vendor/faiss/gpu/impl/RemapIndices.cpp +43 -0
- data/vendor/faiss/gpu/impl/RemapIndices.h +24 -0
- data/vendor/faiss/gpu/perf/IndexWrapper-inl.h +71 -0
- data/vendor/faiss/gpu/perf/IndexWrapper.h +39 -0
- data/vendor/faiss/gpu/perf/PerfClustering.cpp +115 -0
- data/vendor/faiss/gpu/perf/PerfIVFPQAdd.cpp +139 -0
- data/vendor/faiss/gpu/perf/WriteIndex.cpp +102 -0
- data/vendor/faiss/gpu/test/TestGpuIndexBinaryFlat.cpp +130 -0
- data/vendor/faiss/gpu/test/TestGpuIndexFlat.cpp +371 -0
- data/vendor/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +550 -0
- data/vendor/faiss/gpu/test/TestGpuIndexIVFPQ.cpp +450 -0
- data/vendor/faiss/gpu/test/TestGpuMemoryException.cpp +84 -0
- data/vendor/faiss/gpu/test/TestUtils.cpp +315 -0
- data/vendor/faiss/gpu/test/TestUtils.h +93 -0
- data/vendor/faiss/gpu/test/demo_ivfpq_indexing_gpu.cpp +159 -0
- data/vendor/faiss/gpu/utils/DeviceMemory.cpp +77 -0
- data/vendor/faiss/gpu/utils/DeviceMemory.h +71 -0
- data/vendor/faiss/gpu/utils/DeviceUtils.h +185 -0
- data/vendor/faiss/gpu/utils/MemorySpace.cpp +89 -0
- data/vendor/faiss/gpu/utils/MemorySpace.h +44 -0
- data/vendor/faiss/gpu/utils/StackDeviceMemory.cpp +239 -0
- data/vendor/faiss/gpu/utils/StackDeviceMemory.h +129 -0
- data/vendor/faiss/gpu/utils/StaticUtils.h +83 -0
- data/vendor/faiss/gpu/utils/Timer.cpp +60 -0
- data/vendor/faiss/gpu/utils/Timer.h +52 -0
- data/vendor/faiss/impl/AuxIndexStructures.cpp +305 -0
- data/vendor/faiss/impl/AuxIndexStructures.h +246 -0
- data/vendor/faiss/impl/FaissAssert.h +95 -0
- data/vendor/faiss/impl/FaissException.cpp +66 -0
- data/vendor/faiss/impl/FaissException.h +71 -0
- data/vendor/faiss/impl/HNSW.cpp +818 -0
- data/vendor/faiss/impl/HNSW.h +275 -0
- data/vendor/faiss/impl/PolysemousTraining.cpp +953 -0
- data/vendor/faiss/impl/PolysemousTraining.h +158 -0
- data/vendor/faiss/impl/ProductQuantizer.cpp +876 -0
- data/vendor/faiss/impl/ProductQuantizer.h +242 -0
- data/vendor/faiss/impl/ScalarQuantizer.cpp +1628 -0
- data/vendor/faiss/impl/ScalarQuantizer.h +120 -0
- data/vendor/faiss/impl/ThreadedIndex-inl.h +192 -0
- data/vendor/faiss/impl/ThreadedIndex.h +80 -0
- data/vendor/faiss/impl/index_read.cpp +793 -0
- data/vendor/faiss/impl/index_write.cpp +558 -0
- data/vendor/faiss/impl/io.cpp +142 -0
- data/vendor/faiss/impl/io.h +98 -0
- data/vendor/faiss/impl/lattice_Zn.cpp +712 -0
- data/vendor/faiss/impl/lattice_Zn.h +199 -0
- data/vendor/faiss/index_factory.cpp +392 -0
- data/vendor/faiss/index_factory.h +25 -0
- data/vendor/faiss/index_io.h +75 -0
- data/vendor/faiss/misc/test_blas.cpp +84 -0
- data/vendor/faiss/tests/test_binary_flat.cpp +64 -0
- data/vendor/faiss/tests/test_dealloc_invlists.cpp +183 -0
- data/vendor/faiss/tests/test_ivfpq_codec.cpp +67 -0
- data/vendor/faiss/tests/test_ivfpq_indexing.cpp +98 -0
- data/vendor/faiss/tests/test_lowlevel_ivf.cpp +566 -0
- data/vendor/faiss/tests/test_merge.cpp +258 -0
- data/vendor/faiss/tests/test_omp_threads.cpp +14 -0
- data/vendor/faiss/tests/test_ondisk_ivf.cpp +220 -0
- data/vendor/faiss/tests/test_pairs_decoding.cpp +189 -0
- data/vendor/faiss/tests/test_params_override.cpp +231 -0
- data/vendor/faiss/tests/test_pq_encoding.cpp +98 -0
- data/vendor/faiss/tests/test_sliding_ivf.cpp +240 -0
- data/vendor/faiss/tests/test_threaded_index.cpp +253 -0
- data/vendor/faiss/tests/test_transfer_invlists.cpp +159 -0
- data/vendor/faiss/tutorial/cpp/1-Flat.cpp +98 -0
- data/vendor/faiss/tutorial/cpp/2-IVFFlat.cpp +81 -0
- data/vendor/faiss/tutorial/cpp/3-IVFPQ.cpp +93 -0
- data/vendor/faiss/tutorial/cpp/4-GPU.cpp +119 -0
- data/vendor/faiss/tutorial/cpp/5-Multiple-GPUs.cpp +99 -0
- data/vendor/faiss/utils/Heap.cpp +122 -0
- data/vendor/faiss/utils/Heap.h +495 -0
- data/vendor/faiss/utils/WorkerThread.cpp +126 -0
- data/vendor/faiss/utils/WorkerThread.h +61 -0
- data/vendor/faiss/utils/distances.cpp +765 -0
- data/vendor/faiss/utils/distances.h +243 -0
- data/vendor/faiss/utils/distances_simd.cpp +809 -0
- data/vendor/faiss/utils/extra_distances.cpp +336 -0
- data/vendor/faiss/utils/extra_distances.h +54 -0
- data/vendor/faiss/utils/hamming-inl.h +472 -0
- data/vendor/faiss/utils/hamming.cpp +792 -0
- data/vendor/faiss/utils/hamming.h +220 -0
- data/vendor/faiss/utils/random.cpp +192 -0
- data/vendor/faiss/utils/random.h +60 -0
- data/vendor/faiss/utils/utils.cpp +783 -0
- data/vendor/faiss/utils/utils.h +181 -0
- metadata +216 -2
@@ -0,0 +1,118 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) Facebook, Inc. and its 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
|
+
#ifndef FAISS_INDEX_IVF_FLAT_H
|
11
|
+
#define FAISS_INDEX_IVF_FLAT_H
|
12
|
+
|
13
|
+
#include <unordered_map>
|
14
|
+
#include <stdint.h>
|
15
|
+
|
16
|
+
#include <faiss/IndexIVF.h>
|
17
|
+
|
18
|
+
|
19
|
+
namespace faiss {
|
20
|
+
|
21
|
+
/** Inverted file with stored vectors. Here the inverted file
|
22
|
+
* pre-selects the vectors to be searched, but they are not otherwise
|
23
|
+
* encoded, the code array just contains the raw float entries.
|
24
|
+
*/
|
25
|
+
struct IndexIVFFlat: IndexIVF {
|
26
|
+
|
27
|
+
IndexIVFFlat (
|
28
|
+
Index * quantizer, size_t d, size_t nlist_,
|
29
|
+
MetricType = METRIC_L2);
|
30
|
+
|
31
|
+
/// same as add_with_ids, with precomputed coarse quantizer
|
32
|
+
virtual void add_core (idx_t n, const float * x, const int64_t *xids,
|
33
|
+
const int64_t *precomputed_idx);
|
34
|
+
|
35
|
+
/// implemented for all IndexIVF* classes
|
36
|
+
void add_with_ids(idx_t n, const float* x, const idx_t* xids) override;
|
37
|
+
|
38
|
+
void encode_vectors(idx_t n, const float* x,
|
39
|
+
const idx_t *list_nos,
|
40
|
+
uint8_t * codes,
|
41
|
+
bool include_listnos=false) const override;
|
42
|
+
|
43
|
+
|
44
|
+
InvertedListScanner *get_InvertedListScanner (bool store_pairs)
|
45
|
+
const override;
|
46
|
+
|
47
|
+
/** Update a subset of vectors.
|
48
|
+
*
|
49
|
+
* The index must have a direct_map
|
50
|
+
*
|
51
|
+
* @param nv nb of vectors to update
|
52
|
+
* @param idx vector indices to update, size nv
|
53
|
+
* @param v vectors of new values, size nv*d
|
54
|
+
*/
|
55
|
+
virtual void update_vectors (int nv, idx_t *idx, const float *v);
|
56
|
+
|
57
|
+
void reconstruct_from_offset (int64_t list_no, int64_t offset,
|
58
|
+
float* recons) const override;
|
59
|
+
|
60
|
+
void sa_decode (idx_t n, const uint8_t *bytes,
|
61
|
+
float *x) const override;
|
62
|
+
|
63
|
+
IndexIVFFlat () {}
|
64
|
+
};
|
65
|
+
|
66
|
+
|
67
|
+
struct IndexIVFFlatDedup: IndexIVFFlat {
|
68
|
+
|
69
|
+
/** Maps ids stored in the index to the ids of vectors that are
|
70
|
+
* the same. When a vector is unique, it does not appear in the
|
71
|
+
* instances map */
|
72
|
+
std::unordered_multimap <idx_t, idx_t> instances;
|
73
|
+
|
74
|
+
IndexIVFFlatDedup (
|
75
|
+
Index * quantizer, size_t d, size_t nlist_,
|
76
|
+
MetricType = METRIC_L2);
|
77
|
+
|
78
|
+
/// also dedups the training set
|
79
|
+
void train(idx_t n, const float* x) override;
|
80
|
+
|
81
|
+
/// implemented for all IndexIVF* classes
|
82
|
+
void add_with_ids(idx_t n, const float* x, const idx_t* xids) override;
|
83
|
+
|
84
|
+
void search_preassigned (idx_t n, const float *x, idx_t k,
|
85
|
+
const idx_t *assign,
|
86
|
+
const float *centroid_dis,
|
87
|
+
float *distances, idx_t *labels,
|
88
|
+
bool store_pairs,
|
89
|
+
const IVFSearchParameters *params=nullptr
|
90
|
+
) const override;
|
91
|
+
|
92
|
+
size_t remove_ids(const IDSelector& sel) override;
|
93
|
+
|
94
|
+
/// not implemented
|
95
|
+
void range_search(
|
96
|
+
idx_t n,
|
97
|
+
const float* x,
|
98
|
+
float radius,
|
99
|
+
RangeSearchResult* result) const override;
|
100
|
+
|
101
|
+
/// not implemented
|
102
|
+
void update_vectors (int nv, idx_t *idx, const float *v) override;
|
103
|
+
|
104
|
+
|
105
|
+
/// not implemented
|
106
|
+
void reconstruct_from_offset (int64_t list_no, int64_t offset,
|
107
|
+
float* recons) const override;
|
108
|
+
|
109
|
+
IndexIVFFlatDedup () {}
|
110
|
+
|
111
|
+
|
112
|
+
};
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
} // namespace faiss
|
117
|
+
|
118
|
+
#endif
|
@@ -0,0 +1,1207 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) Facebook, Inc. and its 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/IndexIVFPQ.h>
|
11
|
+
|
12
|
+
#include <cmath>
|
13
|
+
#include <cstdio>
|
14
|
+
#include <cassert>
|
15
|
+
#include <stdint.h>
|
16
|
+
|
17
|
+
#include <algorithm>
|
18
|
+
|
19
|
+
#include <faiss/utils/Heap.h>
|
20
|
+
#include <faiss/utils/utils.h>
|
21
|
+
#include <faiss/utils/distances.h>
|
22
|
+
|
23
|
+
#include <faiss/Clustering.h>
|
24
|
+
#include <faiss/IndexFlat.h>
|
25
|
+
|
26
|
+
#include <faiss/utils/hamming.h>
|
27
|
+
|
28
|
+
#include <faiss/impl/FaissAssert.h>
|
29
|
+
|
30
|
+
#include <faiss/impl/AuxIndexStructures.h>
|
31
|
+
|
32
|
+
namespace faiss {
|
33
|
+
|
34
|
+
/*****************************************
|
35
|
+
* IndexIVFPQ implementation
|
36
|
+
******************************************/
|
37
|
+
|
38
|
+
IndexIVFPQ::IndexIVFPQ (Index * quantizer, size_t d, size_t nlist,
|
39
|
+
size_t M, size_t nbits_per_idx):
|
40
|
+
IndexIVF (quantizer, d, nlist, 0, METRIC_L2),
|
41
|
+
pq (d, M, nbits_per_idx)
|
42
|
+
{
|
43
|
+
FAISS_THROW_IF_NOT (nbits_per_idx <= 8);
|
44
|
+
code_size = pq.code_size;
|
45
|
+
invlists->code_size = code_size;
|
46
|
+
is_trained = false;
|
47
|
+
by_residual = true;
|
48
|
+
use_precomputed_table = 0;
|
49
|
+
scan_table_threshold = 0;
|
50
|
+
|
51
|
+
polysemous_training = nullptr;
|
52
|
+
do_polysemous_training = false;
|
53
|
+
polysemous_ht = 0;
|
54
|
+
|
55
|
+
}
|
56
|
+
|
57
|
+
|
58
|
+
/****************************************************************
|
59
|
+
* training */
|
60
|
+
|
61
|
+
void IndexIVFPQ::train_residual (idx_t n, const float *x)
|
62
|
+
{
|
63
|
+
train_residual_o (n, x, nullptr);
|
64
|
+
}
|
65
|
+
|
66
|
+
|
67
|
+
void IndexIVFPQ::train_residual_o (idx_t n, const float *x, float *residuals_2)
|
68
|
+
{
|
69
|
+
const float * x_in = x;
|
70
|
+
|
71
|
+
x = fvecs_maybe_subsample (
|
72
|
+
d, (size_t*)&n, pq.cp.max_points_per_centroid * pq.ksub,
|
73
|
+
x, verbose, pq.cp.seed);
|
74
|
+
|
75
|
+
ScopeDeleter<float> del_x (x_in == x ? nullptr : x);
|
76
|
+
|
77
|
+
const float *trainset;
|
78
|
+
ScopeDeleter<float> del_residuals;
|
79
|
+
if (by_residual) {
|
80
|
+
if(verbose) printf("computing residuals\n");
|
81
|
+
idx_t * assign = new idx_t [n]; // assignement to coarse centroids
|
82
|
+
ScopeDeleter<idx_t> del (assign);
|
83
|
+
quantizer->assign (n, x, assign);
|
84
|
+
float *residuals = new float [n * d];
|
85
|
+
del_residuals.set (residuals);
|
86
|
+
for (idx_t i = 0; i < n; i++)
|
87
|
+
quantizer->compute_residual (x + i * d, residuals+i*d, assign[i]);
|
88
|
+
|
89
|
+
trainset = residuals;
|
90
|
+
} else {
|
91
|
+
trainset = x;
|
92
|
+
}
|
93
|
+
if (verbose)
|
94
|
+
printf ("training %zdx%zd product quantizer on %ld vectors in %dD\n",
|
95
|
+
pq.M, pq.ksub, n, d);
|
96
|
+
pq.verbose = verbose;
|
97
|
+
pq.train (n, trainset);
|
98
|
+
|
99
|
+
if (do_polysemous_training) {
|
100
|
+
if (verbose)
|
101
|
+
printf("doing polysemous training for PQ\n");
|
102
|
+
PolysemousTraining default_pt;
|
103
|
+
PolysemousTraining *pt = polysemous_training;
|
104
|
+
if (!pt) pt = &default_pt;
|
105
|
+
pt->optimize_pq_for_hamming (pq, n, trainset);
|
106
|
+
}
|
107
|
+
|
108
|
+
// prepare second-level residuals for refine PQ
|
109
|
+
if (residuals_2) {
|
110
|
+
uint8_t *train_codes = new uint8_t [pq.code_size * n];
|
111
|
+
ScopeDeleter<uint8_t> del (train_codes);
|
112
|
+
pq.compute_codes (trainset, train_codes, n);
|
113
|
+
|
114
|
+
for (idx_t i = 0; i < n; i++) {
|
115
|
+
const float *xx = trainset + i * d;
|
116
|
+
float * res = residuals_2 + i * d;
|
117
|
+
pq.decode (train_codes + i * pq.code_size, res);
|
118
|
+
for (int j = 0; j < d; j++)
|
119
|
+
res[j] = xx[j] - res[j];
|
120
|
+
}
|
121
|
+
|
122
|
+
}
|
123
|
+
|
124
|
+
if (by_residual) {
|
125
|
+
precompute_table ();
|
126
|
+
}
|
127
|
+
|
128
|
+
}
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
/****************************************************************
|
136
|
+
* IVFPQ as codec */
|
137
|
+
|
138
|
+
|
139
|
+
/* produce a binary signature based on the residual vector */
|
140
|
+
void IndexIVFPQ::encode (idx_t key, const float * x, uint8_t * code) const
|
141
|
+
{
|
142
|
+
if (by_residual) {
|
143
|
+
float residual_vec[d];
|
144
|
+
quantizer->compute_residual (x, residual_vec, key);
|
145
|
+
pq.compute_code (residual_vec, code);
|
146
|
+
}
|
147
|
+
else pq.compute_code (x, code);
|
148
|
+
}
|
149
|
+
|
150
|
+
void IndexIVFPQ::encode_multiple (size_t n, idx_t *keys,
|
151
|
+
const float * x, uint8_t * xcodes,
|
152
|
+
bool compute_keys) const
|
153
|
+
{
|
154
|
+
if (compute_keys)
|
155
|
+
quantizer->assign (n, x, keys);
|
156
|
+
|
157
|
+
encode_vectors (n, x, keys, xcodes);
|
158
|
+
}
|
159
|
+
|
160
|
+
void IndexIVFPQ::decode_multiple (size_t n, const idx_t *keys,
|
161
|
+
const uint8_t * xcodes, float * x) const
|
162
|
+
{
|
163
|
+
pq.decode (xcodes, x, n);
|
164
|
+
if (by_residual) {
|
165
|
+
std::vector<float> centroid (d);
|
166
|
+
for (size_t i = 0; i < n; i++) {
|
167
|
+
quantizer->reconstruct (keys[i], centroid.data());
|
168
|
+
float *xi = x + i * d;
|
169
|
+
for (size_t j = 0; j < d; j++) {
|
170
|
+
xi [j] += centroid [j];
|
171
|
+
}
|
172
|
+
}
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
|
177
|
+
|
178
|
+
|
179
|
+
/****************************************************************
|
180
|
+
* add */
|
181
|
+
|
182
|
+
|
183
|
+
void IndexIVFPQ::add_with_ids (idx_t n, const float * x, const idx_t *xids)
|
184
|
+
{
|
185
|
+
add_core_o (n, x, xids, nullptr);
|
186
|
+
}
|
187
|
+
|
188
|
+
|
189
|
+
static float * compute_residuals (
|
190
|
+
const Index *quantizer,
|
191
|
+
Index::idx_t n, const float* x,
|
192
|
+
const Index::idx_t *list_nos)
|
193
|
+
{
|
194
|
+
size_t d = quantizer->d;
|
195
|
+
float *residuals = new float [n * d];
|
196
|
+
// TODO: parallelize?
|
197
|
+
for (size_t i = 0; i < n; i++) {
|
198
|
+
if (list_nos[i] < 0)
|
199
|
+
memset (residuals + i * d, 0, sizeof(*residuals) * d);
|
200
|
+
else
|
201
|
+
quantizer->compute_residual (
|
202
|
+
x + i * d, residuals + i * d, list_nos[i]);
|
203
|
+
}
|
204
|
+
return residuals;
|
205
|
+
}
|
206
|
+
|
207
|
+
void IndexIVFPQ::encode_vectors(idx_t n, const float* x,
|
208
|
+
const idx_t *list_nos,
|
209
|
+
uint8_t * codes,
|
210
|
+
bool include_listnos) const
|
211
|
+
{
|
212
|
+
if (by_residual) {
|
213
|
+
float *to_encode = compute_residuals (quantizer, n, x, list_nos);
|
214
|
+
ScopeDeleter<float> del (to_encode);
|
215
|
+
pq.compute_codes (to_encode, codes, n);
|
216
|
+
} else {
|
217
|
+
pq.compute_codes (x, codes, n);
|
218
|
+
}
|
219
|
+
|
220
|
+
if (include_listnos) {
|
221
|
+
size_t coarse_size = coarse_code_size();
|
222
|
+
for (idx_t i = n - 1; i >= 0; i--) {
|
223
|
+
uint8_t * code = codes + i * (coarse_size + code_size);
|
224
|
+
memmove (code + coarse_size,
|
225
|
+
codes + i * code_size, code_size);
|
226
|
+
encode_listno (list_nos[i], code);
|
227
|
+
}
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
231
|
+
|
232
|
+
|
233
|
+
void IndexIVFPQ::sa_decode (idx_t n, const uint8_t *codes,
|
234
|
+
float *x) const
|
235
|
+
{
|
236
|
+
size_t coarse_size = coarse_code_size ();
|
237
|
+
|
238
|
+
#pragma omp parallel
|
239
|
+
{
|
240
|
+
std::vector<float> residual (d);
|
241
|
+
|
242
|
+
#pragma omp for
|
243
|
+
for (size_t i = 0; i < n; i++) {
|
244
|
+
const uint8_t *code = codes + i * (code_size + coarse_size);
|
245
|
+
int64_t list_no = decode_listno (code);
|
246
|
+
float *xi = x + i * d;
|
247
|
+
pq.decode (code + coarse_size, xi);
|
248
|
+
if (by_residual) {
|
249
|
+
quantizer->reconstruct (list_no, residual.data());
|
250
|
+
for (size_t j = 0; j < d; j++) {
|
251
|
+
xi[j] += residual[j];
|
252
|
+
}
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
|
259
|
+
void IndexIVFPQ::add_core_o (idx_t n, const float * x, const idx_t *xids,
|
260
|
+
float *residuals_2, const idx_t *precomputed_idx)
|
261
|
+
{
|
262
|
+
|
263
|
+
idx_t bs = 32768;
|
264
|
+
if (n > bs) {
|
265
|
+
for (idx_t i0 = 0; i0 < n; i0 += bs) {
|
266
|
+
idx_t i1 = std::min(i0 + bs, n);
|
267
|
+
if (verbose) {
|
268
|
+
printf("IndexIVFPQ::add_core_o: adding %ld:%ld / %ld\n",
|
269
|
+
i0, i1, n);
|
270
|
+
}
|
271
|
+
add_core_o (i1 - i0, x + i0 * d,
|
272
|
+
xids ? xids + i0 : nullptr,
|
273
|
+
residuals_2 ? residuals_2 + i0 * d : nullptr,
|
274
|
+
precomputed_idx ? precomputed_idx + i0 : nullptr);
|
275
|
+
}
|
276
|
+
return;
|
277
|
+
}
|
278
|
+
|
279
|
+
InterruptCallback::check();
|
280
|
+
|
281
|
+
FAISS_THROW_IF_NOT (is_trained);
|
282
|
+
double t0 = getmillisecs ();
|
283
|
+
const idx_t * idx;
|
284
|
+
ScopeDeleter<idx_t> del_idx;
|
285
|
+
|
286
|
+
if (precomputed_idx) {
|
287
|
+
idx = precomputed_idx;
|
288
|
+
} else {
|
289
|
+
idx_t * idx0 = new idx_t [n];
|
290
|
+
del_idx.set (idx0);
|
291
|
+
quantizer->assign (n, x, idx0);
|
292
|
+
idx = idx0;
|
293
|
+
}
|
294
|
+
|
295
|
+
double t1 = getmillisecs ();
|
296
|
+
uint8_t * xcodes = new uint8_t [n * code_size];
|
297
|
+
ScopeDeleter<uint8_t> del_xcodes (xcodes);
|
298
|
+
|
299
|
+
const float *to_encode = nullptr;
|
300
|
+
ScopeDeleter<float> del_to_encode;
|
301
|
+
|
302
|
+
if (by_residual) {
|
303
|
+
to_encode = compute_residuals (quantizer, n, x, idx);
|
304
|
+
del_to_encode.set (to_encode);
|
305
|
+
} else {
|
306
|
+
to_encode = x;
|
307
|
+
}
|
308
|
+
pq.compute_codes (to_encode, xcodes, n);
|
309
|
+
|
310
|
+
double t2 = getmillisecs ();
|
311
|
+
// TODO: parallelize?
|
312
|
+
size_t n_ignore = 0;
|
313
|
+
for (size_t i = 0; i < n; i++) {
|
314
|
+
idx_t key = idx[i];
|
315
|
+
if (key < 0) {
|
316
|
+
n_ignore ++;
|
317
|
+
if (residuals_2)
|
318
|
+
memset (residuals_2, 0, sizeof(*residuals_2) * d);
|
319
|
+
continue;
|
320
|
+
}
|
321
|
+
idx_t id = xids ? xids[i] : ntotal + i;
|
322
|
+
|
323
|
+
uint8_t *code = xcodes + i * code_size;
|
324
|
+
size_t offset = invlists->add_entry (key, id, code);
|
325
|
+
|
326
|
+
if (residuals_2) {
|
327
|
+
float *res2 = residuals_2 + i * d;
|
328
|
+
const float *xi = to_encode + i * d;
|
329
|
+
pq.decode (code, res2);
|
330
|
+
for (int j = 0; j < d; j++)
|
331
|
+
res2[j] = xi[j] - res2[j];
|
332
|
+
}
|
333
|
+
|
334
|
+
if (maintain_direct_map)
|
335
|
+
direct_map.push_back (key << 32 | offset);
|
336
|
+
}
|
337
|
+
|
338
|
+
|
339
|
+
double t3 = getmillisecs ();
|
340
|
+
if(verbose) {
|
341
|
+
char comment[100] = {0};
|
342
|
+
if (n_ignore > 0)
|
343
|
+
snprintf (comment, 100, "(%ld vectors ignored)", n_ignore);
|
344
|
+
printf(" add_core times: %.3f %.3f %.3f %s\n",
|
345
|
+
t1 - t0, t2 - t1, t3 - t2, comment);
|
346
|
+
}
|
347
|
+
ntotal += n;
|
348
|
+
}
|
349
|
+
|
350
|
+
|
351
|
+
void IndexIVFPQ::reconstruct_from_offset (int64_t list_no, int64_t offset,
|
352
|
+
float* recons) const
|
353
|
+
{
|
354
|
+
const uint8_t* code = invlists->get_single_code (list_no, offset);
|
355
|
+
|
356
|
+
if (by_residual) {
|
357
|
+
std::vector<float> centroid(d);
|
358
|
+
quantizer->reconstruct (list_no, centroid.data());
|
359
|
+
|
360
|
+
pq.decode (code, recons);
|
361
|
+
for (int i = 0; i < d; ++i) {
|
362
|
+
recons[i] += centroid[i];
|
363
|
+
}
|
364
|
+
} else {
|
365
|
+
pq.decode (code, recons);
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
|
370
|
+
|
371
|
+
/// 2G by default, accommodates tables up to PQ32 w/ 65536 centroids
|
372
|
+
size_t IndexIVFPQ::precomputed_table_max_bytes = ((size_t)1) << 31;
|
373
|
+
|
374
|
+
/** Precomputed tables for residuals
|
375
|
+
*
|
376
|
+
* During IVFPQ search with by_residual, we compute
|
377
|
+
*
|
378
|
+
* d = || x - y_C - y_R ||^2
|
379
|
+
*
|
380
|
+
* where x is the query vector, y_C the coarse centroid, y_R the
|
381
|
+
* refined PQ centroid. The expression can be decomposed as:
|
382
|
+
*
|
383
|
+
* d = || x - y_C ||^2 + || y_R ||^2 + 2 * (y_C|y_R) - 2 * (x|y_R)
|
384
|
+
* --------------- --------------------------- -------
|
385
|
+
* term 1 term 2 term 3
|
386
|
+
*
|
387
|
+
* When using multiprobe, we use the following decomposition:
|
388
|
+
* - term 1 is the distance to the coarse centroid, that is computed
|
389
|
+
* during the 1st stage search.
|
390
|
+
* - term 2 can be precomputed, as it does not involve x. However,
|
391
|
+
* because of the PQ, it needs nlist * M * ksub storage. This is why
|
392
|
+
* use_precomputed_table is off by default
|
393
|
+
* - term 3 is the classical non-residual distance table.
|
394
|
+
*
|
395
|
+
* Since y_R defined by a product quantizer, it is split across
|
396
|
+
* subvectors and stored separately for each subvector. If the coarse
|
397
|
+
* quantizer is a MultiIndexQuantizer then the table can be stored
|
398
|
+
* more compactly.
|
399
|
+
*
|
400
|
+
* At search time, the tables for term 2 and term 3 are added up. This
|
401
|
+
* is faster when the length of the lists is > ksub * M.
|
402
|
+
*/
|
403
|
+
|
404
|
+
void IndexIVFPQ::precompute_table ()
|
405
|
+
{
|
406
|
+
if (use_precomputed_table == -1)
|
407
|
+
return;
|
408
|
+
|
409
|
+
if (use_precomputed_table == 0) { // then choose the type of table
|
410
|
+
if (quantizer->metric_type == METRIC_INNER_PRODUCT) {
|
411
|
+
if (verbose) {
|
412
|
+
printf("IndexIVFPQ::precompute_table: precomputed "
|
413
|
+
"tables not needed for inner product quantizers\n");
|
414
|
+
}
|
415
|
+
return;
|
416
|
+
}
|
417
|
+
const MultiIndexQuantizer *miq =
|
418
|
+
dynamic_cast<const MultiIndexQuantizer *> (quantizer);
|
419
|
+
if (miq && pq.M % miq->pq.M == 0)
|
420
|
+
use_precomputed_table = 2;
|
421
|
+
else {
|
422
|
+
size_t table_size = pq.M * pq.ksub * nlist * sizeof(float);
|
423
|
+
if (table_size > precomputed_table_max_bytes) {
|
424
|
+
if (verbose) {
|
425
|
+
printf(
|
426
|
+
"IndexIVFPQ::precompute_table: not precomputing table, "
|
427
|
+
"it would be too big: %ld bytes (max %ld)\n",
|
428
|
+
table_size, precomputed_table_max_bytes);
|
429
|
+
use_precomputed_table = 0;
|
430
|
+
}
|
431
|
+
return;
|
432
|
+
}
|
433
|
+
use_precomputed_table = 1;
|
434
|
+
}
|
435
|
+
} // otherwise assume user has set appropriate flag on input
|
436
|
+
|
437
|
+
if (verbose) {
|
438
|
+
printf ("precomputing IVFPQ tables type %d\n",
|
439
|
+
use_precomputed_table);
|
440
|
+
}
|
441
|
+
|
442
|
+
// squared norms of the PQ centroids
|
443
|
+
std::vector<float> r_norms (pq.M * pq.ksub, NAN);
|
444
|
+
for (int m = 0; m < pq.M; m++)
|
445
|
+
for (int j = 0; j < pq.ksub; j++)
|
446
|
+
r_norms [m * pq.ksub + j] =
|
447
|
+
fvec_norm_L2sqr (pq.get_centroids (m, j), pq.dsub);
|
448
|
+
|
449
|
+
if (use_precomputed_table == 1) {
|
450
|
+
|
451
|
+
precomputed_table.resize (nlist * pq.M * pq.ksub);
|
452
|
+
std::vector<float> centroid (d);
|
453
|
+
|
454
|
+
for (size_t i = 0; i < nlist; i++) {
|
455
|
+
quantizer->reconstruct (i, centroid.data());
|
456
|
+
|
457
|
+
float *tab = &precomputed_table[i * pq.M * pq.ksub];
|
458
|
+
pq.compute_inner_prod_table (centroid.data(), tab);
|
459
|
+
fvec_madd (pq.M * pq.ksub, r_norms.data(), 2.0, tab, tab);
|
460
|
+
}
|
461
|
+
} else if (use_precomputed_table == 2) {
|
462
|
+
const MultiIndexQuantizer *miq =
|
463
|
+
dynamic_cast<const MultiIndexQuantizer *> (quantizer);
|
464
|
+
FAISS_THROW_IF_NOT (miq);
|
465
|
+
const ProductQuantizer &cpq = miq->pq;
|
466
|
+
FAISS_THROW_IF_NOT (pq.M % cpq.M == 0);
|
467
|
+
|
468
|
+
precomputed_table.resize(cpq.ksub * pq.M * pq.ksub);
|
469
|
+
|
470
|
+
// reorder PQ centroid table
|
471
|
+
std::vector<float> centroids (d * cpq.ksub, NAN);
|
472
|
+
|
473
|
+
for (int m = 0; m < cpq.M; m++) {
|
474
|
+
for (size_t i = 0; i < cpq.ksub; i++) {
|
475
|
+
memcpy (centroids.data() + i * d + m * cpq.dsub,
|
476
|
+
cpq.get_centroids (m, i),
|
477
|
+
sizeof (*centroids.data()) * cpq.dsub);
|
478
|
+
}
|
479
|
+
}
|
480
|
+
|
481
|
+
pq.compute_inner_prod_tables (cpq.ksub, centroids.data (),
|
482
|
+
precomputed_table.data ());
|
483
|
+
|
484
|
+
for (size_t i = 0; i < cpq.ksub; i++) {
|
485
|
+
float *tab = &precomputed_table[i * pq.M * pq.ksub];
|
486
|
+
fvec_madd (pq.M * pq.ksub, r_norms.data(), 2.0, tab, tab);
|
487
|
+
}
|
488
|
+
|
489
|
+
}
|
490
|
+
|
491
|
+
}
|
492
|
+
|
493
|
+
namespace {
|
494
|
+
|
495
|
+
using idx_t = Index::idx_t;
|
496
|
+
|
497
|
+
|
498
|
+
#define TIC t0 = get_cycles()
|
499
|
+
#define TOC get_cycles () - t0
|
500
|
+
|
501
|
+
|
502
|
+
|
503
|
+
/** QueryTables manages the various ways of searching an
|
504
|
+
* IndexIVFPQ. The code contains a lot of branches, depending on:
|
505
|
+
* - metric_type: are we computing L2 or Inner product similarity?
|
506
|
+
* - by_residual: do we encode raw vectors or residuals?
|
507
|
+
* - use_precomputed_table: are x_R|x_C tables precomputed?
|
508
|
+
* - polysemous_ht: are we filtering with polysemous codes?
|
509
|
+
*/
|
510
|
+
struct QueryTables {
|
511
|
+
|
512
|
+
/*****************************************************
|
513
|
+
* General data from the IVFPQ
|
514
|
+
*****************************************************/
|
515
|
+
|
516
|
+
const IndexIVFPQ & ivfpq;
|
517
|
+
const IVFSearchParameters *params;
|
518
|
+
|
519
|
+
// copied from IndexIVFPQ for easier access
|
520
|
+
int d;
|
521
|
+
const ProductQuantizer & pq;
|
522
|
+
MetricType metric_type;
|
523
|
+
bool by_residual;
|
524
|
+
int use_precomputed_table;
|
525
|
+
int polysemous_ht;
|
526
|
+
|
527
|
+
// pre-allocated data buffers
|
528
|
+
float * sim_table, * sim_table_2;
|
529
|
+
float * residual_vec, *decoded_vec;
|
530
|
+
|
531
|
+
// single data buffer
|
532
|
+
std::vector<float> mem;
|
533
|
+
|
534
|
+
// for table pointers
|
535
|
+
std::vector<const float *> sim_table_ptrs;
|
536
|
+
|
537
|
+
explicit QueryTables (const IndexIVFPQ & ivfpq,
|
538
|
+
const IVFSearchParameters *params):
|
539
|
+
ivfpq(ivfpq),
|
540
|
+
d(ivfpq.d),
|
541
|
+
pq (ivfpq.pq),
|
542
|
+
metric_type (ivfpq.metric_type),
|
543
|
+
by_residual (ivfpq.by_residual),
|
544
|
+
use_precomputed_table (ivfpq.use_precomputed_table)
|
545
|
+
{
|
546
|
+
mem.resize (pq.ksub * pq.M * 2 + d * 2);
|
547
|
+
sim_table = mem.data ();
|
548
|
+
sim_table_2 = sim_table + pq.ksub * pq.M;
|
549
|
+
residual_vec = sim_table_2 + pq.ksub * pq.M;
|
550
|
+
decoded_vec = residual_vec + d;
|
551
|
+
|
552
|
+
// for polysemous
|
553
|
+
polysemous_ht = ivfpq.polysemous_ht;
|
554
|
+
if (auto ivfpq_params =
|
555
|
+
dynamic_cast<const IVFPQSearchParameters *>(params)) {
|
556
|
+
polysemous_ht = ivfpq_params->polysemous_ht;
|
557
|
+
}
|
558
|
+
if (polysemous_ht != 0) {
|
559
|
+
q_code.resize (pq.code_size);
|
560
|
+
}
|
561
|
+
init_list_cycles = 0;
|
562
|
+
sim_table_ptrs.resize (pq.M);
|
563
|
+
}
|
564
|
+
|
565
|
+
/*****************************************************
|
566
|
+
* What we do when query is known
|
567
|
+
*****************************************************/
|
568
|
+
|
569
|
+
// field specific to query
|
570
|
+
const float * qi;
|
571
|
+
|
572
|
+
// query-specific intialization
|
573
|
+
void init_query (const float * qi) {
|
574
|
+
this->qi = qi;
|
575
|
+
if (metric_type == METRIC_INNER_PRODUCT)
|
576
|
+
init_query_IP ();
|
577
|
+
else
|
578
|
+
init_query_L2 ();
|
579
|
+
if (!by_residual && polysemous_ht != 0)
|
580
|
+
pq.compute_code (qi, q_code.data());
|
581
|
+
}
|
582
|
+
|
583
|
+
void init_query_IP () {
|
584
|
+
// precompute some tables specific to the query qi
|
585
|
+
pq.compute_inner_prod_table (qi, sim_table);
|
586
|
+
}
|
587
|
+
|
588
|
+
void init_query_L2 () {
|
589
|
+
if (!by_residual) {
|
590
|
+
pq.compute_distance_table (qi, sim_table);
|
591
|
+
} else if (use_precomputed_table) {
|
592
|
+
pq.compute_inner_prod_table (qi, sim_table_2);
|
593
|
+
}
|
594
|
+
}
|
595
|
+
|
596
|
+
/*****************************************************
|
597
|
+
* When inverted list is known: prepare computations
|
598
|
+
*****************************************************/
|
599
|
+
|
600
|
+
// fields specific to list
|
601
|
+
Index::idx_t key;
|
602
|
+
float coarse_dis;
|
603
|
+
std::vector<uint8_t> q_code;
|
604
|
+
|
605
|
+
uint64_t init_list_cycles;
|
606
|
+
|
607
|
+
/// once we know the query and the centroid, we can prepare the
|
608
|
+
/// sim_table that will be used for accumulation
|
609
|
+
/// and dis0, the initial value
|
610
|
+
float precompute_list_tables () {
|
611
|
+
float dis0 = 0;
|
612
|
+
uint64_t t0; TIC;
|
613
|
+
if (by_residual) {
|
614
|
+
if (metric_type == METRIC_INNER_PRODUCT)
|
615
|
+
dis0 = precompute_list_tables_IP ();
|
616
|
+
else
|
617
|
+
dis0 = precompute_list_tables_L2 ();
|
618
|
+
}
|
619
|
+
init_list_cycles += TOC;
|
620
|
+
return dis0;
|
621
|
+
}
|
622
|
+
|
623
|
+
float precompute_list_table_pointers () {
|
624
|
+
float dis0 = 0;
|
625
|
+
uint64_t t0; TIC;
|
626
|
+
if (by_residual) {
|
627
|
+
if (metric_type == METRIC_INNER_PRODUCT)
|
628
|
+
FAISS_THROW_MSG ("not implemented");
|
629
|
+
else
|
630
|
+
dis0 = precompute_list_table_pointers_L2 ();
|
631
|
+
}
|
632
|
+
init_list_cycles += TOC;
|
633
|
+
return dis0;
|
634
|
+
}
|
635
|
+
|
636
|
+
/*****************************************************
|
637
|
+
* compute tables for inner prod
|
638
|
+
*****************************************************/
|
639
|
+
|
640
|
+
float precompute_list_tables_IP ()
|
641
|
+
{
|
642
|
+
// prepare the sim_table that will be used for accumulation
|
643
|
+
// and dis0, the initial value
|
644
|
+
ivfpq.quantizer->reconstruct (key, decoded_vec);
|
645
|
+
// decoded_vec = centroid
|
646
|
+
float dis0 = fvec_inner_product (qi, decoded_vec, d);
|
647
|
+
|
648
|
+
if (polysemous_ht) {
|
649
|
+
for (int i = 0; i < d; i++) {
|
650
|
+
residual_vec [i] = qi[i] - decoded_vec[i];
|
651
|
+
}
|
652
|
+
pq.compute_code (residual_vec, q_code.data());
|
653
|
+
}
|
654
|
+
return dis0;
|
655
|
+
}
|
656
|
+
|
657
|
+
|
658
|
+
/*****************************************************
|
659
|
+
* compute tables for L2 distance
|
660
|
+
*****************************************************/
|
661
|
+
|
662
|
+
float precompute_list_tables_L2 ()
|
663
|
+
{
|
664
|
+
float dis0 = 0;
|
665
|
+
|
666
|
+
if (use_precomputed_table == 0 || use_precomputed_table == -1) {
|
667
|
+
ivfpq.quantizer->compute_residual (qi, residual_vec, key);
|
668
|
+
pq.compute_distance_table (residual_vec, sim_table);
|
669
|
+
|
670
|
+
if (polysemous_ht != 0) {
|
671
|
+
pq.compute_code (residual_vec, q_code.data());
|
672
|
+
}
|
673
|
+
|
674
|
+
} else if (use_precomputed_table == 1) {
|
675
|
+
dis0 = coarse_dis;
|
676
|
+
|
677
|
+
fvec_madd (pq.M * pq.ksub,
|
678
|
+
&ivfpq.precomputed_table [key * pq.ksub * pq.M],
|
679
|
+
-2.0, sim_table_2,
|
680
|
+
sim_table);
|
681
|
+
|
682
|
+
|
683
|
+
if (polysemous_ht != 0) {
|
684
|
+
ivfpq.quantizer->compute_residual (qi, residual_vec, key);
|
685
|
+
pq.compute_code (residual_vec, q_code.data());
|
686
|
+
}
|
687
|
+
|
688
|
+
} else if (use_precomputed_table == 2) {
|
689
|
+
dis0 = coarse_dis;
|
690
|
+
|
691
|
+
const MultiIndexQuantizer *miq =
|
692
|
+
dynamic_cast<const MultiIndexQuantizer *> (ivfpq.quantizer);
|
693
|
+
FAISS_THROW_IF_NOT (miq);
|
694
|
+
const ProductQuantizer &cpq = miq->pq;
|
695
|
+
int Mf = pq.M / cpq.M;
|
696
|
+
|
697
|
+
const float *qtab = sim_table_2; // query-specific table
|
698
|
+
float *ltab = sim_table; // (output) list-specific table
|
699
|
+
|
700
|
+
long k = key;
|
701
|
+
for (int cm = 0; cm < cpq.M; cm++) {
|
702
|
+
// compute PQ index
|
703
|
+
int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
|
704
|
+
k >>= cpq.nbits;
|
705
|
+
|
706
|
+
// get corresponding table
|
707
|
+
const float *pc = &ivfpq.precomputed_table
|
708
|
+
[(ki * pq.M + cm * Mf) * pq.ksub];
|
709
|
+
|
710
|
+
if (polysemous_ht == 0) {
|
711
|
+
|
712
|
+
// sum up with query-specific table
|
713
|
+
fvec_madd (Mf * pq.ksub,
|
714
|
+
pc,
|
715
|
+
-2.0, qtab,
|
716
|
+
ltab);
|
717
|
+
ltab += Mf * pq.ksub;
|
718
|
+
qtab += Mf * pq.ksub;
|
719
|
+
} else {
|
720
|
+
for (int m = cm * Mf; m < (cm + 1) * Mf; m++) {
|
721
|
+
q_code[m] = fvec_madd_and_argmin
|
722
|
+
(pq.ksub, pc, -2, qtab, ltab);
|
723
|
+
pc += pq.ksub;
|
724
|
+
ltab += pq.ksub;
|
725
|
+
qtab += pq.ksub;
|
726
|
+
}
|
727
|
+
}
|
728
|
+
|
729
|
+
}
|
730
|
+
}
|
731
|
+
|
732
|
+
return dis0;
|
733
|
+
}
|
734
|
+
|
735
|
+
float precompute_list_table_pointers_L2 ()
|
736
|
+
{
|
737
|
+
float dis0 = 0;
|
738
|
+
|
739
|
+
if (use_precomputed_table == 1) {
|
740
|
+
dis0 = coarse_dis;
|
741
|
+
|
742
|
+
const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M];
|
743
|
+
for (int m = 0; m < pq.M; m++) {
|
744
|
+
sim_table_ptrs [m] = s;
|
745
|
+
s += pq.ksub;
|
746
|
+
}
|
747
|
+
} else if (use_precomputed_table == 2) {
|
748
|
+
dis0 = coarse_dis;
|
749
|
+
|
750
|
+
const MultiIndexQuantizer *miq =
|
751
|
+
dynamic_cast<const MultiIndexQuantizer *> (ivfpq.quantizer);
|
752
|
+
FAISS_THROW_IF_NOT (miq);
|
753
|
+
const ProductQuantizer &cpq = miq->pq;
|
754
|
+
int Mf = pq.M / cpq.M;
|
755
|
+
|
756
|
+
long k = key;
|
757
|
+
int m0 = 0;
|
758
|
+
for (int cm = 0; cm < cpq.M; cm++) {
|
759
|
+
int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
|
760
|
+
k >>= cpq.nbits;
|
761
|
+
|
762
|
+
const float *pc = &ivfpq.precomputed_table
|
763
|
+
[(ki * pq.M + cm * Mf) * pq.ksub];
|
764
|
+
|
765
|
+
for (int m = m0; m < m0 + Mf; m++) {
|
766
|
+
sim_table_ptrs [m] = pc;
|
767
|
+
pc += pq.ksub;
|
768
|
+
}
|
769
|
+
m0 += Mf;
|
770
|
+
}
|
771
|
+
} else {
|
772
|
+
FAISS_THROW_MSG ("need precomputed tables");
|
773
|
+
}
|
774
|
+
|
775
|
+
if (polysemous_ht) {
|
776
|
+
FAISS_THROW_MSG ("not implemented");
|
777
|
+
// Not clear that it makes sense to implemente this,
|
778
|
+
// because it costs M * ksub, which is what we wanted to
|
779
|
+
// avoid with the tables pointers.
|
780
|
+
}
|
781
|
+
|
782
|
+
return dis0;
|
783
|
+
}
|
784
|
+
|
785
|
+
|
786
|
+
};
|
787
|
+
|
788
|
+
|
789
|
+
|
790
|
+
template<class C>
|
791
|
+
struct KnnSearchResults {
|
792
|
+
idx_t key;
|
793
|
+
const idx_t *ids;
|
794
|
+
|
795
|
+
// heap params
|
796
|
+
size_t k;
|
797
|
+
float * heap_sim;
|
798
|
+
idx_t * heap_ids;
|
799
|
+
|
800
|
+
size_t nup;
|
801
|
+
|
802
|
+
inline void add (idx_t j, float dis) {
|
803
|
+
if (C::cmp (heap_sim[0], dis)) {
|
804
|
+
heap_pop<C> (k, heap_sim, heap_ids);
|
805
|
+
idx_t id = ids ? ids[j] : (key << 32 | j);
|
806
|
+
heap_push<C> (k, heap_sim, heap_ids, dis, id);
|
807
|
+
nup++;
|
808
|
+
}
|
809
|
+
}
|
810
|
+
|
811
|
+
};
|
812
|
+
|
813
|
+
template<class C>
|
814
|
+
struct RangeSearchResults {
|
815
|
+
idx_t key;
|
816
|
+
const idx_t *ids;
|
817
|
+
|
818
|
+
// wrapped result structure
|
819
|
+
float radius;
|
820
|
+
RangeQueryResult & rres;
|
821
|
+
|
822
|
+
inline void add (idx_t j, float dis) {
|
823
|
+
if (C::cmp (radius, dis)) {
|
824
|
+
idx_t id = ids ? ids[j] : (key << 32 | j);
|
825
|
+
rres.add (dis, id);
|
826
|
+
}
|
827
|
+
}
|
828
|
+
};
|
829
|
+
|
830
|
+
|
831
|
+
|
832
|
+
/*****************************************************
|
833
|
+
* Scaning the codes.
|
834
|
+
* The scanning functions call their favorite precompute_*
|
835
|
+
* function to precompute the tables they need.
|
836
|
+
*****************************************************/
|
837
|
+
template <typename IDType, MetricType METRIC_TYPE>
|
838
|
+
struct IVFPQScannerT: QueryTables {
|
839
|
+
|
840
|
+
const uint8_t * list_codes;
|
841
|
+
const IDType * list_ids;
|
842
|
+
size_t list_size;
|
843
|
+
|
844
|
+
IVFPQScannerT (const IndexIVFPQ & ivfpq, const IVFSearchParameters *params):
|
845
|
+
QueryTables (ivfpq, params)
|
846
|
+
{
|
847
|
+
FAISS_THROW_IF_NOT (pq.nbits == 8);
|
848
|
+
assert(METRIC_TYPE == metric_type);
|
849
|
+
}
|
850
|
+
|
851
|
+
float dis0;
|
852
|
+
|
853
|
+
void init_list (idx_t list_no, float coarse_dis,
|
854
|
+
int mode) {
|
855
|
+
this->key = list_no;
|
856
|
+
this->coarse_dis = coarse_dis;
|
857
|
+
|
858
|
+
if (mode == 2) {
|
859
|
+
dis0 = precompute_list_tables ();
|
860
|
+
} else if (mode == 1) {
|
861
|
+
dis0 = precompute_list_table_pointers ();
|
862
|
+
}
|
863
|
+
}
|
864
|
+
|
865
|
+
/*****************************************************
|
866
|
+
* Scaning the codes: simple PQ scan.
|
867
|
+
*****************************************************/
|
868
|
+
|
869
|
+
/// version of the scan where we use precomputed tables
|
870
|
+
template<class SearchResultType>
|
871
|
+
void scan_list_with_table (size_t ncode, const uint8_t *codes,
|
872
|
+
SearchResultType & res) const
|
873
|
+
{
|
874
|
+
for (size_t j = 0; j < ncode; j++) {
|
875
|
+
|
876
|
+
float dis = dis0;
|
877
|
+
const float *tab = sim_table;
|
878
|
+
|
879
|
+
for (size_t m = 0; m < pq.M; m++) {
|
880
|
+
dis += tab[*codes++];
|
881
|
+
tab += pq.ksub;
|
882
|
+
}
|
883
|
+
|
884
|
+
res.add(j, dis);
|
885
|
+
}
|
886
|
+
}
|
887
|
+
|
888
|
+
|
889
|
+
/// tables are not precomputed, but pointers are provided to the
|
890
|
+
/// relevant X_c|x_r tables
|
891
|
+
template<class SearchResultType>
|
892
|
+
void scan_list_with_pointer (size_t ncode, const uint8_t *codes,
|
893
|
+
SearchResultType & res) const
|
894
|
+
{
|
895
|
+
for (size_t j = 0; j < ncode; j++) {
|
896
|
+
|
897
|
+
float dis = dis0;
|
898
|
+
const float *tab = sim_table_2;
|
899
|
+
|
900
|
+
for (size_t m = 0; m < pq.M; m++) {
|
901
|
+
int ci = *codes++;
|
902
|
+
dis += sim_table_ptrs [m][ci] - 2 * tab [ci];
|
903
|
+
tab += pq.ksub;
|
904
|
+
}
|
905
|
+
res.add (j, dis);
|
906
|
+
}
|
907
|
+
}
|
908
|
+
|
909
|
+
|
910
|
+
/// nothing is precomputed: access residuals on-the-fly
|
911
|
+
template<class SearchResultType>
|
912
|
+
void scan_on_the_fly_dist (size_t ncode, const uint8_t *codes,
|
913
|
+
SearchResultType &res) const
|
914
|
+
{
|
915
|
+
const float *dvec;
|
916
|
+
float dis0 = 0;
|
917
|
+
if (by_residual) {
|
918
|
+
if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
|
919
|
+
ivfpq.quantizer->reconstruct (key, residual_vec);
|
920
|
+
dis0 = fvec_inner_product (residual_vec, qi, d);
|
921
|
+
} else {
|
922
|
+
ivfpq.quantizer->compute_residual (qi, residual_vec, key);
|
923
|
+
}
|
924
|
+
dvec = residual_vec;
|
925
|
+
} else {
|
926
|
+
dvec = qi;
|
927
|
+
dis0 = 0;
|
928
|
+
}
|
929
|
+
|
930
|
+
for (size_t j = 0; j < ncode; j++) {
|
931
|
+
|
932
|
+
pq.decode (codes, decoded_vec);
|
933
|
+
codes += pq.code_size;
|
934
|
+
|
935
|
+
float dis;
|
936
|
+
if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
|
937
|
+
dis = dis0 + fvec_inner_product (decoded_vec, qi, d);
|
938
|
+
} else {
|
939
|
+
dis = fvec_L2sqr (decoded_vec, dvec, d);
|
940
|
+
}
|
941
|
+
res.add (j, dis);
|
942
|
+
}
|
943
|
+
}
|
944
|
+
|
945
|
+
/*****************************************************
|
946
|
+
* Scanning codes with polysemous filtering
|
947
|
+
*****************************************************/
|
948
|
+
|
949
|
+
template <class HammingComputer, class SearchResultType>
|
950
|
+
void scan_list_polysemous_hc (
|
951
|
+
size_t ncode, const uint8_t *codes,
|
952
|
+
SearchResultType & res) const
|
953
|
+
{
|
954
|
+
int ht = ivfpq.polysemous_ht;
|
955
|
+
size_t n_hamming_pass = 0, nup = 0;
|
956
|
+
|
957
|
+
int code_size = pq.code_size;
|
958
|
+
|
959
|
+
HammingComputer hc (q_code.data(), code_size);
|
960
|
+
|
961
|
+
for (size_t j = 0; j < ncode; j++) {
|
962
|
+
const uint8_t *b_code = codes;
|
963
|
+
int hd = hc.hamming (b_code);
|
964
|
+
if (hd < ht) {
|
965
|
+
n_hamming_pass ++;
|
966
|
+
|
967
|
+
float dis = dis0;
|
968
|
+
const float *tab = sim_table;
|
969
|
+
|
970
|
+
for (size_t m = 0; m < pq.M; m++) {
|
971
|
+
dis += tab[*b_code++];
|
972
|
+
tab += pq.ksub;
|
973
|
+
}
|
974
|
+
|
975
|
+
res.add (j, dis);
|
976
|
+
}
|
977
|
+
codes += code_size;
|
978
|
+
}
|
979
|
+
#pragma omp critical
|
980
|
+
{
|
981
|
+
indexIVFPQ_stats.n_hamming_pass += n_hamming_pass;
|
982
|
+
}
|
983
|
+
}
|
984
|
+
|
985
|
+
template<class SearchResultType>
|
986
|
+
void scan_list_polysemous (
|
987
|
+
size_t ncode, const uint8_t *codes,
|
988
|
+
SearchResultType &res) const
|
989
|
+
{
|
990
|
+
switch (pq.code_size) {
|
991
|
+
#define HANDLE_CODE_SIZE(cs) \
|
992
|
+
case cs: \
|
993
|
+
scan_list_polysemous_hc \
|
994
|
+
<HammingComputer ## cs, SearchResultType> \
|
995
|
+
(ncode, codes, res); \
|
996
|
+
break
|
997
|
+
HANDLE_CODE_SIZE(4);
|
998
|
+
HANDLE_CODE_SIZE(8);
|
999
|
+
HANDLE_CODE_SIZE(16);
|
1000
|
+
HANDLE_CODE_SIZE(20);
|
1001
|
+
HANDLE_CODE_SIZE(32);
|
1002
|
+
HANDLE_CODE_SIZE(64);
|
1003
|
+
#undef HANDLE_CODE_SIZE
|
1004
|
+
default:
|
1005
|
+
if (pq.code_size % 8 == 0)
|
1006
|
+
scan_list_polysemous_hc
|
1007
|
+
<HammingComputerM8, SearchResultType>
|
1008
|
+
(ncode, codes, res);
|
1009
|
+
else
|
1010
|
+
scan_list_polysemous_hc
|
1011
|
+
<HammingComputerM4, SearchResultType>
|
1012
|
+
(ncode, codes, res);
|
1013
|
+
break;
|
1014
|
+
}
|
1015
|
+
}
|
1016
|
+
|
1017
|
+
};
|
1018
|
+
|
1019
|
+
|
1020
|
+
/* We put as many parameters as possible in template. Hopefully the
|
1021
|
+
* gain in runtime is worth the code bloat. C is the comparator < or
|
1022
|
+
* >, it is directly related to METRIC_TYPE. precompute_mode is how
|
1023
|
+
* much we precompute (2 = precompute distance tables, 1 = precompute
|
1024
|
+
* pointers to distances, 0 = compute distances one by one).
|
1025
|
+
* Currently only 2 is supported */
|
1026
|
+
template<MetricType METRIC_TYPE, class C, int precompute_mode>
|
1027
|
+
struct IVFPQScanner:
|
1028
|
+
IVFPQScannerT<Index::idx_t, METRIC_TYPE>,
|
1029
|
+
InvertedListScanner
|
1030
|
+
{
|
1031
|
+
bool store_pairs;
|
1032
|
+
|
1033
|
+
IVFPQScanner(const IndexIVFPQ & ivfpq, bool store_pairs):
|
1034
|
+
IVFPQScannerT<Index::idx_t, METRIC_TYPE>(ivfpq, nullptr),
|
1035
|
+
store_pairs(store_pairs)
|
1036
|
+
{
|
1037
|
+
}
|
1038
|
+
|
1039
|
+
void set_query (const float *query) override {
|
1040
|
+
this->init_query (query);
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
void set_list (idx_t list_no, float coarse_dis) override {
|
1044
|
+
this->init_list (list_no, coarse_dis, precompute_mode);
|
1045
|
+
}
|
1046
|
+
|
1047
|
+
float distance_to_code (const uint8_t *code) const override {
|
1048
|
+
assert(precompute_mode == 2);
|
1049
|
+
float dis = this->dis0;
|
1050
|
+
const float *tab = this->sim_table;
|
1051
|
+
|
1052
|
+
for (size_t m = 0; m < this->pq.M; m++) {
|
1053
|
+
dis += tab[*code++];
|
1054
|
+
tab += this->pq.ksub;
|
1055
|
+
}
|
1056
|
+
return dis;
|
1057
|
+
}
|
1058
|
+
|
1059
|
+
size_t scan_codes (size_t ncode,
|
1060
|
+
const uint8_t *codes,
|
1061
|
+
const idx_t *ids,
|
1062
|
+
float *heap_sim, idx_t *heap_ids,
|
1063
|
+
size_t k) const override
|
1064
|
+
{
|
1065
|
+
KnnSearchResults<C> res = {
|
1066
|
+
/* key */ this->key,
|
1067
|
+
/* ids */ this->store_pairs ? nullptr : ids,
|
1068
|
+
/* k */ k,
|
1069
|
+
/* heap_sim */ heap_sim,
|
1070
|
+
/* heap_ids */ heap_ids,
|
1071
|
+
/* nup */ 0
|
1072
|
+
};
|
1073
|
+
|
1074
|
+
if (this->polysemous_ht > 0) {
|
1075
|
+
assert(precompute_mode == 2);
|
1076
|
+
this->scan_list_polysemous (ncode, codes, res);
|
1077
|
+
} else if (precompute_mode == 2) {
|
1078
|
+
this->scan_list_with_table (ncode, codes, res);
|
1079
|
+
} else if (precompute_mode == 1) {
|
1080
|
+
this->scan_list_with_pointer (ncode, codes, res);
|
1081
|
+
} else if (precompute_mode == 0) {
|
1082
|
+
this->scan_on_the_fly_dist (ncode, codes, res);
|
1083
|
+
} else {
|
1084
|
+
FAISS_THROW_MSG("bad precomp mode");
|
1085
|
+
}
|
1086
|
+
return res.nup;
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
void scan_codes_range (size_t ncode,
|
1090
|
+
const uint8_t *codes,
|
1091
|
+
const idx_t *ids,
|
1092
|
+
float radius,
|
1093
|
+
RangeQueryResult & rres) const override
|
1094
|
+
{
|
1095
|
+
RangeSearchResults<C> res = {
|
1096
|
+
/* key */ this->key,
|
1097
|
+
/* ids */ this->store_pairs ? nullptr : ids,
|
1098
|
+
/* radius */ radius,
|
1099
|
+
/* rres */ rres
|
1100
|
+
};
|
1101
|
+
|
1102
|
+
if (this->polysemous_ht > 0) {
|
1103
|
+
assert(precompute_mode == 2);
|
1104
|
+
this->scan_list_polysemous (ncode, codes, res);
|
1105
|
+
} else if (precompute_mode == 2) {
|
1106
|
+
this->scan_list_with_table (ncode, codes, res);
|
1107
|
+
} else if (precompute_mode == 1) {
|
1108
|
+
this->scan_list_with_pointer (ncode, codes, res);
|
1109
|
+
} else if (precompute_mode == 0) {
|
1110
|
+
this->scan_on_the_fly_dist (ncode, codes, res);
|
1111
|
+
} else {
|
1112
|
+
FAISS_THROW_MSG("bad precomp mode");
|
1113
|
+
}
|
1114
|
+
|
1115
|
+
}
|
1116
|
+
};
|
1117
|
+
|
1118
|
+
|
1119
|
+
|
1120
|
+
|
1121
|
+
} // anonymous namespace
|
1122
|
+
|
1123
|
+
InvertedListScanner *
|
1124
|
+
IndexIVFPQ::get_InvertedListScanner (bool store_pairs) const
|
1125
|
+
{
|
1126
|
+
if (metric_type == METRIC_INNER_PRODUCT) {
|
1127
|
+
return new IVFPQScanner<METRIC_INNER_PRODUCT, CMin<float, idx_t>, 2>
|
1128
|
+
(*this, store_pairs);
|
1129
|
+
} else if (metric_type == METRIC_L2) {
|
1130
|
+
return new IVFPQScanner<METRIC_L2, CMax<float, idx_t>, 2>
|
1131
|
+
(*this, store_pairs);
|
1132
|
+
}
|
1133
|
+
return nullptr;
|
1134
|
+
|
1135
|
+
}
|
1136
|
+
|
1137
|
+
|
1138
|
+
|
1139
|
+
IndexIVFPQStats indexIVFPQ_stats;
|
1140
|
+
|
1141
|
+
void IndexIVFPQStats::reset () {
|
1142
|
+
memset (this, 0, sizeof (*this));
|
1143
|
+
}
|
1144
|
+
|
1145
|
+
|
1146
|
+
|
1147
|
+
IndexIVFPQ::IndexIVFPQ ()
|
1148
|
+
{
|
1149
|
+
// initialize some runtime values
|
1150
|
+
use_precomputed_table = 0;
|
1151
|
+
scan_table_threshold = 0;
|
1152
|
+
do_polysemous_training = false;
|
1153
|
+
polysemous_ht = 0;
|
1154
|
+
polysemous_training = nullptr;
|
1155
|
+
}
|
1156
|
+
|
1157
|
+
|
1158
|
+
struct CodeCmp {
|
1159
|
+
const uint8_t *tab;
|
1160
|
+
size_t code_size;
|
1161
|
+
bool operator () (int a, int b) const {
|
1162
|
+
return cmp (a, b) > 0;
|
1163
|
+
}
|
1164
|
+
int cmp (int a, int b) const {
|
1165
|
+
return memcmp (tab + a * code_size, tab + b * code_size,
|
1166
|
+
code_size);
|
1167
|
+
}
|
1168
|
+
};
|
1169
|
+
|
1170
|
+
|
1171
|
+
size_t IndexIVFPQ::find_duplicates (idx_t *dup_ids, size_t *lims) const
|
1172
|
+
{
|
1173
|
+
size_t ngroup = 0;
|
1174
|
+
lims[0] = 0;
|
1175
|
+
for (size_t list_no = 0; list_no < nlist; list_no++) {
|
1176
|
+
size_t n = invlists->list_size (list_no);
|
1177
|
+
std::vector<int> ord (n);
|
1178
|
+
for (int i = 0; i < n; i++) ord[i] = i;
|
1179
|
+
InvertedLists::ScopedCodes codes (invlists, list_no);
|
1180
|
+
CodeCmp cs = { codes.get(), code_size };
|
1181
|
+
std::sort (ord.begin(), ord.end(), cs);
|
1182
|
+
|
1183
|
+
InvertedLists::ScopedIds list_ids (invlists, list_no);
|
1184
|
+
int prev = -1; // all elements from prev to i-1 are equal
|
1185
|
+
for (int i = 0; i < n; i++) {
|
1186
|
+
if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) {
|
1187
|
+
// same as previous => remember
|
1188
|
+
if (prev + 1 == i) { // start new group
|
1189
|
+
ngroup++;
|
1190
|
+
lims[ngroup] = lims[ngroup - 1];
|
1191
|
+
dup_ids [lims [ngroup]++] = list_ids [ord [prev]];
|
1192
|
+
}
|
1193
|
+
dup_ids [lims [ngroup]++] = list_ids [ord [i]];
|
1194
|
+
} else { // not same as previous.
|
1195
|
+
prev = i;
|
1196
|
+
}
|
1197
|
+
}
|
1198
|
+
}
|
1199
|
+
return ngroup;
|
1200
|
+
}
|
1201
|
+
|
1202
|
+
|
1203
|
+
|
1204
|
+
|
1205
|
+
|
1206
|
+
|
1207
|
+
} // namespace faiss
|