15 #include "VectorTransform.h"
16 #include "FaissAssert.h"
20 namespace faiss {
namespace ivflib {
23 void check_compatible_for_merge (
const Index * index0,
33 FAISS_THROW_IF_NOT_MSG (pt1,
"both indexes should be pretransforms");
35 FAISS_THROW_IF_NOT (pt0->chain.size() == pt1->chain.size());
36 for (
int i = 0; i < pt0->chain.size(); i++) {
37 FAISS_THROW_IF_NOT (
typeid(pt0->chain[i]) ==
typeid(pt1->chain[i]));
43 FAISS_THROW_IF_NOT (
typeid(index0) ==
typeid(index1));
44 FAISS_THROW_IF_NOT (index0->d == index1->d &&
45 index0->metric_type == index1->metric_type);
51 FAISS_THROW_IF_NOT (ivf1);
60 const IndexIVF * extract_index_ivf (
const Index * index)
63 dynamic_cast<const IndexPreTransform *>(index)) {
67 auto *ivf =
dynamic_cast<const IndexIVF *
>(index);
69 FAISS_THROW_IF_NOT (ivf);
74 IndexIVF * extract_index_ivf (Index * index) {
75 return const_cast<IndexIVF*
> (extract_index_ivf ((
const Index*)(index)));
80 check_compatible_for_merge (index0, index1);
81 IndexIVF * ivf0 = extract_index_ivf (index0);
82 IndexIVF * ivf1 = extract_index_ivf (index1);
84 ivf0->merge_from (*ivf1, shift_ids ? ivf0->ntotal : 0);
87 index0->
ntotal = ivf0->ntotal;
88 index1->
ntotal = ivf1->ntotal;
94 const float* x,
int n,
97 std::unique_ptr<float[]> del;
98 if (
auto index_pre = dynamic_cast<faiss::IndexPreTransform*>(index)) {
99 x = index_pre->apply_chain(n, x);
100 del.reset((
float*)x);
101 index = index_pre->index;
116 idx_t* query_centroid_ids,
117 idx_t* result_centroid_ids)
119 const float *x = xin;
120 std::unique_ptr<float []> del;
121 if (
auto index_pre = dynamic_cast<faiss::IndexPreTransform*>(index)) {
122 x = index_pre->apply_chain(n, x);
123 del.reset((
float*)x);
124 index = index_pre->index;
129 size_t nprobe = index_ivf->
nprobe;
130 std::vector<idx_t> cent_nos (n * nprobe);
131 std::vector<float> cent_dis (n * nprobe);
133 n, x, nprobe, cent_dis.data(), cent_nos.data());
135 if (query_centroid_ids) {
136 for (
size_t i = 0; i < n; i++)
137 query_centroid_ids[i] = cent_nos[i * nprobe];
141 cent_nos.data(), cent_dis.data(),
142 distances, labels,
true);
144 for (
size_t i = 0; i < n * k; i++) {
145 idx_t label = labels[i];
147 if (result_centroid_ids)
148 result_centroid_ids[i] = -1;
150 long list_no = label >> 32;
151 long list_index = label & 0xffffffff;
152 if (result_centroid_ids)
153 result_centroid_ids[i] = list_no;
165 FAISS_THROW_IF_NOT_MSG (
ils,
166 "only supports indexes with ArrayInvertedLists");
171 static void shift_and_add (std::vector<T> & dst,
173 const std::vector<T> & src)
176 memmove (dst.data(), dst.data() +
remove,
177 (dst.size() -
remove) *
sizeof (T));
178 size_t insert_point = dst.size() -
remove;
179 dst.resize (insert_point + src.size());
180 memcpy (dst.data() + insert_point, src.data (), src.size() *
sizeof(T));
184 static void remove_from_begin (std::vector<T> & v,
188 v.erase (v.begin(), v.begin() +
remove);
193 FAISS_THROW_IF_NOT_MSG (!remove_oldest ||
n_slice > 0,
194 "cannot remove slice: there is none");
198 check_compatible_for_merge (index, sub_index);
200 extract_index_ivf (sub_index)->
invlists);
201 FAISS_THROW_IF_NOT_MSG (ils2,
"supports only ArrayInvertedLists");
203 IndexIVF *index_ivf = extract_index_ivf (index);
205 if (remove_oldest && ils2) {
206 for (
int i = 0; i <
nlist; i++) {
207 std::vector<size_t> & sizesi =
sizes[i];
208 size_t amount_to_remove = sizesi[0];
209 index_ivf->
ntotal += ils2->
ids[i].size() - amount_to_remove;
211 shift_and_add (
ils->
ids[i], amount_to_remove, ils2->
ids[i]);
214 for (
int j = 0; j + 1 <
n_slice; j++) {
215 sizesi[j] = sizesi[j + 1] - amount_to_remove;
217 sizesi[n_slice - 1] =
ils->
ids[i].size();
220 for (
int i = 0; i <
nlist; i++) {
221 index_ivf->
ntotal += ils2->
ids[i].size();
222 shift_and_add (
ils->
ids[i], 0, ils2->
ids[i]);
223 shift_and_add (
ils->codes[i], 0, ils2->codes[i]);
227 }
else if (remove_oldest) {
228 for (
int i = 0; i <
nlist; i++) {
229 size_t amount_to_remove =
sizes[i][0];
230 index_ivf->
ntotal -= amount_to_remove;
231 remove_from_begin (
ils->
ids[i], amount_to_remove);
232 remove_from_begin (
ils->codes[i],
234 for (
int j = 0; j + 1 <
n_slice; j++) {
235 sizes[i][j] =
sizes[i][j + 1] - amount_to_remove;
241 FAISS_THROW_MSG (
"nothing to do???");
252 get_invlist_range (
const Index *index,
long i0,
long i1)
254 const IndexIVF *ivf = extract_index_ivf (index);
256 FAISS_THROW_IF_NOT (0 <= i0 && i0 <= i1 && i1 <= ivf->nlist);
262 for (
long i = i0; i < i1; i++) {
263 il->add_entries(i - i0, src->
list_size(i),
272 void set_invlist_range (Index *index,
long i0,
long i1,
273 ArrayInvertedLists * src)
275 IndexIVF *ivf = extract_index_ivf (index);
277 FAISS_THROW_IF_NOT (0 <= i0 && i0 <= i1 && i1 <= ivf->nlist);
279 ArrayInvertedLists *dst =
dynamic_cast<ArrayInvertedLists *
>(ivf->invlists);
280 FAISS_THROW_IF_NOT_MSG (dst,
"only ArrayInvertedLists supported");
281 FAISS_THROW_IF_NOT (src->nlist == i1 - i0 &&
282 dst->code_size == src->code_size);
284 size_t ntotal = index->ntotal;
285 for (
long i = i0 ; i < i1; i++) {
286 ntotal -= dst->list_size (i);
287 ntotal += src->list_size (i - i0);
288 std::swap (src->codes[i - i0], dst->codes[i]);
289 std::swap (src->ids[i - i0], dst->ids[i]);
291 ivf->ntotal = index->ntotal = ntotal;
295 void search_with_parameters (
const Index *index,
296 idx_t n,
const float *x, idx_t k,
297 float *distances, idx_t *labels,
298 IVFSearchParameters *params)
300 FAISS_THROW_IF_NOT (params);
301 const float *prev_x = x;
302 ScopeDeleter<float> del;
304 if (
auto ip = dynamic_cast<const IndexPreTransform *> (index)) {
305 x = ip->apply_chain (n, x);
312 std::vector<idx_t> Iq(params->nprobe * n);
313 std::vector<float> Dq(params->nprobe * n);
315 const IndexIVF *index_ivf =
dynamic_cast<const IndexIVF *
>(index);
316 FAISS_THROW_IF_NOT (index_ivf);
318 index_ivf->quantizer->search(n, x, params->nprobe,
319 Dq.data(), Iq.data());
321 index_ivf->search_preassigned(n, x, k, Iq.data(), Dq.data(),
virtual void search_preassigned(idx_t n, const float *x, idx_t k, const idx_t *assign, const float *centroid_dis, float *distances, idx_t *labels, bool store_pairs, const IVFSearchParameters *params=nullptr) const
simple (default) implementation as an array of inverted lists
void check_compatible_for_merge(const IndexIVF &other) const
size_t nprobe
number of probes at query time
void assign(idx_t n, const float *x, idx_t *labels, idx_t k=1)
virtual size_t list_size(size_t list_no) const =0
get the size of a list
size_t nlist
same as index->nlist
virtual idx_t get_single_id(size_t list_no, size_t offset) const
size_t code_size
code size per vector in bytes
ArrayInvertedLists * ils
InvertedLists of index.
int n_slice
number of slices currently in index
std::vector< std::vector< size_t > > sizes
cumulative list sizes at each slice
SlidingIndexWindow(Index *index)
index should be initially empty and trained
idx_t ntotal
total nb of indexed vectors
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const =0
void step(const Index *sub_index, bool remove_oldest)
size_t nlist
number of possible key values
InvertedLists * invlists
Acess to the actual data.
std::vector< std::vector< idx_t > > ids
Inverted lists for indexes.
Index * quantizer
quantizer that maps vectors to inverted lists