Faiss

- Main Page
- Namespaces
- Classes
- Files

- File List

All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
/tmp/faiss/IndexHNSW.cpp
1 /**
2 * Copyright (c) 2015-present, Facebook, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD+Patents license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9 // -*- c++ -*-
10
11 #include "IndexHNSW.h"
12
13
14 #include <cstdlib>
15 #include <cassert>
16 #include <cstring>
17 #include <cstdio>
18 #include <cmath>
19 #include <omp.h>
20
21 #include <unordered_set>
22 #include <queue>
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <stdint.h>
28
29 #ifdef __SSE__
30 #include <immintrin.h>
31 #endif
32
33 #include "utils.h"
34 #include "Heap.h"
35 #include "FaissAssert.h"
36 #include "IndexFlat.h"
37 #include "IndexIVFPQ.h"
38
39
40 extern "C" {
41
42 /* declare BLAS functions, see http://www.netlib.org/clapack/cblas/ */
43
44 int sgemm_ (const char *transa, const char *transb, FINTEGER *m, FINTEGER *
45 n, FINTEGER *k, const float *alpha, const float *a,
46 FINTEGER *lda, const float *b, FINTEGER *
47 ldb, float *beta, float *c, FINTEGER *ldc);
48
49 }
50
51 namespace faiss {
52
53 using idx_t = Index::idx_t;
54 using MinimaxHeap = HNSW::MinimaxHeap;
55 using storage_idx_t = HNSW::storage_idx_t;
56 using NodeDistCloser = HNSW::NodeDistCloser;
57 using NodeDistFarther = HNSW::NodeDistFarther;
58 using DistanceComputer = HNSW::DistanceComputer;
59
60 HNSWStats hnsw_stats;
61
62 /**************************************************************
63 * add / search blocks of descriptors
64 **************************************************************/
65
66 namespace {
67
68
69 void hnsw_add_vertices(IndexHNSW &index_hnsw,
70 size_t n0,
71 size_t n, const float *x,
72 bool verbose,
73 bool preset_levels = false) {
74 HNSW & hnsw = index_hnsw.hnsw;
75 size_t ntotal = n0 + n;
76 double t0 = getmillisecs();
77 if (verbose) {
78 printf("hnsw_add_vertices: adding %ld elements on top of %ld "
79 "(preset_levels=%d)\n",
80 n, n0, int(preset_levels));
81 }
82
83 int max_level = hnsw.prepare_level_tab(n, preset_levels);
84
85 if (verbose) {
86 printf(" max_level = %d\n", max_level);
87 }
88
89 std::vector<omp_lock_t> locks(ntotal);
90 for(int i = 0; i < ntotal; i++)
91 omp_init_lock(&locks[i]);
92
93 // add vectors from highest to lowest level
94 std::vector<int> hist;
95 std::vector<int> order(n);
96
97 { // make buckets with vectors of the same level
98
99 // build histogram
100 for (int i = 0; i < n; i++) {
101 storage_idx_t pt_id = i + n0;
102 int pt_level = hnsw.levels[pt_id] - 1;
103 while (pt_level >= hist.size())
104 hist.push_back(0);
105 hist[pt_level] ++;
106 }
107
108 // accumulate
109 std::vector<int> offsets(hist.size() + 1, 0);
110 for (int i = 0; i < hist.size() - 1; i++) {
111 offsets[i + 1] = offsets[i] + hist[i];
112 }
113
114 // bucket sort
115 for (int i = 0; i < n; i++) {
116 storage_idx_t pt_id = i + n0;
117 int pt_level = hnsw.levels[pt_id] - 1;
118 order[offsets[pt_level]++] = pt_id;
119 }
120 }
121
122 { // perform add
123 RandomGenerator rng2(789);
124
125 int i1 = n;
126
127 for (int pt_level = hist.size() - 1; pt_level >= 0; pt_level--) {
128 int i0 = i1 - hist[pt_level];
129
130 if (verbose) {
131 printf("Adding %d elements at level %d\n",
132 i1 - i0, pt_level);
133 }
134
135 // random permutation to get rid of dataset order bias
136 for (int j = i0; j < i1; j++)
137 std::swap(order[j], order[j + rng2.rand_int(i1 - j)]);
138
139 #pragma omp parallel
140 {
141 VisitedTable vt (ntotal);
142
143 DistanceComputer *dis = index_hnsw.get_distance_computer();
144 ScopeDeleter1<DistanceComputer> del(dis);
145 int prev_display = verbose && omp_get_thread_num() == 0 ? 0 : -1;
146
147 #pragma omp for schedule(dynamic)
148 for (int i = i0; i < i1; i++) {
149 storage_idx_t pt_id = order[i];
150 dis->set_query (x + (pt_id - n0) * dis->d);
151
152 hnsw.add_with_locks(*dis, pt_level, pt_id, locks, vt);
153
154 if (prev_display >= 0 && i - i0 > prev_display + 10000) {
155 prev_display = i - i0;
156 printf(" %d / %d\r", i - i0, i1 - i0);
157 fflush(stdout);
158 }
159 }
160 }
161 i1 = i0;
162 }
163 FAISS_ASSERT(i1 == 0);
164 }
165 if (verbose) {
166 printf("Done in %.3f ms\n", getmillisecs() - t0);
167 }
168
169 for(int i = 0; i < ntotal; i++) {
170 omp_destroy_lock(&locks[i]);
171 }
172 }
173
174
175 } // namespace
176
177
178
179
180 /**************************************************************
181 * IndexHNSW implementation
182 **************************************************************/
183
184 IndexHNSW::IndexHNSW(int d, int M):
185 Index(d, METRIC_L2),
186 hnsw(M),
187 own_fields(false),
188 storage(nullptr),
189 reconstruct_from_neighbors(nullptr)
190 {}
191
192 IndexHNSW::IndexHNSW(Index *storage, int M):
193 Index(storage->d, METRIC_L2),
194 hnsw(M),
195 own_fields(false),
196 storage(storage),
197 reconstruct_from_neighbors(nullptr)
198 {}
199
200 IndexHNSW::~IndexHNSW() {
201 if (own_fields) {
202 delete storage;
203 }
204 }
205
206 void IndexHNSW::train(idx_t n, const float* x)
207 {
208 // hnsw structure does not require training
209 storage->train (n, x);
210 is_trained = true;
211 }
212
213 void IndexHNSW::search (idx_t n, const float *x, idx_t k,
214 float *distances, idx_t *labels) const
215
216 {
217
218 #pragma omp parallel
219 {
220 VisitedTable vt (ntotal);
221 DistanceComputer *dis = get_distance_computer();
222 ScopeDeleter1<DistanceComputer> del(dis);
223 size_t nreorder = 0;
224
225 #pragma omp for
226 for(idx_t i = 0; i < n; i++) {
227 idx_t * idxi = labels + i * k;
228 float * simi = distances + i * k;
229 dis->set_query(x + i * d);
230
231 maxheap_heapify (k, simi, idxi);
232 hnsw.search(*dis, k, idxi, simi, vt);
233
234 maxheap_reorder (k, simi, idxi);
235
236 if (reconstruct_from_neighbors &&
237 reconstruct_from_neighbors->k_reorder != 0) {
238 int k_reorder = reconstruct_from_neighbors->k_reorder;
239 if (k_reorder == -1 || k_reorder > k) k_reorder = k;
240
241 nreorder += reconstruct_from_neighbors->compute_distances(
242 k_reorder, idxi, x + i * d, simi);
243
244 // sort top k_reorder
245 maxheap_heapify (k_reorder, simi, idxi, simi, idxi, k_reorder);
246 maxheap_reorder (k_reorder, simi, idxi);
247 }
248 }
249 #pragma omp critical
250 {
251 hnsw_stats.nreorder += nreorder;
252 }
253 }
254
255
256 }
257
258
259 void IndexHNSW::add(idx_t n, const float *x)
260 {
261 FAISS_THROW_IF_NOT(is_trained);
262 int n0 = ntotal;
263 storage->add(n, x);
264 ntotal = storage->ntotal;
265
266 hnsw_add_vertices (*this, n0, n, x, verbose,
267 hnsw.levels.size() == ntotal);
268 }
269
270 void IndexHNSW::reset()
271 {
272 hnsw.reset();
273 storage->reset();
274 ntotal = 0;
275 }
276
277 void IndexHNSW::reconstruct (idx_t key, float* recons) const
278 {
279 storage->reconstruct(key, recons);
280 }
281
282 void IndexHNSW::shrink_level_0_neighbors(int new_size)
283 {
284 #pragma omp parallel
285 {
286 DistanceComputer *dis = get_distance_computer();
287 ScopeDeleter1<DistanceComputer> del(dis);
288
289 #pragma omp for
290 for (idx_t i = 0; i < ntotal; i++) {
291
292 size_t begin, end;
293 hnsw.neighbor_range(i, 0, &begin, &end);
294
295 std::priority_queue<NodeDistFarther> initial_list;
296
297 for (size_t j = begin; j < end; j++) {
298 int v1 = hnsw.neighbors[j];
299 if (v1 < 0) break;
300 initial_list.emplace(dis->symmetric_dis(i, v1), v1);
301
302 // initial_list.emplace(qdis(v1), v1);
303 }
304
305 std::vector<NodeDistFarther> shrunk_list;
306 HNSW::shrink_neighbor_list(*dis, initial_list,
307 shrunk_list, new_size);
308
309 for (size_t j = begin; j < end; j++) {
310 if (j - begin < shrunk_list.size())
311 hnsw.neighbors[j] = shrunk_list[j - begin].id;
312 else
313 hnsw.neighbors[j] = -1;
314 }
315 }
316 }
317
318 }
319
320 void IndexHNSW::search_level_0(
321 idx_t n, const float *x, idx_t k,
322 const storage_idx_t *nearest, const float *nearest_d,
323 float *distances, idx_t *labels, int nprobe,
324 int search_type) const
325 {
326
327 storage_idx_t ntotal = hnsw.levels.size();
328 #pragma omp parallel
329 {
330 DistanceComputer *qdis = get_distance_computer();
331 ScopeDeleter1<DistanceComputer> del(qdis);
332
333 VisitedTable vt (ntotal);
334
335 #pragma omp for
336 for(idx_t i = 0; i < n; i++) {
337 idx_t * idxi = labels + i * k;
338 float * simi = distances + i * k;
339
340 qdis->set_query(x + i * d);
341 maxheap_heapify (k, simi, idxi);
342
343 if (search_type == 1) {
344
345 int nres = 0;
346
347 for(int j = 0; j < nprobe; j++) {
348 storage_idx_t cj = nearest[i * nprobe + j];
349
350 if (cj < 0) break;
351
352 if (vt.get(cj)) continue;
353
354 int candidates_size = std::max(hnsw.efSearch, int(k));
355 MinimaxHeap candidates(candidates_size);
356
357 candidates.push(cj, nearest_d[i * nprobe + j]);
358
359 nres = hnsw.search_from_candidates(
360 *qdis, k, idxi, simi,
361 candidates, vt, 0, nres
362 );
363 }
364 } else if (search_type == 2) {
365
366 int candidates_size = std::max(hnsw.efSearch, int(k));
367 candidates_size = std::max(candidates_size, nprobe);
368
369 MinimaxHeap candidates(candidates_size);
370 for(int j = 0; j < nprobe; j++) {
371 storage_idx_t cj = nearest[i * nprobe + j];
372
373 if (cj < 0) break;
374 candidates.push(cj, nearest_d[i * nprobe + j]);
375 }
376 hnsw.search_from_candidates(
377 *qdis, k, idxi, simi,
378 candidates, vt, 0
379 );
380
381 }
382 vt.advance();
383
384 maxheap_reorder (k, simi, idxi);
385
386 }
387 }
388
389
390 }
391
392 void IndexHNSW::init_level_0_from_knngraph(
393 int k, const float *D, const idx_t *I)
394 {
395 int dest_size = hnsw.nb_neighbors (0);
396
397 #pragma omp parallel for
398 for (idx_t i = 0; i < ntotal; i++) {
399 DistanceComputer *qdis = get_distance_computer();
400 float vec[d];
401 storage->reconstruct(i, vec);
402 qdis->set_query(vec);
403
404 std::priority_queue<NodeDistFarther> initial_list;
405
406 for (size_t j = 0; j < k; j++) {
407 int v1 = I[i * k + j];
408 if (v1 == i) continue;
409 if (v1 < 0) break;
410 initial_list.emplace(D[i * k + j], v1);
411 }
412
413 std::vector<NodeDistFarther> shrunk_list;
414 HNSW::shrink_neighbor_list(*qdis, initial_list, shrunk_list, dest_size);
415
416 size_t begin, end;
417 hnsw.neighbor_range(i, 0, &begin, &end);
418
419 for (size_t j = begin; j < end; j++) {
420 if (j - begin < shrunk_list.size())
421 hnsw.neighbors[j] = shrunk_list[j - begin].id;
422 else
423 hnsw.neighbors[j] = -1;
424 }
425 }
426 }
427
428
429
430 void IndexHNSW::init_level_0_from_entry_points(
431 int n, const storage_idx_t *points,
432 const storage_idx_t *nearests)
433 {
434
435 std::vector<omp_lock_t> locks(ntotal);
436 for(int i = 0; i < ntotal; i++)
437 omp_init_lock(&locks[i]);
438
439 #pragma omp parallel
440 {
441 VisitedTable vt (ntotal);
442
443 DistanceComputer *dis = get_distance_computer();
444 ScopeDeleter1<DistanceComputer> del(dis);
445 float vec[storage->d];
446
447 #pragma omp for schedule(dynamic)
448 for (int i = 0; i < n; i++) {
449 storage_idx_t pt_id = points[i];
450 storage_idx_t nearest = nearests[i];
451 storage->reconstruct (pt_id, vec);
452 dis->set_query (vec);
453
454 hnsw.add_links_starting_from(*dis, pt_id,
455 nearest, (*dis)(nearest),
456 0, locks.data(), vt);
457
458 if (verbose && i % 10000 == 0) {
459 printf(" %d / %d\r", i, n);
460 fflush(stdout);
461 }
462 }
463 }
464 if (verbose) {
465 printf("\n");
466 }
467
468 for(int i = 0; i < ntotal; i++)
469 omp_destroy_lock(&locks[i]);
470 }
471
472 void IndexHNSW::reorder_links()
473 {
474 int M = hnsw.nb_neighbors(0);
475
476 #pragma omp parallel
477 {
478 std::vector<float> distances (M);
479 std::vector<size_t> order (M);
480 std::vector<storage_idx_t> tmp (M);
481 DistanceComputer *dis = get_distance_computer();
482 ScopeDeleter1<DistanceComputer> del(dis);
483
484 #pragma omp for
485 for(storage_idx_t i = 0; i < ntotal; i++) {
486
487 size_t begin, end;
488 hnsw.neighbor_range(i, 0, &begin, &end);
489
490 for (size_t j = begin; j < end; j++) {
491 storage_idx_t nj = hnsw.neighbors[j];
492 if (nj < 0) {
493 end = j;
494 break;
495 }
496 distances[j - begin] = dis->symmetric_dis(i, nj);
497 tmp [j - begin] = nj;
498 }
499
500 fvec_argsort (end - begin, distances.data(), order.data());
501 for (size_t j = begin; j < end; j++) {
502 hnsw.neighbors[j] = tmp[order[j - begin]];
503 }
504 }
505
506 }
507 }
508
509
510 void IndexHNSW::link_singletons()
511 {
512 printf("search for singletons\n");
513
514 std::vector<bool> seen(ntotal);
515
516 for (size_t i = 0; i < ntotal; i++) {
517 size_t begin, end;
518 hnsw.neighbor_range(i, 0, &begin, &end);
519 for (size_t j = begin; j < end; j++) {
520 storage_idx_t ni = hnsw.neighbors[j];
521 if (ni >= 0) seen[ni] = true;
522 }
523 }
524
525 int n_sing = 0, n_sing_l1 = 0;
526 std::vector<storage_idx_t> singletons;
527 for (storage_idx_t i = 0; i < ntotal; i++) {
528 if (!seen[i]) {
529 singletons.push_back(i);
530 n_sing++;
531 if (hnsw.levels[i] > 1)
532 n_sing_l1++;
533 }
534 }
535
536 printf(" Found %d / %ld singletons (%d appear in a level above)\n",
537 n_sing, ntotal, n_sing_l1);
538
539 std::vector<float>recons(singletons.size() * d);
540 for (int i = 0; i < singletons.size(); i++) {
541
542 FAISS_ASSERT(!"not implemented");
543
544 }
545
546
547 }
548
549
550 namespace {
551
552
553 // storage that explicitly reconstructs vectors before computing distances
554 struct GenericDistanceComputer: DistanceComputer {
555
556 const Index & storage;
557 std::vector<float> buf;
558 const float *q;
559
560 GenericDistanceComputer(const Index & storage): storage(storage)
561 {
562 d = storage.d;
563 buf.resize(d * 2);
564 }
565
566 float operator () (storage_idx_t i) override
567 {
568 storage.reconstruct(i, buf.data());
569 return fvec_L2sqr(q, buf.data(), d);
570 }
571
572 float symmetric_dis(storage_idx_t i, storage_idx_t j) override
573 {
574 storage.reconstruct(i, buf.data());
575 storage.reconstruct(j, buf.data() + d);
576 return fvec_L2sqr(buf.data() + d, buf.data(), d);
577 }
578
579 void set_query(const float *x) override {
580 q = x;
581 }
582
583
584 };
585
586
587 } // namespace
588
589 DistanceComputer * IndexHNSW::get_distance_computer () const
590 {
591 return new GenericDistanceComputer (*storage);
592 }
593
594
595 /**************************************************************
596 * ReconstructFromNeighbors implementation
597 **************************************************************/
598
599
600 ReconstructFromNeighbors::ReconstructFromNeighbors(
601 const IndexHNSW & index, size_t k, size_t nsq):
602 index(index), k(k), nsq(nsq) {
603 M = index.hnsw.nb_neighbors(0);
604 FAISS_ASSERT(k <= 256);
605 code_size = k == 1 ? 0 : nsq;
606 ntotal = 0;
607 d = index.d;
608 FAISS_ASSERT(d % nsq == 0);
609 dsub = d / nsq;
610 k_reorder = -1;
611 }
612
613 void ReconstructFromNeighbors::reconstruct(storage_idx_t i, float *x, float *tmp) const
614 {
615
616
617 const HNSW & hnsw = index.hnsw;
618 size_t begin, end;
619 hnsw.neighbor_range(i, 0, &begin, &end);
620
621 if (k == 1 || nsq == 1) {
622 const float * beta;
623 if (k == 1) {
624 beta = codebook.data();
625 } else {
626 int idx = codes[i];
627 beta = codebook.data() + idx * (M + 1);
628 }
629
630 float w0 = beta[0]; // weight of image itself
631 index.storage->reconstruct(i, tmp);
632
633 for (int l = 0; l < d; l++)
634 x[l] = w0 * tmp[l];
635
636 for (size_t j = begin; j < end; j++) {
637
638 storage_idx_t ji = hnsw.neighbors[j];
639 if (ji < 0) ji = i;
640 float w = beta[j - begin + 1];
641 index.storage->reconstruct(ji, tmp);
642 for (int l = 0; l < d; l++)
643 x[l] += w * tmp[l];
644 }
645 } else if (nsq == 2) {
646 int idx0 = codes[2 * i];
647 int idx1 = codes[2 * i + 1];
648
649 const float *beta0 = codebook.data() + idx0 * (M + 1);
650 const float *beta1 = codebook.data() + (idx1 + k) * (M + 1);
651
652 index.storage->reconstruct(i, tmp);
653
654 float w0;
655
656 w0 = beta0[0];
657 for (int l = 0; l < dsub; l++)
658 x[l] = w0 * tmp[l];
659
660 w0 = beta1[0];
661 for (int l = dsub; l < d; l++)
662 x[l] = w0 * tmp[l];
663
664 for (size_t j = begin; j < end; j++) {
665 storage_idx_t ji = hnsw.neighbors[j];
666 if (ji < 0) ji = i;
667 index.storage->reconstruct(ji, tmp);
668 float w;
669 w = beta0[j - begin + 1];
670 for (int l = 0; l < dsub; l++)
671 x[l] += w * tmp[l];
672
673 w = beta1[j - begin + 1];
674 for (int l = dsub; l < d; l++)
675 x[l] += w * tmp[l];
676 }
677 } else {
678 const float *betas[nsq];
679 {
680 const float *b = codebook.data();
681 const uint8_t *c = &codes[i * code_size];
682 for (int sq = 0; sq < nsq; sq++) {
683 betas[sq] = b + (*c++) * (M + 1);
684 b += (M + 1) * k;
685 }
686 }
687
688 index.storage->reconstruct(i, tmp);
689 {
690 int d0 = 0;
691 for (int sq = 0; sq < nsq; sq++) {
692 float w = *(betas[sq]++);
693 int d1 = d0 + dsub;
694 for (int l = d0; l < d1; l++) {
695 x[l] = w * tmp[l];
696 }
697 d0 = d1;
698 }
699 }
700
701 for (size_t j = begin; j < end; j++) {
702 storage_idx_t ji = hnsw.neighbors[j];
703 if (ji < 0) ji = i;
704
705 index.storage->reconstruct(ji, tmp);
706 int d0 = 0;
707 for (int sq = 0; sq < nsq; sq++) {
708 float w = *(betas[sq]++);
709 int d1 = d0 + dsub;
710 for (int l = d0; l < d1; l++) {
711 x[l] += w * tmp[l];
712 }
713 d0 = d1;
714 }
715 }
716 }
717 }
718
719 void ReconstructFromNeighbors::reconstruct_n(storage_idx_t n0,
720 storage_idx_t ni,
721 float *x) const
722 {
723 #pragma omp parallel
724 {
725 std::vector<float> tmp(index.d);
726 #pragma omp for
727 for (storage_idx_t i = 0; i < ni; i++) {
728 reconstruct(n0 + i, x + i * index.d, tmp.data());
729 }
730 }
731 }
732
733 size_t ReconstructFromNeighbors::compute_distances(
734 size_t n, const idx_t *shortlist,
735 const float *query, float *distances) const
736 {
737 std::vector<float> tmp(2 * index.d);
738 size_t ncomp = 0;
739 for (int i = 0; i < n; i++) {
740 if (shortlist[i] < 0) break;
741 reconstruct(shortlist[i], tmp.data(), tmp.data() + index.d);
742 distances[i] = fvec_L2sqr(query, tmp.data(), index.d);
743 ncomp++;
744 }
745 return ncomp;
746 }
747
748 void ReconstructFromNeighbors::get_neighbor_table(storage_idx_t i, float *tmp1) const
749 {
750 const HNSW & hnsw = index.hnsw;
751 size_t begin, end;
752 hnsw.neighbor_range(i, 0, &begin, &end);
753 size_t d = index.d;
754
755 index.storage->reconstruct(i, tmp1);
756
757 for (size_t j = begin; j < end; j++) {
758 storage_idx_t ji = hnsw.neighbors[j];
759 if (ji < 0) ji = i;
760 index.storage->reconstruct(ji, tmp1 + (j - begin + 1) * d);
761 }
762
763 }
764
765
766 /// called by add_codes
767 void ReconstructFromNeighbors::estimate_code(
768 const float *x, storage_idx_t i, uint8_t *code) const
769 {
770
771 // fill in tmp table with the neighbor values
772 float *tmp1 = new float[d * (M + 1) + (d * k)];
773 float *tmp2 = tmp1 + d * (M + 1);
774 ScopeDeleter<float> del(tmp1);
775
776 // collect coordinates of base
777 get_neighbor_table (i, tmp1);
778
779 for (size_t sq = 0; sq < nsq; sq++) {
780 int d0 = sq * dsub;
781
782 {
783 FINTEGER ki = k, di = d, m1 = M + 1;
784 FINTEGER dsubi = dsub;
785 float zero = 0, one = 1;
786
787 sgemm_ ("N", "N", &dsubi, &ki, &m1, &one,
788 tmp1 + d0, &di,
789 codebook.data() + sq * (m1 * k), &m1,
790 &zero, tmp2, &dsubi);
791 }
792
793 float min = HUGE_VAL;
794 int argmin = -1;
795 for (size_t j = 0; j < k; j++) {
796 float dis = fvec_L2sqr(x + d0, tmp2 + j * dsub, dsub);
797 if (dis < min) {
798 min = dis;
799 argmin = j;
800 }
801 }
802 code[sq] = argmin;
803 }
804
805 }
806
807 void ReconstructFromNeighbors::add_codes(size_t n, const float *x)
808 {
809 if (k == 1) { // nothing to encode
810 ntotal += n;
811 return;
812 }
813 codes.resize(codes.size() + code_size * n);
814 #pragma omp parallel for
815 for (int i = 0; i < n; i++) {
816 estimate_code(x + i * index.d, ntotal + i,
817 codes.data() + (ntotal + i) * code_size);
818 }
819 ntotal += n;
820 FAISS_ASSERT (codes.size() == ntotal * code_size);
821 }
822
823
824 /**************************************************************
825 * IndexHNSWFlat implementation
826 **************************************************************/
827
828
829 namespace {
830
831
832 struct FlatL2Dis: DistanceComputer {
833 Index::idx_t nb;
834 const float *q;
835 const float *b;
836 size_t ndis;
837
838 float operator () (storage_idx_t i) override
839 {
840 ndis++;
841 return (fvec_L2sqr(q, b + i * d, d));
842 }
843
844 float symmetric_dis(storage_idx_t i, storage_idx_t j) override
845 {
846 return (fvec_L2sqr(b + j * d, b + i * d, d));
847 }
848
849
850 FlatL2Dis(const IndexFlatL2 & storage, const float *q = nullptr):
851 q(q)
852 {
853 nb = storage.ntotal;
854 d = storage.d;
855 b = storage.xb.data();
856 ndis = 0;
857 }
858
859 void set_query(const float *x) override {
860 q = x;
861 }
862
863 virtual ~FlatL2Dis () {
864 #pragma omp critical
865 {
866 hnsw_stats.ndis += ndis;
867 }
868 }
869 };
870
871
872 } // namespace
873
874
875 IndexHNSWFlat::IndexHNSWFlat()
876 {
877 is_trained = true;
878 }
879
880
881 IndexHNSWFlat::IndexHNSWFlat(int d, int M):
882 IndexHNSW(new IndexFlatL2(d), M)
883 {
884 own_fields = true;
885 is_trained = true;
886 }
887
888
889 DistanceComputer * IndexHNSWFlat::get_distance_computer () const
890 {
891 return new FlatL2Dis (*dynamic_cast<IndexFlatL2*> (storage));
892 }
893
894
895
896
897 /**************************************************************
898 * IndexHNSWPQ implementation
899 **************************************************************/
900
901
902 namespace {
903
904
905 struct PQDis: DistanceComputer {
906 Index::idx_t nb;
907 const uint8_t *codes;
908 size_t code_size;
909 const ProductQuantizer & pq;
910 const float *sdc;
911 std::vector<float> precomputed_table;
912 size_t ndis;
913
914 float operator () (storage_idx_t i) override
915 {
916 const uint8_t *code = codes + i * code_size;
917 const float *dt = precomputed_table.data();
918 float accu = 0;
919 for (int j = 0; j < pq.M; j++) {
920 accu += dt[*code++];
921 dt += 256;
922 }
923 ndis++;
924 return accu;
925 }
926
927 float symmetric_dis(storage_idx_t i, storage_idx_t j) override
928 {
929 const float * sdci = sdc;
930 float accu = 0;
931 const uint8_t *codei = codes + i * code_size;
932 const uint8_t *codej = codes + j * code_size;
933
934 for (int l = 0; l < pq.M; l++) {
935 accu += sdci[(*codei++) + (*codej++) * 256];
936 sdci += 256 * 256;
937 }
938 return accu;
939 }
940
941 PQDis(const IndexPQ& storage, const float* /*q*/ = nullptr)
942 : pq(storage.pq) {
943 precomputed_table.resize(pq.M * pq.ksub);
944 nb = storage.ntotal;
945 d = storage.d;
946 codes = storage.codes.data();
947 code_size = pq.code_size;
948 FAISS_ASSERT(pq.ksub == 256);
949 FAISS_ASSERT(pq.sdc_table.size() == pq.ksub * pq.ksub * pq.M);
950 sdc = pq.sdc_table.data();
951 ndis = 0;
952 }
953
954 void set_query(const float *x) override {
955 pq.compute_distance_table(x, precomputed_table.data());
956 }
957
958 virtual ~PQDis () {
959 #pragma omp critical
960 {
961 hnsw_stats.ndis += ndis;
962 }
963 }
964 };
965
966
967 } // namespace
968
969
970 IndexHNSWPQ::IndexHNSWPQ() {}
971
972 IndexHNSWPQ::IndexHNSWPQ(int d, int pq_m, int M):
973 IndexHNSW(new IndexPQ(d, pq_m, 8), M)
974 {
975 own_fields = true;
976 is_trained = false;
977 }
978
979 void IndexHNSWPQ::train(idx_t n, const float* x)
980 {
981 IndexHNSW::train (n, x);
982 (dynamic_cast<IndexPQ*> (storage))->pq.compute_sdc_table();
983 }
984
985
986
987 DistanceComputer * IndexHNSWPQ::get_distance_computer () const
988 {
989 return new PQDis (*dynamic_cast<IndexPQ*> (storage));
990 }
991
992
993 /**************************************************************
994 * IndexHNSWSQ implementation
995 **************************************************************/
996
997
998 namespace {
999
1000
1001 struct SQDis: DistanceComputer {
1002 Index::idx_t nb;
1003 const uint8_t *codes;
1004 size_t code_size;
1005 const ScalarQuantizer & sq;
1006 const float *q;
1007 ScalarQuantizer::DistanceComputer * dc;
1008
1009 float operator () (storage_idx_t i) override
1010 {
1011 const uint8_t *code = codes + i * code_size;
1012
1013 return dc->compute_distance (q, code);
1014 }
1015
1016 float symmetric_dis(storage_idx_t i, storage_idx_t j) override
1017 {
1018 const uint8_t *codei = codes + i * code_size;
1019 const uint8_t *codej = codes + j * code_size;
1020 return dc->compute_code_distance (codei, codej);
1021 }
1022
1023 SQDis(const IndexScalarQuantizer& storage, const float* /*q*/ = nullptr)
1024 : sq(storage.sq) {
1025 nb = storage.ntotal;
1026 d = storage.d;
1027 codes = storage.codes.data();
1028 code_size = sq.code_size;
1029 dc = sq.get_distance_computer();
1030 }
1031
1032 void set_query(const float *x) override {
1033 q = x;
1034 }
1035
1036 virtual ~SQDis () {
1037 delete dc;
1038 }
1039 };
1040
1041
1042 } // namespace
1043
1044
1045 IndexHNSWSQ::IndexHNSWSQ(int d, ScalarQuantizer::QuantizerType qtype, int M):
1046 IndexHNSW (new IndexScalarQuantizer (d, qtype), M)
1047 {
1048 own_fields = true;
1049 }
1050
1051 IndexHNSWSQ::IndexHNSWSQ() {}
1052
1053 DistanceComputer * IndexHNSWSQ::get_distance_computer () const
1054 {
1055 return new SQDis (*dynamic_cast<IndexScalarQuantizer*> (storage));
1056 }
1057
1058
1059
1060
1061 /**************************************************************
1062 * IndexHNSW2Level implementation
1063 **************************************************************/
1064
1065
1066
1067 IndexHNSW2Level::IndexHNSW2Level(Index *quantizer, size_t nlist, int m_pq, int M):
1068 IndexHNSW (new Index2Layer (quantizer, nlist, m_pq), M)
1069 {
1070 own_fields = true;
1071 is_trained = false;
1072 }
1073
1074 IndexHNSW2Level::IndexHNSW2Level() {}
1075
1076
1077 namespace {
1078
1079
1080 struct Distance2Level: DistanceComputer {
1081
1082 const Index2Layer & storage;
1083 std::vector<float> buf;
1084 const float *q;
1085
1086 const float *pq_l1_tab, *pq_l2_tab;
1087
1088 Distance2Level(const Index2Layer & storage): storage(storage)
1089 {
1090 d = storage.d;
1091 FAISS_ASSERT(storage.pq.dsub == 4);
1092 pq_l2_tab = storage.pq.centroids.data();
1093 buf.resize(2 * d);
1094 }
1095
1096 float symmetric_dis(storage_idx_t i, storage_idx_t j) override
1097 {
1098 storage.reconstruct(i, buf.data());
1099 storage.reconstruct(j, buf.data() + d);
1100 return fvec_L2sqr(buf.data() + d, buf.data(), d);
1101 }
1102
1103 void set_query(const float *x) override {
1104 q = x;
1105 }
1106 };
1107
1108
1109 // well optimized for xNN+PQNN
1110 struct DistanceXPQ4: Distance2Level {
1111
1112 int M, k;
1113
1114 DistanceXPQ4(const Index2Layer & storage):
1115 Distance2Level (storage)
1116 {
1117 const IndexFlat *quantizer =
1118 dynamic_cast<IndexFlat*> (storage.q1.quantizer);
1119
1120 FAISS_ASSERT(quantizer);
1121 M = storage.pq.M;
1122 pq_l1_tab = quantizer->xb.data();
1123 }
1124
1125 float operator () (storage_idx_t i) override
1126 {
1127 #ifdef __SSE__
1128 const uint8_t *code = storage.codes.data() + i * storage.code_size;
1129 long key = 0;
1130 memcpy (&key, code, storage.code_size_1);
1131 code += storage.code_size_1;
1132
1133 // walking pointers
1134 const float *qa = q;
1135 const __m128 *l1_t = (const __m128 *)(pq_l1_tab + d * key);
1136 const __m128 *pq_l2_t = (const __m128 *)pq_l2_tab;
1137 __m128 accu = _mm_setzero_ps();
1138
1139 for (int m = 0; m < M; m++) {
1140 __m128 qi = _mm_loadu_ps(qa);
1141 __m128 recons = l1_t[m] + pq_l2_t[*code++];
1142 __m128 diff = qi - recons;
1143 accu += diff * diff;
1144 pq_l2_t += 256;
1145 qa += 4;
1146 }
1147
1148 accu = _mm_hadd_ps (accu, accu);
1149 accu = _mm_hadd_ps (accu, accu);
1150 return _mm_cvtss_f32 (accu);
1151 #else
1152 FAISS_THROW_MSG("not implemented for non-x64 platforms");
1153 #endif
1154 }
1155
1156 };
1157
1158 // well optimized for 2xNN+PQNN
1159 struct Distance2xXPQ4: Distance2Level {
1160
1161 int M_2, mi_nbits;
1162
1163 Distance2xXPQ4(const Index2Layer & storage):
1164 Distance2Level (storage)
1165 {
1166 const MultiIndexQuantizer *mi =
1167 dynamic_cast<MultiIndexQuantizer*> (storage.q1.quantizer);
1168
1169 FAISS_ASSERT(mi);
1170 FAISS_ASSERT(storage.pq.M % 2 == 0);
1171 M_2 = storage.pq.M / 2;
1172 mi_nbits = mi->pq.nbits;
1173 pq_l1_tab = mi->pq.centroids.data();
1174 }
1175
1176 float operator () (storage_idx_t i) override
1177 {
1178 const uint8_t *code = storage.codes.data() + i * storage.code_size;
1179 long key01 = 0;
1180 memcpy (&key01, code, storage.code_size_1);
1181 code += storage.code_size_1;
1182 #ifdef __SSE__
1183
1184 // walking pointers
1185 const float *qa = q;
1186 const __m128 *pq_l1_t = (const __m128 *)pq_l1_tab;
1187 const __m128 *pq_l2_t = (const __m128 *)pq_l2_tab;
1188 __m128 accu = _mm_setzero_ps();
1189
1190 for (int mi_m = 0; mi_m < 2; mi_m++) {
1191 long l1_idx = key01 & ((1L << mi_nbits) - 1);
1192 const __m128 * pq_l1 = pq_l1_t + M_2 * l1_idx;
1193
1194 for (int m = 0; m < M_2; m++) {
1195 __m128 qi = _mm_loadu_ps(qa);
1196 __m128 recons = pq_l1[m] + pq_l2_t[*code++];
1197 __m128 diff = qi - recons;
1198 accu += diff * diff;
1199 pq_l2_t += 256;
1200 qa += 4;
1201 }
1202 pq_l1_t += M_2 << mi_nbits;
1203 key01 >>= mi_nbits;
1204 }
1205 accu = _mm_hadd_ps (accu, accu);
1206 accu = _mm_hadd_ps (accu, accu);
1207 return _mm_cvtss_f32 (accu);
1208 #else
1209 FAISS_THROW_MSG("not implemented for non-x64 platforms");
1210 #endif
1211 }
1212
1213 };
1214
1215
1216 } // namespace
1217
1218
1219 DistanceComputer * IndexHNSW2Level::get_distance_computer () const
1220 {
1221 const Index2Layer *storage2l =
1222 dynamic_cast<Index2Layer*>(storage);
1223
1224 if (storage2l) {
1225 #ifdef __SSE__
1226
1227 const MultiIndexQuantizer *mi =
1228 dynamic_cast<MultiIndexQuantizer*> (storage2l->q1.quantizer);
1229
1230 if (mi && storage2l->pq.M % 2 == 0 && storage2l->pq.dsub == 4) {
1231 return new Distance2xXPQ4(*storage2l);
1232 }
1233
1234 const IndexFlat *fl =
1235 dynamic_cast<IndexFlat*> (storage2l->q1.quantizer);
1236
1237 if (fl && storage2l->pq.dsub == 4) {
1238 return new DistanceXPQ4(*storage2l);
1239 }
1240 #endif
1241 }
1242
1243 // IVFPQ and cases not handled above
1244 return new GenericDistanceComputer (*storage);
1245
1246 }
1247
1248
1249 namespace {
1250
1251
1252 // same as search_from_candidates but uses v
1253 // visno -> is in result list
1254 // visno + 1 -> in result list + in candidates
1255 int search_from_candidates_2(const HNSW & hnsw,
1256 DistanceComputer & qdis, int k,
1257 idx_t *I, float * D,
1258 MinimaxHeap &candidates,
1259 VisitedTable &vt,
1260 int level, int nres_in = 0)
1261 {
1262 int nres = nres_in;
1263 int ndis = 0;
1264 for (int i = 0; i < candidates.size(); i++) {
1265 idx_t v1 = candidates.ids[i];
1266 FAISS_ASSERT(v1 >= 0);
1267 vt.visited[v1] = vt.visno + 1;
1268 }
1269
1270 int nstep = 0;
1271
1272 while (candidates.size() > 0) {
1273 float d0 = 0;
1274 int v0 = candidates.pop_min(&d0);
1275
1276 size_t begin, end;
1277 hnsw.neighbor_range(v0, level, &begin, &end);
1278
1279 for (size_t j = begin; j < end; j++) {
1280 int v1 = hnsw.neighbors[j];
1281 if (v1 < 0) break;
1282 if (vt.visited[v1] == vt.visno + 1) {
1283 // nothing to do
1284 } else {
1285 ndis++;
1286 float d = qdis(v1);
1287 candidates.push(v1, d);
1288
1289 // never seen before --> add to heap
1290 if (vt.visited[v1] < vt.visno) {
1291 if (nres < k) {
1292 faiss::maxheap_push (++nres, D, I, d, v1);
1293 } else if (d < D[0]) {
1294 faiss::maxheap_pop (nres--, D, I);
1295 faiss::maxheap_push (++nres, D, I, d, v1);
1296 }
1297 }
1298 vt.visited[v1] = vt.visno + 1;
1299 }
1300 }
1301
1302 nstep++;
1303 if (nstep > hnsw.efSearch) {
1304 break;
1305 }
1306 }
1307
1308 if (level == 0) {
1309 #pragma omp critical
1310 {
1311 hnsw_stats.n1 ++;
1312 if (candidates.size() == 0)
1313 hnsw_stats.n2 ++;
1314 }
1315 }
1316
1317
1318 return nres;
1319 }
1320
1321
1322 } // namespace
1323
1324 void IndexHNSW2Level::search (idx_t n, const float *x, idx_t k,
1325 float *distances, idx_t *labels) const
1326 {
1327 if (dynamic_cast<const Index2Layer*>(storage)) {
1328 IndexHNSW::search (n, x, k, distances, labels);
1329
1330 } else { // "mixed" search
1331
1332 const IndexIVFPQ *index_ivfpq =
1333 dynamic_cast<const IndexIVFPQ*>(storage);
1334
1335 int nprobe = index_ivfpq->nprobe;
1336
1337 long * coarse_assign = new long [n * nprobe];
1338 ScopeDeleter<long> del (coarse_assign);
1339 float * coarse_dis = new float [n * nprobe];
1340 ScopeDeleter<float> del2 (coarse_dis);
1341
1342 index_ivfpq->quantizer->search (n, x, nprobe, coarse_dis, coarse_assign);
1343
1344 index_ivfpq->search_preassigned (
1345 n, x, k, coarse_assign, coarse_dis, distances, labels, false);
1346
1347 #pragma omp parallel
1348 {
1349 VisitedTable vt (ntotal);
1350 DistanceComputer *dis = get_distance_computer();
1351 ScopeDeleter1<DistanceComputer> del(dis);
1352
1353 int candidates_size = hnsw.upper_beam;
1354 MinimaxHeap candidates(candidates_size);
1355
1356 #pragma omp for
1357 for(idx_t i = 0; i < n; i++) {
1358 idx_t * idxi = labels + i * k;
1359 float * simi = distances + i * k;
1360 dis->set_query(x + i * d);
1361
1362 // mark all inverted list elements as visited
1363
1364 for (int j = 0; j < nprobe; j++) {
1365 idx_t key = coarse_assign[j + i * nprobe];
1366 if (key < 0) break;
1367 size_t list_length = index_ivfpq->get_list_size (key);
1368 const idx_t * ids = index_ivfpq->invlists->get_ids (key);
1369
1370 for (int jj = 0; jj < list_length; jj++) {
1371 vt.set (ids[jj]);
1372 }
1373 }
1374
1375 candidates.clear();
1376 // copy the upper_beam elements to candidates list
1377
1378 int search_policy = 2;
1379
1380 if (search_policy == 1) {
1381
1382 for (int j = 0 ; j < hnsw.upper_beam && j < k; j++) {
1383 if (idxi[j] < 0) break;
1384 candidates.push (idxi[j], simi[j]);
1385 // search_from_candidates adds them back
1386 idxi[j] = -1;
1387 simi[j] = HUGE_VAL;
1388 }
1389
1390 // reorder from sorted to heap
1391 maxheap_heapify (k, simi, idxi, simi, idxi, k);
1392
1393 hnsw.search_from_candidates(
1394 *dis, k, idxi, simi,
1395 candidates, vt, 0, k
1396 );
1397
1398 vt.advance();
1399
1400 } else if (search_policy == 2) {
1401
1402 for (int j = 0 ; j < hnsw.upper_beam && j < k; j++) {
1403 if (idxi[j] < 0) break;
1404 candidates.push (idxi[j], simi[j]);
1405 }
1406
1407 // reorder from sorted to heap
1408 maxheap_heapify (k, simi, idxi, simi, idxi, k);
1409
1410 search_from_candidates_2 (
1411 hnsw, *dis, k, idxi, simi,
1412 candidates, vt, 0, k);
1413 vt.advance ();
1414 vt.advance ();
1415
1416 }
1417
1418 maxheap_reorder (k, simi, idxi);
1419 }
1420 }
1421 }
1422
1423
1424 }
1425
1426
1427 void IndexHNSW2Level::flip_to_ivf ()
1428 {
1429 Index2Layer *storage2l =
1430 dynamic_cast<Index2Layer*>(storage);
1431
1432 FAISS_THROW_IF_NOT (storage2l);
1433
1434 IndexIVFPQ * index_ivfpq =
1435 new IndexIVFPQ (storage2l->q1.quantizer,
1436 d, storage2l->q1.nlist,
1437 storage2l->pq.M, 8);
1438 index_ivfpq->pq = storage2l->pq;
1439 index_ivfpq->is_trained = storage2l->is_trained;
1440 index_ivfpq->precompute_table();
1441 index_ivfpq->own_fields = storage2l->q1.own_fields;
1442 storage2l->transfer_to_IVFPQ(*index_ivfpq);
1443 index_ivfpq->make_direct_map (true);
1444
1445 storage = index_ivfpq;
1446 delete storage2l;
1447
1448 }
1449
1450
1451 } // namespace faiss
faiss::IndexIVFPQ::precompute_table
void precompute_table()
build precomputed table
Definition: IndexIVFPQ.cpp:364
faiss::Index2Layer::transfer_to_IVFPQ
void transfer_to_IVFPQ(IndexIVFPQ &other) const
transfer the flat codes to an IVFPQ index
Definition: IndexIVFPQ.cpp:1466
faiss::ScalarQuantizer::QuantizerType
QuantizerType
Definition: IndexScalarQuantizer.h:34
faiss::HNSW::neighbor_range
void neighbor_range(idx_t no, int layer_no, size_t *begin, size_t *end) const
range of entries in the neighbors table of vertex no at layer_no
Definition: HNSW.cpp:43
faiss::IndexIVF::search_preassigned
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
Definition: IndexIVF.cpp:189
faiss::VisitedTable::get
bool get(int no) const
get flag #no
Definition: HNSW.h:248
faiss::fvec_L2sqr
float fvec_L2sqr(const float *x, const float *y, size_t d)
Squared L2 distance between two vectors.
Definition: utils_simd.cpp:502
faiss::IndexHNSWPQ::train
void train(idx_t n, const float *x) override
Trains the storage if needed.
Definition: IndexHNSW.cpp:979
faiss::InvertedLists::get_ids
virtual const idx_t * get_ids(size_t list_no) const =0
faiss::IndexHNSW2Level::search
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
entry point for search
Definition: IndexHNSW.cpp:1324
faiss::Index::reset
virtual void reset()=0
removes all elements from the database.
faiss::IndexPQ
Definition: IndexPQ.h:27
faiss::IndexIVF::nprobe
size_t nprobe
number of probes at query time
Definition: IndexIVF.h:98
faiss::HNSW::DistanceComputer::symmetric_dis
virtual float symmetric_dis(storage_idx_t i, storage_idx_t j)=0
compute distance between two stored vectors
faiss::Index::train
virtual void train(idx_t n, const float *x)
Definition: Index.cpp:24
faiss::ScopeDeleter1
Definition: FaissException.h:52
faiss::ScopeDeleter
Definition: FaissException.h:39
faiss::Index2Layer::q1
Level1Quantizer q1
first level quantizer
Definition: IndexIVFPQ.h:207
faiss::VisitedTable::advance
void advance()
reset all flags to false
Definition: HNSW.h:253
faiss::Index2Layer
Definition: IndexIVFPQ.h:205
faiss::ReconstructFromNeighbors::get_neighbor_table
void get_neighbor_table(storage_idx_t i, float *out) const
get the M+1 -by-d table for neighbor coordinates for vector i
Definition: IndexHNSW.cpp:748
faiss::IndexHNSW::train
void train(idx_t n, const float *x) override
Trains the storage if needed.
Definition: IndexHNSW.cpp:206
faiss::Index::d
int d
vector dimension
Definition: Index.h:66
faiss::VisitedTable
set implementation optimized for fast access.
Definition: HNSW.h:235
faiss::IndexIVFPQ
Definition: IndexIVFPQ.h:35
faiss::IndexHNSW::add
void add(idx_t n, const float *x) override
Definition: IndexHNSW.cpp:259
faiss::ReconstructFromNeighbors::reconstruct
void reconstruct(storage_idx_t i, float *x, float *tmp) const
called by compute_distances
Definition: IndexHNSW.cpp:613
faiss::Index::add
virtual void add(idx_t n, const float *x)=0
faiss::Index::idx_t
long idx_t
all indices are this type
Definition: Index.h:64
faiss::HNSW::DistanceComputer::set_query
virtual void set_query(const float *x)=0
called before computing distances
faiss::Index::ntotal
idx_t ntotal
total nb of indexed vectors
Definition: Index.h:67
faiss::getmillisecs
double getmillisecs()
ms elapsed since some arbitrary epoch
Definition: utils.cpp:70
faiss::ReconstructFromNeighbors::estimate_code
void estimate_code(const float *x, storage_idx_t i, uint8_t *code) const
called by add_codes
Definition: IndexHNSW.cpp:767
faiss::Index::search
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const =0
faiss::HNSW
Definition: HNSW.h:47
faiss::ReconstructFromNeighbors::add_codes
void add_codes(size_t n, const float *x)
Definition: IndexHNSW.cpp:807
faiss::IndexIVF::make_direct_map
void make_direct_map(bool new_maintain_direct_map=true)
Definition: IndexIVF.cpp:144
faiss::IndexIVFPQ::pq
ProductQuantizer pq
produces the codes
Definition: IndexIVFPQ.h:38
faiss::IndexIVF::invlists
InvertedLists * invlists
Acess to the actual data.
Definition: IndexIVF.h:93
faiss::IndexHNSW::reset
void reset() override
removes all elements from the database.
Definition: IndexHNSW.cpp:270
faiss::ProductQuantizer::M
size_t M
number of subquantizers
Definition: ProductQuantizer.h:27
faiss::IndexHNSW::init_level_0_from_entry_points
void init_level_0_from_entry_points(int npt, const storage_idx_t *points, const storage_idx_t *nearests)
alternative graph building
Definition: IndexHNSW.cpp:430
faiss::IndexHNSW::search
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
entry point for search
Definition: IndexHNSW.cpp:213
faiss::Level1Quantizer::quantizer
Index * quantizer
quantizer that maps vectors to inverted lists
Definition: IndexIVF.h:33
faiss::IndexHNSW::init_level_0_from_knngraph
void init_level_0_from_knngraph(int k, const float *D, const idx_t *I)
alternative graph building
Definition: IndexHNSW.cpp:392
faiss::IndexHNSW::search_level_0
void search_level_0(idx_t n, const float *x, idx_t k, const storage_idx_t *nearest, const float *nearest_d, float *distances, idx_t *labels, int nprobe=1, int search_type=1) const
Definition: IndexHNSW.cpp:320
faiss::HNSW::MinimaxHeap
Definition: HNSW.h:78
faiss::Index::is_trained
bool is_trained
set if the Index does not require training, or if training is done already
Definition: Index.h:71
faiss::HNSW::neighbors
std::vector< storage_idx_t > neighbors
Definition: HNSW.h:134
faiss::IndexHNSW::reconstruct
void reconstruct(idx_t key, float *recons) const override
Definition: IndexHNSW.cpp:277
faiss::Index::reconstruct
virtual void reconstruct(idx_t key, float *recons) const
Definition: Index.cpp:55
faiss::Index2Layer::pq
ProductQuantizer pq
second level quantizer is always a PQ
Definition: IndexIVFPQ.h:210
faiss::HNSW::storage_idx_t
int storage_idx_t
internal storage of vectors (32 bits: this is expensive)
Definition: HNSW.h:49
faiss::HNSW::DistanceComputer
Definition: HNSW.h:60
faiss::Level1Quantizer::own_fields
bool own_fields
whether object owns the quantizer
Definition: IndexIVF.h:42
faiss::VisitedTable::set
void set(int no)
set flog #no to true
Definition: HNSW.h:243
faiss::Level1Quantizer::nlist
size_t nlist
number of possible key values
Definition: IndexIVF.h:34
faiss::HNSW::shrink_neighbor_list
static void shrink_neighbor_list(DistanceComputer &qdis, std::priority_queue< NodeDistFarther > &input, std::vector< NodeDistFarther > &output, int max_size)
Definition: HNSW.cpp:237