20 #include "FaissAssert.h"
21 #include "IndexFlat.h"
22 #include "AuxIndexStructures.h"
31 IndexIVF::IndexIVF (
Index * quantizer,
size_t d,
size_t nlist,
36 quantizer (quantizer),
37 quantizer_trains_alone (false),
40 maintain_direct_map (false)
42 FAISS_THROW_IF_NOT (d == quantizer->
d);
56 IndexIVF::IndexIVF ():
57 nlist (0), nprobe (1), quantizer (nullptr),
58 quantizer_trains_alone (false), own_fields (false),
59 maintain_direct_map (false)
74 if (new_maintain_direct_map) {
75 direct_map.resize (
ntotal, -1);
76 for (
size_t key = 0; key <
nlist; key++) {
77 const std::vector<long> & idlist =
ids[key];
79 for (
long ofs = 0; ofs < idlist.size(); ofs++) {
80 FAISS_THROW_IF_NOT_MSG (
81 0 <= idlist [ofs] && idlist[ofs] <
ntotal,
82 "direct map supported only for seuquential ids");
83 direct_map [idlist [ofs]] = key << 32 | ofs;
97 for (
size_t i = 0; i <
ids.size(); i++)
106 printf (
"IVF quantizer does not need training.\n");
109 printf (
"IVF quantizer trains alone...\n");
112 "nlist not consistent with quantizer size");
115 printf (
"Training IVF quantizer on %ld vectors in %dD\n",
125 printf (
"Training IVF residual\n");
133 printf(
"IndexIVF: no residual training\n");
141 std::vector<int> hist (
nlist);
142 for (
int i = 0; i <
nlist; i++) {
143 hist[i] =
ids[i].size();
150 std::vector<int> sizes(40);
151 for (
int i = 0; i <
nlist; i++) {
152 for (
int j = 0; j < sizes.size(); j++) {
153 if ((
ids[i].size() >> j) == 0) {
159 for (
int i = 0; i < sizes.size(); i++) {
161 printf (
"list size in < %d: %d instances\n",
171 FAISS_THROW_IF_NOT (other.
d ==
d);
175 "direct map copy not implemented");
176 FAISS_THROW_IF_NOT_MSG (
typeid (*
this) ==
typeid (other),
177 "can only merge indexes of the same type");
178 for (
long i = 0; i <
nlist; i++) {
179 std::vector<idx_t> & src = other.
ids[i];
180 std::vector<idx_t> & dest =
ids[i];
181 for (
long j = 0; j < src.size(); j++)
182 dest.push_back (src[j] + add_id);
192 IndexIVF::~IndexIVF()
203 IndexIVFFlat::IndexIVFFlat (Index * quantizer,
205 IndexIVF (quantizer, d, nlist, metric)
221 const long *precomputed_idx)
226 "cannot have direct map and add with ids");
230 if (precomputed_idx) {
231 idx = precomputed_idx;
233 long * idx0 =
new long [n];
234 quantizer->assign (n, x, idx0);
239 for (
size_t i = 0; i < n; i++) {
240 long id = xids ? xids[i] :
ntotal + i;
241 long list_no = idx [i];
244 assert (list_no < nlist);
246 ids[list_no].push_back (
id);
247 const float *xi = x + i *
d;
249 for (
size_t j = 0 ; j <
d ; j++)
250 vecs[list_no].push_back (xi [j]);
253 direct_map.push_back (list_no << 32 | (
ids[list_no].size() - 1));
257 printf(
"IndexIVFFlat::add_core: added %ld / %ld vectors\n",
263 void IndexIVFFlatStats::reset()
265 memset ((
void*)
this, 0,
sizeof (*
this));
269 IndexIVFFlatStats indexIVFFlat_stats;
274 const long * __restrict keys,
278 const size_t k = res->
k;
279 size_t nlistv = 0, ndis = 0;
281 #pragma omp parallel for reduction(+: nlistv, ndis)
282 for (
size_t i = 0; i < nx; i++) {
283 const float * xi = x + i *
d;
284 const long * keysi = keys + i *
nprobe;
285 float * __restrict simi = res->
get_val (i);
286 long * __restrict idxi = res->
get_ids (i);
287 minheap_heapify (k, simi, idxi);
289 for (
size_t ik = 0; ik <
nprobe; ik++) {
290 long key = keysi[ik];
295 if (key >= (
long) nlist) {
296 fprintf (stderr,
"Invalid key=%ld at ik=%ld nlist=%ld\n",
301 const size_t list_size =
ids[key].size();
302 const float * list_vecs =
vecs[key].data();
304 for (
size_t j = 0; j < list_size; j++) {
305 const float * yj = list_vecs + d * j;
306 float ip = fvec_inner_product (xi, yj, d);
308 minheap_pop (k, simi, idxi);
309 minheap_push (k, simi, idxi, ip,
ids[key][j]);
314 minheap_reorder (k, simi, idxi);
316 indexIVFFlat_stats.nq += nx;
317 indexIVFFlat_stats.nlist += nlistv;
318 indexIVFFlat_stats.ndis += ndis;
325 const long * __restrict keys,
328 const size_t k = res->
k;
329 size_t nlistv = 0, ndis = 0;
331 #pragma omp parallel for reduction(+: nlistv, ndis)
332 for (
size_t i = 0; i < nx; i++) {
333 const float * xi = x + i *
d;
334 const long * keysi = keys + i *
nprobe;
335 float * __restrict disi = res->
get_val (i);
336 long * __restrict idxi = res->
get_ids (i);
337 maxheap_heapify (k, disi, idxi);
339 for (
size_t ik = 0; ik <
nprobe; ik++) {
340 long key = keysi[ik];
345 if (key >= (
long) nlist) {
346 fprintf (stderr,
"Invalid key=%ld at ik=%ld nlist=%ld\n",
351 const size_t list_size =
ids[key].size();
352 const float * list_vecs =
vecs[key].data();
354 for (
size_t j = 0; j < list_size; j++) {
355 const float * yj = list_vecs + d * j;
357 if (disij < disi[0]) {
358 maxheap_pop (k, disi, idxi);
359 maxheap_push (k, disi, idxi, disij,
ids[key][j]);
364 maxheap_reorder (k, disi, idxi);
366 indexIVFFlat_stats.nq += nx;
367 indexIVFFlat_stats.nlist += nlistv;
368 indexIVFFlat_stats.ndis += ndis;
373 float *distances,
idx_t *labels)
const
377 quantizer->assign (n, x, idx,
nprobe);
384 float *distances,
idx_t *labels)
const
388 size_t(n), size_t(k), labels, distances};
393 size_t(n), size_t(k), labels, distances};
405 quantizer->assign (nx, x, keys,
nprobe);
411 for (
size_t i = 0; i < nx; i++) {
412 const float * xi = x + i *
d;
413 const long * keysi = keys + i *
nprobe;
418 for (
size_t ik = 0; ik <
nprobe; ik++) {
419 long key = keysi[ik];
420 if (key < 0 || key >= (
long) nlist) {
421 fprintf (stderr,
"Invalid key=%ld at ik=%ld nlist=%ld\n",
426 const size_t list_size =
ids[key].size();
427 const float * list_vecs =
vecs[key].data();
429 for (
size_t j = 0; j < list_size; j++) {
430 const float * yj = list_vecs + d * j;
433 if (disij < radius) {
434 qres.add (disij,
ids[key][j]);
437 float disij = fvec_inner_product(xi, yj, d);
438 if (disij > radius) {
439 qres.add (disij,
ids[key][j]);
453 for (
int i = 0; i <
nlist; i++) {
454 std::vector<float> & src = other.
vecs[i];
455 std::vector<float> & dest =
vecs[i];
456 for (
int j = 0; j < src.size(); j++)
457 dest.push_back (src[j]);
463 long a1,
long a2)
const
465 FAISS_THROW_IF_NOT (nlist == other.
nlist);
468 for (
long list_no = 0; list_no <
nlist; list_no++) {
469 const std::vector<idx_t> & ids_in =
ids[list_no];
470 std::vector<idx_t> & ids_out = other.
ids[list_no];
471 const std::vector<float> & vecs_in =
vecs[list_no];
472 std::vector<float> & vecs_out = other.
vecs[list_no];
474 for (
long i = 0; i < ids_in.size(); i++) {
475 idx_t id = ids_in[i];
476 if (subset_type == 0 && a1 <=
id &&
id < a2) {
477 ids_out.push_back (
id);
478 vecs_out.insert (vecs_out.end(),
479 vecs_in.begin() + i *
d,
480 vecs_in.begin() + (i + 1) * d);
491 std::vector<idx_t>
assign (n);
492 quantizer->assign (n, x, assign.data());
494 for (
int i = 0; i < n; i++) {
495 idx_t id = new_ids[i];
496 FAISS_THROW_IF_NOT_MSG (0 <=
id &&
id <
ntotal,
497 "id to update out of range");
499 long dm = direct_map[id];
500 long ofs = dm & 0xffffffff;
502 size_t l =
ids[il].size();
504 long id2 =
ids[il].back();
506 direct_map[id2] = (il << 32) | ofs;
507 memcpy (
vecs[il].data() + ofs * d,
508 vecs[il].data() + (l - 1) * d,
509 d *
sizeof(
vecs[il][0]));
512 vecs[il].resize((l - 1) * d);
516 size_t l =
ids[il].size();
517 long dm = (il << 32) | l;
519 ids[il].push_back (
id);
520 vecs[il].resize((l + 1) * d);
521 memcpy (
vecs[il].data() + l * d,
523 d *
sizeof(
vecs[il][0]));
535 for (
size_t key = 0; key <
nlist; key++) {
543 "direct map remove not implemented");
545 #pragma omp parallel for reduction(+: nremove)
546 for (
long i = 0; i <
nlist; i++) {
547 std::vector<idx_t> & idsi =
ids[i];
548 float *vecsi =
vecs[i].data();
550 long l = idsi.size(), j = 0;
552 if (sel.is_member (idsi[j])) {
555 memmove (vecsi + j * d,
556 vecsi + l * d, d *
sizeof (
float));
561 if (l < idsi.size()) {
562 nremove += idsi.size() - l;
564 vecs[i].resize (l * d);
574 FAISS_THROW_IF_NOT_MSG (direct_map.size() ==
ntotal,
575 "direct map is not initialized");
576 int list_no = direct_map[key] >> 32;
577 int ofs = direct_map[key] & 0xffffffff;
578 memcpy (recons, &
vecs[list_no][ofs * d], d *
sizeof(recons[0]));
588 IndexIVFFlatIPBounds::IndexIVFFlatIPBounds (
589 Index * quantizer,
size_t d,
size_t nlist,
591 IndexIVFFlat(quantizer, d, nlist, METRIC_INNER_PRODUCT), fsize(fsize)
593 part_norms.resize(nlist);
599 const long *precomputed_idx) {
605 if (precomputed_idx) {
606 idx = precomputed_idx;
608 long * idx0 =
new long [n];
609 quantizer->
assign (n, x, idx0);
616 const float * xi = x +
fsize;
617 for (
size_t i = 0; i < n; i++) {
628 void search_bounds_knn_inner_product (
636 size_t k = res->
k, nx = res->
nh, nprobe = ivf.
nprobe;
638 int fsize = ivf.
fsize;
640 size_t nlistv = 0, ndis = 0, npartial = 0;
642 #pragma omp parallel for reduction(+: nlistv, ndis, npartial)
643 for (
size_t i = 0; i < nx; i++) {
644 const float * xi = x + i * d;
645 const long * keysi = keys + i * nprobe;
646 float qnorm = qnorms[i];
647 float * __restrict simi = res->
get_val (i);
648 long * __restrict idxi = res->
get_ids (i);
649 minheap_heapify (k, simi, idxi);
651 for (
size_t ik = 0; ik < nprobe; ik++) {
652 long key = keysi[ik];
657 assert (key < (
long) ivf.
nlist);
660 const size_t list_size = ivf.
ids[key].size();
661 const float * yj = ivf.
vecs[key].data();
662 const float * bnorms = ivf.
part_norms[key].data();
664 for (
size_t j = 0; j < list_size; j++) {
665 float ip_part = fvec_inner_product (xi, yj, fsize);
666 float bound = ip_part + bnorms[j] * qnorm;
668 if (bound > simi[0]) {
669 float ip = ip_part + fvec_inner_product (
670 xi + fsize, yj + fsize, d - fsize);
672 minheap_pop (k, simi, idxi);
673 minheap_push (k, simi, idxi, ip, ivf.
ids[key][j]);
679 npartial += list_size;
681 minheap_reorder (k, simi, idxi);
683 indexIVFFlat_stats.nq += nx;
684 indexIVFFlat_stats.nlist += nlistv;
685 indexIVFFlat_stats.ndis += ndis;
686 indexIVFFlat_stats.npartial += npartial;
695 float *distances,
idx_t *labels)
const
700 quantizer->
assign (n, x, idx, nprobe);
702 float * qnorms =
new float [n];
705 #pragma omp parallel for
706 for (
size_t i = 0; i < n; i++) {
708 x + i * d + fsize, d - fsize));
712 size_t(n), size_t(k), labels, distances};
714 search_bounds_knn_inner_product (*
this, x, idx, &res, qnorms);
void search_preassigned(idx_t n, const float *x, idx_t k, const idx_t *assign, float *distances, idx_t *labels) const
perform search, without computing the assignment to the quantizer
int niter
clustering iterations
result structure for a single query
float fvec_L2sqr(const float *x, const float *y, size_t d)
Squared L2 distance between two vectors.
void search_knn_L2sqr(size_t nx, const float *x, const long *keys, float_maxheap_array_t *res) const
Implementation of the search for the L2 metric.
T * get_val(size_t key)
Return the list of values for a heap.
double imbalance_factor() const
1= perfectly balanced, >1: imbalanced
virtual void reset()=0
removes all elements from the database.
size_t nprobe
number of probes at query time
virtual void train(idx_t, const float *)
void reconstruct(idx_t key, float *recons) const override
void assign(idx_t n, const float *x, idx_t *labels, idx_t k=1)
bool quantizer_trains_alone
just pass over the trainset to quantizer
void range_search(idx_t n, const float *x, float radius, RangeSearchResult *result) const override
void copy_subset_to(IndexIVFFlat &other, int subset_type, long a1, long a2) const
void merge_from_residuals(IndexIVF &other) override
virtual void add_with_ids(idx_t n, const float *x, const long *xids)
virtual void train_residual(idx_t n, const float *x)
size_t k
allocated size per heap
double imbalance_factor(int n, int k, const long *assign)
a balanced assignment has a IF of 1
long remove_ids(const IDSelector &sel) override
std::vector< std::vector< long > > ids
Inverted lists for indexes.
Index * quantizer
quantizer that maps vectors to inverted lists
void train(idx_t n, const float *x) override
Trains the quantizer and calls train_residual to train sub-quantizers.
ClusteringParameters cp
to override default clustering params
void add_with_ids(idx_t n, const float *x, const long *xids) override
implemented for all IndexIVF* classes
bool own_fields
whether object owns the quantizer
long idx_t
all indices are this type
void reset() override
removes all elements from the database.
idx_t ntotal
total nb of indexed vectors
bool verbose
verbosity level
void reset() override
removes all elements from the database.
QueryResult & new_result(idx_t qno)
begin a new result
void update_vectors(int nv, idx_t *idx, const float *v)
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
std::vector< std::vector< float > > part_norms
norm of remainder (dimensions fsize:d)
float fvec_norm_L2sqr(const float *x, size_t d)
size_t fsize
nb of dimensions of pre-filter
the entries in the buffers are split per query
virtual void merge_from_residuals(IndexIVF &other)=0
void make_direct_map(bool new_maintain_direct_map=true)
TI * get_ids(size_t key)
Correspponding identifiers.
MetricType metric_type
type of metric this index uses for search
void print_stats() const
display some stats about the inverted lists
void add_core(idx_t n, const float *x, const long *xids, const long *precomputed_idx) override
same as add_with_ids, with precomputed coarse quantizer
size_t nlist
number of possible key values
void add(idx_t n, const float *x) override
Quantizes x and calls add_with_key.
virtual void train(idx_t n, const float *x, faiss::Index &index)
Index is used during the assignment stage.
bool is_trained
set if the Index does not require training, or if training is done already
void search_knn_inner_product(size_t nx, const float *x, const long *keys, float_minheap_array_t *res) const
Implementation of the search for the inner product metric.
bool maintain_direct_map
map for direct access to the elements. Enables reconstruct().
bool spherical
do we want normalized centroids?
virtual void merge_from(IndexIVF &other, idx_t add_id)
MetricType
Some algorithms support both an inner product vetsion and a L2 search version.
std::vector< std::vector< float > > vecs
virtual void add_core(idx_t n, const float *x, const long *xids, const long *precomputed_idx)
same as add_with_ids, with precomputed coarse quantizer
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override