11 #include "IndexHNSW.h"
21 #include <unordered_set>
24 #include <sys/types.h>
30 #include <immintrin.h>
35 #include "FaissAssert.h"
36 #include "IndexFlat.h"
37 #include "IndexIVFPQ.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;
58 using DistanceComputer = HNSW::DistanceComputer;
69 void hnsw_add_vertices(IndexHNSW &index_hnsw,
71 size_t n,
const float *x,
73 bool preset_levels =
false) {
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));
83 int max_level = hnsw.prepare_level_tab(n, preset_levels);
86 printf(
" max_level = %d\n", max_level);
89 std::vector<omp_lock_t> locks(ntotal);
90 for(
int i = 0; i < ntotal; i++)
91 omp_init_lock(&locks[i]);
94 std::vector<int> hist;
95 std::vector<int> order(n);
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())
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];
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;
123 RandomGenerator rng2(789);
127 for (
int pt_level = hist.size() - 1; pt_level >= 0; pt_level--) {
128 int i0 = i1 - hist[pt_level];
131 printf(
"Adding %d elements at level %d\n",
136 for (
int j = i0; j < i1; j++)
137 std::swap(order[j], order[j + rng2.rand_int(i1 - j)]);
141 VisitedTable vt (ntotal);
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;
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);
152 hnsw.add_with_locks(*dis, pt_level, pt_id, locks, vt);
154 if (prev_display >= 0 && i - i0 > prev_display + 10000) {
155 prev_display = i - i0;
156 printf(
" %d / %d\r", i - i0, i1 - i0);
163 FAISS_ASSERT(i1 == 0);
169 for(
int i = 0; i < ntotal; i++) {
170 omp_destroy_lock(&locks[i]);
184 IndexHNSW::IndexHNSW(
int d,
int M):
189 reconstruct_from_neighbors(nullptr)
192 IndexHNSW::IndexHNSW(Index *storage,
int M):
193 Index(storage->d, METRIC_L2),
197 reconstruct_from_neighbors(nullptr)
200 IndexHNSW::~IndexHNSW() {
209 storage->
train (n, x);
214 float *distances,
idx_t *labels)
const
226 for(
idx_t i = 0; i < n; i++) {
227 idx_t * idxi = labels + i * k;
228 float * simi = distances + i * k;
231 maxheap_heapify (k, simi, idxi);
232 hnsw.search(*dis, k, idxi, simi, vt);
234 maxheap_reorder (k, simi, idxi);
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;
241 nreorder += reconstruct_from_neighbors->compute_distances(
242 k_reorder, idxi, x + i * d, simi);
245 maxheap_heapify (k_reorder, simi, idxi, simi, idxi, k_reorder);
246 maxheap_reorder (k_reorder, simi, idxi);
251 hnsw_stats.nreorder += nreorder;
266 hnsw_add_vertices (*
this, n0, n, x, verbose,
267 hnsw.levels.size() ==
ntotal);
282 void IndexHNSW::shrink_level_0_neighbors(
int new_size)
290 for (idx_t i = 0; i <
ntotal; i++) {
293 hnsw.neighbor_range(i, 0, &begin, &end);
295 std::priority_queue<NodeDistFarther> initial_list;
297 for (
size_t j = begin; j < end; j++) {
298 int v1 = hnsw.neighbors[j];
305 std::vector<NodeDistFarther> shrunk_list;
307 shrunk_list, new_size);
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;
313 hnsw.neighbors[j] = -1;
322 const storage_idx_t *nearest,
const float *nearest_d,
323 float *distances,
idx_t *labels,
int nprobe,
324 int search_type)
const
327 storage_idx_t ntotal = hnsw.levels.size();
336 for(
idx_t i = 0; i < n; i++) {
337 idx_t * idxi = labels + i * k;
338 float * simi = distances + i * k;
341 maxheap_heapify (k, simi, idxi);
343 if (search_type == 1) {
347 for(
int j = 0; j < nprobe; j++) {
348 storage_idx_t cj = nearest[i * nprobe + j];
352 if (vt.
get(cj))
continue;
354 int candidates_size = std::max(hnsw.efSearch,
int(k));
357 candidates.push(cj, nearest_d[i * nprobe + j]);
359 nres = hnsw.search_from_candidates(
360 *qdis, k, idxi, simi,
361 candidates, vt, 0, nres
364 }
else if (search_type == 2) {
366 int candidates_size = std::max(hnsw.efSearch,
int(k));
367 candidates_size = std::max(candidates_size, nprobe);
370 for(
int j = 0; j < nprobe; j++) {
371 storage_idx_t cj = nearest[i * nprobe + j];
374 candidates.push(cj, nearest_d[i * nprobe + j]);
376 hnsw.search_from_candidates(
377 *qdis, k, idxi, simi,
384 maxheap_reorder (k, simi, idxi);
393 int k,
const float *D,
const idx_t *I)
395 int dest_size = hnsw.nb_neighbors (0);
397 #pragma omp parallel for
404 std::priority_queue<NodeDistFarther> initial_list;
406 for (
size_t j = 0; j < k; j++) {
407 int v1 = I[i * k + j];
408 if (v1 == i)
continue;
410 initial_list.emplace(D[i * k + j], v1);
413 std::vector<NodeDistFarther> shrunk_list;
417 hnsw.neighbor_range(i, 0, &begin, &end);
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;
423 hnsw.neighbors[j] = -1;
431 int n,
const storage_idx_t *points,
432 const storage_idx_t *nearests)
435 std::vector<omp_lock_t> locks(ntotal);
436 for(
int i = 0; i <
ntotal; i++)
437 omp_init_lock(&locks[i]);
445 float vec[storage->
d];
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];
454 hnsw.add_links_starting_from(*dis, pt_id,
455 nearest, (*dis)(nearest),
456 0, locks.data(), vt);
458 if (verbose && i % 10000 == 0) {
459 printf(
" %d / %d\r", i, n);
468 for(
int i = 0; i <
ntotal; i++)
469 omp_destroy_lock(&locks[i]);
472 void IndexHNSW::reorder_links()
474 int M = hnsw.nb_neighbors(0);
478 std::vector<float> distances (M);
479 std::vector<size_t> order (M);
480 std::vector<storage_idx_t> tmp (M);
485 for(storage_idx_t i = 0; i <
ntotal; i++) {
488 hnsw.neighbor_range(i, 0, &begin, &end);
490 for (
size_t j = begin; j < end; j++) {
491 storage_idx_t nj = hnsw.neighbors[j];
497 tmp [j - begin] = nj;
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]];
510 void IndexHNSW::link_singletons()
512 printf(
"search for singletons\n");
514 std::vector<bool> seen(ntotal);
516 for (
size_t i = 0; i <
ntotal; i++) {
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;
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++) {
529 singletons.push_back(i);
531 if (hnsw.levels[i] > 1)
536 printf(
" Found %d / %ld singletons (%d appear in a level above)\n",
537 n_sing, ntotal, n_sing_l1);
539 std::vector<float>recons(singletons.size() *
d);
540 for (
int i = 0; i < singletons.size(); i++) {
542 FAISS_ASSERT(!
"not implemented");
554 struct GenericDistanceComputer: DistanceComputer {
556 const Index & storage;
557 std::vector<float> buf;
560 GenericDistanceComputer(
const Index & storage): storage(storage)
566 float operator () (storage_idx_t i)
override
568 storage.reconstruct(i, buf.data());
572 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
574 storage.reconstruct(i, buf.data());
575 storage.reconstruct(j, buf.data() + d);
576 return fvec_L2sqr(buf.data() + d, buf.data(), d);
579 void set_query(
const float *x)
override {
589 DistanceComputer * IndexHNSW::get_distance_computer ()
const
591 return new GenericDistanceComputer (*storage);
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;
608 FAISS_ASSERT(
d % nsq == 0);
617 const HNSW & hnsw = index.hnsw;
621 if (k == 1 || nsq == 1) {
624 beta = codebook.data();
627 beta = codebook.data() + idx * (M + 1);
631 index.storage->reconstruct(i, tmp);
633 for (
int l = 0; l < d; l++)
636 for (
size_t j = begin; j < end; j++) {
640 float w = beta[j - begin + 1];
641 index.storage->reconstruct(ji, tmp);
642 for (
int l = 0; l < d; l++)
645 }
else if (nsq == 2) {
646 int idx0 = codes[2 * i];
647 int idx1 = codes[2 * i + 1];
649 const float *beta0 = codebook.data() + idx0 * (M + 1);
650 const float *beta1 = codebook.data() + (idx1 + k) * (M + 1);
652 index.storage->reconstruct(i, tmp);
657 for (
int l = 0; l < dsub; l++)
661 for (
int l = dsub; l < d; l++)
664 for (
size_t j = begin; j < end; j++) {
667 index.storage->reconstruct(ji, tmp);
669 w = beta0[j - begin + 1];
670 for (
int l = 0; l < dsub; l++)
673 w = beta1[j - begin + 1];
674 for (
int l = dsub; l < d; l++)
678 const float *betas[nsq];
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);
688 index.storage->reconstruct(i, tmp);
691 for (
int sq = 0; sq < nsq; sq++) {
692 float w = *(betas[sq]++);
694 for (
int l = d0; l < d1; l++) {
701 for (
size_t j = begin; j < end; j++) {
705 index.storage->reconstruct(ji, tmp);
707 for (
int sq = 0; sq < nsq; sq++) {
708 float w = *(betas[sq]++);
710 for (
int l = d0; l < d1; l++) {
719 void ReconstructFromNeighbors::reconstruct_n(storage_idx_t n0,
725 std::vector<float> tmp(index.d);
727 for (storage_idx_t i = 0; i < ni; i++) {
733 size_t ReconstructFromNeighbors::compute_distances(
734 size_t n,
const idx_t *shortlist,
735 const float *query,
float *distances)
const
737 std::vector<float> tmp(2 * index.d);
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);
750 const HNSW & hnsw = index.hnsw;
755 index.storage->reconstruct(i, tmp1);
757 for (
size_t j = begin; j < end; j++) {
760 index.storage->reconstruct(ji, tmp1 + (j - begin + 1) * d);
768 const float *x, storage_idx_t i, uint8_t *code)
const
772 float *tmp1 =
new float[d * (M + 1) + (d * k)];
773 float *tmp2 = tmp1 + d * (M + 1);
779 for (
size_t sq = 0; sq < nsq; sq++) {
783 FINTEGER ki = k, di = d, m1 = M + 1;
784 FINTEGER dsubi = dsub;
785 float zero = 0, one = 1;
787 sgemm_ (
"N",
"N", &dsubi, &ki, &m1, &one,
789 codebook.data() + sq * (m1 * k), &m1,
790 &zero, tmp2, &dsubi);
793 float min = HUGE_VAL;
795 for (
size_t j = 0; j < k; j++) {
796 float dis =
fvec_L2sqr(x + d0, tmp2 + j * dsub, dsub);
813 codes.resize(codes.size() + code_size * n);
814 #pragma omp parallel for
815 for (
int i = 0; i < n; i++) {
817 codes.data() + (ntotal + i) * code_size);
820 FAISS_ASSERT (codes.size() == ntotal * code_size);
838 float operator () (storage_idx_t i)
override
844 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
850 FlatL2Dis(
const IndexFlatL2 & storage,
const float *q =
nullptr):
855 b = storage.xb.data();
859 void set_query(
const float *x)
override {
863 virtual ~FlatL2Dis () {
866 hnsw_stats.ndis += ndis;
875 IndexHNSWFlat::IndexHNSWFlat()
881 IndexHNSWFlat::IndexHNSWFlat(
int d,
int M):
882 IndexHNSW(new IndexFlatL2(d), M)
889 DistanceComputer * IndexHNSWFlat::get_distance_computer ()
const
891 return new FlatL2Dis (*dynamic_cast<IndexFlatL2*> (storage));
905 struct PQDis: DistanceComputer {
907 const uint8_t *codes;
909 const ProductQuantizer & pq;
911 std::vector<float> precomputed_table;
914 float operator () (storage_idx_t i)
override
916 const uint8_t *code = codes + i * code_size;
917 const float *dt = precomputed_table.data();
919 for (
int j = 0; j < pq.M; j++) {
927 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
929 const float * sdci = sdc;
931 const uint8_t *codei = codes + i * code_size;
932 const uint8_t *codej = codes + j * code_size;
934 for (
int l = 0; l < pq.M; l++) {
935 accu += sdci[(*codei++) + (*codej++) * 256];
941 PQDis(
const IndexPQ& storage,
const float* =
nullptr)
943 precomputed_table.resize(pq.M * pq.ksub);
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();
954 void set_query(
const float *x)
override {
955 pq.compute_distance_table(x, precomputed_table.data());
961 hnsw_stats.ndis += ndis;
970 IndexHNSWPQ::IndexHNSWPQ() {}
972 IndexHNSWPQ::IndexHNSWPQ(
int d,
int pq_m,
int M):
973 IndexHNSW(new IndexPQ(d, pq_m, 8), M)
982 (
dynamic_cast<IndexPQ*
> (storage))->pq.compute_sdc_table();
989 return new PQDis (*dynamic_cast<IndexPQ*> (storage));
1001 struct SQDis: DistanceComputer {
1003 const uint8_t *codes;
1005 const ScalarQuantizer & sq;
1007 ScalarQuantizer::DistanceComputer * dc;
1009 float operator () (storage_idx_t i)
override
1011 const uint8_t *code = codes + i * code_size;
1013 return dc->compute_distance (q, code);
1016 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
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);
1023 SQDis(
const IndexScalarQuantizer& storage,
const float* =
nullptr)
1025 nb = storage.ntotal;
1027 codes = storage.codes.data();
1028 code_size = sq.code_size;
1029 dc = sq.get_distance_computer();
1032 void set_query(
const float *x)
override {
1046 IndexHNSW (new IndexScalarQuantizer (d, qtype), M)
1051 IndexHNSWSQ::IndexHNSWSQ() {}
1053 DistanceComputer * IndexHNSWSQ::get_distance_computer ()
const
1055 return new SQDis (*dynamic_cast<IndexScalarQuantizer*> (storage));
1067 IndexHNSW2Level::IndexHNSW2Level(Index *quantizer,
size_t nlist,
int m_pq,
int M):
1068 IndexHNSW (new Index2Layer (quantizer, nlist, m_pq), M)
1074 IndexHNSW2Level::IndexHNSW2Level() {}
1080 struct Distance2Level: DistanceComputer {
1082 const Index2Layer & storage;
1083 std::vector<float> buf;
1086 const float *pq_l1_tab, *pq_l2_tab;
1088 Distance2Level(
const Index2Layer & storage): storage(storage)
1091 FAISS_ASSERT(storage.pq.dsub == 4);
1092 pq_l2_tab = storage.pq.centroids.data();
1096 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
1098 storage.reconstruct(i, buf.data());
1099 storage.reconstruct(j, buf.data() + d);
1100 return fvec_L2sqr(buf.data() + d, buf.data(), d);
1103 void set_query(
const float *x)
override {
1110 struct DistanceXPQ4: Distance2Level {
1114 DistanceXPQ4(
const Index2Layer & storage):
1115 Distance2Level (storage)
1117 const IndexFlat *quantizer =
1118 dynamic_cast<IndexFlat*
> (storage.q1.quantizer);
1120 FAISS_ASSERT(quantizer);
1122 pq_l1_tab = quantizer->xb.data();
1125 float operator () (storage_idx_t i)
override
1128 const uint8_t *code = storage.codes.data() + i * storage.code_size;
1130 memcpy (&key, code, storage.code_size_1);
1131 code += storage.code_size_1;
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();
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;
1148 accu = _mm_hadd_ps (accu, accu);
1149 accu = _mm_hadd_ps (accu, accu);
1150 return _mm_cvtss_f32 (accu);
1152 FAISS_THROW_MSG(
"not implemented for non-x64 platforms");
1159 struct Distance2xXPQ4: Distance2Level {
1163 Distance2xXPQ4(
const Index2Layer & storage):
1164 Distance2Level (storage)
1166 const MultiIndexQuantizer *mi =
1167 dynamic_cast<MultiIndexQuantizer*
> (storage.q1.quantizer);
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();
1176 float operator () (storage_idx_t i)
override
1178 const uint8_t *code = storage.codes.data() + i * storage.code_size;
1180 memcpy (&key01, code, storage.code_size_1);
1181 code += storage.code_size_1;
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();
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;
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;
1202 pq_l1_t += M_2 << mi_nbits;
1205 accu = _mm_hadd_ps (accu, accu);
1206 accu = _mm_hadd_ps (accu, accu);
1207 return _mm_cvtss_f32 (accu);
1209 FAISS_THROW_MSG(
"not implemented for non-x64 platforms");
1219 DistanceComputer * IndexHNSW2Level::get_distance_computer ()
const
1221 const Index2Layer *storage2l =
1222 dynamic_cast<Index2Layer*
>(storage);
1227 const MultiIndexQuantizer *mi =
1228 dynamic_cast<MultiIndexQuantizer*
> (storage2l->q1.quantizer);
1230 if (mi && storage2l->pq.M % 2 == 0 && storage2l->pq.dsub == 4) {
1231 return new Distance2xXPQ4(*storage2l);
1234 const IndexFlat *fl =
1235 dynamic_cast<IndexFlat*
> (storage2l->q1.quantizer);
1237 if (fl && storage2l->pq.dsub == 4) {
1238 return new DistanceXPQ4(*storage2l);
1244 return new GenericDistanceComputer (*storage);
1255 int search_from_candidates_2(
const HNSW & hnsw,
1256 DistanceComputer & qdis,
int k,
1257 idx_t *I,
float * D,
1258 MinimaxHeap &candidates,
1260 int level,
int nres_in = 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;
1272 while (candidates.size() > 0) {
1274 int v0 = candidates.pop_min(&d0);
1277 hnsw.neighbor_range(v0, level, &begin, &end);
1279 for (
size_t j = begin; j < end; j++) {
1280 int v1 = hnsw.neighbors[j];
1282 if (vt.visited[v1] == vt.visno + 1) {
1287 candidates.push(v1, d);
1290 if (vt.visited[v1] < vt.visno) {
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);
1298 vt.visited[v1] = vt.visno + 1;
1303 if (nstep > hnsw.efSearch) {
1309 #pragma omp critical
1312 if (candidates.size() == 0)
1325 float *distances,
idx_t *labels)
const
1327 if (dynamic_cast<const Index2Layer*>(storage)) {
1335 int nprobe = index_ivfpq->
nprobe;
1337 long * coarse_assign =
new long [n * nprobe];
1339 float * coarse_dis =
new float [n * nprobe];
1342 index_ivfpq->
quantizer->
search (n, x, nprobe, coarse_dis, coarse_assign);
1345 n, x, k, coarse_assign, coarse_dis, distances, labels,
false);
1347 #pragma omp parallel
1353 int candidates_size = hnsw.upper_beam;
1357 for(
idx_t i = 0; i < n; i++) {
1358 idx_t * idxi = labels + i * k;
1359 float * simi = distances + i * k;
1364 for (
int j = 0; j < nprobe; j++) {
1365 idx_t key = coarse_assign[j + i * nprobe];
1367 size_t list_length = index_ivfpq->get_list_size (key);
1370 for (
int jj = 0; jj < list_length; jj++) {
1378 int search_policy = 2;
1380 if (search_policy == 1) {
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]);
1391 maxheap_heapify (k, simi, idxi, simi, idxi, k);
1393 hnsw.search_from_candidates(
1394 *dis, k, idxi, simi,
1395 candidates, vt, 0, k
1400 }
else if (search_policy == 2) {
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]);
1408 maxheap_heapify (k, simi, idxi, simi, idxi, k);
1410 search_from_candidates_2 (
1411 hnsw, *dis, k, idxi, simi,
1412 candidates, vt, 0, k);
1418 maxheap_reorder (k, simi, idxi);
1427 void IndexHNSW2Level::flip_to_ivf ()
1432 FAISS_THROW_IF_NOT (storage2l);
1437 storage2l->
pq.
M, 8);
1438 index_ivfpq->
pq = storage2l->
pq;
1445 storage = index_ivfpq;
void precompute_table()
build precomputed table
void transfer_to_IVFPQ(IndexIVFPQ &other) const
transfer the flat codes to an IVFPQ index
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
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 float symmetric_dis(storage_idx_t i, storage_idx_t j)=0
compute distance between two stored vectors
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.
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
long idx_t
all indices are this type
virtual void set_query(const float *x)=0
called before computing distances
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
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
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)