19 #include <unordered_set>
22 #include <sys/types.h>
27 #include <immintrin.h>
31 #include "FaissAssert.h"
32 #include "IndexFlat.h"
33 #include "IndexIVFPQ.h"
40 int sgemm_ (
const char *transa,
const char *transb, FINTEGER *m, FINTEGER *
41 n, FINTEGER *k,
const float *alpha,
const float *a,
42 FINTEGER *lda,
const float *b, FINTEGER *
43 ldb,
float *beta,
float *c, FINTEGER *ldc);
55 std::vector<uint8_t> visited;
59 visited(size), visno(1)
66 bool get(
int no)
const {
67 return visited[no] == visno;
74 memset (visited.data(), 0,
sizeof(visited[0]) * visited.size());
85 typedef HNSW::DistanceComputer DistanceComputer;
89 struct NodeDistCloser {
92 NodeDistCloser(
float d,
int id): d(d), id(id) {}
93 bool operator<(
const NodeDistCloser &obj1)
const {
return d < obj1.d; }
96 struct NodeDistFarther {
99 NodeDistFarther(
float d,
int id): d(d), id(id) {}
100 bool operator<(
const NodeDistFarther &obj1)
const {
return d > obj1.d; }
111 std::vector<storage_idx_t> ids;
112 std::vector<float> dis;
115 explicit MinimaxHeap(
int n): n(n), k(0), nvalid(0), ids(n), dis(n) {}
117 void push(storage_idx_t i,
float v)
120 if (v >= dis[0])
return;
121 faiss::heap_pop<HC> (k--, dis.data(), ids.data());
124 faiss::heap_push<HC> (++k, dis.data(), ids.data(), v, i);
133 int size()
const {
return nvalid;}
135 void clear() {nvalid = k = 0; }
137 int pop_min(
float *vmin_out =
nullptr)
143 if (ids[i] != -1)
break;
146 if (i == -1)
return -1;
151 if (ids[i] != -1 && dis[i] < vmin) {
157 if (vmin_out) *vmin_out = vmin;
164 int count_below(
float thresh) {
166 for(
int i = 0; i < k; i++) {
184 void shrink_neighbor_list(DistanceComputer & qdis,
185 std::priority_queue<NodeDistFarther> &input,
186 std::vector<NodeDistFarther> &output,
189 while (input.size() > 0) {
190 NodeDistFarther v1 = input.top();
192 float dist_v1_q = v1.d;
195 for (NodeDistFarther v2 : output) {
196 float dist_v1_v2 = qdis.symmetric_dis(v2.id, v1.id);
198 if (dist_v1_v2 < dist_v1_q) {
204 output.push_back(v1);
205 if (output.size() >= max_size)
215 void shrink_neighbor_list(DistanceComputer & qdis,
216 std::priority_queue<NodeDistCloser> &resultSet1,
220 if (resultSet1.size() < max_size) {
223 std::priority_queue<NodeDistFarther> resultSet;
224 std::vector<NodeDistFarther> returnlist;
226 while (resultSet1.size() > 0) {
227 resultSet.emplace(resultSet1.top().d, resultSet1.top().id);
231 shrink_neighbor_list (qdis, resultSet, returnlist, max_size);
233 for (NodeDistFarther curen2 : returnlist) {
234 resultSet1.emplace(curen2.d, curen2.id);
242 void add_link(HNSW & hnsw,
243 DistanceComputer & qdis,
244 storage_idx_t src, storage_idx_t dest,
248 hnsw.neighbor_range(src, level, &begin, &end);
249 if (hnsw.neighbors[end - 1] == -1) {
253 if (hnsw.neighbors[i - 1] != -1)
break;
256 hnsw.neighbors[i] = dest;
263 std::priority_queue<NodeDistCloser> resultSet;
264 resultSet.emplace(qdis.symmetric_dis(src, dest), dest);
265 for (
size_t i = begin; i < end; i++) {
266 storage_idx_t neigh = hnsw.neighbors[i];
267 resultSet.emplace(qdis.symmetric_dis(src, neigh), neigh);
270 shrink_neighbor_list(qdis, resultSet, end - begin);
274 while (resultSet.size()) {
275 hnsw.neighbors[i++] = resultSet.top().id;
280 hnsw.neighbors[i++] = -1;
285 void search_neighbors_to_add(HNSW & hnsw,
286 DistanceComputer &qdis,
287 std::priority_queue<NodeDistCloser> &results,
294 std::priority_queue<NodeDistFarther> candidates;
296 NodeDistFarther ev(d_entry_point, entry_point);
298 results.emplace(d_entry_point, entry_point);
301 while (!candidates.empty()) {
303 const NodeDistFarther &currEv = candidates.top();
305 if (currEv.d > results.top().d) {
308 int currNode = currEv.id;
313 hnsw.neighbor_range(currNode, level, &begin, &end);
314 for(
size_t i = begin; i < end; i++) {
315 storage_idx_t nodeId = hnsw.neighbors[i];
316 if (nodeId < 0)
break;
317 if (vt.get(nodeId))
continue;
320 float dis = qdis(nodeId);
321 NodeDistFarther evE1(dis, nodeId);
323 if (results.size() < hnsw.efConstruction ||
324 results.top().d > dis) {
326 results.emplace(dis, nodeId);
327 candidates.emplace(dis, nodeId);
328 if (results.size() > hnsw.efConstruction) {
340 void add_links_starting_from(HNSW & hnsw,
341 DistanceComputer &ptdis,
343 storage_idx_t nearest,
350 std::priority_queue<NodeDistCloser> link_targets;
352 search_neighbors_to_add(
353 hnsw, ptdis, link_targets, nearest, d_nearest,
357 int M = hnsw.nb_neighbors(level);
359 shrink_neighbor_list(ptdis, link_targets, M);
361 while (!link_targets.empty()) {
362 int other_id = link_targets.top().id;
364 omp_set_lock(&locks[other_id]);
365 add_link(hnsw, ptdis, other_id, pt_id, level);
366 omp_unset_lock(&locks[other_id]);
368 add_link(hnsw, ptdis, pt_id, other_id, level);
379 void greedy_update_nearest(
const HNSW & hnsw,
380 DistanceComputer & qdis,
382 storage_idx_t & nearest,
386 storage_idx_t prev_nearest = nearest;
389 hnsw.neighbor_range(nearest, level, &begin, &end);
390 for(
size_t i = begin; i < end; i++) {
391 storage_idx_t v = hnsw.neighbors[i];
394 if (dis < d_nearest) {
399 if (nearest == prev_nearest) {
409 int search_from_candidates(
const HNSW & hnsw,
410 DistanceComputer & qdis,
int k,
412 MinimaxHeap &candidates,
414 int level,
int nres_in = 0)
418 for (
int i = 0; i < candidates.size(); i++) {
419 idx_t v1 = candidates.ids[i];
420 float d = candidates.dis[i];
421 FAISS_ASSERT(v1 >= 0);
423 faiss::maxheap_push (++nres, D, I, d, v1);
424 }
else if (d < D[0]) {
425 faiss::maxheap_pop (nres--, D, I);
426 faiss::maxheap_push (++nres, D, I, d, v1);
431 bool do_dis_check = hnsw.check_relative_distance;
434 while (candidates.size() > 0) {
436 int v0 = candidates.pop_min(&d0);
443 int n_dis_below = candidates.count_below(d0);
444 if(n_dis_below >= hnsw.efSearch) {
449 hnsw.neighbor_range(v0, level, &begin, &end);
451 for (
size_t j = begin; j < end; j++) {
452 int v1 = hnsw.neighbors[j];
461 faiss::maxheap_push (++nres, D, I, d, v1);
462 }
else if (d < D[0]) {
463 faiss::maxheap_pop (nres--, D, I);
464 faiss::maxheap_push (++nres, D, I, d, v1);
466 candidates.push(v1, d);
470 if (!do_dis_check && nstep > hnsw.efSearch) {
479 if (candidates.size() == 0)
481 hnsw_stats.n3 += ndis;
505 FAISS_THROW_IF_NOT(
levels.size() == 0);
518 size_t * begin,
size_t * end)
const
557 for (
int level = 0; ;level++) {
558 float proba = exp(-level / levelMult) * (1 - exp(-1 / levelMult));
559 if (proba < 1e-9)
break;
561 nn += level == 0 ? M * 2 : M;
566 void HNSW::clear_neighbor_tables(
int level)
568 for (
int i = 0; i <
levels.size(); i++) {
571 for (
size_t j = begin; j < end; j++)
588 void HNSW::print_neighbor_stats(
int level)
const
591 printf(
"stats on level %d, max %d neighbors per vertex:\n",
593 size_t tot_neigh = 0, tot_common = 0, tot_reciprocal = 0, n_node = 0;
594 #pragma omp parallel for reduction(+: tot_neigh) reduction(+: tot_common) \
595 reduction(+: tot_reciprocal) reduction(+: n_node)
596 for (
int i = 0; i <
levels.size(); i++) {
601 std::unordered_set<int> neighset;
602 for (
size_t j = begin; j < end; j++) {
606 int n_neigh = neighset.size();
608 int n_reciprocal = 0;
609 for (
size_t j = begin; j < end; j++) {
612 FAISS_ASSERT(i2 != i);
615 for (
size_t j2 = begin2; j2 < end2; j2++) {
622 if (neighset.count(i3)) {
628 tot_neigh += n_neigh;
629 tot_common += n_common;
630 tot_reciprocal += n_reciprocal;
633 float normalizer = n_node;
634 printf(
" nb of nodes at that level %ld\n", n_node);
635 printf(
" neighbors per node: %.2f (%ld)\n", tot_neigh / normalizer, tot_neigh);
636 printf(
" nb of reciprocal neighbors: %.2f\n", tot_reciprocal / normalizer);
637 printf(
" nb of neighbors that are also neighbor-of-neighbors: %.2f (%ld)\n",
638 tot_common / normalizer, tot_common);
644 HNSWStats hnsw_stats;
646 void HNSWStats::reset ()
648 memset(
this, 0,
sizeof(*
this));
658 DistanceComputer & ptdis,
int pt_level,
int pt_id,
659 std::vector<omp_lock_t> & locks,
664 storage_idx_t nearest;
679 omp_set_lock(&locks[pt_id]);
682 float d_nearest = ptdis(nearest);
684 for(; level > pt_level; level--) {
685 greedy_update_nearest(*
this, ptdis, level, nearest, d_nearest);
688 for(; level >= 0; level--) {
689 add_links_starting_from(*
this, ptdis, pt_id, nearest, d_nearest,
690 level, locks.data(), vt);
693 omp_unset_lock(&locks[pt_id]);
712 int k, idx_t *I,
float * D,
720 float d_nearest = qdis(nearest);
722 for(
int level =
max_level; level >= 1; level--) {
723 greedy_update_nearest(*
this, qdis, level, nearest, d_nearest);
726 int candidates_size = std::max(
efSearch, k);
727 MinimaxHeap candidates(candidates_size);
729 candidates.push(nearest, d_nearest);
731 search_from_candidates (
732 *
this, qdis, k, I, D, candidates, vt, 0);
738 MinimaxHeap candidates(candidates_size);
740 std::vector<idx_t> I_to_next(candidates_size);
741 std::vector<float> D_to_next(candidates_size);
745 D_to_next[0] = qdis(entry_point);
747 for(
int level =
max_level; level >= 0; level--) {
753 for (
int i = 0; i < nres; i++) {
754 candidates.push(I_to_next[i], D_to_next[i]);
758 nres = search_from_candidates (
759 *
this, qdis, k, I, D, candidates, vt, 0);
761 nres = search_from_candidates (
762 *
this, qdis, candidates_size,
763 I_to_next.data(), D_to_next.data(),
764 candidates, vt, level);
778 int prepare_level_tab (
HNSW & hnsw,
size_t n,
bool preset_levels =
false)
780 size_t n0 = hnsw.
offsets.size() - 1;
783 FAISS_ASSERT (n0 + n == hnsw.
levels.size());
785 FAISS_ASSERT (n0 == hnsw.
levels.size());
786 for (
int i = 0; i < n; i++) {
788 hnsw.
levels.push_back(pt_level + 1);
793 for (
int i = 0; i < n; i++) {
794 int pt_level = hnsw.
levels[i + n0] - 1;
795 if (pt_level > max_level) max_level = pt_level;
803 void hnsw_add_vertices(IndexHNSW &index_hnsw,
805 size_t n,
const float *x,
807 bool preset_levels =
false) {
808 HNSW & hnsw = index_hnsw.hnsw;
809 size_t ntotal = n0 + n;
812 printf(
"hnsw_add_vertices: adding %ld elements on top of %ld "
813 "(preset_levels=%d)\n",
814 n, n0,
int(preset_levels));
817 int max_level = prepare_level_tab (index_hnsw.hnsw, n, preset_levels);
820 printf(
" max_level = %d\n", max_level);
823 std::vector<omp_lock_t> locks(ntotal);
824 for(
int i = 0; i < ntotal; i++)
825 omp_init_lock(&locks[i]);
828 std::vector<int> hist;
829 std::vector<int> order(n);
834 for (
int i = 0; i < n; i++) {
835 storage_idx_t pt_id = i + n0;
836 int pt_level = hnsw.levels[pt_id] - 1;
837 while (pt_level >= hist.size())
843 std::vector<int> offsets(hist.size() + 1, 0);
844 for (
int i = 0; i < hist.size() - 1; i++) {
845 offsets[i + 1] = offsets[i] + hist[i];
849 for (
int i = 0; i < n; i++) {
850 storage_idx_t pt_id = i + n0;
851 int pt_level = hnsw.levels[pt_id] - 1;
852 order[offsets[pt_level]++] = pt_id;
857 RandomGenerator rng2(789);
861 for (
int pt_level = hist.size() - 1; pt_level >= 0; pt_level--) {
862 int i0 = i1 - hist[pt_level];
865 printf(
"Adding %d elements at level %d\n",
870 for (
int j = i0; j < i1; j++)
871 std::swap(order[j], order[j + rng2.rand_int(i1 - j)]);
875 VisitedTable vt (ntotal);
877 DistanceComputer *dis = index_hnsw.get_distance_computer();
878 ScopeDeleter1<DistanceComputer> del(dis);
879 int prev_display = verbose && omp_get_thread_num() == 0 ? 0 : -1;
881 #pragma omp for schedule(dynamic)
882 for (
int i = i0; i < i1; i++) {
883 storage_idx_t pt_id = order[i];
884 dis->set_query (x + (pt_id - n0) * dis->d);
886 hnsw.add_with_locks (
887 *dis, pt_level, pt_id, locks,
890 if (prev_display >= 0 && i - i0 > prev_display + 10000) {
891 prev_display = i - i0;
892 printf(
" %d / %d\r", i - i0, i1 - i0);
899 FAISS_ASSERT(i1 == 0);
904 for(
int i = 0; i < ntotal; i++)
905 omp_destroy_lock(&locks[i]);
915 int max_level = prepare_level_tab (*
this, n);
918 for (
int level = max_level - 1; level >= 0; level++) {
919 std::vector<int> elts;
920 for (
int i = 0; i < n; i++) {
925 printf (
"linking %ld elements in level %d\n",
928 if (elts.size() == 1)
continue;
930 for (
int ii = 0; ii < elts.size(); ii++) {
934 for (
size_t j = begin; j < end; j++) {
937 other = elts[rng2.
rand_int(elts.size())];
956 IndexHNSW::IndexHNSW(
int d,
int M):
961 reconstruct_from_neighbors(nullptr)
964 IndexHNSW::IndexHNSW(Index *storage,
int M):
965 Index(storage->d, METRIC_L2),
969 reconstruct_from_neighbors(nullptr)
972 IndexHNSW::~IndexHNSW() {
981 storage->
train (n, x);
986 float *distances, idx_t *labels)
const
993 DistanceComputer *dis = get_distance_computer();
998 for(
int i = 0; i < n; i++) {
999 idx_t * idxi = labels + i * k;
1000 float * simi = distances + i * k;
1001 dis->set_query(x + i * d);
1003 maxheap_heapify (k, simi, idxi);
1004 hnsw.search (*dis, k, idxi, simi, vt);
1006 maxheap_reorder (k, simi, idxi);
1008 if (reconstruct_from_neighbors &&
1009 reconstruct_from_neighbors->k_reorder != 0) {
1010 int k_reorder = reconstruct_from_neighbors->k_reorder;
1011 if (k_reorder == -1 || k_reorder > k) k_reorder = k;
1013 nreorder += reconstruct_from_neighbors->compute_distances(
1014 k_reorder, idxi, x + i * d, simi);
1017 maxheap_heapify (k_reorder, simi, idxi, simi, idxi, k_reorder);
1018 maxheap_reorder (k_reorder, simi, idxi);
1021 #pragma omp critical
1023 hnsw_stats.nreorder += nreorder;
1036 ntotal = storage->
ntotal;
1038 hnsw_add_vertices (*
this, n0, n, x, verbose,
1039 hnsw.levels.size() ==
ntotal);
1055 void IndexHNSW::shrink_level_0_neighbors(
int new_size)
1057 #pragma omp parallel
1059 DistanceComputer *dis = get_distance_computer();
1063 for (idx_t i = 0; i <
ntotal; i++) {
1066 hnsw.neighbor_range(i, 0, &begin, &end);
1068 std::priority_queue<NodeDistFarther> initial_list;
1070 for (
size_t j = begin; j < end; j++) {
1071 int v1 = hnsw.neighbors[j];
1073 initial_list.emplace(dis->symmetric_dis(i, v1), v1);
1078 std::vector<NodeDistFarther> shrunk_list;
1079 shrink_neighbor_list (*dis, initial_list, shrunk_list, new_size);
1081 for (
size_t j = begin; j < end; j++) {
1082 if (j - begin < shrunk_list.size())
1083 hnsw.neighbors[j] = shrunk_list[j - begin].id;
1085 hnsw.neighbors[j] = -1;
1093 idx_t n,
const float *x, idx_t k,
1094 const storage_idx_t *nearest,
const float *nearest_d,
1095 float *distances, idx_t *labels,
int nprobe,
1096 int search_type)
const
1099 storage_idx_t ntotal = hnsw.levels.size();
1100 #pragma omp parallel
1102 DistanceComputer *qdis = get_distance_computer();
1108 for(idx_t i = 0; i < n; i++) {
1109 idx_t * idxi = labels + i * k;
1110 float * simi = distances + i * k;
1112 qdis->set_query(x + i * d);
1113 maxheap_heapify (k, simi, idxi);
1115 if (search_type == 1) {
1119 for(
int j = 0; j < nprobe; j++) {
1120 storage_idx_t cj = nearest[i * nprobe + j];
1124 if (vt.
get(cj))
continue;
1126 int candidates_size = std::max(hnsw.efSearch,
int(k));
1127 MinimaxHeap candidates(candidates_size);
1129 candidates.push(cj, nearest_d[i * nprobe + j]);
1131 nres = search_from_candidates (
1132 hnsw, *qdis, k, idxi, simi,
1133 candidates, vt, 0, nres);
1135 }
else if (search_type == 2) {
1137 int candidates_size = std::max(hnsw.efSearch,
int(k));
1138 candidates_size = std::max(candidates_size, nprobe);
1140 MinimaxHeap candidates(candidates_size);
1141 for(
int j = 0; j < nprobe; j++) {
1142 storage_idx_t cj = nearest[i * nprobe + j];
1145 candidates.push(cj, nearest_d[i * nprobe + j]);
1147 search_from_candidates (
1148 hnsw, *qdis, k, idxi, simi,
1154 maxheap_reorder (k, simi, idxi);
1163 int k,
const float *D,
const idx_t *I)
1165 int dest_size = hnsw.nb_neighbors (0);
1167 #pragma omp parallel for
1168 for (idx_t i = 0; i <
ntotal; i++) {
1169 DistanceComputer *qdis = get_distance_computer();
1172 qdis->set_query(vec);
1174 std::priority_queue<NodeDistFarther> initial_list;
1176 for (
size_t j = 0; j < k; j++) {
1177 int v1 = I[i * k + j];
1178 if (v1 == i)
continue;
1180 initial_list.emplace(D[i * k + j], v1);
1183 std::vector<NodeDistFarther> shrunk_list;
1184 shrink_neighbor_list (*qdis, initial_list, shrunk_list, dest_size);
1187 hnsw.neighbor_range(i, 0, &begin, &end);
1189 for (
size_t j = begin; j < end; j++) {
1190 if (j - begin < shrunk_list.size())
1191 hnsw.neighbors[j] = shrunk_list[j - begin].id;
1193 hnsw.neighbors[j] = -1;
1201 int n,
const storage_idx_t *points,
1202 const storage_idx_t *nearests)
1205 std::vector<omp_lock_t> locks(ntotal);
1206 for(
int i = 0; i <
ntotal; i++)
1207 omp_init_lock(&locks[i]);
1209 #pragma omp parallel
1213 DistanceComputer *dis = get_distance_computer();
1215 float vec[storage->
d];
1217 #pragma omp for schedule(dynamic)
1218 for (
int i = 0; i < n; i++) {
1219 storage_idx_t pt_id = points[i];
1220 storage_idx_t nearest = nearests[i];
1222 dis->set_query (vec);
1224 add_links_starting_from(hnsw, *dis, pt_id, nearest, (*dis)(nearest),
1225 0, locks.data(), vt);
1227 if (verbose && i % 10000 == 0) {
1228 printf(
" %d / %d\r", i, n);
1237 for(
int i = 0; i <
ntotal; i++)
1238 omp_destroy_lock(&locks[i]);
1241 void IndexHNSW::reorder_links()
1243 int M = hnsw.nb_neighbors(0);
1245 #pragma omp parallel
1247 std::vector<float> distances (M);
1248 std::vector<size_t> order (M);
1249 std::vector<storage_idx_t> tmp (M);
1250 DistanceComputer *dis = get_distance_computer();
1254 for(storage_idx_t i = 0; i <
ntotal; i++) {
1257 hnsw.neighbor_range(i, 0, &begin, &end);
1259 for (
size_t j = begin; j < end; j++) {
1260 storage_idx_t nj = hnsw.neighbors[j];
1265 distances[j - begin] = dis->symmetric_dis(i, nj);
1266 tmp [j - begin] = nj;
1269 fvec_argsort (end - begin, distances.data(), order.data());
1270 for (
size_t j = begin; j < end; j++) {
1271 hnsw.neighbors[j] = tmp[order[j - begin]];
1279 void IndexHNSW::link_singletons()
1281 printf(
"search for singletons\n");
1283 std::vector<bool> seen(ntotal);
1285 for (
size_t i = 0; i <
ntotal; i++) {
1287 hnsw.neighbor_range(i, 0, &begin, &end);
1288 for (
size_t j = begin; j < end; j++) {
1289 storage_idx_t ni = hnsw.neighbors[j];
1290 if (ni >= 0) seen[ni] =
true;
1294 int n_sing = 0, n_sing_l1 = 0;
1295 std::vector<storage_idx_t> singletons;
1296 for (storage_idx_t i = 0; i <
ntotal; i++) {
1298 singletons.push_back(i);
1300 if (hnsw.levels[i] > 1)
1305 printf(
" Found %d / %ld singletons (%d appear in a level above)\n",
1306 n_sing, ntotal, n_sing_l1);
1308 std::vector<float>recons(singletons.size() *
d);
1309 for (
int i = 0; i < singletons.size(); i++) {
1311 FAISS_ASSERT(!
"not implemented");
1324 const Index & storage;
1325 std::vector<float> buf;
1334 float operator () (storage_idx_t i)
override
1340 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
1344 return fvec_L2sqr(buf.data() + d, buf.data(), d);
1354 HNSW::DistanceComputer * IndexHNSW::get_distance_computer ()
const
1356 return new GenericDistanceComputer (*storage);
1365 ReconstructFromNeighbors::ReconstructFromNeighbors(
1366 const IndexHNSW & index,
size_t k,
size_t nsq):
1367 index(index), k(k), nsq(nsq) {
1368 M = index.hnsw.nb_neighbors(0);
1369 FAISS_ASSERT(k <= 256);
1370 code_size = k == 1 ? 0 : nsq;
1373 FAISS_ASSERT(d % nsq == 0);
1382 const HNSW & hnsw = index.hnsw;
1386 if (k == 1 || nsq == 1) {
1389 beta = codebook.data();
1392 beta = codebook.data() + idx * (M + 1);
1396 index.storage->reconstruct(i, tmp);
1398 for (
int l = 0; l < d; l++)
1401 for (
size_t j = begin; j < end; j++) {
1405 float w = beta[j - begin + 1];
1406 index.storage->reconstruct(ji, tmp);
1407 for (
int l = 0; l < d; l++)
1410 }
else if (nsq == 2) {
1411 int idx0 = codes[2 * i];
1412 int idx1 = codes[2 * i + 1];
1414 const float *beta0 = codebook.data() + idx0 * (M + 1);
1415 const float *beta1 = codebook.data() + (idx1 + k) * (M + 1);
1417 index.storage->reconstruct(i, tmp);
1422 for (
int l = 0; l < dsub; l++)
1426 for (
int l = dsub; l < d; l++)
1429 for (
size_t j = begin; j < end; j++) {
1432 index.storage->reconstruct(ji, tmp);
1434 w = beta0[j - begin + 1];
1435 for (
int l = 0; l < dsub; l++)
1438 w = beta1[j - begin + 1];
1439 for (
int l = dsub; l < d; l++)
1443 const float *betas[nsq];
1445 const float *b = codebook.data();
1446 const uint8_t *c = &codes[i * code_size];
1447 for (
int sq = 0; sq < nsq; sq++) {
1448 betas[sq] = b + (*c++) * (M + 1);
1453 index.storage->reconstruct(i, tmp);
1456 for (
int sq = 0; sq < nsq; sq++) {
1457 float w = *(betas[sq]++);
1459 for (
int l = d0; l < d1; l++) {
1466 for (
size_t j = begin; j < end; j++) {
1470 index.storage->reconstruct(ji, tmp);
1472 for (
int sq = 0; sq < nsq; sq++) {
1473 float w = *(betas[sq]++);
1475 for (
int l = d0; l < d1; l++) {
1484 void ReconstructFromNeighbors::reconstruct_n(storage_idx_t n0,
1488 #pragma omp parallel
1490 std::vector<float> tmp(index.d);
1492 for (storage_idx_t i = 0; i < ni; i++) {
1498 size_t ReconstructFromNeighbors::compute_distances(
size_t n,
const idx_t *shortlist,
1499 const float *query,
float *distances)
const
1501 std::vector<float> tmp(2 * index.d);
1503 for (
int i = 0; i < n; i++) {
1504 if (shortlist[i] < 0)
break;
1505 reconstruct(shortlist[i], tmp.data(), tmp.data() + index.d);
1506 distances[i] =
fvec_L2sqr(query, tmp.data(), index.d);
1514 const HNSW & hnsw = index.hnsw;
1519 index.storage->reconstruct(i, tmp1);
1521 for (
size_t j = begin; j < end; j++) {
1524 index.storage->reconstruct(ji, tmp1 + (j - begin + 1) * d);
1532 const float *x, storage_idx_t i, uint8_t *code)
const
1536 float *tmp1 =
new float[d * (M + 1) + (d * k)];
1537 float *tmp2 = tmp1 + d * (M + 1);
1543 for (
int sq = 0; sq < nsq; sq++) {
1548 FINTEGER ki = k, di = d, m1 = M + 1;
1549 FINTEGER dsubi = dsub;
1550 float zero = 0, one = 1;
1552 sgemm_ (
"N",
"N", &dsubi, &ki, &m1, &one,
1554 codebook.data() + sq * (m1 * k), &m1,
1555 &zero, tmp2, &dsubi);
1558 float min = HUGE_VAL;
1560 for (
int j = 0; j < k; j++) {
1561 float dis =
fvec_L2sqr(x + d0, tmp2 + j * dsub, dsub);
1578 codes.resize(codes.size() + code_size * n);
1579 #pragma omp parallel for
1580 for (
int i = 0; i < n; i++) {
1582 codes.data() + (ntotal + i) * code_size);
1585 FAISS_ASSERT (codes.size() == ntotal * code_size);
1600 float operator () (storage_idx_t i)
override
1606 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
1608 return (
fvec_L2sqr(b + j * d, b + i * d, d));
1617 b = storage.
xb.data();
1626 #pragma omp critical
1628 hnsw_stats.ndis += ndis;
1634 IndexHNSWFlat::IndexHNSWFlat()
1640 IndexHNSWFlat::IndexHNSWFlat(
int d,
int M):
1641 IndexHNSW(new IndexFlatL2(d), M)
1648 HNSW::DistanceComputer * IndexHNSWFlat::get_distance_computer ()
const
1650 return new FlatL2Dis (*dynamic_cast<IndexFlatL2*> (storage));
1663 const uint8_t *codes;
1667 std::vector<float> precomputed_table;
1670 float operator () (storage_idx_t i)
override
1672 const uint8_t *code = codes + i * code_size;
1673 const float *dt = precomputed_table.data();
1675 for (
int j = 0; j < pq.
M; j++) {
1676 accu += dt[*code++];
1683 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
1685 const float * sdci = sdc;
1687 const uint8_t *codei = codes + i * code_size;
1688 const uint8_t *codej = codes + j * code_size;
1690 for (
int l = 0; l < pq.
M; l++) {
1691 accu += sdci[(*codei++) + (*codej++) * 256];
1698 PQDis(
const IndexPQ & storage,
const float *q =
nullptr):
1701 precomputed_table.resize(pq.
M * pq.
ksub);
1704 codes = storage.
codes.data();
1706 FAISS_ASSERT(pq.
ksub == 256);
1717 #pragma omp critical
1719 hnsw_stats.ndis += ndis;
1724 IndexHNSWPQ::IndexHNSWPQ() {}
1726 IndexHNSWPQ::IndexHNSWPQ(
int d,
int pq_m,
int M):
1727 IndexHNSW(new IndexPQ(d, pq_m, 8), M)
1736 (
dynamic_cast<IndexPQ*
> (storage))->pq.compute_sdc_table();
1743 return new PQDis (*dynamic_cast<IndexPQ*> (storage));
1754 const uint8_t *codes;
1760 float operator () (storage_idx_t i)
override
1762 const uint8_t *code = codes + i * code_size;
1767 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
1769 const uint8_t *codei = codes + i * code_size;
1770 const uint8_t *codej = codes + j * code_size;
1780 codes = storage.
codes.data();
1782 dc = sq.get_distance_computer();
1795 IndexHNSW (new IndexScalarQuantizer (d, qtype), M)
1800 IndexHNSWSQ::IndexHNSWSQ() {}
1802 HNSW::DistanceComputer * IndexHNSWSQ::get_distance_computer ()
const
1804 return new SQDis (*dynamic_cast<IndexScalarQuantizer*> (storage));
1816 IndexHNSW2Level::IndexHNSW2Level(Index *quantizer,
size_t nlist,
int m_pq,
int M):
1817 IndexHNSW (new Index2Layer (quantizer, nlist, m_pq), M)
1823 IndexHNSW2Level::IndexHNSW2Level() {}
1828 std::vector<float> buf;
1831 const float *pq_l1_tab, *pq_l2_tab;
1836 FAISS_ASSERT(storage.
pq.
dsub == 4);
1841 float symmetric_dis(storage_idx_t i, storage_idx_t j)
override
1845 return fvec_L2sqr(buf.data() + d, buf.data(), d);
1867 FAISS_ASSERT(quantizer);
1869 pq_l1_tab = quantizer->
xb.data();
1872 float operator () (storage_idx_t i)
override
1874 const uint8_t *code = storage.
codes.data() + i * storage.
code_size;
1880 const float *qa = q;
1881 const __m128 *l1_t = (
const __m128 *)(pq_l1_tab + d * key);
1882 const __m128 *pq_l2_t = (
const __m128 *)pq_l2_tab;
1883 __m128 accu = _mm_setzero_ps();
1885 for (
int m = 0; m < M; m++) {
1886 __m128 qi = _mm_loadu_ps(qa);
1887 __m128 recons = l1_t[m] + pq_l2_t[*code++];
1888 __m128 diff = qi - recons;
1889 accu += diff * diff;
1894 accu = _mm_hadd_ps (accu, accu);
1895 accu = _mm_hadd_ps (accu, accu);
1896 return _mm_cvtss_f32 (accu);
1913 FAISS_ASSERT(storage.
pq.
M % 2 == 0);
1914 M_2 = storage.
pq.
M / 2;
1915 mi_nbits = mi->pq.
nbits;
1919 float operator () (storage_idx_t i)
override
1921 const uint8_t *code = storage.
codes.data() + i * storage.
code_size;
1927 const float *qa = q;
1928 const __m128 *pq_l1_t = (
const __m128 *)pq_l1_tab;
1929 const __m128 *pq_l2_t = (
const __m128 *)pq_l2_tab;
1930 __m128 accu = _mm_setzero_ps();
1932 for (
int mi_m = 0; mi_m < 2; mi_m++) {
1933 long l1_idx = key01 & ((1L << mi_nbits) - 1);
1934 const __m128 * pq_l1 = pq_l1_t + M_2 * l1_idx;
1936 for (
int m = 0; m < M_2; m++) {
1937 __m128 qi = _mm_loadu_ps(qa);
1938 __m128 recons = pq_l1[m] + pq_l2_t[*code++];
1939 __m128 diff = qi - recons;
1940 accu += diff * diff;
1944 pq_l1_t += M_2 << mi_nbits;
1947 accu = _mm_hadd_ps (accu, accu);
1948 accu = _mm_hadd_ps (accu, accu);
1949 return _mm_cvtss_f32 (accu);
1966 if (mi && storage2l->
pq.
M % 2 == 0 && storage2l->
pq.
dsub == 4) {
1970 const IndexFlat *fl =
1971 dynamic_cast<IndexFlat*
> (storage2l->
q1.
quantizer);
1973 if (fl && storage2l->
pq.
dsub == 4) {
1974 return new DistanceXPQ4(*storage2l);
1979 return new GenericDistanceComputer (*storage);
1990 int search_from_candidates_2(
const HNSW & hnsw,
1991 DistanceComputer & qdis,
int k,
1992 idx_t *I,
float * D,
1993 MinimaxHeap &candidates,
1995 int level,
int nres_in = 0)
1999 for (
int i = 0; i < candidates.size(); i++) {
2000 idx_t v1 = candidates.ids[i];
2001 float d = candidates.dis[i];
2002 FAISS_ASSERT(v1 >= 0);
2003 vt.visited[v1] = vt.visno + 1;
2006 bool do_dis_check = hnsw.check_relative_distance;
2009 while (candidates.size() > 0) {
2011 int v0 = candidates.pop_min(&d0);
2018 int n_dis_below = candidates.count_below(d0);
2019 if(n_dis_below >= hnsw.efSearch) {
2024 hnsw.neighbor_range(v0, level, &begin, &end);
2026 for (
size_t j = begin; j < end; j++) {
2027 int v1 = hnsw.neighbors[j];
2029 if (vt.visited[v1] == vt.visno + 1) {
2034 candidates.push(v1, d);
2037 if (vt.visited[v1] < vt.visno) {
2039 faiss::maxheap_push (++nres, D, I, d, v1);
2040 }
else if (d < D[0]) {
2041 faiss::maxheap_pop (nres--, D, I);
2042 faiss::maxheap_push (++nres, D, I, d, v1);
2045 vt.visited[v1] = vt.visno + 1;
2050 if (!do_dis_check && nstep > hnsw.efSearch) {
2056 #pragma omp critical
2059 if (candidates.size() == 0)
2072 float *distances, idx_t *labels)
const
2074 if (dynamic_cast<const Index2Layer*>(storage)) {
2082 int nprobe = index_ivfpq->
nprobe;
2084 long * coarse_assign =
new long [n * nprobe];
2086 float * coarse_dis =
new float [n * nprobe];
2089 index_ivfpq->
quantizer->
search (n, x, nprobe, coarse_dis, coarse_assign);
2092 n, x, k, coarse_assign, coarse_dis, distances, labels,
false);
2094 #pragma omp parallel
2097 DistanceComputer *dis = get_distance_computer();
2100 int candidates_size = hnsw.upper_beam;
2101 MinimaxHeap candidates(candidates_size);
2104 for(
int i = 0; i < n; i++) {
2105 idx_t * idxi = labels + i * k;
2106 float * simi = distances + i * k;
2107 dis->set_query(x + i * d);
2111 for (
int j = 0; j < nprobe; j++) {
2112 idx_t key = coarse_assign[j + i * nprobe];
2114 size_t list_length = index_ivfpq->get_list_size (key);
2117 for (
int jj = 0; jj < list_length; jj++) {
2125 int search_policy = 2;
2127 if (search_policy == 1) {
2129 for (
int j = 0 ; j < hnsw.upper_beam && j < k; j++) {
2130 if (idxi[j] < 0)
break;
2131 candidates.push (idxi[j], simi[j]);
2138 maxheap_heapify (k, simi, idxi, simi, idxi, k);
2140 search_from_candidates (
2141 hnsw, *dis, k, idxi, simi,
2142 candidates, vt, 0, k);
2146 }
else if (search_policy == 2) {
2148 for (
int j = 0 ; j < hnsw.upper_beam && j < k; j++) {
2149 if (idxi[j] < 0)
break;
2150 candidates.push (idxi[j], simi[j]);
2154 maxheap_heapify (k, simi, idxi, simi, idxi, k);
2156 search_from_candidates_2 (
2157 hnsw, *dis, k, idxi, simi,
2158 candidates, vt, 0, k);
2164 maxheap_reorder (k, simi, idxi);
2173 void IndexHNSW2Level::flip_to_ivf ()
2178 FAISS_THROW_IF_NOT (storage2l);
2183 storage2l->
pq.
M, 8);
2184 index_ivfpq->
pq = storage2l->
pq;
2191 storage = index_ivfpq;
random generator that can be used in multithreaded contexts
void precompute_table()
build precomputed table
void add_with_locks(DistanceComputer &ptdis, int pt_level, int pt_id, std::vector< omp_lock_t > &locks, VisitedTable &vt)
std::vector< uint8_t > codes
Codes. Size ntotal * pq.code_size.
void transfer_to_IVFPQ(IndexIVFPQ &other) const
transfer the flat codes to an IVFPQ index
size_t code_size
bytes per vector
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
size_t nbits
number of bits per quantization index
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
int nb_neighbors(int layer_no) const
nb of neighbors for this level
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
entry point for search
size_t code_size
code_size_1 + code_size_2
virtual void reset()=0
removes all elements from the database.
storage_idx_t entry_point
entry point in the search structure (one of the points with maximum level
int cum_nb_neighbors(int layer_no) const
cumumlative nb up to (and excluding) this level
virtual float compute_code_distance(const uint8_t *code1, const uint8_t *code2)=0
code-to-code distance computation
size_t nprobe
number of probes at query time
std::vector< double > assign_probas
assignment probability to each layer (sum=1)
std::vector< float > sdc_table
Symmetric Distance Table.
virtual void train(idx_t n, const float *x)
float rand_float()
between 0 and 1
std::vector< int > cum_nneighbor_per_level
size_t dsub
dimensionality of each subvector
Level1Quantizer q1
first level quantizer
std::vector< uint8_t > codes
Codes. Size ntotal * code_size.
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 set_query(const float *x) override
called before computing distances
void train(idx_t n, const float *x) override
Trains the storage if needed.
void compute_distance_table(const float *x, float *dis_table) const
size_t code_size
byte per indexed vector
std::vector< size_t > offsets
std::vector< uint8_t > codes
Codes. Size ntotal * pq.code_size.
set implementation optimized for fast access.
void add(idx_t n, const float *x) override
void set_query(const float *x) override
called before computing distances
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
int rand_int()
random 31-bit positive integer
ScalarQuantizer sq
Used to encode the vectors.
size_t ksub
number of centroids for each subquantizer
int efSearch
expansion factor at search time
long idx_t
all indices are this type
bool check_relative_distance
during search: do we check whether the next best distance is good enough?
HNSW(int M=32)
only mandatory parameter: nb of neighbors
ProductQuantizer pq
The product quantizer used to encode the vectors.
idx_t ntotal
total nb of indexed vectors
int upper_beam
number of entry points in levels > 0.
double getmillisecs()
ms elapsed since some arbitrary epoch
void set_nb_neighbors(int level_no, int n)
set nb of neighbors for this level (before adding anything)
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
faiss::Index::idx_t idx_t
Faiss results are 64-bit.
void add_codes(size_t n, const float *x)
void make_direct_map(bool new_maintain_direct_map=true)
void set_query(const float *x) override
called before computing distances
int random_level()
pick a random level for a new point
void set_default_probas(int M, float levelMult)
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
size_t code_size_1
size of the code for the first level (ceil(log8(q1.nlist)))
void search(DistanceComputer &qdis, int k, idx_t *I, float *D, VisitedTable &vt) const
search interface
void init_level_0_from_entry_points(int npt, const storage_idx_t *points, const storage_idx_t *nearests)
alternative graph building
void set_query(const float *x) override
called before computing distances
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
entry point for search
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 override
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
void fill_with_random_links(size_t n)
add n random levels to table (for debugging...)
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 efConstruction
expansion factor at construction time
int storage_idx_t
internal storage of vectors (32 bits: this is expensive)
std::vector< float > xb
database vectors, size ntotal * d
bool own_fields
whether object owns the quantizer
void set(int no)
set flog #no to true
void set_query(const float *x) override
called before computing distances
void reconstruct(idx_t key, float *recons) const override
std::vector< int > levels
level of each vector (base level = 1), size = ntotal
int max_level
maximum level
size_t nlist
number of possible key values
virtual float compute_distance(const float *x, const uint8_t *code)=0
vector-to-code distance computation
std::vector< float > centroids
Centroid table, size M * ksub * dsub.