10 #include "IndexHNSW.h"
20 #include <unordered_set>
23 #include <sys/types.h>
29 #include <immintrin.h>
34 #include "FaissAssert.h"
35 #include "IndexFlat.h"
36 #include "IndexIVFPQ.h"
37 #include "AuxIndexStructures.h"
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);
54 using MinimaxHeap = HNSW::MinimaxHeap;
56 using NodeDistCloser = HNSW::NodeDistCloser;
57 using NodeDistFarther = HNSW::NodeDistFarther;
68 void hnsw_add_vertices(IndexHNSW &index_hnsw,
70 size_t n,
const float *x,
72 bool preset_levels =
false) {
73 size_t d = index_hnsw.d;
74 HNSW & hnsw = index_hnsw.hnsw;
75 size_t ntotal = n0 + n;
78 printf(
"hnsw_add_vertices: adding %ld elements on top of %ld "
79 "(preset_levels=%d)\n",
80 n, n0,
int(preset_levels));
87 int max_level = hnsw.prepare_level_tab(n, preset_levels);
90 printf(
" max_level = %d\n", max_level);
93 std::vector<omp_lock_t> locks(ntotal);
94 for(
int i = 0; i < ntotal; i++)
95 omp_init_lock(&locks[i]);
98 std::vector<int> hist;
99 std::vector<int> order(n);
104 for (
int i = 0; i < n; i++) {
105 storage_idx_t pt_id = i + n0;
106 int pt_level = hnsw.levels[pt_id] - 1;
107 while (pt_level >= hist.size())
113 std::vector<int> offsets(hist.size() + 1, 0);
114 for (
int i = 0; i < hist.size() - 1; i++) {
115 offsets[i + 1] = offsets[i] + hist[i];
119 for (
int i = 0; i < n; i++) {
120 storage_idx_t pt_id = i + n0;
121 int pt_level = hnsw.levels[pt_id] - 1;
122 order[offsets[pt_level]++] = pt_id;
128 (max_level * index_hnsw.d * hnsw.efConstruction);
131 RandomGenerator rng2(789);
135 for (
int pt_level = hist.size() - 1; pt_level >= 0; pt_level--) {
136 int i0 = i1 - hist[pt_level];
139 printf(
"Adding %d elements at level %d\n",
144 for (
int j = i0; j < i1; j++)
145 std::swap(order[j], order[j + rng2.rand_int(i1 - j)]);
147 bool interrupt =
false;
149 #pragma omp parallel if(i1 > i0 + 100)
151 VisitedTable vt (ntotal);
153 DistanceComputer *dis = index_hnsw.get_distance_computer();
154 ScopeDeleter1<DistanceComputer> del(dis);
155 int prev_display = verbose && omp_get_thread_num() == 0 ? 0 : -1;
158 #pragma omp for schedule(dynamic)
159 for (
int i = i0; i < i1; i++) {
160 storage_idx_t pt_id = order[i];
161 dis->set_query (x + (pt_id - n0) * d);
168 hnsw.add_with_locks(*dis, pt_level, pt_id, locks, vt);
170 if (prev_display >= 0 && i - i0 > prev_display + 10000) {
171 prev_display = i - i0;
172 printf(
" %d / %d\r", i - i0, i1 - i0);
176 if (counter % check_period == 0) {
189 FAISS_THROW_MSG (
"computation interrupted");
193 FAISS_ASSERT(i1 == 0);
199 for(
int i = 0; i < ntotal; i++) {
200 omp_destroy_lock(&locks[i]);
214 IndexHNSW::IndexHNSW(
int d,
int M):
219 reconstruct_from_neighbors(nullptr)
222 IndexHNSW::IndexHNSW(Index *storage,
int M):
223 Index(storage->d, METRIC_L2),
227 reconstruct_from_neighbors(nullptr)
230 IndexHNSW::~IndexHNSW() {
239 storage->
train (n, x);
244 float *distances,
idx_t *labels)
const
250 hnsw.max_level * d * hnsw.efSearch);
252 for (
idx_t i0 = 0; i0 < n; i0 += check_period) {
253 idx_t i1 = std::min(i0 + check_period, n);
255 #pragma omp parallel reduction(+ : nreorder)
262 for(
idx_t i = i0; i < i1; i++) {
263 idx_t * idxi = labels + i * k;
264 float * simi = distances + i * k;
267 maxheap_heapify (k, simi, idxi);
268 hnsw.search(*dis, k, idxi, simi, vt);
270 maxheap_reorder (k, simi, idxi);
272 if (reconstruct_from_neighbors &&
273 reconstruct_from_neighbors->k_reorder != 0) {
274 int k_reorder = reconstruct_from_neighbors->k_reorder;
275 if (k_reorder == -1 || k_reorder > k) k_reorder = k;
277 nreorder += reconstruct_from_neighbors->compute_distances(
278 k_reorder, idxi, x + i * d, simi);
281 maxheap_heapify (k_reorder, simi, idxi, simi, idxi, k_reorder);
282 maxheap_reorder (k_reorder, simi, idxi);
290 hnsw_stats.nreorder += nreorder;
301 hnsw_add_vertices (*
this, n0, n, x, verbose,
302 hnsw.levels.size() ==
ntotal);
317 void IndexHNSW::shrink_level_0_neighbors(
int new_size)
325 for (idx_t i = 0; i <
ntotal; i++) {
328 hnsw.neighbor_range(i, 0, &begin, &end);
330 std::priority_queue<NodeDistFarther> initial_list;
332 for (
size_t j = begin; j < end; j++) {
333 int v1 = hnsw.neighbors[j];
340 std::vector<NodeDistFarther> shrunk_list;
342 shrunk_list, new_size);
344 for (
size_t j = begin; j < end; j++) {
345 if (j - begin < shrunk_list.size())
346 hnsw.neighbors[j] = shrunk_list[j - begin].id;
348 hnsw.neighbors[j] = -1;
357 const storage_idx_t *nearest,
const float *nearest_d,
358 float *distances,
idx_t *labels,
int nprobe,
359 int search_type)
const
362 storage_idx_t ntotal = hnsw.levels.size();
371 for(
idx_t i = 0; i < n; i++) {
372 idx_t * idxi = labels + i * k;
373 float * simi = distances + i * k;
376 maxheap_heapify (k, simi, idxi);
378 if (search_type == 1) {
382 for(
int j = 0; j < nprobe; j++) {
383 storage_idx_t cj = nearest[i * nprobe + j];
387 if (vt.
get(cj))
continue;
389 int candidates_size = std::max(hnsw.efSearch,
int(k));
392 candidates.push(cj, nearest_d[i * nprobe + j]);
394 nres = hnsw.search_from_candidates(
395 *qdis, k, idxi, simi,
396 candidates, vt, 0, nres
399 }
else if (search_type == 2) {
401 int candidates_size = std::max(hnsw.efSearch,
int(k));
402 candidates_size = std::max(candidates_size, nprobe);
405 for(
int j = 0; j < nprobe; j++) {
406 storage_idx_t cj = nearest[i * nprobe + j];
409 candidates.push(cj, nearest_d[i * nprobe + j]);
411 hnsw.search_from_candidates(
412 *qdis, k, idxi, simi,
419 maxheap_reorder (k, simi, idxi);
428 int k,
const float *D,
const idx_t *I)
430 int dest_size = hnsw.nb_neighbors (0);
432 #pragma omp parallel for
439 std::priority_queue<NodeDistFarther> initial_list;
441 for (
size_t j = 0; j < k; j++) {
442 int v1 = I[i * k + j];
443 if (v1 == i)
continue;
445 initial_list.emplace(D[i * k + j], v1);
448 std::vector<NodeDistFarther> shrunk_list;
452 hnsw.neighbor_range(i, 0, &begin, &end);
454 for (
size_t j = begin; j < end; j++) {
455 if (j - begin < shrunk_list.size())
456 hnsw.neighbors[j] = shrunk_list[j - begin].id;
458 hnsw.neighbors[j] = -1;
466 int n,
const storage_idx_t *points,
467 const storage_idx_t *nearests)
470 std::vector<omp_lock_t> locks(ntotal);
471 for(
int i = 0; i <
ntotal; i++)
472 omp_init_lock(&locks[i]);
480 float vec[storage->
d];
482 #pragma omp for schedule(dynamic)
483 for (
int i = 0; i < n; i++) {
484 storage_idx_t pt_id = points[i];
485 storage_idx_t nearest = nearests[i];
489 hnsw.add_links_starting_from(*dis, pt_id,
490 nearest, (*dis)(nearest),
491 0, locks.data(), vt);
493 if (verbose && i % 10000 == 0) {
494 printf(
" %d / %d\r", i, n);
503 for(
int i = 0; i <
ntotal; i++)
504 omp_destroy_lock(&locks[i]);
507 void IndexHNSW::reorder_links()
509 int M = hnsw.nb_neighbors(0);
513 std::vector<float> distances (M);
514 std::vector<size_t> order (M);
515 std::vector<storage_idx_t> tmp (M);
520 for(storage_idx_t i = 0; i <
ntotal; i++) {
523 hnsw.neighbor_range(i, 0, &begin, &end);
525 for (
size_t j = begin; j < end; j++) {
526 storage_idx_t nj = hnsw.neighbors[j];
532 tmp [j - begin] = nj;
535 fvec_argsort (end - begin, distances.data(), order.data());
536 for (
size_t j = begin; j < end; j++) {
537 hnsw.neighbors[j] = tmp[order[j - begin]];
545 void IndexHNSW::link_singletons()
547 printf(
"search for singletons\n");
549 std::vector<bool> seen(ntotal);
551 for (
size_t i = 0; i <
ntotal; i++) {
553 hnsw.neighbor_range(i, 0, &begin, &end);
554 for (
size_t j = begin; j < end; j++) {
555 storage_idx_t ni = hnsw.neighbors[j];
556 if (ni >= 0) seen[ni] =
true;
560 int n_sing = 0, n_sing_l1 = 0;
561 std::vector<storage_idx_t> singletons;
562 for (storage_idx_t i = 0; i <
ntotal; i++) {
564 singletons.push_back(i);
566 if (hnsw.levels[i] > 1)
571 printf(
" Found %d / %ld singletons (%d appear in a level above)\n",
572 n_sing, ntotal, n_sing_l1);
574 std::vector<float>recons(singletons.size() *
d);
575 for (
int i = 0; i < singletons.size(); i++) {
577 FAISS_ASSERT(!
"not implemented");
589 struct GenericDistanceComputer: DistanceComputer {
591 const Index & storage;
592 std::vector<float> buf;
595 GenericDistanceComputer(
const Index & storage): storage(storage)
601 float operator () (idx_t i)
override
603 storage.reconstruct(i, buf.data());
607 float symmetric_dis(idx_t i, idx_t j)
override
609 storage.reconstruct(i, buf.data());
610 storage.reconstruct(j, buf.data() + d);
611 return fvec_L2sqr(buf.data() + d, buf.data(), d);
614 void set_query(
const float *x)
override {
624 DistanceComputer * IndexHNSW::get_distance_computer ()
const
626 return new GenericDistanceComputer (*storage);
635 ReconstructFromNeighbors::ReconstructFromNeighbors(
636 const IndexHNSW & index,
size_t k,
size_t nsq):
637 index(index), k(k), nsq(nsq) {
638 M = index.hnsw.nb_neighbors(0);
639 FAISS_ASSERT(k <= 256);
640 code_size = k == 1 ? 0 : nsq;
643 FAISS_ASSERT(d % nsq == 0);
652 const HNSW & hnsw = index.hnsw;
656 if (k == 1 || nsq == 1) {
659 beta = codebook.data();
662 beta = codebook.data() + idx * (M + 1);
666 index.storage->reconstruct(i, tmp);
668 for (
int l = 0; l < d; l++)
671 for (
size_t j = begin; j < end; j++) {
675 float w = beta[j - begin + 1];
676 index.storage->reconstruct(ji, tmp);
677 for (
int l = 0; l < d; l++)
680 }
else if (nsq == 2) {
681 int idx0 = codes[2 * i];
682 int idx1 = codes[2 * i + 1];
684 const float *beta0 = codebook.data() + idx0 * (M + 1);
685 const float *beta1 = codebook.data() + (idx1 + k) * (M + 1);
687 index.storage->reconstruct(i, tmp);
692 for (
int l = 0; l < dsub; l++)
696 for (
int l = dsub; l < d; l++)
699 for (
size_t j = begin; j < end; j++) {
702 index.storage->reconstruct(ji, tmp);
704 w = beta0[j - begin + 1];
705 for (
int l = 0; l < dsub; l++)
708 w = beta1[j - begin + 1];
709 for (
int l = dsub; l < d; l++)
713 const float *betas[nsq];
715 const float *b = codebook.data();
716 const uint8_t *c = &codes[i * code_size];
717 for (
int sq = 0; sq < nsq; sq++) {
718 betas[sq] = b + (*c++) * (M + 1);
723 index.storage->reconstruct(i, tmp);
726 for (
int sq = 0; sq < nsq; sq++) {
727 float w = *(betas[sq]++);
729 for (
int l = d0; l < d1; l++) {
736 for (
size_t j = begin; j < end; j++) {
740 index.storage->reconstruct(ji, tmp);
742 for (
int sq = 0; sq < nsq; sq++) {
743 float w = *(betas[sq]++);
745 for (
int l = d0; l < d1; l++) {
754 void ReconstructFromNeighbors::reconstruct_n(storage_idx_t n0,
760 std::vector<float> tmp(index.d);
762 for (storage_idx_t i = 0; i < ni; i++) {
768 size_t ReconstructFromNeighbors::compute_distances(
769 size_t n,
const idx_t *shortlist,
770 const float *query,
float *distances)
const
772 std::vector<float> tmp(2 * index.d);
774 for (
int i = 0; i < n; i++) {
775 if (shortlist[i] < 0)
break;
776 reconstruct(shortlist[i], tmp.data(), tmp.data() + index.d);
777 distances[i] =
fvec_L2sqr(query, tmp.data(), index.d);
785 const HNSW & hnsw = index.hnsw;
790 index.storage->reconstruct(i, tmp1);
792 for (
size_t j = begin; j < end; j++) {
795 index.storage->reconstruct(ji, tmp1 + (j - begin + 1) * d);
803 const float *x, storage_idx_t i, uint8_t *code)
const
807 float *tmp1 =
new float[d * (M + 1) + (d * k)];
808 float *tmp2 = tmp1 + d * (M + 1);
814 for (
size_t sq = 0; sq < nsq; sq++) {
818 FINTEGER ki = k, di = d, m1 = M + 1;
819 FINTEGER dsubi = dsub;
820 float zero = 0, one = 1;
822 sgemm_ (
"N",
"N", &dsubi, &ki, &m1, &one,
824 codebook.data() + sq * (m1 * k), &m1,
825 &zero, tmp2, &dsubi);
828 float min = HUGE_VAL;
830 for (
size_t j = 0; j < k; j++) {
831 float dis =
fvec_L2sqr(x + d0, tmp2 + j * dsub, dsub);
848 codes.resize(codes.size() + code_size * n);
849 #pragma omp parallel for
850 for (
int i = 0; i < n; i++) {
852 codes.data() + (ntotal + i) * code_size);
855 FAISS_ASSERT (codes.size() == ntotal * code_size);
874 float operator () (idx_t i)
override
880 float symmetric_dis(idx_t i, idx_t j)
override
886 FlatL2Dis(
const IndexFlatL2 & storage,
const float *q =
nullptr):
891 b = storage.xb.data();
895 void set_query(
const float *x)
override {
899 ~FlatL2Dis()
override {
902 hnsw_stats.ndis += ndis;
911 IndexHNSWFlat::IndexHNSWFlat()
917 IndexHNSWFlat::IndexHNSWFlat(
int d,
int M):
918 IndexHNSW(new IndexFlatL2(d), M)
925 DistanceComputer * IndexHNSWFlat::get_distance_computer ()
const
927 return new FlatL2Dis (*dynamic_cast<IndexFlatL2*> (storage));
941 struct PQDis: DistanceComputer {
944 const uint8_t *codes;
946 const ProductQuantizer & pq;
948 std::vector<float> precomputed_table;
951 float operator () (idx_t i)
override
953 const uint8_t *code = codes + i * code_size;
954 const float *dt = precomputed_table.data();
956 for (
int j = 0; j < pq.M; j++) {
964 float symmetric_dis(idx_t i, idx_t j)
override
966 const float * sdci = sdc;
968 const uint8_t *codei = codes + i * code_size;
969 const uint8_t *codej = codes + j * code_size;
971 for (
int l = 0; l < pq.M; l++) {
972 accu += sdci[(*codei++) + (*codej++) * 256];
978 PQDis(
const IndexPQ& storage,
const float* =
nullptr)
980 precomputed_table.resize(pq.M * pq.ksub);
983 codes = storage.codes.data();
984 code_size = pq.code_size;
985 FAISS_ASSERT(pq.ksub == 256);
986 FAISS_ASSERT(pq.sdc_table.size() == pq.ksub * pq.ksub * pq.M);
987 sdc = pq.sdc_table.data();
991 void set_query(
const float *x)
override {
992 pq.compute_distance_table(x, precomputed_table.data());
998 hnsw_stats.ndis += ndis;
1007 IndexHNSWPQ::IndexHNSWPQ() {}
1009 IndexHNSWPQ::IndexHNSWPQ(
int d,
int pq_m,
int M):
1010 IndexHNSW(new IndexPQ(d, pq_m, 8), M)
1019 (
dynamic_cast<IndexPQ*
> (storage))->pq.compute_sdc_table();
1026 return new PQDis (*dynamic_cast<IndexPQ*> (storage));
1036 IndexHNSW (new IndexScalarQuantizer (d, qtype), M)
1042 IndexHNSWSQ::IndexHNSWSQ() {}
1044 DistanceComputer * IndexHNSWSQ::get_distance_computer ()
const
1046 return (dynamic_cast<const IndexScalarQuantizer*> (storage))->
1047 get_distance_computer ();
1059 IndexHNSW2Level::IndexHNSW2Level(Index *quantizer,
size_t nlist,
int m_pq,
int M):
1060 IndexHNSW (new Index2Layer (quantizer, nlist, m_pq), M)
1066 IndexHNSW2Level::IndexHNSW2Level() {}
1072 struct Distance2Level: DistanceComputer {
1074 const Index2Layer & storage;
1075 std::vector<float> buf;
1078 const float *pq_l1_tab, *pq_l2_tab;
1080 Distance2Level(
const Index2Layer & storage): storage(storage)
1083 FAISS_ASSERT(storage.pq.dsub == 4);
1084 pq_l2_tab = storage.pq.centroids.data();
1088 float symmetric_dis(idx_t i, idx_t j)
override
1090 storage.reconstruct(i, buf.data());
1091 storage.reconstruct(j, buf.data() + d);
1092 return fvec_L2sqr(buf.data() + d, buf.data(), d);
1095 void set_query(
const float *x)
override {
1102 struct DistanceXPQ4: Distance2Level {
1106 DistanceXPQ4(
const Index2Layer & storage):
1107 Distance2Level (storage)
1109 const IndexFlat *quantizer =
1110 dynamic_cast<IndexFlat*
> (storage.q1.quantizer);
1112 FAISS_ASSERT(quantizer);
1114 pq_l1_tab = quantizer->xb.data();
1117 float operator () (idx_t i)
override
1120 const uint8_t *code = storage.codes.data() + i * storage.code_size;
1122 memcpy (&key, code, storage.code_size_1);
1123 code += storage.code_size_1;
1126 const float *qa = q;
1127 const __m128 *l1_t = (
const __m128 *)(pq_l1_tab + d * key);
1128 const __m128 *pq_l2_t = (
const __m128 *)pq_l2_tab;
1129 __m128 accu = _mm_setzero_ps();
1131 for (
int m = 0; m < M; m++) {
1132 __m128 qi = _mm_loadu_ps(qa);
1133 __m128 recons = l1_t[m] + pq_l2_t[*code++];
1134 __m128 diff = qi - recons;
1135 accu += diff * diff;
1140 accu = _mm_hadd_ps (accu, accu);
1141 accu = _mm_hadd_ps (accu, accu);
1142 return _mm_cvtss_f32 (accu);
1144 FAISS_THROW_MSG(
"not implemented for non-x64 platforms");
1151 struct Distance2xXPQ4: Distance2Level {
1155 Distance2xXPQ4(
const Index2Layer & storage):
1156 Distance2Level (storage)
1158 const MultiIndexQuantizer *mi =
1159 dynamic_cast<MultiIndexQuantizer*
> (storage.q1.quantizer);
1162 FAISS_ASSERT(storage.pq.M % 2 == 0);
1163 M_2 = storage.pq.M / 2;
1164 mi_nbits = mi->pq.nbits;
1165 pq_l1_tab = mi->pq.centroids.data();
1168 float operator () (idx_t i)
override
1170 const uint8_t *code = storage.codes.data() + i * storage.code_size;
1172 memcpy (&key01, code, storage.code_size_1);
1173 code += storage.code_size_1;
1177 const float *qa = q;
1178 const __m128 *pq_l1_t = (
const __m128 *)pq_l1_tab;
1179 const __m128 *pq_l2_t = (
const __m128 *)pq_l2_tab;
1180 __m128 accu = _mm_setzero_ps();
1182 for (
int mi_m = 0; mi_m < 2; mi_m++) {
1183 long l1_idx = key01 & ((1L << mi_nbits) - 1);
1184 const __m128 * pq_l1 = pq_l1_t + M_2 * l1_idx;
1186 for (
int m = 0; m < M_2; m++) {
1187 __m128 qi = _mm_loadu_ps(qa);
1188 __m128 recons = pq_l1[m] + pq_l2_t[*code++];
1189 __m128 diff = qi - recons;
1190 accu += diff * diff;
1194 pq_l1_t += M_2 << mi_nbits;
1197 accu = _mm_hadd_ps (accu, accu);
1198 accu = _mm_hadd_ps (accu, accu);
1199 return _mm_cvtss_f32 (accu);
1201 FAISS_THROW_MSG(
"not implemented for non-x64 platforms");
1211 DistanceComputer * IndexHNSW2Level::get_distance_computer ()
const
1213 const Index2Layer *storage2l =
1214 dynamic_cast<Index2Layer*
>(storage);
1219 const MultiIndexQuantizer *mi =
1220 dynamic_cast<MultiIndexQuantizer*
> (storage2l->q1.quantizer);
1222 if (mi && storage2l->pq.M % 2 == 0 && storage2l->pq.dsub == 4) {
1223 return new Distance2xXPQ4(*storage2l);
1226 const IndexFlat *fl =
1227 dynamic_cast<IndexFlat*
> (storage2l->q1.quantizer);
1229 if (fl && storage2l->pq.dsub == 4) {
1230 return new DistanceXPQ4(*storage2l);
1236 return new GenericDistanceComputer (*storage);
1247 int search_from_candidates_2(
const HNSW & hnsw,
1248 DistanceComputer & qdis,
int k,
1249 idx_t *I,
float * D,
1250 MinimaxHeap &candidates,
1252 int level,
int nres_in = 0)
1256 for (
int i = 0; i < candidates.size(); i++) {
1257 idx_t v1 = candidates.ids[i];
1258 FAISS_ASSERT(v1 >= 0);
1259 vt.visited[v1] = vt.visno + 1;
1264 while (candidates.size() > 0) {
1266 int v0 = candidates.pop_min(&d0);
1269 hnsw.neighbor_range(v0, level, &begin, &end);
1271 for (
size_t j = begin; j < end; j++) {
1272 int v1 = hnsw.neighbors[j];
1274 if (vt.visited[v1] == vt.visno + 1) {
1279 candidates.push(v1, d);
1282 if (vt.visited[v1] < vt.visno) {
1284 faiss::maxheap_push (++nres, D, I, d, v1);
1285 }
else if (d < D[0]) {
1286 faiss::maxheap_pop (nres--, D, I);
1287 faiss::maxheap_push (++nres, D, I, d, v1);
1290 vt.visited[v1] = vt.visno + 1;
1295 if (nstep > hnsw.efSearch) {
1301 #pragma omp critical
1304 if (candidates.size() == 0)
1317 float *distances,
idx_t *labels)
const
1319 if (dynamic_cast<const Index2Layer*>(storage)) {
1327 int nprobe = index_ivfpq->
nprobe;
1329 long * coarse_assign =
new long [n * nprobe];
1331 float * coarse_dis =
new float [n * nprobe];
1334 index_ivfpq->
quantizer->
search (n, x, nprobe, coarse_dis, coarse_assign);
1337 n, x, k, coarse_assign, coarse_dis, distances, labels,
false);
1339 #pragma omp parallel
1345 int candidates_size = hnsw.upper_beam;
1349 for(
idx_t i = 0; i < n; i++) {
1350 idx_t * idxi = labels + i * k;
1351 float * simi = distances + i * k;
1356 for (
int j = 0; j < nprobe; j++) {
1357 idx_t key = coarse_assign[j + i * nprobe];
1359 size_t list_length = index_ivfpq->get_list_size (key);
1362 for (
int jj = 0; jj < list_length; jj++) {
1370 int search_policy = 2;
1372 if (search_policy == 1) {
1374 for (
int j = 0 ; j < hnsw.upper_beam && j < k; j++) {
1375 if (idxi[j] < 0)
break;
1376 candidates.push (idxi[j], simi[j]);
1383 maxheap_heapify (k, simi, idxi, simi, idxi, k);
1385 hnsw.search_from_candidates(
1386 *dis, k, idxi, simi,
1387 candidates, vt, 0, k
1392 }
else if (search_policy == 2) {
1394 for (
int j = 0 ; j < hnsw.upper_beam && j < k; j++) {
1395 if (idxi[j] < 0)
break;
1396 candidates.push (idxi[j], simi[j]);
1400 maxheap_heapify (k, simi, idxi, simi, idxi, k);
1402 search_from_candidates_2 (
1403 hnsw, *dis, k, idxi, simi,
1404 candidates, vt, 0, k);
1410 maxheap_reorder (k, simi, idxi);
1419 void IndexHNSW2Level::flip_to_ivf ()
1424 FAISS_THROW_IF_NOT (storage2l);
1429 storage2l->
pq.
M, 8);
1430 index_ivfpq->
pq = storage2l->
pq;
1437 storage = index_ivfpq;
void precompute_table()
build precomputed table
void transfer_to_IVFPQ(IndexIVFPQ &other) const
transfer the flat codes to an IVFPQ index
virtual float symmetric_dis(idx_t i, idx_t j)=0
compute distance between two stored vectors
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
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
virtual void set_query(const float *x)=0
called before computing distances
bool get(int no) const
get flag #no
float fvec_L2sqr(const float *x, const float *y, size_t d)
Squared L2 distance between two vectors.
void train(idx_t n, const float *x) override
Trains the storage if needed.
virtual const idx_t * get_ids(size_t list_no) const =0
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
entry point for search
virtual void reset()=0
removes all elements from the database.
size_t nprobe
number of probes at query time
virtual void train(idx_t n, const float *x)
Level1Quantizer q1
first level quantizer
void advance()
reset all flags to false
void get_neighbor_table(storage_idx_t i, float *out) const
get the M+1 -by-d table for neighbor coordinates for vector i
void train(idx_t n, const float *x) override
Trains the storage if needed.
long idx_t
all indices are this type
set implementation optimized for fast access.
void add(idx_t n, const float *x) override
void reconstruct(storage_idx_t i, float *x, float *tmp) const
called by compute_distances
virtual void add(idx_t n, const float *x)=0
idx_t ntotal
total nb of indexed vectors
double getmillisecs()
ms elapsed since some arbitrary epoch
void estimate_code(const float *x, storage_idx_t i, uint8_t *code) const
called by add_codes
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const =0
static bool is_interrupted()
void add_codes(size_t n, const float *x)
void make_direct_map(bool new_maintain_direct_map=true)
ProductQuantizer pq
produces the codes
InvertedLists * invlists
Acess to the actual data.
void reset() override
removes all elements from the database.
size_t M
number of subquantizers
static size_t get_period_hint(size_t flops)
void init_level_0_from_entry_points(int npt, const storage_idx_t *points, const storage_idx_t *nearests)
alternative graph building
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
entry point for search
Index * quantizer
quantizer that maps vectors to inverted lists
void init_level_0_from_knngraph(int k, const float *D, const idx_t *I)
alternative graph building
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
bool is_trained
set if the Index does not require training, or if training is done already
std::vector< storage_idx_t > neighbors
void reconstruct(idx_t key, float *recons) const override
virtual void reconstruct(idx_t key, float *recons) const
ProductQuantizer pq
second level quantizer is always a PQ
int storage_idx_t
internal storage of vectors (32 bits: this is expensive)
bool own_fields
whether object owns the quantizer
void set(int no)
set flog #no to true
size_t nlist
number of possible key values
static void shrink_neighbor_list(DistanceComputer &qdis, std::priority_queue< NodeDistFarther > &input, std::vector< NodeDistFarther > &output, int max_size)