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,171 @@
|
|
|
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/Index.h>
|
|
11
|
+
|
|
12
|
+
#include <faiss/impl/AuxIndexStructures.h>
|
|
13
|
+
#include <faiss/impl/FaissAssert.h>
|
|
14
|
+
#include <faiss/utils/distances.h>
|
|
15
|
+
|
|
16
|
+
#include <cstring>
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
namespace faiss {
|
|
20
|
+
|
|
21
|
+
Index::~Index ()
|
|
22
|
+
{
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
void Index::train(idx_t /*n*/, const float* /*x*/) {
|
|
27
|
+
// does nothing by default
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
void Index::range_search (idx_t , const float *, float,
|
|
32
|
+
RangeSearchResult *) const
|
|
33
|
+
{
|
|
34
|
+
FAISS_THROW_MSG ("range search not implemented");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void Index::assign (idx_t n, const float * x, idx_t * labels, idx_t k)
|
|
38
|
+
{
|
|
39
|
+
float * distances = new float[n * k];
|
|
40
|
+
ScopeDeleter<float> del(distances);
|
|
41
|
+
search (n, x, k, distances, labels);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void Index::add_with_ids(
|
|
45
|
+
idx_t /*n*/,
|
|
46
|
+
const float* /*x*/,
|
|
47
|
+
const idx_t* /*xids*/) {
|
|
48
|
+
FAISS_THROW_MSG ("add_with_ids not implemented for this type of index");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
size_t Index::remove_ids(const IDSelector& /*sel*/) {
|
|
52
|
+
FAISS_THROW_MSG ("remove_ids not implemented for this type of index");
|
|
53
|
+
return -1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
void Index::reconstruct (idx_t, float * ) const {
|
|
58
|
+
FAISS_THROW_MSG ("reconstruct not implemented for this type of index");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
void Index::reconstruct_n (idx_t i0, idx_t ni, float *recons) const {
|
|
63
|
+
for (idx_t i = 0; i < ni; i++) {
|
|
64
|
+
reconstruct (i0 + i, recons + i * d);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
void Index::search_and_reconstruct (idx_t n, const float *x, idx_t k,
|
|
70
|
+
float *distances, idx_t *labels,
|
|
71
|
+
float *recons) const {
|
|
72
|
+
search (n, x, k, distances, labels);
|
|
73
|
+
for (idx_t i = 0; i < n; ++i) {
|
|
74
|
+
for (idx_t j = 0; j < k; ++j) {
|
|
75
|
+
idx_t ij = i * k + j;
|
|
76
|
+
idx_t key = labels[ij];
|
|
77
|
+
float* reconstructed = recons + ij * d;
|
|
78
|
+
if (key < 0) {
|
|
79
|
+
// Fill with NaNs
|
|
80
|
+
memset(reconstructed, -1, sizeof(*reconstructed) * d);
|
|
81
|
+
} else {
|
|
82
|
+
reconstruct (key, reconstructed);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void Index::compute_residual (const float * x,
|
|
89
|
+
float * residual, idx_t key) const {
|
|
90
|
+
reconstruct (key, residual);
|
|
91
|
+
for (size_t i = 0; i < d; i++) {
|
|
92
|
+
residual[i] = x[i] - residual[i];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void Index::compute_residual_n (idx_t n, const float* xs,
|
|
97
|
+
float* residuals,
|
|
98
|
+
const idx_t* keys) const {
|
|
99
|
+
#pragma omp parallel for
|
|
100
|
+
for (idx_t i = 0; i < n; ++i) {
|
|
101
|
+
compute_residual(&xs[i * d], &residuals[i * d], keys[i]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
size_t Index::sa_code_size () const
|
|
108
|
+
{
|
|
109
|
+
FAISS_THROW_MSG ("standalone codec not implemented for this type of index");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
void Index::sa_encode (idx_t, const float *,
|
|
113
|
+
uint8_t *) const
|
|
114
|
+
{
|
|
115
|
+
FAISS_THROW_MSG ("standalone codec not implemented for this type of index");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
void Index::sa_decode (idx_t, const uint8_t *,
|
|
119
|
+
float *) const
|
|
120
|
+
{
|
|
121
|
+
FAISS_THROW_MSG ("standalone codec not implemented for this type of index");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
namespace {
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
// storage that explicitly reconstructs vectors before computing distances
|
|
129
|
+
struct GenericDistanceComputer : DistanceComputer {
|
|
130
|
+
size_t d;
|
|
131
|
+
const Index& storage;
|
|
132
|
+
std::vector<float> buf;
|
|
133
|
+
const float *q;
|
|
134
|
+
|
|
135
|
+
explicit GenericDistanceComputer(const Index& storage)
|
|
136
|
+
: storage(storage) {
|
|
137
|
+
d = storage.d;
|
|
138
|
+
buf.resize(d * 2);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
float operator () (idx_t i) override {
|
|
142
|
+
storage.reconstruct(i, buf.data());
|
|
143
|
+
return fvec_L2sqr(q, buf.data(), d);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
float symmetric_dis(idx_t i, idx_t j) override {
|
|
147
|
+
storage.reconstruct(i, buf.data());
|
|
148
|
+
storage.reconstruct(j, buf.data() + d);
|
|
149
|
+
return fvec_L2sqr(buf.data() + d, buf.data(), d);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
void set_query(const float *x) override {
|
|
153
|
+
q = x;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
} // namespace
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
DistanceComputer * Index::get_distance_computer() const {
|
|
163
|
+
if (metric_type == METRIC_L2) {
|
|
164
|
+
return new GenericDistanceComputer(*this);
|
|
165
|
+
} else {
|
|
166
|
+
FAISS_THROW_MSG ("get_distance_computer() not implemented");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
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_H
|
|
11
|
+
#define FAISS_INDEX_H
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
#include <cstdio>
|
|
15
|
+
#include <typeinfo>
|
|
16
|
+
#include <string>
|
|
17
|
+
#include <sstream>
|
|
18
|
+
|
|
19
|
+
#define FAISS_VERSION_MAJOR 1
|
|
20
|
+
#define FAISS_VERSION_MINOR 6
|
|
21
|
+
#define FAISS_VERSION_PATCH 1
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @namespace faiss
|
|
25
|
+
*
|
|
26
|
+
* Throughout the library, vectors are provided as float * pointers.
|
|
27
|
+
* Most algorithms can be optimized when several vectors are processed
|
|
28
|
+
* (added/searched) together in a batch. In this case, they are passed
|
|
29
|
+
* in as a matrix. When n vectors of size d are provided as float * x,
|
|
30
|
+
* component j of vector i is
|
|
31
|
+
*
|
|
32
|
+
* x[ i * d + j ]
|
|
33
|
+
*
|
|
34
|
+
* where 0 <= i < n and 0 <= j < d. In other words, matrices are
|
|
35
|
+
* always compact. When specifying the size of the matrix, we call it
|
|
36
|
+
* an n*d matrix, which implies a row-major storage.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
namespace faiss {
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
/// Some algorithms support both an inner product version and a L2 search version.
|
|
44
|
+
enum MetricType {
|
|
45
|
+
METRIC_INNER_PRODUCT = 0, ///< maximum inner product search
|
|
46
|
+
METRIC_L2 = 1, ///< squared L2 search
|
|
47
|
+
METRIC_L1, ///< L1 (aka cityblock)
|
|
48
|
+
METRIC_Linf, ///< infinity distance
|
|
49
|
+
METRIC_Lp, ///< L_p distance, p is given by metric_arg
|
|
50
|
+
|
|
51
|
+
/// some additional metrics defined in scipy.spatial.distance
|
|
52
|
+
METRIC_Canberra = 20,
|
|
53
|
+
METRIC_BrayCurtis,
|
|
54
|
+
METRIC_JensenShannon,
|
|
55
|
+
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/// Forward declarations see AuxIndexStructures.h
|
|
60
|
+
struct IDSelector;
|
|
61
|
+
struct RangeSearchResult;
|
|
62
|
+
struct DistanceComputer;
|
|
63
|
+
|
|
64
|
+
/** Abstract structure for an index
|
|
65
|
+
*
|
|
66
|
+
* Supports adding vertices and searching them.
|
|
67
|
+
*
|
|
68
|
+
* Currently only asymmetric queries are supported:
|
|
69
|
+
* database-to-database queries are not implemented.
|
|
70
|
+
*/
|
|
71
|
+
struct Index {
|
|
72
|
+
using idx_t = int64_t; ///< all indices are this type
|
|
73
|
+
using component_t = float;
|
|
74
|
+
using distance_t = float;
|
|
75
|
+
|
|
76
|
+
int d; ///< vector dimension
|
|
77
|
+
idx_t ntotal; ///< total nb of indexed vectors
|
|
78
|
+
bool verbose; ///< verbosity level
|
|
79
|
+
|
|
80
|
+
/// set if the Index does not require training, or if training is
|
|
81
|
+
/// done already
|
|
82
|
+
bool is_trained;
|
|
83
|
+
|
|
84
|
+
/// type of metric this index uses for search
|
|
85
|
+
MetricType metric_type;
|
|
86
|
+
float metric_arg; ///< argument of the metric type
|
|
87
|
+
|
|
88
|
+
explicit Index (idx_t d = 0, MetricType metric = METRIC_L2):
|
|
89
|
+
d(d),
|
|
90
|
+
ntotal(0),
|
|
91
|
+
verbose(false),
|
|
92
|
+
is_trained(true),
|
|
93
|
+
metric_type (metric),
|
|
94
|
+
metric_arg(0) {}
|
|
95
|
+
|
|
96
|
+
virtual ~Index ();
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/** Perform training on a representative set of vectors
|
|
100
|
+
*
|
|
101
|
+
* @param n nb of training vectors
|
|
102
|
+
* @param x training vecors, size n * d
|
|
103
|
+
*/
|
|
104
|
+
virtual void train(idx_t n, const float* x);
|
|
105
|
+
|
|
106
|
+
/** Add n vectors of dimension d to the index.
|
|
107
|
+
*
|
|
108
|
+
* Vectors are implicitly assigned labels ntotal .. ntotal + n - 1
|
|
109
|
+
* This function slices the input vectors in chuncks smaller than
|
|
110
|
+
* blocksize_add and calls add_core.
|
|
111
|
+
* @param x input matrix, size n * d
|
|
112
|
+
*/
|
|
113
|
+
virtual void add (idx_t n, const float *x) = 0;
|
|
114
|
+
|
|
115
|
+
/** Same as add, but stores xids instead of sequential ids.
|
|
116
|
+
*
|
|
117
|
+
* The default implementation fails with an assertion, as it is
|
|
118
|
+
* not supported by all indexes.
|
|
119
|
+
*
|
|
120
|
+
* @param xids if non-null, ids to store for the vectors (size n)
|
|
121
|
+
*/
|
|
122
|
+
virtual void add_with_ids (idx_t n, const float * x, const idx_t *xids);
|
|
123
|
+
|
|
124
|
+
/** query n vectors of dimension d to the index.
|
|
125
|
+
*
|
|
126
|
+
* return at most k vectors. If there are not enough results for a
|
|
127
|
+
* query, the result array is padded with -1s.
|
|
128
|
+
*
|
|
129
|
+
* @param x input vectors to search, size n * d
|
|
130
|
+
* @param labels output labels of the NNs, size n*k
|
|
131
|
+
* @param distances output pairwise distances, size n*k
|
|
132
|
+
*/
|
|
133
|
+
virtual void search (idx_t n, const float *x, idx_t k,
|
|
134
|
+
float *distances, idx_t *labels) const = 0;
|
|
135
|
+
|
|
136
|
+
/** query n vectors of dimension d to the index.
|
|
137
|
+
*
|
|
138
|
+
* return all vectors with distance < radius. Note that many
|
|
139
|
+
* indexes do not implement the range_search (only the k-NN search
|
|
140
|
+
* is mandatory).
|
|
141
|
+
*
|
|
142
|
+
* @param x input vectors to search, size n * d
|
|
143
|
+
* @param radius search radius
|
|
144
|
+
* @param result result table
|
|
145
|
+
*/
|
|
146
|
+
virtual void range_search (idx_t n, const float *x, float radius,
|
|
147
|
+
RangeSearchResult *result) const;
|
|
148
|
+
|
|
149
|
+
/** return the indexes of the k vectors closest to the query x.
|
|
150
|
+
*
|
|
151
|
+
* This function is identical as search but only return labels of neighbors.
|
|
152
|
+
* @param x input vectors to search, size n * d
|
|
153
|
+
* @param labels output labels of the NNs, size n*k
|
|
154
|
+
*/
|
|
155
|
+
void assign (idx_t n, const float * x, idx_t * labels, idx_t k = 1);
|
|
156
|
+
|
|
157
|
+
/// removes all elements from the database.
|
|
158
|
+
virtual void reset() = 0;
|
|
159
|
+
|
|
160
|
+
/** removes IDs from the index. Not supported by all
|
|
161
|
+
* indexes. Returns the number of elements removed.
|
|
162
|
+
*/
|
|
163
|
+
virtual size_t remove_ids (const IDSelector & sel);
|
|
164
|
+
|
|
165
|
+
/** Reconstruct a stored vector (or an approximation if lossy coding)
|
|
166
|
+
*
|
|
167
|
+
* this function may not be defined for some indexes
|
|
168
|
+
* @param key id of the vector to reconstruct
|
|
169
|
+
* @param recons reconstucted vector (size d)
|
|
170
|
+
*/
|
|
171
|
+
virtual void reconstruct (idx_t key, float * recons) const;
|
|
172
|
+
|
|
173
|
+
/** Reconstruct vectors i0 to i0 + ni - 1
|
|
174
|
+
*
|
|
175
|
+
* this function may not be defined for some indexes
|
|
176
|
+
* @param recons reconstucted vector (size ni * d)
|
|
177
|
+
*/
|
|
178
|
+
virtual void reconstruct_n (idx_t i0, idx_t ni, float *recons) const;
|
|
179
|
+
|
|
180
|
+
/** Similar to search, but also reconstructs the stored vectors (or an
|
|
181
|
+
* approximation in the case of lossy coding) for the search results.
|
|
182
|
+
*
|
|
183
|
+
* If there are not enough results for a query, the resulting arrays
|
|
184
|
+
* is padded with -1s.
|
|
185
|
+
*
|
|
186
|
+
* @param recons reconstructed vectors size (n, k, d)
|
|
187
|
+
**/
|
|
188
|
+
virtual void search_and_reconstruct (idx_t n, const float *x, idx_t k,
|
|
189
|
+
float *distances, idx_t *labels,
|
|
190
|
+
float *recons) const;
|
|
191
|
+
|
|
192
|
+
/** Computes a residual vector after indexing encoding.
|
|
193
|
+
*
|
|
194
|
+
* The residual vector is the difference between a vector and the
|
|
195
|
+
* reconstruction that can be decoded from its representation in
|
|
196
|
+
* the index. The residual can be used for multiple-stage indexing
|
|
197
|
+
* methods, like IndexIVF's methods.
|
|
198
|
+
*
|
|
199
|
+
* @param x input vector, size d
|
|
200
|
+
* @param residual output residual vector, size d
|
|
201
|
+
* @param key encoded index, as returned by search and assign
|
|
202
|
+
*/
|
|
203
|
+
virtual void compute_residual (const float * x,
|
|
204
|
+
float * residual, idx_t key) const;
|
|
205
|
+
|
|
206
|
+
/** Computes a residual vector after indexing encoding (batch form).
|
|
207
|
+
* Equivalent to calling compute_residual for each vector.
|
|
208
|
+
*
|
|
209
|
+
* The residual vector is the difference between a vector and the
|
|
210
|
+
* reconstruction that can be decoded from its representation in
|
|
211
|
+
* the index. The residual can be used for multiple-stage indexing
|
|
212
|
+
* methods, like IndexIVF's methods.
|
|
213
|
+
*
|
|
214
|
+
* @param n number of vectors
|
|
215
|
+
* @param xs input vectors, size (n x d)
|
|
216
|
+
* @param residuals output residual vectors, size (n x d)
|
|
217
|
+
* @param keys encoded index, as returned by search and assign
|
|
218
|
+
*/
|
|
219
|
+
virtual void compute_residual_n (idx_t n, const float* xs,
|
|
220
|
+
float* residuals,
|
|
221
|
+
const idx_t* keys) const;
|
|
222
|
+
|
|
223
|
+
/** Get a DistanceComputer (defined in AuxIndexStructures) object
|
|
224
|
+
* for this kind of index.
|
|
225
|
+
*
|
|
226
|
+
* DistanceComputer is implemented for indexes that support random
|
|
227
|
+
* access of their vectors.
|
|
228
|
+
*/
|
|
229
|
+
virtual DistanceComputer * get_distance_computer() const;
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
/* The standalone codec interface */
|
|
233
|
+
|
|
234
|
+
/** size of the produced codes in bytes */
|
|
235
|
+
virtual size_t sa_code_size () const;
|
|
236
|
+
|
|
237
|
+
/** encode a set of vectors
|
|
238
|
+
*
|
|
239
|
+
* @param n number of vectors
|
|
240
|
+
* @param x input vectors, size n * d
|
|
241
|
+
* @param bytes output encoded vectors, size n * sa_code_size()
|
|
242
|
+
*/
|
|
243
|
+
virtual void sa_encode (idx_t n, const float *x,
|
|
244
|
+
uint8_t *bytes) const;
|
|
245
|
+
|
|
246
|
+
/** encode a set of vectors
|
|
247
|
+
*
|
|
248
|
+
* @param n number of vectors
|
|
249
|
+
* @param bytes input encoded vectors, size n * sa_code_size()
|
|
250
|
+
* @param x output vectors, size n * d
|
|
251
|
+
*/
|
|
252
|
+
virtual void sa_decode (idx_t n, const uint8_t *bytes,
|
|
253
|
+
float *x) const;
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
#endif
|
|
@@ -0,0 +1,437 @@
|
|
|
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/Index2Layer.h>
|
|
11
|
+
|
|
12
|
+
#include <cmath>
|
|
13
|
+
#include <cstdio>
|
|
14
|
+
#include <cassert>
|
|
15
|
+
#include <stdint.h>
|
|
16
|
+
|
|
17
|
+
#ifdef __SSE__
|
|
18
|
+
#include <immintrin.h>
|
|
19
|
+
#endif
|
|
20
|
+
|
|
21
|
+
#include <algorithm>
|
|
22
|
+
|
|
23
|
+
#include <faiss/IndexIVFPQ.h>
|
|
24
|
+
|
|
25
|
+
#include <faiss/impl/FaissAssert.h>
|
|
26
|
+
#include <faiss/utils/utils.h>
|
|
27
|
+
#include <faiss/impl/AuxIndexStructures.h>
|
|
28
|
+
#include <faiss/IndexFlat.h>
|
|
29
|
+
#include <faiss/utils/distances.h>
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
#include <faiss/utils/Heap.h>
|
|
34
|
+
|
|
35
|
+
#include <faiss/Clustering.h>
|
|
36
|
+
|
|
37
|
+
#include <faiss/utils/hamming.h>
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
namespace faiss {
|
|
44
|
+
|
|
45
|
+
using idx_t = Index::idx_t;
|
|
46
|
+
|
|
47
|
+
/*************************************
|
|
48
|
+
* Index2Layer implementation
|
|
49
|
+
*************************************/
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Index2Layer::Index2Layer (Index * quantizer, size_t nlist,
|
|
53
|
+
int M, int nbit,
|
|
54
|
+
MetricType metric):
|
|
55
|
+
Index (quantizer->d, metric),
|
|
56
|
+
q1 (quantizer, nlist),
|
|
57
|
+
pq (quantizer->d, M, nbit)
|
|
58
|
+
{
|
|
59
|
+
is_trained = false;
|
|
60
|
+
for (int nbyte = 0; nbyte < 7; nbyte++) {
|
|
61
|
+
if ((1L << (8 * nbyte)) >= nlist) {
|
|
62
|
+
code_size_1 = nbyte;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
code_size_2 = pq.code_size;
|
|
67
|
+
code_size = code_size_1 + code_size_2;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Index2Layer::Index2Layer ()
|
|
71
|
+
{
|
|
72
|
+
code_size = code_size_1 = code_size_2 = 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Index2Layer::~Index2Layer ()
|
|
76
|
+
{}
|
|
77
|
+
|
|
78
|
+
void Index2Layer::train(idx_t n, const float* x)
|
|
79
|
+
{
|
|
80
|
+
if (verbose) {
|
|
81
|
+
printf ("training level-1 quantizer %ld vectors in %dD\n",
|
|
82
|
+
n, d);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
q1.train_q1 (n, x, verbose, metric_type);
|
|
86
|
+
|
|
87
|
+
if (verbose) {
|
|
88
|
+
printf("computing residuals\n");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const float * x_in = x;
|
|
92
|
+
|
|
93
|
+
x = fvecs_maybe_subsample (
|
|
94
|
+
d, (size_t*)&n, pq.cp.max_points_per_centroid * pq.ksub,
|
|
95
|
+
x, verbose, pq.cp.seed);
|
|
96
|
+
|
|
97
|
+
ScopeDeleter<float> del_x (x_in == x ? nullptr : x);
|
|
98
|
+
|
|
99
|
+
std::vector<idx_t> assign(n); // assignement to coarse centroids
|
|
100
|
+
q1.quantizer->assign (n, x, assign.data());
|
|
101
|
+
std::vector<float> residuals(n * d);
|
|
102
|
+
for (idx_t i = 0; i < n; i++) {
|
|
103
|
+
q1.quantizer->compute_residual (
|
|
104
|
+
x + i * d, residuals.data() + i * d, assign[i]);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (verbose)
|
|
108
|
+
printf ("training %zdx%zd product quantizer on %ld vectors in %dD\n",
|
|
109
|
+
pq.M, pq.ksub, n, d);
|
|
110
|
+
pq.verbose = verbose;
|
|
111
|
+
pq.train (n, residuals.data());
|
|
112
|
+
|
|
113
|
+
is_trained = true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
void Index2Layer::add(idx_t n, const float* x)
|
|
117
|
+
{
|
|
118
|
+
idx_t bs = 32768;
|
|
119
|
+
if (n > bs) {
|
|
120
|
+
for (idx_t i0 = 0; i0 < n; i0 += bs) {
|
|
121
|
+
idx_t i1 = std::min(i0 + bs, n);
|
|
122
|
+
if (verbose) {
|
|
123
|
+
printf("Index2Layer::add: adding %ld:%ld / %ld\n",
|
|
124
|
+
i0, i1, n);
|
|
125
|
+
}
|
|
126
|
+
add (i1 - i0, x + i0 * d);
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
std::vector<idx_t> codes1 (n);
|
|
132
|
+
q1.quantizer->assign (n, x, codes1.data());
|
|
133
|
+
std::vector<float> residuals(n * d);
|
|
134
|
+
for (idx_t i = 0; i < n; i++) {
|
|
135
|
+
q1.quantizer->compute_residual (
|
|
136
|
+
x + i * d, residuals.data() + i * d, codes1[i]);
|
|
137
|
+
}
|
|
138
|
+
std::vector<uint8_t> codes2 (n * code_size_2);
|
|
139
|
+
|
|
140
|
+
pq.compute_codes (residuals.data(), codes2.data(), n);
|
|
141
|
+
|
|
142
|
+
codes.resize ((ntotal + n) * code_size);
|
|
143
|
+
uint8_t *wp = &codes[ntotal * code_size];
|
|
144
|
+
|
|
145
|
+
{
|
|
146
|
+
int i = 0x11223344;
|
|
147
|
+
const char *ip = (char*)&i;
|
|
148
|
+
FAISS_THROW_IF_NOT_MSG (ip[0] == 0x44,
|
|
149
|
+
"works only on a little-endian CPU");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// copy to output table
|
|
153
|
+
for (idx_t i = 0; i < n; i++) {
|
|
154
|
+
memcpy (wp, &codes1[i], code_size_1);
|
|
155
|
+
wp += code_size_1;
|
|
156
|
+
memcpy (wp, &codes2[i * code_size_2], code_size_2);
|
|
157
|
+
wp += code_size_2;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
ntotal += n;
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
void Index2Layer::search(
|
|
165
|
+
idx_t /*n*/,
|
|
166
|
+
const float* /*x*/,
|
|
167
|
+
idx_t /*k*/,
|
|
168
|
+
float* /*distances*/,
|
|
169
|
+
idx_t* /*labels*/) const {
|
|
170
|
+
FAISS_THROW_MSG("not implemented");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
void Index2Layer::reconstruct_n(idx_t i0, idx_t ni, float* recons) const
|
|
175
|
+
{
|
|
176
|
+
float recons1[d];
|
|
177
|
+
FAISS_THROW_IF_NOT (i0 >= 0 && i0 + ni <= ntotal);
|
|
178
|
+
const uint8_t *rp = &codes[i0 * code_size];
|
|
179
|
+
|
|
180
|
+
for (idx_t i = 0; i < ni; i++) {
|
|
181
|
+
idx_t key = 0;
|
|
182
|
+
memcpy (&key, rp, code_size_1);
|
|
183
|
+
q1.quantizer->reconstruct (key, recons1);
|
|
184
|
+
rp += code_size_1;
|
|
185
|
+
pq.decode (rp, recons);
|
|
186
|
+
for (idx_t j = 0; j < d; j++) {
|
|
187
|
+
recons[j] += recons1[j];
|
|
188
|
+
}
|
|
189
|
+
rp += code_size_2;
|
|
190
|
+
recons += d;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
void Index2Layer::transfer_to_IVFPQ (IndexIVFPQ & other) const
|
|
195
|
+
{
|
|
196
|
+
FAISS_THROW_IF_NOT (other.nlist == q1.nlist);
|
|
197
|
+
FAISS_THROW_IF_NOT (other.code_size == code_size_2);
|
|
198
|
+
FAISS_THROW_IF_NOT (other.ntotal == 0);
|
|
199
|
+
|
|
200
|
+
const uint8_t *rp = codes.data();
|
|
201
|
+
|
|
202
|
+
for (idx_t i = 0; i < ntotal; i++) {
|
|
203
|
+
idx_t key = 0;
|
|
204
|
+
memcpy (&key, rp, code_size_1);
|
|
205
|
+
rp += code_size_1;
|
|
206
|
+
other.invlists->add_entry (key, i, rp);
|
|
207
|
+
rp += code_size_2;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
other.ntotal = ntotal;
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
void Index2Layer::reconstruct(idx_t key, float* recons) const
|
|
217
|
+
{
|
|
218
|
+
reconstruct_n (key, 1, recons);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
void Index2Layer::reset()
|
|
222
|
+
{
|
|
223
|
+
ntotal = 0;
|
|
224
|
+
codes.clear ();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
namespace {
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
struct Distance2Level : DistanceComputer {
|
|
232
|
+
size_t d;
|
|
233
|
+
const Index2Layer& storage;
|
|
234
|
+
std::vector<float> buf;
|
|
235
|
+
const float *q;
|
|
236
|
+
|
|
237
|
+
const float *pq_l1_tab, *pq_l2_tab;
|
|
238
|
+
|
|
239
|
+
explicit Distance2Level(const Index2Layer& storage)
|
|
240
|
+
: storage(storage) {
|
|
241
|
+
d = storage.d;
|
|
242
|
+
FAISS_ASSERT(storage.pq.dsub == 4);
|
|
243
|
+
pq_l2_tab = storage.pq.centroids.data();
|
|
244
|
+
buf.resize(2 * d);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
float symmetric_dis(idx_t i, idx_t j) override {
|
|
248
|
+
storage.reconstruct(i, buf.data());
|
|
249
|
+
storage.reconstruct(j, buf.data() + d);
|
|
250
|
+
return fvec_L2sqr(buf.data() + d, buf.data(), d);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
void set_query(const float *x) override {
|
|
254
|
+
q = x;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// well optimized for xNN+PQNN
|
|
259
|
+
struct DistanceXPQ4 : Distance2Level {
|
|
260
|
+
|
|
261
|
+
int M, k;
|
|
262
|
+
|
|
263
|
+
explicit DistanceXPQ4(const Index2Layer& storage)
|
|
264
|
+
: Distance2Level (storage) {
|
|
265
|
+
const IndexFlat *quantizer =
|
|
266
|
+
dynamic_cast<IndexFlat*> (storage.q1.quantizer);
|
|
267
|
+
|
|
268
|
+
FAISS_ASSERT(quantizer);
|
|
269
|
+
M = storage.pq.M;
|
|
270
|
+
pq_l1_tab = quantizer->xb.data();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
float operator () (idx_t i) override {
|
|
274
|
+
#ifdef __SSE__
|
|
275
|
+
const uint8_t *code = storage.codes.data() + i * storage.code_size;
|
|
276
|
+
long key = 0;
|
|
277
|
+
memcpy (&key, code, storage.code_size_1);
|
|
278
|
+
code += storage.code_size_1;
|
|
279
|
+
|
|
280
|
+
// walking pointers
|
|
281
|
+
const float *qa = q;
|
|
282
|
+
const __m128 *l1_t = (const __m128 *)(pq_l1_tab + d * key);
|
|
283
|
+
const __m128 *pq_l2_t = (const __m128 *)pq_l2_tab;
|
|
284
|
+
__m128 accu = _mm_setzero_ps();
|
|
285
|
+
|
|
286
|
+
for (int m = 0; m < M; m++) {
|
|
287
|
+
__m128 qi = _mm_loadu_ps(qa);
|
|
288
|
+
__m128 recons = l1_t[m] + pq_l2_t[*code++];
|
|
289
|
+
__m128 diff = qi - recons;
|
|
290
|
+
accu += diff * diff;
|
|
291
|
+
pq_l2_t += 256;
|
|
292
|
+
qa += 4;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
accu = _mm_hadd_ps (accu, accu);
|
|
296
|
+
accu = _mm_hadd_ps (accu, accu);
|
|
297
|
+
return _mm_cvtss_f32 (accu);
|
|
298
|
+
#else
|
|
299
|
+
FAISS_THROW_MSG("not implemented for non-x64 platforms");
|
|
300
|
+
#endif
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// well optimized for 2xNN+PQNN
|
|
306
|
+
struct Distance2xXPQ4 : Distance2Level {
|
|
307
|
+
|
|
308
|
+
int M_2, mi_nbits;
|
|
309
|
+
|
|
310
|
+
explicit Distance2xXPQ4(const Index2Layer& storage)
|
|
311
|
+
: Distance2Level(storage) {
|
|
312
|
+
const MultiIndexQuantizer *mi =
|
|
313
|
+
dynamic_cast<MultiIndexQuantizer*> (storage.q1.quantizer);
|
|
314
|
+
|
|
315
|
+
FAISS_ASSERT(mi);
|
|
316
|
+
FAISS_ASSERT(storage.pq.M % 2 == 0);
|
|
317
|
+
M_2 = storage.pq.M / 2;
|
|
318
|
+
mi_nbits = mi->pq.nbits;
|
|
319
|
+
pq_l1_tab = mi->pq.centroids.data();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
float operator () (idx_t i) override {
|
|
323
|
+
const uint8_t *code = storage.codes.data() + i * storage.code_size;
|
|
324
|
+
long key01 = 0;
|
|
325
|
+
memcpy (&key01, code, storage.code_size_1);
|
|
326
|
+
code += storage.code_size_1;
|
|
327
|
+
#ifdef __SSE__
|
|
328
|
+
|
|
329
|
+
// walking pointers
|
|
330
|
+
const float *qa = q;
|
|
331
|
+
const __m128 *pq_l1_t = (const __m128 *)pq_l1_tab;
|
|
332
|
+
const __m128 *pq_l2_t = (const __m128 *)pq_l2_tab;
|
|
333
|
+
__m128 accu = _mm_setzero_ps();
|
|
334
|
+
|
|
335
|
+
for (int mi_m = 0; mi_m < 2; mi_m++) {
|
|
336
|
+
long l1_idx = key01 & ((1L << mi_nbits) - 1);
|
|
337
|
+
const __m128 * pq_l1 = pq_l1_t + M_2 * l1_idx;
|
|
338
|
+
|
|
339
|
+
for (int m = 0; m < M_2; m++) {
|
|
340
|
+
__m128 qi = _mm_loadu_ps(qa);
|
|
341
|
+
__m128 recons = pq_l1[m] + pq_l2_t[*code++];
|
|
342
|
+
__m128 diff = qi - recons;
|
|
343
|
+
accu += diff * diff;
|
|
344
|
+
pq_l2_t += 256;
|
|
345
|
+
qa += 4;
|
|
346
|
+
}
|
|
347
|
+
pq_l1_t += M_2 << mi_nbits;
|
|
348
|
+
key01 >>= mi_nbits;
|
|
349
|
+
}
|
|
350
|
+
accu = _mm_hadd_ps (accu, accu);
|
|
351
|
+
accu = _mm_hadd_ps (accu, accu);
|
|
352
|
+
return _mm_cvtss_f32 (accu);
|
|
353
|
+
#else
|
|
354
|
+
FAISS_THROW_MSG("not implemented for non-x64 platforms");
|
|
355
|
+
#endif
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
} // namespace
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
DistanceComputer * Index2Layer::get_distance_computer() const {
|
|
365
|
+
#ifdef __SSE__
|
|
366
|
+
const MultiIndexQuantizer *mi =
|
|
367
|
+
dynamic_cast<MultiIndexQuantizer*> (q1.quantizer);
|
|
368
|
+
|
|
369
|
+
if (mi && pq.M % 2 == 0 && pq.dsub == 4) {
|
|
370
|
+
return new Distance2xXPQ4(*this);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const IndexFlat *fl =
|
|
374
|
+
dynamic_cast<IndexFlat*> (q1.quantizer);
|
|
375
|
+
|
|
376
|
+
if (fl && pq.dsub == 4) {
|
|
377
|
+
return new DistanceXPQ4(*this);
|
|
378
|
+
}
|
|
379
|
+
#endif
|
|
380
|
+
|
|
381
|
+
return Index::get_distance_computer();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
/* The standalone codec interface */
|
|
386
|
+
size_t Index2Layer::sa_code_size () const
|
|
387
|
+
{
|
|
388
|
+
return code_size;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
void Index2Layer::sa_encode (idx_t n, const float *x, uint8_t *bytes) const
|
|
392
|
+
{
|
|
393
|
+
FAISS_THROW_IF_NOT (is_trained);
|
|
394
|
+
std::unique_ptr<int64_t []> list_nos (new int64_t [n]);
|
|
395
|
+
q1.quantizer->assign (n, x, list_nos.get());
|
|
396
|
+
std::vector<float> residuals(n * d);
|
|
397
|
+
for (idx_t i = 0; i < n; i++) {
|
|
398
|
+
q1.quantizer->compute_residual (
|
|
399
|
+
x + i * d, residuals.data() + i * d, list_nos[i]);
|
|
400
|
+
}
|
|
401
|
+
pq.compute_codes (residuals.data(), bytes, n);
|
|
402
|
+
|
|
403
|
+
for (idx_t i = n - 1; i >= 0; i--) {
|
|
404
|
+
uint8_t * code = bytes + i * code_size;
|
|
405
|
+
memmove (code + code_size_1,
|
|
406
|
+
bytes + i * code_size_2, code_size_2);
|
|
407
|
+
q1.encode_listno (list_nos[i], code);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
void Index2Layer::sa_decode (idx_t n, const uint8_t *bytes, float *x) const
|
|
413
|
+
{
|
|
414
|
+
|
|
415
|
+
#pragma omp parallel
|
|
416
|
+
{
|
|
417
|
+
std::vector<float> residual (d);
|
|
418
|
+
|
|
419
|
+
#pragma omp for
|
|
420
|
+
for (size_t i = 0; i < n; i++) {
|
|
421
|
+
const uint8_t *code = bytes + i * code_size;
|
|
422
|
+
int64_t list_no = q1.decode_listno (code);
|
|
423
|
+
float *xi = x + i * d;
|
|
424
|
+
pq.decode (code + code_size_1, xi);
|
|
425
|
+
q1.quantizer->reconstruct (list_no, residual.data());
|
|
426
|
+
for (size_t j = 0; j < d; j++) {
|
|
427
|
+
xi[j] += residual[j];
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
} // namespace faiss
|