13 #include "IndexIVFPQ.h"
26 #include "Clustering.h"
27 #include "IndexFlat.h"
31 #include "FaissAssert.h"
33 #include "AuxIndexStructures.h"
45 IndexIVFPQ::IndexIVFPQ (Index * quantizer,
size_t d,
size_t nlist,
46 size_t M,
size_t nbits_per_idx):
47 IndexIVF (quantizer, d, nlist, METRIC_L2),
48 pq (d, M, nbits_per_idx)
50 FAISS_THROW_IF_NOT (nbits_per_idx <= 8);
51 code_size = pq.code_size;
55 use_precomputed_table = 0;
56 scan_table_threshold = 0;
59 polysemous_training =
nullptr;
60 do_polysemous_training =
false;
74 const float * x_in = x;
82 const float *trainset;
85 if(
verbose) printf(
"computing residuals\n");
88 quantizer->assign (n, x, assign);
89 float *residuals =
new float [n *
d];
90 del_residuals.set (residuals);
91 for (
idx_t i = 0; i < n; i++)
92 quantizer->compute_residual (x + i * d, residuals+i*d, assign[i]);
99 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
102 pq.train (n, trainset);
107 if (!pt) pt = &default_pt;
113 uint8_t *train_codes =
new uint8_t [
pq.
code_size * n];
117 for (
idx_t i = 0; i < n; i++) {
118 const float *xx = trainset + i *
d;
119 float * res = residuals_2 + i *
d;
121 for (
int j = 0; j <
d; j++)
122 res[j] = xx[j] - res[j];
135 void IndexIVFPQ::encode (
long key,
const float * x, uint8_t * code)
const
138 float residual_vec[
d];
139 quantizer->compute_residual (x, residual_vec, key);
150 const float * x, uint8_t * xcodes,
151 bool compute_keys)
const
154 quantizer->assign (n, x, keys);
157 float *residuals =
new float [n *
d];
160 for (
size_t i = 0; i < n; i++)
161 quantizer->compute_residual (x + i * d, residuals + i * d, keys[i]);
169 const uint8_t * xcodes,
float * x)
const
173 std::vector<float> centroid (d);
174 for (
size_t i = 0; i < n; i++) {
175 quantizer->reconstruct (keys[i], centroid.data());
176 float *xi = x + i *
d;
177 for (
size_t j = 0; j <
d; j++) {
178 xi [j] += centroid [j];
192 float *residuals_2,
const long *precomputed_idx)
199 if (precomputed_idx) {
200 idx = precomputed_idx;
202 long * idx0 =
new long [n];
204 quantizer->assign (n, x, idx0);
209 uint8_t * xcodes =
new uint8_t [n *
code_size];
212 const float *to_encode =
nullptr;
216 float *residuals =
new float [n *
d];
218 for (
size_t i = 0; i < n; i++) {
220 memset (residuals + i * d, 0,
sizeof(*residuals) * d);
222 quantizer->compute_residual (
223 x + i * d, residuals + i * d, idx[i]);
225 to_encode = residuals;
226 del_to_encode.set (to_encode);
235 for (
size_t i = 0; i < n; i++) {
240 memset (residuals_2, 0,
sizeof(*residuals_2) * d);
244 ids[key].push_back (
id);
247 codes[key].push_back (code[j]);
250 float *res2 = residuals_2 + i *
d;
251 const float *xi = to_encode + i *
d;
253 for (
int j = 0; j <
d; j++)
254 res2[j] = xi[j] - res2[j];
258 direct_map.push_back (key << 32 | (
ids[key].size() - 1));
264 char comment[100] = {0};
266 snprintf (comment, 100,
"(%ld vectors ignored)", n_ignore);
267 printf(
" add_core times: %.3f %.3f %.3f %s\n",
268 t1 - t0, t2 - t1, t3 - t2, comment);
275 FAISS_THROW_IF_NOT (ni == 0 || (i0 >= 0 && i0 + ni <=
ntotal));
277 std::vector<float> centroid (d);
279 for (
int key = 0; key <
nlist; key++) {
280 const std::vector<long> & idlist =
ids[key];
281 const uint8_t * code_line = codes[key].data();
283 for (
long ofs = 0; ofs < idlist.size(); ofs++) {
284 long id = idlist[ofs];
285 if (!(
id >= i0 &&
id < i0 + ni))
continue;
286 float *r = recons + d * (
id - i0);
288 quantizer->reconstruct (key, centroid.data());
290 for (
int j = 0; j <
d; j++) {
304 FAISS_THROW_IF_NOT (direct_map.size() ==
ntotal);
305 int list_no = direct_map[key] >> 32;
306 int ofs = direct_map[key] & 0xffffffff;
308 quantizer->reconstruct (list_no, recons);
309 const uint8_t * code = &(codes[list_no][ofs *
pq.
code_size]);
311 for (
size_t m = 0; m <
pq.
M; m++) {
312 float * out = recons + m *
pq.
dsub;
314 for (
size_t i = 0; i <
pq.
dsub; i++) {
325 for (
int i = 0; i <
nlist; i++) {
326 codes[i].insert (codes[i].end(),
327 other.codes[i].begin(), other.codes[i].end());
328 other.codes[i].clear();
333 long a1,
long a2)
const
335 FAISS_THROW_IF_NOT (nlist == other.
nlist);
338 for (
long list_no = 0; list_no <
nlist; list_no++) {
339 const std::vector<idx_t> & ids_in =
ids[list_no];
340 std::vector<idx_t> & ids_out = other.
ids[list_no];
341 const std::vector<uint8_t> & codes_in = codes[list_no];
342 std::vector<uint8_t> & codes_out = other.codes[list_no];
344 for (
long i = 0; i < ids_in.size(); i++) {
345 idx_t id = ids_in[i];
346 if (subset_type == 0 && a1 <=
id &&
id < a2) {
347 ids_out.push_back (
id);
348 codes_out.insert (codes_out.end(),
350 codes_in.begin() + (i + 1) * code_size);
396 if (quantizer->metric_type == METRIC_INNER_PRODUCT) {
397 fprintf(stderr,
"IndexIVFPQ::precompute_table: WARN precomputed "
398 "tables not supported for inner product quantizers\n");
403 if (miq &&
pq.
M % miq->pq.
M == 0)
411 std::vector<float> r_norms (
pq.
M *
pq.
ksub, NAN);
412 for (
int m = 0; m <
pq.
M; m++)
413 for (
int j = 0; j <
pq.
ksub; j++)
414 r_norms [m *
pq.
ksub + j] =
420 std::vector<float> centroid (d);
422 for (
size_t i = 0; i <
nlist; i++) {
423 quantizer->reconstruct (i, centroid.data());
426 pq.compute_inner_prod_table (centroid.data(), tab);
432 FAISS_THROW_IF_NOT (miq);
434 FAISS_THROW_IF_NOT (
pq.
M % cpq.
M == 0);
439 std::vector<float> centroids (d * cpq.
ksub, NAN);
441 for (
int m = 0; m < cpq.
M; m++) {
442 for (
size_t i = 0; i < cpq.
ksub; i++) {
443 memcpy (centroids.data() + i * d + m * cpq.
dsub,
445 sizeof (*centroids.data()) * cpq.
dsub);
449 pq.compute_inner_prod_tables (cpq.
ksub, centroids.data (),
452 for (
size_t i = 0; i < cpq.
ksub; i++) {
462 static uint64_t get_cycles () {
464 asm volatile(
"rdtsc \n\t"
467 return ((uint64_t)high << 32) | (low);
470 #define TIC t0 = get_cycles()
471 #define TOC get_cycles () - t0
488 const IndexIVFPQ & ivfpq;
492 const ProductQuantizer & pq;
495 int use_precomputed_table;
498 float * sim_table, * sim_table_2;
499 float * residual_vec, *decoded_vec;
502 std::vector<float> mem;
505 std::vector<const float *> sim_table_ptrs;
507 explicit QueryTables (
const IndexIVFPQ & ivfpq):
511 metric_type (ivfpq.metric_type),
512 by_residual (ivfpq.by_residual),
513 use_precomputed_table (ivfpq.use_precomputed_table)
515 mem.resize (pq.ksub * pq.M * 2 + d *2);
516 sim_table = mem.data();
517 sim_table_2 = sim_table + pq.ksub * pq.M;
518 residual_vec = sim_table_2 + pq.ksub * pq.M;
519 decoded_vec = residual_vec + d;
522 if (ivfpq.polysemous_ht != 0) {
523 q_code.resize (pq.code_size);
525 init_list_cycles = 0;
526 sim_table_ptrs.resize (pq.M);
537 void init_query (
const float * qi) {
539 if (metric_type == METRIC_INNER_PRODUCT)
543 if (!by_residual && ivfpq.polysemous_ht != 0)
544 pq.compute_code (qi, q_code.data());
547 void init_query_IP () {
549 pq.compute_inner_prod_table (qi, sim_table);
551 for (
int i = 0; i < pq.ksub * pq.M; i++) {
552 sim_table[i] = - sim_table[i];
556 void init_query_L2 () {
558 pq.compute_distance_table (qi, sim_table);
559 }
else if (use_precomputed_table) {
560 pq.compute_inner_prod_table (qi, sim_table_2);
571 std::vector<uint8_t> q_code;
573 uint64_t init_list_cycles;
578 float precompute_list_tables () {
582 if (metric_type == METRIC_INNER_PRODUCT)
583 dis0 = precompute_list_tables_IP ();
585 dis0 = precompute_list_tables_L2 ();
587 init_list_cycles += TOC;
591 float precompute_list_table_pointers () {
595 if (metric_type == METRIC_INNER_PRODUCT)
596 FAISS_THROW_MSG (
"not implemented");
598 dis0 = precompute_list_table_pointers_L2 ();
600 init_list_cycles += TOC;
608 float precompute_list_tables_IP ()
612 ivfpq.quantizer->reconstruct (key, decoded_vec);
614 float dis0 = -fvec_inner_product (qi, decoded_vec, d);
616 if (ivfpq.polysemous_ht) {
617 for (
int i = 0; i < d; i++) {
618 residual_vec [i] = qi[i] - decoded_vec[i];
620 pq.compute_code (residual_vec, q_code.data());
630 float precompute_list_tables_L2 ()
634 if (use_precomputed_table == 0) {
635 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
636 pq.compute_distance_table (residual_vec, sim_table);
637 }
else if (use_precomputed_table == 1) {
641 &ivfpq.precomputed_table [key * pq.ksub * pq.M],
644 }
else if (use_precomputed_table == 2) {
647 const MultiIndexQuantizer *miq =
648 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
649 FAISS_THROW_IF_NOT (miq);
650 const ProductQuantizer &cpq = miq->pq;
651 int Mf = pq.M / cpq.M;
653 const float *qtab = sim_table_2;
654 float *ltab = sim_table;
657 for (
int cm = 0; cm < cpq.M; cm++) {
659 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
663 const float *pc = &ivfpq.precomputed_table
664 [(ki * pq.M + cm * Mf) * pq.ksub];
666 if (ivfpq.polysemous_ht == 0) {
673 ltab += Mf * pq.ksub;
674 qtab += Mf * pq.ksub;
676 for (
int m = cm * Mf; m < (cm + 1) * Mf; m++) {
678 (pq.ksub, pc, -2, qtab, ltab);
691 float precompute_list_table_pointers_L2 ()
695 if (use_precomputed_table == 1) {
698 const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M];
699 for (
int m = 0; m < pq.M; m++) {
700 sim_table_ptrs [m] = s;
703 }
else if (use_precomputed_table == 2) {
706 const MultiIndexQuantizer *miq =
707 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
708 FAISS_THROW_IF_NOT (miq);
709 const ProductQuantizer &cpq = miq->pq;
710 int Mf = pq.M / cpq.M;
714 for (
int cm = 0; cm < cpq.M; cm++) {
715 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
718 const float *pc = &ivfpq.precomputed_table
719 [(ki * pq.M + cm * Mf) * pq.ksub];
721 for (
int m = m0; m < m0 + Mf; m++) {
722 sim_table_ptrs [m] = pc;
728 FAISS_THROW_MSG (
"need precomputed tables");
731 if (ivfpq.polysemous_ht) {
732 FAISS_THROW_MSG (
"not implemented");
750 template <
typename IDType>
751 struct InvertedListScanner: QueryTables {
753 const uint8_t * __restrict list_codes;
754 const IDType * list_ids;
757 explicit InvertedListScanner (
const IndexIVFPQ & ivfpq):
760 FAISS_THROW_IF_NOT (pq.byte_per_idx == 1);
766 size_t list_size_in,
const IDType *list_ids_in,
767 const uint8_t *list_codes_in) {
769 this->coarse_dis = coarse_dis;
770 list_size = list_size_in;
771 list_codes = list_codes_in;
772 list_ids = list_ids_in;
780 void scan_list_with_table (
781 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
783 float dis0 = precompute_list_tables ();
785 for (
size_t j = 0; j < list_size; j++) {
788 const float *tab = sim_table;
790 for (
size_t m = 0; m < pq.M; m++) {
791 dis += tab[*list_codes++];
795 if (dis < heap_sim[0]) {
796 maxheap_pop (k, heap_sim, heap_ids);
797 long id = store_pairs ? (key << 32 | j) : list_ids[j];
798 maxheap_push (k, heap_sim, heap_ids, dis,
id);
806 void scan_list_with_pointer (
807 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
810 float dis0 = precompute_list_table_pointers ();
812 for (
size_t j = 0; j < list_size; j++) {
815 const float *tab = sim_table_2;
817 for (
size_t m = 0; m < pq.M; m++) {
818 int ci = *list_codes++;
819 dis += sim_table_ptrs [m][ci] - 2 * tab [ci];
823 if (dis < heap_sim[0]) {
824 maxheap_pop (k, heap_sim, heap_ids);
825 long id = store_pairs ? (key << 32 | j) : list_ids[j];
826 maxheap_push (k, heap_sim, heap_ids, dis,
id);
833 void scan_on_the_fly_dist (
834 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
837 if (by_residual && use_precomputed_table) {
838 scan_list_with_pointer (k, heap_sim, heap_ids, store_pairs);
846 if (metric_type == METRIC_INNER_PRODUCT) {
847 ivfpq.quantizer->reconstruct (key, residual_vec);
848 dis0 = fvec_inner_product (residual_vec, qi, d);
850 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
858 for (
size_t j = 0; j < list_size; j++) {
860 pq.decode (list_codes, decoded_vec);
861 list_codes += pq.code_size;
864 if (metric_type == METRIC_INNER_PRODUCT) {
865 dis = -dis0 - fvec_inner_product (decoded_vec, qi, d);
870 if (dis < heap_sim[0]) {
871 maxheap_pop (k, heap_sim, heap_ids);
872 long id = store_pairs ? (key << 32 | j) : list_ids[j];
873 maxheap_push (k, heap_sim, heap_ids, dis,
id);
883 size_t n_hamming_pass;
886 template <
class HammingComputer>
887 void scan_list_polysemous_hc (
888 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
890 float dis0 = precompute_list_tables ();
891 int ht = ivfpq.polysemous_ht;
893 int code_size = pq.code_size;
895 HammingComputer hc (q_code.data(), code_size);
897 for (
size_t j = 0; j < list_size; j++) {
898 const uint8_t *b_code = list_codes;
899 int hd = hc.hamming (b_code);
904 const float *tab = sim_table;
906 for (
size_t m = 0; m < pq.M; m++) {
907 dis += tab[*b_code++];
911 if (dis < heap_sim[0]) {
912 maxheap_pop (k, heap_sim, heap_ids);
913 long id = store_pairs ? (key << 32 | j) : list_ids[j];
914 maxheap_push (k, heap_sim, heap_ids, dis,
id);
917 list_codes += code_size;
921 void scan_list_polysemous (
922 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
924 switch (pq.code_size) {
925 #define HANDLE_CODE_SIZE(cs) \
927 scan_list_polysemous_hc <HammingComputer ## cs> \
928 (k, heap_sim, heap_ids, store_pairs); \
932 HANDLE_CODE_SIZE(16);
933 HANDLE_CODE_SIZE(20);
934 HANDLE_CODE_SIZE(32);
935 HANDLE_CODE_SIZE(64);
936 #undef HANDLE_CODE_SIZE
938 if (pq.code_size % 8 == 0)
939 scan_list_polysemous_hc <HammingComputerM8>
940 (k, heap_sim, heap_ids, store_pairs);
942 scan_list_polysemous_hc <HammingComputerM4>
943 (k, heap_sim, heap_ids, store_pairs);
956 IndexIVFPQStats indexIVFPQ_stats;
958 void IndexIVFPQStats::reset () {
959 memset (
this, 0,
sizeof (*
this));
967 const float * coarse_dis,
969 bool store_pairs)
const
971 const size_t k = res->
k;
975 InvertedListScanner<long> qt (*
this);
976 size_t stats_nlist = 0;
977 size_t stats_ncode = 0;
978 uint64_t init_query_cycles = 0;
979 uint64_t scan_cycles = 0;
980 uint64_t heap_cycles = 0;
983 for (
size_t i = 0; i < nx; i++) {
984 const float *qi = qx + i *
d;
985 const long * keysi = keys + i *
nprobe;
986 const float *coarse_dis_i = coarse_dis + i *
nprobe;
987 float * heap_sim = res->
get_val (i);
988 long * heap_ids = res->
get_ids (i);
992 maxheap_heapify (k, heap_sim, heap_ids);
997 init_query_cycles += TOC;
1001 for (
size_t ik = 0; ik <
nprobe; ik++) {
1002 long key = keysi[ik];
1007 if (key >= (
long) nlist) {
1008 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
1011 size_t list_size =
ids[key].size();
1015 if (list_size == 0)
continue;
1017 qt.init_list (key, coarse_dis_i[ik],
1018 list_size,
ids[key].data(),
1023 qt.scan_list_polysemous
1024 (k, heap_sim, heap_ids, store_pairs);
1026 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1028 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1034 stats_ncode += nscan;
1036 maxheap_reorder (k, heap_sim, heap_ids);
1039 for (
size_t j = 0; j < k; j++)
1040 heap_sim[j] = -heap_sim[j];
1045 #pragma omp critical
1047 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1048 indexIVFPQ_stats.nlist += stats_nlist;
1049 indexIVFPQ_stats.ncode += stats_ncode;
1051 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1052 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1053 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1054 indexIVFPQ_stats.heap_cycles += heap_cycles;
1058 indexIVFPQ_stats.nq += nx;
1063 float *distances,
idx_t *labels)
const
1065 long * idx =
new long [n *
nprobe];
1067 float * coarse_dis =
new float [n *
nprobe];
1072 quantizer->search (n, x,
nprobe, coarse_dis, idx);
1073 indexIVFPQ_stats.assign_cycles += TOC;
1079 indexIVFPQ_stats.search_cycles += TOC;
1086 for (
size_t key = 0; key <
nlist; key++) {
1094 "direct map remove not implemented");
1096 #pragma omp parallel for reduction(+: nremove)
1097 for (
long i = 0; i <
nlist; i++) {
1098 std::vector<idx_t> & idsi =
ids[i];
1099 uint8_t * codesi = codes[i].data();
1101 long l = idsi.size(), j = 0;
1103 if (sel.is_member (idsi[j])) {
1105 idsi [j] = idsi [l];
1106 memmove (codesi + j * code_size,
1107 codesi + l * code_size, code_size);
1112 if (l < idsi.size()) {
1113 nremove += idsi.size() - l;
1115 codes[i].resize (l * code_size);
1123 IndexIVFPQ::IndexIVFPQ ()
1138 bool operator () (
int a,
int b)
const {
1139 return cmp (a, b) > 0;
1141 int cmp (
int a,
int b)
const {
1142 return memcmp (tab + a * code_size, tab + b * code_size,
1152 for (
size_t list_no = 0; list_no <
nlist; list_no++) {
1153 size_t n =
ids[list_no].size();
1154 std::vector<int> ord (n);
1155 for (
int i = 0; i < n; i++) ord[i] = i;
1156 CodeCmp cs = { codes[list_no].data(), code_size };
1157 std::sort (ord.begin(), ord.end(), cs);
1159 const idx_t *list_ids =
ids[list_no].data();
1161 for (
int i = 0; i < n; i++) {
1162 if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) {
1164 if (prev + 1 == i) {
1166 lims[ngroup] = lims[ngroup - 1];
1167 dup_ids [lims [ngroup]++] = list_ids [ord [prev]];
1169 dup_ids [lims [ngroup]++] = list_ids [ord [i]];
1185 IndexIVFPQR::IndexIVFPQR (
1186 Index * quantizer,
size_t d,
size_t nlist,
1187 size_t M,
size_t nbits_per_idx,
1188 size_t M_refine,
size_t nbits_per_idx_refine):
1189 IndexIVFPQ (quantizer, d, nlist, M, nbits_per_idx),
1190 refine_pq (d, M_refine, nbits_per_idx_refine),
1196 IndexIVFPQR::IndexIVFPQR ():
1216 float * residual_2 =
new float [n *
d];
1222 printf (
"training %zdx%zd 2nd level PQ quantizer on %ld %dD-vectors\n",
1238 const long *precomputed_idx) {
1240 float * residual_2 =
new float [n *
d];
1245 add_core_o (n, x, xids, residual_2, precomputed_idx);
1258 float *distances,
idx_t *labels)
const
1261 long * idx =
new long [n *
nprobe];
1263 float * L1_dis =
new float [n *
nprobe];
1267 quantizer->search (n, x,
nprobe, L1_dis, idx);
1268 indexIVFPQ_stats.assign_cycles += TOC;
1271 size_t k_coarse = long(k *
k_factor);
1272 idx_t *coarse_labels =
new idx_t [k_coarse * n];
1275 float *coarse_distances =
new float [k_coarse * n];
1279 size_t(n), k_coarse, coarse_labels, coarse_distances};
1284 indexIVFPQ_stats.search_cycles += TOC;
1289 size_t n_refine = 0;
1290 #pragma omp parallel reduction(+ : n_refine)
1293 float *residual_1 =
new float [2 *
d];
1295 float *residual_2 = residual_1 +
d;
1297 for (
idx_t i = 0; i < n; i++) {
1298 const float *xq = x + i *
d;
1299 const long * shortlist = coarse_labels + k_coarse * i;
1300 float * heap_sim = distances + k * i;
1301 long * heap_ids = labels + k * i;
1302 maxheap_heapify (k, heap_sim, heap_ids);
1304 for (
int j = 0; j < k_coarse; j++) {
1305 long sl = shortlist[j];
1307 if (sl == -1)
continue;
1309 int list_no = sl >> 32;
1310 int ofs = sl & 0xffffffff;
1312 assert (list_no >= 0 && list_no < nlist);
1313 assert (ofs >= 0 && ofs <
ids[list_no].size());
1316 quantizer->compute_residual (xq, residual_1, list_no);
1319 const uint8_t * l2code = &codes[list_no][ofs *
pq.
code_size];
1321 for (
int l = 0; l <
d; l++)
1322 residual_2[l] = residual_1[l] - residual_2[l];
1326 assert (0 <=
id &&
id <
ntotal);
1330 float dis =
fvec_L2sqr (residual_1, residual_2, d);
1332 if (dis < heap_sim[0]) {
1333 maxheap_pop (k, heap_sim, heap_ids);
1334 maxheap_push (k, heap_sim, heap_ids, dis,
id);
1338 maxheap_reorder (k, heap_sim, heap_ids);
1341 indexIVFPQ_stats.nrefine += n_refine;
1342 indexIVFPQ_stats.refine_cycles += TOC;
1347 std::vector<float> r3 (d);
1351 for (
idx_t i = i0; i < i0 + ni; i++) {
1352 float *r = recons + i *
d;
1355 for (
int j = 0; j <
d; j++)
1372 FAISS_THROW_MSG(
"not implemented");
1380 IndexIVFPQCompact::IndexIVFPQCompact ()
1389 IndexIVFPQCompact::IndexIVFPQCompact (
const IndexIVFPQ &other)
1391 FAISS_THROW_IF_NOT_MSG (other.ntotal < (1UL << 31),
1392 "IndexIVFPQCompact cannot store more than 2G images");
1406 nlist = other.nlist;
1408 quantizer = other.quantizer;
1411 direct_map = other.direct_map;
1417 code_size = other.code_size;
1428 limits =
new uint32_t [nlist + 1];
1435 for (
size_t i = 0; i <
nlist; i++) {
1437 const std::vector<long> &other_ids = other.ids[i];
1438 for (
size_t j = 0; j < other_ids.size(); j++) {
1439 long id = other_ids[j];
1440 FAISS_THROW_IF_NOT_MSG (
id < (1UL << 31),
1441 "IndexIVFPQCompact cannot store ids > 2G");
1445 other.codes[i].data(),
1446 other.codes[i].size());
1447 ofs += other_ids.size();
1449 FAISS_THROW_IF_NOT (ofs ==
ntotal);
1455 FAISS_THROW_MSG (
"cannot add to an IndexIVFPQCompact");
1459 FAISS_THROW_MSG (
"cannot reset an IndexIVFPQCompact");
1463 FAISS_THROW_MSG (
"cannot train an IndexIVFPQCompact");
1469 IndexIVFPQCompact::~IndexIVFPQCompact ()
1476 munmap (mmap_buffer, mmap_length);
1486 const float * coarse_dis,
1488 bool store_pairs)
const
1490 const size_t k = res->
k;
1492 #pragma omp parallel
1494 InvertedListScanner<uint32_t> qt (*
this);
1495 size_t stats_nlist = 0;
1496 size_t stats_ncode = 0;
1497 uint64_t init_query_cycles = 0;
1498 uint64_t scan_cycles = 0;
1499 uint64_t heap_cycles = 0;
1502 for (
size_t i = 0; i < nx; i++) {
1503 const float *qi = qx + i *
d;
1504 const long * keysi = keys + i *
nprobe;
1505 const float *coarse_dis_i = coarse_dis + i *
nprobe;
1506 float * heap_sim = res->
get_val (i);
1507 long * heap_ids = res->
get_ids (i);
1511 maxheap_heapify (k, heap_sim, heap_ids);
1516 init_query_cycles += TOC;
1520 for (
size_t ik = 0; ik <
nprobe; ik++) {
1521 long key = keysi[ik];
1526 if (key >= (
long) nlist) {
1527 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
1534 if (list_size == 0)
continue;
1536 qt.init_list (key, coarse_dis_i[ik],
1542 qt.scan_list_polysemous
1543 (k, heap_sim, heap_ids, store_pairs);
1545 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1547 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1553 stats_ncode += nscan;
1555 maxheap_reorder (k, heap_sim, heap_ids);
1558 for (
size_t j = 0; j < k; j++) {
1559 heap_sim[i] = -heap_sim[i];
1565 #pragma omp critical
1567 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1568 indexIVFPQ_stats.nlist += stats_nlist;
1569 indexIVFPQ_stats.ncode += stats_ncode;
1571 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1572 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1573 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1574 indexIVFPQ_stats.heap_cycles += heap_cycles;
1578 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
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.
void add(idx_t, const float *) override
the three following functions will fail at runtime
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
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
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
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
void reset() override
removes all elements from the database.
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
long remove_ids(const IDSelector &sel) override
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
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
void reset() override
removes all elements from the database.
bool verbose
verbosity level
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
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
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
long remove_ids(const IDSelector &sel) override
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?
void add_with_ids(idx_t n, const float *x, const long *xids) override
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
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