Faiss

- Main Page
- Namespaces
- Classes
- Files

- File List

All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
/data/users/matthijs/github_faiss/faiss/MetaIndexes.cpp
1 /**
2 * Copyright (c) 2015-present, Facebook, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the CC-by-NC license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9 // Copyright 2004-present Facebook. All Rights Reserved
10 // -*- c++ -*-
11
12 #include "MetaIndexes.h"
13
14 #include <pthread.h>
15
16 #include <cstdio>
17
18 #include "FaissAssert.h"
19 #include "Heap.h"
20 #include "AuxIndexStructures.h"
21
22
23 namespace faiss {
24
25 /*****************************************************
26 * IndexIDMap implementation
27 *******************************************************/
28
29 IndexIDMap::IndexIDMap (Index *index):
30 index (index),
31 own_fields (false)
32 {
33 FAISS_THROW_IF_NOT_MSG (index->ntotal == 0, "index must be empty on input");
34 is_trained = index->is_trained;
35 metric_type = index->metric_type;
36 verbose = index->verbose;
37 d = index->d;
38 }
39
40 void IndexIDMap::add (idx_t, const float *)
41 {
42 FAISS_THROW_MSG ("add does not make sense with IndexIDMap, "
43 "use add_with_ids");
44 }
45
46
47 void IndexIDMap::train (idx_t n, const float *x)
48 {
49 index->train (n, x);
50 is_trained = index->is_trained;
51 }
52
53 void IndexIDMap::reset ()
54 {
55 index->reset ();
56 ntotal = 0;
57 }
58
59
60 void IndexIDMap::add_with_ids (idx_t n, const float * x, const long *xids)
61 {
62 index->add (n, x);
63 for (idx_t i = 0; i < n; i++)
64 id_map.push_back (xids[i]);
65 ntotal = index->ntotal;
66 }
67
68
69 void IndexIDMap::search (idx_t n, const float *x, idx_t k,
70 float *distances, idx_t *labels) const
71 {
72 index->search (n, x, k, distances, labels);
73 idx_t *li = labels;
74 for (idx_t i = 0; i < n * k; i++) {
75 li[i] = li[i] < 0 ? li[i] : id_map[li[i]];
76 }
77 }
78
79 namespace {
80
81 struct IDTranslatedSelector: IDSelector {
82 const std::vector <long> & id_map;
83 const IDSelector & sel;
84 IDTranslatedSelector (const std::vector <long> & id_map,
85 const IDSelector & sel):
86 id_map (id_map), sel (sel)
87 {}
88 bool is_member(idx_t id) const override {
89 return sel.is_member(id_map[id]);
90 }
91 };
92
93 }
94
95 long IndexIDMap::remove_ids (const IDSelector & sel)
96 {
97 // remove in sub-index first
98 IDTranslatedSelector sel2 (id_map, sel);
99 long nremove = index->remove_ids (sel2);
100
101 long j = 0;
102 for (idx_t i = 0; i < ntotal; i++) {
103 if (sel.is_member (id_map[i])) {
104 // remove
105 } else {
106 id_map[j] = id_map[i];
107 j++;
108 }
109 }
110 FAISS_ASSERT (j == index->ntotal);
111 ntotal = j;
112 return nremove;
113 }
114
115
116
117
118 IndexIDMap::~IndexIDMap ()
119 {
120 if (own_fields) delete index;
121 }
122
123 /*****************************************************
124 * IndexIDMap2 implementation
125 *******************************************************/
126
127 IndexIDMap2::IndexIDMap2 (Index *index): IndexIDMap (index)
128 {}
129
130 void IndexIDMap2::add_with_ids(idx_t n, const float* x, const long* xids)
131 {
132 size_t prev_ntotal = ntotal;
133 IndexIDMap::add_with_ids (n, x, xids);
134 for (size_t i = prev_ntotal; i < ntotal; i++) {
135 rev_map [id_map [i]] = i;
136 }
137 }
138
139 void IndexIDMap2::construct_rev_map ()
140 {
141 rev_map.clear ();
142 for (size_t i = 0; i < ntotal; i++) {
143 rev_map [id_map [i]] = i;
144 }
145 }
146
147
148 long IndexIDMap2::remove_ids(const IDSelector& sel)
149 {
150 // This is quite inefficient
151 long nremove = IndexIDMap::remove_ids (sel);
152 construct_rev_map ();
153 return nremove;
154 }
155
156 void IndexIDMap2::reconstruct (idx_t key, float * recons) const
157 {
158 try {
159 index->reconstruct (rev_map.at (key), recons);
160 } catch (const std::out_of_range& e) {
161 FAISS_THROW_FMT ("key %ld not found", key);
162 }
163 }
164
165
166
167 /*****************************************************
168 * IndexShards implementation
169 *******************************************************/
170
171 // subroutines
172 namespace {
173
174
175 typedef Index::idx_t idx_t;
176
177
178 template<class Job>
179 struct Thread {
180 Job job;
181 pthread_t thread;
182
183 Thread () {}
184
185 explicit Thread (const Job & job): job(job) {}
186
187 void start () {
188 pthread_create (&thread, nullptr, run, this);
189 }
190
191 void wait () {
192 pthread_join (thread, nullptr);
193 }
194
195 static void * run (void *arg) {
196 static_cast<Thread*> (arg)->job.run();
197 return nullptr;
198 }
199
200 };
201
202
203 /// callback + thread management to train 1 shard
204 struct TrainJob {
205 IndexShards *index; // the relevant index
206 int no; // shard number
207 idx_t n; // train points
208 const float *x;
209
210 void run ()
211 {
212 if (index->verbose)
213 printf ("begin train shard %d on %ld points\n", no, n);
214 index->shard_indexes [no]->train(n, x);
215 if (index->verbose)
216 printf ("end train shard %d\n", no);
217 }
218
219 };
220
221 struct AddJob {
222 IndexShards *index; // the relevant index
223 int no; // shard number
224
225 idx_t n;
226 const float *x;
227 const idx_t *ids;
228
229 void run ()
230 {
231 if (index->verbose)
232 printf ("begin add shard %d on %ld points\n", no, n);
233 if (ids)
234 index->shard_indexes[no]->add_with_ids (n, x, ids);
235 else
236 index->shard_indexes[no]->add (n, x);
237 if (index->verbose)
238 printf ("end add shard %d on %ld points\n", no, n);
239 }
240 };
241
242
243
244 /// callback + thread management to query in 1 shard
245 struct QueryJob {
246 const IndexShards *index; // the relevant index
247 int no; // shard number
248
249 // query params
250 idx_t n;
251 const float *x;
252 idx_t k;
253 float *distances;
254 idx_t *labels;
255
256
257 void run ()
258 {
259 if (index->verbose)
260 printf ("begin query shard %d on %ld points\n", no, n);
261 index->shard_indexes [no]->search (n, x, k,
262 distances, labels);
263 if (index->verbose)
264 printf ("end query shard %d\n", no);
265 }
266
267
268 };
269
270
271
272
273 // add translation to all valid labels
274 void translate_labels (long n, idx_t *labels, long translation)
275 {
276 if (translation == 0) return;
277 for (long i = 0; i < n; i++) {
278 if(labels[i] < 0) return;
279 labels[i] += translation;
280 }
281 }
282
283
284 /** merge result tables from several shards.
285 * @param all_distances size nshard * n * k
286 * @param all_labels idem
287 * @param translartions label translations to apply, size nshard
288 */
289
290 template <class C>
291 void merge_tables (long n, long k, long nshard,
292 float *distances, idx_t *labels,
293 const float *all_distances,
294 idx_t *all_labels,
295 const long *translations)
296 {
297 if(k == 0) {
298 return;
299 }
300
301 long stride = n * k;
302 #pragma omp parallel
303 {
304 std::vector<int> buf (2 * nshard);
305 int * pointer = buf.data();
306 int * shard_ids = pointer + nshard;
307 std::vector<float> buf2 (nshard);
308 float * heap_vals = buf2.data();
309 #pragma omp for
310 for (long i = 0; i < n; i++) {
311 // the heap maps values to the shard where they are
312 // produced.
313 const float *D_in = all_distances + i * k;
314 const idx_t *I_in = all_labels + i * k;
315 int heap_size = 0;
316
317 for (long s = 0; s < nshard; s++) {
318 pointer[s] = 0;
319 if (I_in[stride * s] >= 0)
320 heap_push<C> (++heap_size, heap_vals, shard_ids,
321 D_in[stride * s], s);
322 }
323
324 float *D = distances + i * k;
325 idx_t *I = labels + i * k;
326
327 for (int j = 0; j < k; j++) {
328 if (heap_size == 0) {
329 I[j] = -1;
330 D[j] = C::neutral();
331 } else {
332 // pop best element
333 int s = shard_ids[0];
334 int & p = pointer[s];
335 D[j] = heap_vals[0];
336 I[j] = I_in[stride * s + p] + translations[s];
337
338 heap_pop<C> (heap_size--, heap_vals, shard_ids);
339 p++;
340 if (p < k && I_in[stride * s + p] >= 0)
341 heap_push<C> (++heap_size, heap_vals, shard_ids,
342 D_in[stride * s + p], s);
343 }
344 }
345 }
346 }
347 }
348
349
350 };
351
352
353
354
355 IndexShards::IndexShards (idx_t d, bool threaded, bool successive_ids):
356 Index (d), own_fields (false),
357 threaded (threaded), successive_ids (successive_ids)
358 {
359
360 }
361
362 void IndexShards::add_shard (Index *idx)
363 {
364 shard_indexes.push_back (idx);
365 sync_with_shard_indexes ();
366 }
367
368 void IndexShards::sync_with_shard_indexes ()
369 {
370 if (shard_indexes.empty()) return;
371 Index * index0 = shard_indexes[0];
372 d = index0->d;
373 metric_type = index0->metric_type;
374 is_trained = index0->is_trained;
375 ntotal = index0->ntotal;
376 for (int i = 1; i < shard_indexes.size(); i++) {
377 Index * index = shard_indexes[i];
378 FAISS_THROW_IF_NOT (metric_type == index->metric_type);
379 FAISS_THROW_IF_NOT (d == index->d);
380 ntotal += index->ntotal;
381 }
382 }
383
384
385 void IndexShards::train (idx_t n, const float *x)
386 {
387
388 // pre-alloc because we don't want reallocs
389 std::vector<Thread<TrainJob > > tss (shard_indexes.size());
390 int nt = 0;
391 for (int i = 0; i < shard_indexes.size(); i++) {
392 if(!shard_indexes[i]->is_trained) {
393 TrainJob ts = {this, i, n, x};
394 if (threaded) {
395 tss[nt] = Thread<TrainJob> (ts);
396 tss[nt++].start();
397 } else {
398 ts.run();
399 }
400 }
401 }
402 for (int i = 0; i < nt; i++) {
403 tss[i].wait();
404 }
405 sync_with_shard_indexes ();
406 }
407
408 void IndexShards::add (idx_t n, const float *x)
409 {
410 add_with_ids (n, x, nullptr);
411 }
412
413 /**
414 * Cases (successive_ids, xids):
415 * - true, non-NULL ERROR: it makes no sense to pass in ids and
416 * request them to be shifted
417 * - true, NULL OK, but should be called only once (calls add()
418 * on sub-indexes).
419 * - false, non-NULL OK: will call add_with_ids with passed in xids
420 * distributed evenly over shards
421 * - false, NULL OK: will call add_with_ids on each sub-index,
422 * starting at ntotal
423 */
424
425 void IndexShards::add_with_ids (idx_t n, const float * x, const long *xids)
426 {
427
428 FAISS_THROW_IF_NOT_MSG(!(successive_ids && xids),
429 "It makes no sense to pass in ids and "
430 "request them to be shifted");
431
432 if (successive_ids) {
433 FAISS_THROW_IF_NOT_MSG(!xids,
434 "It makes no sense to pass in ids and "
435 "request them to be shifted");
436 FAISS_THROW_IF_NOT_MSG(ntotal == 0,
437 "when adding to IndexShards with sucessive_ids, "
438 "only add() in a single pass is supported");
439 }
440
441 long nshard = shard_indexes.size();
442 const long *ids = xids;
443 ScopeDeleter<long> del;
444 if (!ids && !successive_ids) {
445 long *aids = new long[n];
446 for (long i = 0; i < n; i++)
447 aids[i] = ntotal + i;
448 ids = aids;
449 del.set (ids);
450 }
451
452 std::vector<Thread<AddJob > > asa (shard_indexes.size());
453 int nt = 0;
454 for (int i = 0; i < nshard; i++) {
455 long i0 = i * n / nshard;
456 long i1 = (i + 1) * n / nshard;
457
458 AddJob as = {this, i,
459 i1 - i0, x + i0 * d,
460 ids ? ids + i0 : nullptr};
461 if (threaded) {
462 asa[nt] = Thread<AddJob>(as);
463 asa[nt++].start();
464 } else {
465 as.run();
466 }
467 }
468 for (int i = 0; i < nt; i++) {
469 asa[i].wait();
470 }
471 ntotal += n;
472 }
473
474
475
476
477
478 void IndexShards::reset ()
479 {
480 for (int i = 0; i < shard_indexes.size(); i++) {
481 shard_indexes[i]->reset ();
482 }
483 sync_with_shard_indexes ();
484 }
485
486 void IndexShards::search (
487 idx_t n, const float *x, idx_t k,
488 float *distances, idx_t *labels) const
489 {
490 long nshard = shard_indexes.size();
491 float *all_distances = new float [nshard * k * n];
492 idx_t *all_labels = new idx_t [nshard * k * n];
493 ScopeDeleter<float> del (all_distances);
494 ScopeDeleter<idx_t> del2 (all_labels);
495
496 #if 1
497
498 // pre-alloc because we don't want reallocs
499 std::vector<Thread<QueryJob> > qss (nshard);
500 for (int i = 0; i < nshard; i++) {
501 QueryJob qs = {
502 this, i, n, x, k,
503 all_distances + i * k * n,
504 all_labels + i * k * n
505 };
506 if (threaded) {
507 qss[i] = Thread<QueryJob> (qs);
508 qss[i].start();
509 } else {
510 qs.run();
511 }
512 }
513
514 if (threaded) {
515 for (int i = 0; i < qss.size(); i++) {
516 qss[i].wait();
517 }
518 }
519 #else
520
521 // pre-alloc because we don't want reallocs
522 std::vector<QueryJob> qss (nshard);
523 for (int i = 0; i < nshard; i++) {
524 QueryJob qs = {
525 this, i, n, x, k,
526 all_distances + i * k * n,
527 all_labels + i * k * n
528 };
529 if (threaded) {
530 qss[i] = qs;
531 } else {
532 qs.run();
533 }
534 }
535
536 if (threaded) {
537 #pragma omp parallel for
538 for (int i = 0; i < qss.size(); i++) {
539 qss[i].run();
540 }
541 }
542
543 #endif
544 std::vector<long> translations (nshard, 0);
545 if (successive_ids) {
546 translations[0] = 0;
547 for (int s = 0; s + 1 < nshard; s++)
548 translations [s + 1] = translations [s] +
549 shard_indexes [s]->ntotal;
550 }
551
552 if (metric_type == METRIC_L2) {
553 merge_tables< CMin<float, int> > (
554 n, k, nshard, distances, labels,
555 all_distances, all_labels, translations.data ());
556 } else {
557 merge_tables< CMax<float, int> > (
558 n, k, nshard, distances, labels,
559 all_distances, all_labels, translations.data ());
560 }
561
562 }
563
564
565
566 IndexShards::~IndexShards ()
567 {
568 if (own_fields) {
569 for (int s = 0; s < shard_indexes.size(); s++)
570 delete shard_indexes [s];
571 }
572 }
573
574
575 /*****************************************************
576 * IndexSplitVectors implementation
577 *******************************************************/
578
579
580 IndexSplitVectors::IndexSplitVectors (idx_t d, bool threaded):
581 Index (d), own_fields (false),
582 threaded (threaded), sum_d (0)
583 {
584
585 }
586
587 void IndexSplitVectors::add_sub_index (Index *index)
588 {
589 sub_indexes.push_back (index);
590 sync_with_sub_indexes ();
591 }
592
593 void IndexSplitVectors::sync_with_sub_indexes ()
594 {
595 if (sub_indexes.empty()) return;
596 Index * index0 = sub_indexes[0];
597 sum_d = index0->d;
598 metric_type = index0->metric_type;
599 is_trained = index0->is_trained;
600 ntotal = index0->ntotal;
601 for (int i = 1; i < sub_indexes.size(); i++) {
602 Index * index = sub_indexes[i];
603 FAISS_THROW_IF_NOT (metric_type == index->metric_type);
604 FAISS_THROW_IF_NOT (ntotal == index->ntotal);
605 sum_d += index->d;
606 }
607
608 }
609
610 void IndexSplitVectors::add (idx_t n, const float *x)
611 {
612 FAISS_THROW_MSG ("not implemented");
613 }
614
615 namespace {
616
617 /// callback + thread management to query in 1 shard
618 struct SplitQueryJob {
619 const IndexSplitVectors *index; // the relevant index
620 int no; // shard number
621
622 // query params
623 idx_t n;
624 const float *x;
625 idx_t k;
626 float *distances;
627 idx_t *labels;
628
629
630 void run ()
631 {
632 if (index->verbose)
633 printf ("begin query shard %d on %ld points\n", no, n);
634 const Index * sub_index = index->sub_indexes[no];
635 long sub_d = sub_index->d, d = index->d;
636 idx_t ofs = 0;
637 for (int i = 0; i < no; i++) ofs += index->sub_indexes[i]->d;
638 float *sub_x = new float [sub_d * n];
639 ScopeDeleter<float> del (sub_x);
640 for (idx_t i = 0; i < n; i++)
641 memcpy (sub_x + i * sub_d, x + ofs + i * d, sub_d * sizeof (sub_x));
642 sub_index->search (n, sub_x, k, distances, labels);
643 if (index->verbose)
644 printf ("end query shard %d\n", no);
645 }
646
647 };
648
649
650
651 }
652
653
654
655 void IndexSplitVectors::search (
656 idx_t n, const float *x, idx_t k,
657 float *distances, idx_t *labels) const
658 {
659 FAISS_THROW_IF_NOT_MSG (k == 1,
660 "search implemented only for k=1");
661 FAISS_THROW_IF_NOT_MSG (sum_d == d,
662 "not enough indexes compared to # dimensions");
663
664 long nshard = sub_indexes.size();
665 float *all_distances = new float [nshard * k * n];
666 idx_t *all_labels = new idx_t [nshard * k * n];
667 ScopeDeleter<float> del (all_distances);
668 ScopeDeleter<idx_t> del2 (all_labels);
669
670 // pre-alloc because we don't want reallocs
671 std::vector<Thread<SplitQueryJob> > qss (nshard);
672 for (int i = 0; i < nshard; i++) {
673 SplitQueryJob qs = {
674 this, i, n, x, k,
675 i == 0 ? distances : all_distances + i * k * n,
676 i == 0 ? labels : all_labels + i * k * n
677 };
678 if (threaded) {
679 qss[i] = Thread<SplitQueryJob> (qs);
680 qss[i].start();
681 } else {
682 qs.run();
683 }
684 }
685
686 if (threaded) {
687 for (int i = 0; i < qss.size(); i++) {
688 qss[i].wait();
689 }
690 }
691
692 long factor = 1;
693 for (int i = 0; i < nshard; i++) {
694 if (i > 0) { // results of 0 are already in the table
695 const float *distances_i = all_distances + i * k * n;
696 const idx_t *labels_i = all_labels + i * k * n;
697 for (long j = 0; j < n; j++) {
698 if (labels[j] >= 0 && labels_i[j] >= 0) {
699 labels[j] += labels_i[j] * factor;
700 distances[j] += distances_i[j];
701 } else {
702 labels[j] = -1;
703 distances[j] = 0.0 / 0.0;
704 }
705 }
706 }
707 factor *= sub_indexes[i]->ntotal;
708 }
709
710 }
711
712
713 void IndexSplitVectors::train (idx_t n, const float *x)
714 {
715 FAISS_THROW_MSG ("not implemented");
716 }
717
718 void IndexSplitVectors::reset ()
719 {
720 FAISS_THROW_MSG ("not implemented");
721 }
722
723
724 IndexSplitVectors::~IndexSplitVectors ()
725 {
726 if (own_fields) {
727 for (int s = 0; s < sub_indexes.size(); s++)
728 delete sub_indexes [s];
729 }
730 }
731
732
733
734
735
736
737 }; // namespace faiss
faiss::IndexShards::train
void train(idx_t n, const float *x) override
Definition: MetaIndexes.cpp:385
faiss::IndexShards::IndexShards
IndexShards(idx_t d, bool threaded=false, bool successive_ids=true)
Definition: MetaIndexes.cpp:355
faiss::Index::reset
virtual void reset()=0
removes all elements from the database.
faiss::IndexIDMap::search
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
Definition: MetaIndexes.cpp:69
faiss::IndexShards::add_with_ids
void add_with_ids(idx_t n, const float *x, const long *xids) override
Definition: MetaIndexes.cpp:425
faiss::IDSelector
Definition: AuxIndexStructures.h:52
faiss::IndexShards::search
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
Definition: MetaIndexes.cpp:486
faiss::IndexIDMap2::add_with_ids
void add_with_ids(idx_t n, const float *x, const long *xids) override
Definition: MetaIndexes.cpp:130
faiss::ScopeDeleter
Definition: FaissException.h:39
faiss::IndexSplitVectors::reset
void reset() override
removes all elements from the database.
Definition: MetaIndexes.cpp:718
faiss::IndexIDMap::add
void add(idx_t n, const float *x) override
this will fail. Use add_with_ids
Definition: MetaIndexes.cpp:40
faiss::IndexSplitVectors
Definition: MetaIndexes.h:131
faiss::Index::d
int d
vector dimension
Definition: Index.h:64
faiss::IndexIDMap::id_map
std::vector< long > id_map
! whether pointers are deleted in destructo
Definition: MetaIndexes.h:29
faiss::Index::add
virtual void add(idx_t n, const float *x)=0
faiss::IndexSplitVectors::train
void train(idx_t n, const float *x) override
Definition: MetaIndexes.cpp:713
faiss::IndexShards::add
void add(idx_t n, const float *x) override
supported only for sub-indices that implement add_with_ids
Definition: MetaIndexes.cpp:408
faiss::Index::idx_t
long idx_t
all indices are this type
Definition: Index.h:62
faiss::Index::ntotal
idx_t ntotal
total nb of indexed vectors
Definition: Index.h:65
faiss::IndexIDMap2::construct_rev_map
void construct_rev_map()
make the rev_map from scratch
Definition: MetaIndexes.cpp:139
faiss::Index::verbose
bool verbose
verbosity level
Definition: Index.h:66
faiss::IndexSplitVectors::add
void add(idx_t n, const float *x) override
Definition: MetaIndexes.cpp:610
faiss::Index::remove_ids
virtual long remove_ids(const IDSelector &sel)
Definition: Index.cpp:37
faiss::IndexIDMap2::remove_ids
long remove_ids(const IDSelector &sel) override
remove ids adapted to IndexFlat
Definition: MetaIndexes.cpp:148
faiss::IndexShards::threaded
bool threaded
should the sub-indexes be deleted along with this?
Definition: MetaIndexes.h:87
faiss::Index::search
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const =0
faiss::IndexShards::reset
void reset() override
removes all elements from the database.
Definition: MetaIndexes.cpp:478
faiss::IndexIDMap::reset
void reset() override
removes all elements from the database.
Definition: MetaIndexes.cpp:53
faiss::IndexSplitVectors::search
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
Definition: MetaIndexes.cpp:655
faiss::IndexIDMap::remove_ids
long remove_ids(const IDSelector &sel) override
remove ids adapted to IndexFlat
Definition: MetaIndexes.cpp:95
faiss::Index::metric_type
MetricType metric_type
type of metric this index uses for search
Definition: Index.h:72
faiss::IndexIDMap2::reconstruct
void reconstruct(idx_t key, float *recons) const override
Definition: MetaIndexes.cpp:156
faiss::IndexIDMap::train
void train(idx_t n, const float *x) override
Definition: MetaIndexes.cpp:47
faiss::Index
Definition: Index.h:60
faiss::Index::is_trained
bool is_trained
set if the Index does not require training, or if training is done already
Definition: Index.h:69
faiss::Index::reconstruct
virtual void reconstruct(idx_t key, float *recons) const
Definition: Index.cpp:44
faiss::Index::train
virtual void train(idx_t n, const float *x)
Definition: Index.h:89
faiss::IndexIDMap::add_with_ids
void add_with_ids(idx_t n, const float *x, const long *xids) override
Definition: MetaIndexes.cpp:60
faiss::IndexSplitVectors::IndexSplitVectors
IndexSplitVectors(idx_t d, bool threaded=false)
sum of dimensions seen so far
Definition: MetaIndexes.cpp:580
faiss::IndexIDMap::own_fields
bool own_fields
! the sub-index
Definition: MetaIndexes.h:28