14 #include "IndexIVFPQ.h"
27 #include "Clustering.h"
28 #include "IndexFlat.h"
32 #include "FaissAssert.h"
34 #include "AuxIndexStructures.h"
46 IndexIVFPQ::IndexIVFPQ (Index * quantizer,
size_t d,
size_t nlist,
47 size_t M,
size_t nbits_per_idx):
48 IndexIVF (quantizer, d, nlist, METRIC_L2),
49 pq (d, M, nbits_per_idx)
51 FAISS_ASSERT (nbits_per_idx <= 8);
52 code_size = pq.code_size;
56 use_precomputed_table = 0;
57 scan_table_threshold = 0;
60 polysemous_training =
nullptr;
61 do_polysemous_training =
false;
69 void IndexIVFPQ::set_typename ()
73 <<
"[" << nlist <<
":" << quantizer->index_typename <<
"]";
74 index_typename = s.str();
86 const float * x_in = x;
92 const float *trainset;
94 if(
verbose) printf(
"computing residuals\n");
96 quantizer->assign (n, x, assign);
97 float *residuals =
new float [n *
d];
98 for (
idx_t i = 0; i < n; i++)
99 quantizer->compute_residual (x + i * d, residuals+i*d, assign[i]);
101 trainset = residuals;
106 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
109 pq.train (n, trainset);
114 if (!pt) pt = &default_pt;
120 uint8_t *train_codes =
new uint8_t [
pq.
code_size * n];
123 for (
idx_t i = 0; i < n; i++) {
124 const float *xx = trainset + i *
d;
125 float * res = residuals_2 + i *
d;
127 for (
int j = 0; j <
d; j++)
128 res[j] = xx[j] - res[j];
131 delete [] train_codes;
139 if (x_in != x)
delete [] x;
144 void IndexIVFPQ::encode (
long key,
const float * x, uint8_t * code)
const
147 float residual_vec[
d];
148 quantizer->compute_residual (x, residual_vec, key);
159 const float * x, uint8_t * xcodes,
160 bool compute_keys)
const
163 quantizer->assign (n, x, keys);
166 float *residuals =
new float [n *
d];
168 for (
size_t i = 0; i < n; i++)
169 quantizer->compute_residual (x + i * d, residuals + i * d, keys[i]);
178 const uint8_t * xcodes,
float * x)
const
182 std::vector<float> centroid (d);
183 for (
size_t i = 0; i < n; i++) {
184 quantizer->reconstruct (keys[i], centroid.data());
185 float *xi = x + i *
d;
186 for (
size_t j = 0; j <
d; j++) {
187 xi [j] += centroid [j];
201 float *residuals_2,
const long *precomputed_idx)
207 if (precomputed_idx) {
208 idx = precomputed_idx;
210 long * idx0 =
new long [n];
211 quantizer->assign (n, x, idx0);
216 uint8_t * xcodes =
new uint8_t [n *
code_size];
218 const float *to_encode =
nullptr;
221 float *residuals =
new float [n *
d];
223 for (
size_t i = 0; i < n; i++) {
225 memset (residuals + i * d, 0,
sizeof(*residuals) * d);
227 quantizer->compute_residual (
228 x + i * d, residuals + i * d, idx[i]);
230 to_encode = residuals;
239 for (
size_t i = 0; i < n; i++) {
244 memset (residuals_2, 0,
sizeof(*residuals_2) * d);
248 ids[key].push_back (
id);
251 codes[key].push_back (code[j]);
254 float *res2 = residuals_2 + i *
d;
255 const float *xi = to_encode + i *
d;
257 for (
int j = 0; j <
d; j++)
258 res2[j] = xi[j] - res2[j];
262 direct_map.push_back (key << 32 | (
ids[key].size() - 1));
269 if (!precomputed_idx)
273 char comment[100] = {0};
275 snprintf (comment, 100,
"(%ld vectors ignored)", n_ignore);
276 printf(
" add_core times: %.3f %.3f %.3f %s\n",
277 t1 - t0, t2 - t1, t3 - t2, comment);
284 FAISS_ASSERT (ni == 0 || (i0 >= 0 && i0 + ni <=
ntotal));
286 std::vector<float> centroid (d);
288 for (
int key = 0; key <
nlist; key++) {
289 const std::vector<long> & idlist =
ids[key];
290 const uint8_t * code_line = codes[key].data();
292 for (
long ofs = 0; ofs < idlist.size(); ofs++) {
293 long id = idlist[ofs];
294 if (!(
id >= i0 &&
id < i0 + ni))
continue;
295 float *r = recons + d * (
id - i0);
297 quantizer->reconstruct (key, centroid.data());
299 for (
int j = 0; j <
d; j++) {
313 FAISS_ASSERT (direct_map.size() ==
ntotal);
314 int list_no = direct_map[key] >> 32;
315 int ofs = direct_map[key] & 0xffffffff;
317 quantizer->reconstruct (list_no, recons);
318 const uint8_t * code = &(codes[list_no][ofs *
pq.
code_size]);
320 for (
size_t m = 0; m <
pq.
M; m++) {
321 float * out = recons + m *
pq.
dsub;
323 for (
size_t i = 0; i <
pq.
dsub; i++) {
334 for (
int i = 0; i <
nlist; i++) {
335 codes[i].insert (codes[i].end(),
336 other.codes[i].begin(), other.codes[i].end());
337 other.codes[i].clear();
342 long a1,
long a2)
const
344 FAISS_ASSERT (nlist == other.
nlist);
347 for (
long list_no = 0; list_no <
nlist; list_no++) {
348 const std::vector<idx_t> & ids_in =
ids[list_no];
349 std::vector<idx_t> & ids_out = other.
ids[list_no];
350 const std::vector<uint8_t> & codes_in = codes[list_no];
351 std::vector<uint8_t> & codes_out = other.codes[list_no];
353 for (
long i = 0; i < ids_in.size(); i++) {
354 idx_t id = ids_in[i];
355 if (subset_type == 0 && a1 <=
id &&
id < a2) {
356 ids_out.push_back (
id);
357 codes_out.insert (codes_out.end(),
359 codes_in.begin() + (i + 1) * code_size);
405 if (quantizer->metric_type == METRIC_INNER_PRODUCT) {
406 fprintf(stderr,
"IndexIVFPQ::precompute_table: WARN precomputed "
407 "tables not supported for inner product quantizers\n");
412 if (miq &&
pq.
M % miq->pq.
M == 0)
420 std::vector<float> r_norms (
pq.
M *
pq.
ksub, NAN);
421 for (
int m = 0; m <
pq.
M; m++)
422 for (
int j = 0; j <
pq.
ksub; j++)
423 r_norms [m *
pq.
ksub + j] =
429 std::vector<float> centroid (d);
431 for (
size_t i = 0; i <
nlist; i++) {
432 quantizer->reconstruct (i, centroid.data());
435 pq.compute_inner_prod_table (centroid.data(), tab);
443 FAISS_ASSERT (
pq.
M % cpq.
M == 0);
448 std::vector<float> centroids (d * cpq.
ksub, NAN);
450 for (
int m = 0; m < cpq.
M; m++) {
451 for (
size_t i = 0; i < cpq.
ksub; i++) {
452 memcpy (centroids.data() + i * d + m * cpq.
dsub,
454 sizeof (*centroids.data()) * cpq.
dsub);
458 pq.compute_inner_prod_tables (cpq.
ksub, centroids.data (),
461 for (
size_t i = 0; i < cpq.
ksub; i++) {
471 static uint64_t get_cycles () {
473 asm volatile(
"rdtsc \n\t"
476 return ((uint64_t)high << 32) | (low);
479 #define TIC t0 = get_cycles()
480 #define TOC get_cycles () - t0
497 const IndexIVFPQ & ivfpq;
501 const ProductQuantizer & pq;
504 int use_precomputed_table;
507 float * sim_table, * sim_table_2;
508 float * residual_vec, *decoded_vec;
511 std::vector<float> mem;
514 std::vector<const float *> sim_table_ptrs;
516 explicit QueryTables (
const IndexIVFPQ & ivfpq):
520 metric_type (ivfpq.metric_type),
521 by_residual (ivfpq.by_residual),
522 use_precomputed_table (ivfpq.use_precomputed_table)
524 mem.resize (pq.ksub * pq.M * 2 + d *2);
525 sim_table = mem.data();
526 sim_table_2 = sim_table + pq.ksub * pq.M;
527 residual_vec = sim_table_2 + pq.ksub * pq.M;
528 decoded_vec = residual_vec + d;
531 if (ivfpq.polysemous_ht != 0) {
532 q_code.resize (pq.code_size);
534 init_list_cycles = 0;
535 sim_table_ptrs.resize (pq.M);
546 void init_query (
const float * qi) {
548 if (metric_type == METRIC_INNER_PRODUCT)
552 if (!by_residual && ivfpq.polysemous_ht != 0)
553 pq.compute_code (qi, q_code.data());
556 void init_query_IP () {
558 pq.compute_inner_prod_table (qi, sim_table);
560 for (
int i = 0; i < pq.ksub * pq.M; i++) {
561 sim_table[i] = - sim_table[i];
565 void init_query_L2 () {
567 pq.compute_distance_table (qi, sim_table);
568 }
else if (use_precomputed_table) {
569 pq.compute_inner_prod_table (qi, sim_table_2);
580 std::vector<uint8_t> q_code;
582 uint64_t init_list_cycles;
587 float precompute_list_tables () {
591 if (metric_type == METRIC_INNER_PRODUCT)
592 dis0 = precompute_list_tables_IP ();
594 dis0 = precompute_list_tables_L2 ();
596 init_list_cycles += TOC;
600 float precompute_list_table_pointers () {
604 if (metric_type == METRIC_INNER_PRODUCT)
605 FAISS_ASSERT (!
"not implemented");
607 dis0 = precompute_list_table_pointers_L2 ();
609 init_list_cycles += TOC;
617 float precompute_list_tables_IP ()
621 ivfpq.quantizer->reconstruct (key, decoded_vec);
623 float dis0 = -fvec_inner_product (qi, decoded_vec, d);
625 if (ivfpq.polysemous_ht) {
626 for (
int i = 0; i < d; i++) {
627 residual_vec [i] = qi[i] - decoded_vec[i];
629 pq.compute_code (residual_vec, q_code.data());
639 float precompute_list_tables_L2 ()
643 if (use_precomputed_table == 0) {
644 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
645 pq.compute_distance_table (residual_vec, sim_table);
646 }
else if (use_precomputed_table == 1) {
650 &ivfpq.precomputed_table [key * pq.ksub * pq.M],
653 }
else if (use_precomputed_table == 2) {
656 const MultiIndexQuantizer *miq =
657 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
659 const ProductQuantizer &cpq = miq->pq;
660 int Mf = pq.M / cpq.M;
662 const float *qtab = sim_table_2;
663 float *ltab = sim_table;
666 for (
int cm = 0; cm < cpq.M; cm++) {
668 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
672 const float *pc = &ivfpq.precomputed_table
673 [(ki * pq.M + cm * Mf) * pq.ksub];
675 if (ivfpq.polysemous_ht == 0) {
682 ltab += Mf * pq.ksub;
683 qtab += Mf * pq.ksub;
685 for (
int m = cm * Mf; m < (cm + 1) * Mf; m++) {
687 (pq.ksub, pc, -2, qtab, ltab);
700 float precompute_list_table_pointers_L2 ()
704 if (use_precomputed_table == 1) {
707 const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M];
708 for (
int m = 0; m < pq.M; m++) {
709 sim_table_ptrs [m] = s;
712 }
else if (use_precomputed_table == 2) {
715 const MultiIndexQuantizer *miq =
716 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
718 const ProductQuantizer &cpq = miq->pq;
719 int Mf = pq.M / cpq.M;
723 for (
int cm = 0; cm < cpq.M; cm++) {
724 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
727 const float *pc = &ivfpq.precomputed_table
728 [(ki * pq.M + cm * Mf) * pq.ksub];
730 for (
int m = m0; m < m0 + Mf; m++) {
731 sim_table_ptrs [m] = pc;
736 }
else FAISS_ASSERT (!
"need precomputed tables");
738 if (ivfpq.polysemous_ht) {
739 FAISS_ASSERT (!
"not implemented");
757 template <
typename IDType>
758 struct InvertedListScanner: QueryTables {
760 const uint8_t * __restrict list_codes;
761 const IDType * list_ids;
764 explicit InvertedListScanner (
const IndexIVFPQ & ivfpq):
767 FAISS_ASSERT(pq.byte_per_idx == 1);
773 size_t list_size_in,
const IDType *list_ids_in,
774 const uint8_t *list_codes_in) {
776 this->coarse_dis = coarse_dis;
777 list_size = list_size_in;
778 list_codes = list_codes_in;
779 list_ids = list_ids_in;
787 void scan_list_with_table (
788 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
790 float dis0 = precompute_list_tables ();
792 for (
size_t j = 0; j < list_size; j++) {
795 const float *tab = sim_table;
797 for (
size_t m = 0; m < pq.M; m++) {
798 dis += tab[*list_codes++];
802 if (dis < heap_sim[0]) {
803 maxheap_pop (k, heap_sim, heap_ids);
804 long id = store_pairs ? (key << 32 | j) : list_ids[j];
805 maxheap_push (k, heap_sim, heap_ids, dis,
id);
813 void scan_list_with_pointer (
814 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
817 float dis0 = precompute_list_table_pointers ();
819 for (
size_t j = 0; j < list_size; j++) {
822 const float *tab = sim_table_2;
824 for (
size_t m = 0; m < pq.M; m++) {
825 int ci = *list_codes++;
826 dis += sim_table_ptrs [m][ci] - 2 * tab [ci];
830 if (dis < heap_sim[0]) {
831 maxheap_pop (k, heap_sim, heap_ids);
832 long id = store_pairs ? (key << 32 | j) : list_ids[j];
833 maxheap_push (k, heap_sim, heap_ids, dis,
id);
840 void scan_on_the_fly_dist (
841 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
844 if (by_residual && use_precomputed_table) {
845 scan_list_with_pointer (k, heap_sim, heap_ids, store_pairs);
853 if (metric_type == METRIC_INNER_PRODUCT) {
854 ivfpq.quantizer->reconstruct (key, residual_vec);
855 dis0 = fvec_inner_product (residual_vec, qi, d);
857 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
865 for (
size_t j = 0; j < list_size; j++) {
867 pq.decode (list_codes, decoded_vec);
868 list_codes += pq.code_size;
871 if (metric_type == METRIC_INNER_PRODUCT) {
872 dis = -dis0 - fvec_inner_product (decoded_vec, qi, d);
877 if (dis < heap_sim[0]) {
878 maxheap_pop (k, heap_sim, heap_ids);
879 long id = store_pairs ? (key << 32 | j) : list_ids[j];
880 maxheap_push (k, heap_sim, heap_ids, dis,
id);
890 size_t n_hamming_pass;
893 template <
class HammingComputer>
894 void scan_list_polysemous_hc (
895 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
897 float dis0 = precompute_list_tables ();
898 int ht = ivfpq.polysemous_ht;
900 int code_size = pq.code_size;
902 HammingComputer hc (q_code.data(), code_size);
904 for (
size_t j = 0; j < list_size; j++) {
905 const uint8_t *b_code = list_codes;
906 int hd = hc.hamming (b_code);
911 const float *tab = sim_table;
913 for (
size_t m = 0; m < pq.M; m++) {
914 dis += tab[*b_code++];
918 if (dis < heap_sim[0]) {
919 maxheap_pop (k, heap_sim, heap_ids);
920 long id = store_pairs ? (key << 32 | j) : list_ids[j];
921 maxheap_push (k, heap_sim, heap_ids, dis,
id);
924 list_codes += code_size;
928 void scan_list_polysemous (
929 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
931 switch (pq.code_size) {
932 #define HANDLE_CODE_SIZE(cs) \
934 scan_list_polysemous_hc <HammingComputer ## cs> \
935 (k, heap_sim, heap_ids, store_pairs); \
939 HANDLE_CODE_SIZE(16);
940 HANDLE_CODE_SIZE(20);
941 HANDLE_CODE_SIZE(32);
942 HANDLE_CODE_SIZE(64);
943 #undef HANDLE_CODE_SIZE
945 if (pq.code_size % 8 == 0)
946 scan_list_polysemous_hc <HammingComputerM8>
947 (k, heap_sim, heap_ids, store_pairs);
949 scan_list_polysemous_hc <HammingComputerM4>
950 (k, heap_sim, heap_ids, store_pairs);
963 IndexIVFPQStats indexIVFPQ_stats;
965 void IndexIVFPQStats::reset () {
966 memset (
this, 0,
sizeof (*
this));
974 const float * coarse_dis,
976 bool store_pairs)
const
978 const size_t k = res->
k;
982 InvertedListScanner<long> qt (*
this);
983 size_t stats_nlist = 0;
984 size_t stats_ncode = 0;
985 uint64_t init_query_cycles = 0;
986 uint64_t scan_cycles = 0;
987 uint64_t heap_cycles = 0;
990 for (
size_t i = 0; i < nx; i++) {
991 const float *qi = qx + i *
d;
992 const long * keysi = keys + i *
nprobe;
993 const float *coarse_dis_i = coarse_dis + i *
nprobe;
994 float * heap_sim = res->
get_val (i);
995 long * heap_ids = res->
get_ids (i);
999 maxheap_heapify (k, heap_sim, heap_ids);
1004 init_query_cycles += TOC;
1008 for (
size_t ik = 0; ik <
nprobe; ik++) {
1009 long key = keysi[ik];
1014 if (key >= (
long) nlist) {
1015 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
1018 size_t list_size =
ids[key].size();
1022 if (list_size == 0)
continue;
1024 qt.init_list (key, coarse_dis_i[ik],
1025 list_size,
ids[key].data(),
1030 qt.scan_list_polysemous
1031 (k, heap_sim, heap_ids, store_pairs);
1033 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1035 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1041 stats_ncode += nscan;
1043 maxheap_reorder (k, heap_sim, heap_ids);
1046 for (
size_t j = 0; j < k; j++)
1047 heap_sim[j] = -heap_sim[j];
1052 #pragma omp critical
1054 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1055 indexIVFPQ_stats.nlist += stats_nlist;
1056 indexIVFPQ_stats.ncode += stats_ncode;
1058 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1059 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1060 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1061 indexIVFPQ_stats.heap_cycles += heap_cycles;
1065 indexIVFPQ_stats.nq += nx;
1070 float *distances,
idx_t *labels)
const
1072 long * idx =
new long [n *
nprobe];
1073 float * coarse_dis =
new float [n *
nprobe];
1076 quantizer->search (n, x,
nprobe, coarse_dis, idx);
1077 indexIVFPQ_stats.assign_cycles += TOC;
1084 delete [] coarse_dis;
1085 indexIVFPQ_stats.search_cycles += TOC;
1092 for (
size_t key = 0; key <
nlist; key++) {
1100 !
"direct map remove not implemented");
1102 #pragma omp parallel for reduction(+: nremove)
1103 for (
long i = 0; i <
nlist; i++) {
1104 std::vector<idx_t> & idsi =
ids[i];
1105 uint8_t * codesi = codes[i].data();
1107 long l = idsi.size(), j = 0;
1109 if (sel.is_member (idsi[j])) {
1111 idsi [j] = idsi [l];
1112 memmove (codesi + j * code_size,
1113 codesi + l * code_size, code_size);
1118 if (l < idsi.size()) {
1119 nremove += idsi.size() - l;
1121 codes[i].resize (l * code_size);
1129 IndexIVFPQ::IndexIVFPQ ()
1144 bool operator () (
int a,
int b)
const {
1145 return cmp (a, b) > 0;
1147 int cmp (
int a,
int b)
const {
1148 return memcmp (tab + a * code_size, tab + b * code_size,
1158 for (
size_t list_no = 0; list_no <
nlist; list_no++) {
1159 size_t n =
ids[list_no].size();
1160 std::vector<int> ord (n);
1161 for (
int i = 0; i < n; i++) ord[i] = i;
1162 CodeCmp cs = { codes[list_no].data(), code_size };
1163 std::sort (ord.begin(), ord.end(), cs);
1165 const idx_t *list_ids =
ids[list_no].data();
1167 for (
int i = 0; i < n; i++) {
1168 if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) {
1170 if (prev + 1 == i) {
1172 lims[ngroup] = lims[ngroup - 1];
1173 dup_ids [lims [ngroup]++] = list_ids [ord [prev]];
1175 dup_ids [lims [ngroup]++] = list_ids [ord [i]];
1191 IndexIVFPQR::IndexIVFPQR (
1192 Index * quantizer,
size_t d,
size_t nlist,
1193 size_t M,
size_t nbits_per_idx,
1194 size_t M_refine,
size_t nbits_per_idx_refine):
1195 IndexIVFPQ (quantizer, d, nlist, M, nbits_per_idx),
1196 refine_pq (d, M_refine, nbits_per_idx_refine),
1203 IndexIVFPQR::IndexIVFPQR ():
1210 void IndexIVFPQR::set_typename()
1212 std::stringstream s;
1215 <<
"[" << nlist <<
":" << quantizer->index_typename <<
"]";
1216 index_typename = s.str();
1233 float * residual_2 =
new float [n *
d];
1238 printf (
"training %zdx%zd 2nd level PQ quantizer on %ld %dD-vectors\n",
1245 delete [] residual_2;
1255 const long *precomputed_idx) {
1257 float * residual_2 =
new float [n *
d];
1261 add_core_o (n, x, xids, residual_2, precomputed_idx);
1268 delete [] residual_2;
1275 float *distances,
idx_t *labels)
const
1278 long * idx =
new long [n *
nprobe];
1279 float * L1_dis =
new float [n *
nprobe];
1282 quantizer->search (n, x,
nprobe, L1_dis, idx);
1283 indexIVFPQ_stats.assign_cycles += TOC;
1286 size_t k_coarse = long(k *
k_factor);
1287 idx_t *coarse_labels =
new idx_t [k_coarse * n];
1289 float *coarse_distances =
new float [k_coarse * n];
1292 size_t(n), k_coarse, coarse_labels, coarse_distances};
1294 delete [] coarse_distances;
1297 indexIVFPQ_stats.search_cycles += TOC;
1302 size_t n_refine = 0;
1303 #pragma omp parallel reduction(+ : n_refine)
1306 float *residual_1 =
new float [2 *
d];
1307 float *residual_2 = residual_1 +
d;
1309 for (
idx_t i = 0; i < n; i++) {
1310 const float *xq = x + i *
d;
1311 const long * shortlist = coarse_labels + k_coarse * i;
1312 float * heap_sim = distances + k * i;
1313 long * heap_ids = labels + k * i;
1314 maxheap_heapify (k, heap_sim, heap_ids);
1316 for (
int j = 0; j < k_coarse; j++) {
1317 long sl = shortlist[j];
1319 if (sl == -1)
continue;
1321 int list_no = sl >> 32;
1322 int ofs = sl & 0xffffffff;
1324 assert (list_no >= 0 && list_no < nlist);
1325 assert (ofs >= 0 && ofs <
ids[list_no].size());
1328 quantizer->compute_residual (xq, residual_1, list_no);
1331 const uint8_t * l2code = &codes[list_no][ofs *
pq.
code_size];
1333 for (
int l = 0; l <
d; l++)
1334 residual_2[l] = residual_1[l] - residual_2[l];
1338 assert (0 <=
id &&
id <
ntotal);
1342 float dis =
fvec_L2sqr (residual_1, residual_2, d);
1344 if (dis < heap_sim[0]) {
1345 maxheap_pop (k, heap_sim, heap_ids);
1346 maxheap_push (k, heap_sim, heap_ids, dis,
id);
1350 maxheap_reorder (k, heap_sim, heap_ids);
1352 delete [] residual_1;
1354 delete [] coarse_labels;
1356 indexIVFPQ_stats.nrefine += n_refine;
1357 indexIVFPQ_stats.refine_cycles += TOC;
1362 float *r3 =
new float [
d];
1366 for (
idx_t i = i0; i < i0 + ni; i++) {
1367 float *r = recons + i *
d;
1370 for (
int j = 0; j <
d; j++)
1388 FAISS_ASSERT(!
"not implemented");
1395 IndexIVFPQCompact::IndexIVFPQCompact ()
1404 IndexIVFPQCompact::IndexIVFPQCompact (
const IndexIVFPQ &other)
1406 FAISS_ASSERT (other.ntotal < (1UL << 31) ||
1407 !
"IndexIVFPQCompact cannot store more than 2G images");
1421 nlist = other.nlist;
1423 quantizer = other.quantizer;
1426 direct_map = other.direct_map;
1432 code_size = other.code_size;
1443 limits =
new uint32_t [nlist + 1];
1450 for (
size_t i = 0; i <
nlist; i++) {
1452 const std::vector<long> &other_ids = other.ids[i];
1453 for (
size_t j = 0; j < other_ids.size(); j++) {
1454 long id = other_ids[j];
1455 FAISS_ASSERT (
id < (1UL << 31) ||
1456 !
"IndexIVFPQCompact cannot store ids > 2G");
1460 other.codes[i].data(),
1461 other.codes[i].size());
1462 ofs += other_ids.size();
1464 FAISS_ASSERT (ofs ==
ntotal);
1470 FAISS_ASSERT (!
"cannot add to an IndexIVFPQCompact");
1474 FAISS_ASSERT (!
"cannot reset an IndexIVFPQCompact");
1478 FAISS_ASSERT (!
"cannot train an IndexIVFPQCompact");
1484 IndexIVFPQCompact::~IndexIVFPQCompact ()
1491 munmap (mmap_buffer, mmap_length);
1501 const float * coarse_dis,
1503 bool store_pairs)
const
1505 const size_t k = res->
k;
1507 #pragma omp parallel
1509 InvertedListScanner<uint32_t> qt (*
this);
1510 size_t stats_nlist = 0;
1511 size_t stats_ncode = 0;
1512 uint64_t init_query_cycles = 0;
1513 uint64_t scan_cycles = 0;
1514 uint64_t heap_cycles = 0;
1517 for (
size_t i = 0; i < nx; i++) {
1518 const float *qi = qx + i *
d;
1519 const long * keysi = keys + i *
nprobe;
1520 const float *coarse_dis_i = coarse_dis + i *
nprobe;
1521 float * heap_sim = res->
get_val (i);
1522 long * heap_ids = res->
get_ids (i);
1526 maxheap_heapify (k, heap_sim, heap_ids);
1531 init_query_cycles += TOC;
1535 for (
size_t ik = 0; ik <
nprobe; ik++) {
1536 long key = keysi[ik];
1541 if (key >= (
long) nlist) {
1542 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
1549 if (list_size == 0)
continue;
1551 qt.init_list (key, coarse_dis_i[ik],
1557 qt.scan_list_polysemous
1558 (k, heap_sim, heap_ids, store_pairs);
1560 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1562 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1568 stats_ncode += nscan;
1570 maxheap_reorder (k, heap_sim, heap_ids);
1573 for (
size_t j = 0; j < k; j++) {
1574 heap_sim[i] = -heap_sim[i];
1580 #pragma omp critical
1582 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1583 indexIVFPQ_stats.nlist += stats_nlist;
1584 indexIVFPQ_stats.ncode += stats_ncode;
1586 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1587 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1588 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1589 indexIVFPQ_stats.heap_cycles += heap_cycles;
1593 indexIVFPQ_stats.nq += nx;
uint32_t * compact_ids
size ntotal
uint8_t * compact_codes
size ntotal * code_size
void precompute_table()
build precomputed table
void copy_subset_to(IndexIVFPQ &other, int subset_type, long a1, long a2) const
size_t nbits
number of bits per quantization index
virtual void reconstruct(idx_t key, float *recons) const override
void decode(const uint8_t *code, float *x) const
decode a vector from a given code (or n vectors if third argument)
ProductQuantizer refine_pq
3rd level quantizer
float fvec_L2sqr(const float *x, const float *y, size_t d)
Squared L2 distance between two vectors.
PolysemousTraining * polysemous_training
if NULL, use default
T * get_val(size_t key)
Return the list of values for a heap.
virtual void add(idx_t, const float *) override
the three following functions will fail at runtime
virtual void search_knn_with_key(size_t nx, const float *qx, const long *keys, const float *coarse_dis, float_maxheap_array_t *res, bool store_pairs=false) const override
virtual void reconstruct_n(idx_t i0, idx_t ni, float *recons) const override
const float * fvecs_maybe_subsample(size_t d, size_t *n, size_t nmax, const float *x, bool verbose, long seed)
size_t nprobe
number of probes at query time
void assign(idx_t n, const float *x, idx_t *labels, idx_t k=1)
bool quantizer_trains_alone
just pass over the trainset to quantizer
virtual void set_typename() override
virtual void merge_from_residuals(IndexIVF &other) override
used to implement merging
void decode_multiple(size_t n, const long *keys, const uint8_t *xcodes, float *x) const
inverse of encode_multiple
void train_residual_o(idx_t n, const float *x, float *residuals_2)
same as train_residual, also output 2nd level residuals
bool do_polysemous_training
reorder PQ centroids after training?
size_t scan_table_threshold
use table computation or on-the-fly?
size_t k
allocated size per heap
virtual void train_residual(idx_t n, const float *x) override
trains the two product quantizers
void add_core(idx_t n, const float *x, const long *xids, const long *precomputed_idx=nullptr)
same as add_with_ids, but optionally use the precomputed list ids
uint32_t * limits
size nlist + 1
size_t dsub
dimensionality of each subvector
int seed
seed for the random number generator
std::vector< float > precomputed_table
void fvec_madd(size_t n, const float *a, float bf, const float *b, float *c)
int polysemous_ht
Hamming thresh for polysemous filtering.
virtual void search_knn_with_key(size_t nx, const float *qx, const long *keys, const float *coarse_dis, float_maxheap_array_t *res, bool store_pairs=false) const
virtual void reset() override
removes all elements from the database.
virtual void add_with_ids(idx_t n, const float *x, const long *xids=nullptr) override
std::vector< std::vector< long > > ids
Inverted lists for indexes.
void compute_codes(const float *x, uint8_t *codes, size_t n) const
same as compute_code for several vectors
Index * quantizer
quantizer that maps vectors to inverted lists
size_t max_codes
max nb of codes to visit to do a query
std::vector< uint8_t > refine_codes
corresponding codes
size_t code_size
byte per indexed vector
virtual long remove_ids(const IDSelector &sel) override
virtual void train_residual(idx_t n, const float *x) override
trains the product quantizer
void encode_multiple(size_t n, long *keys, const float *x, uint8_t *codes, bool compute_keys=false) const
virtual void train(idx_t, const float *) override
Trains the quantizer and calls train_residual to train sub-quantizers.
bool own_fields
whether object owns the quantizer
size_t ksub
number of centroids for each subquantizer
long idx_t
all indices are this type
void compute_code(const float *x, uint8_t *code) const
Quantize one vector with the product quantizer.
idx_t ntotal
total nb of indexed vectors
virtual void reset() override
removes all elements from the database.
bool verbose
verbosity level
virtual void reset() override
removes all elements from the database.
double getmillisecs()
ms elapsed since some arbitrary epoch
optimizes the order of indices in a ProductQuantizer
float fvec_norm_L2sqr(const float *x, size_t d)
ClusteringParameters cp
parameters used during clustering
virtual void merge_from_residuals(IndexIVF &other) override
used to implement merging
bool by_residual
Encode residual or plain vector?
TI * get_ids(size_t key)
Correspponding identifiers.
MetricType metric_type
type of metric this index uses for search
ProductQuantizer pq
produces the codes
size_t M
number of subquantizers
size_t nlist
number of possible key values
virtual void reconstruct_n(idx_t i0, idx_t ni, float *recons) const override
void add_core_o(idx_t n, const float *x, const long *xids, float *residuals_2, const long *precomputed_idx=nullptr)
int fvec_madd_and_argmin(size_t n, const float *a, float bf, const float *b, float *c)
size_t code_size
code size per vector in bytes
virtual long remove_ids(const IDSelector &sel) override
virtual void reset() override
removes all elements from the database.
bool is_trained
set if the Index does not require training, or if training is done already
float * get_centroids(size_t m, size_t i)
return the centroids associated with subvector m
bool maintain_direct_map
map for direct access to the elements. Enables reconstruct().
void optimize_pq_for_hamming(ProductQuantizer &pq, size_t n, const float *x) const
int max_points_per_centroid
to limit size of dataset
bool verbose
verbose during training?
virtual void add_with_ids(idx_t n, const float *x, const long *xids) override
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
size_t find_duplicates(idx_t *ids, size_t *lims) const
MetricType
Some algorithms support both an inner product vetsion and a L2 search version.
float k_factor
factor between k requested in search and the k requested from the IVFPQ
int use_precomputed_table
if by_residual, build precompute tables