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);
54 use_precomputed_table = 0;
55 scan_table_threshold = 0;
57 polysemous_training =
nullptr;
58 do_polysemous_training =
false;
72 const float * x_in = x;
80 const float *trainset;
83 if(
verbose) printf(
"computing residuals\n");
87 float *residuals =
new float [n *
d];
88 del_residuals.set (residuals);
89 for (
idx_t i = 0; i < n; i++)
97 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
100 pq.train (n, trainset);
104 printf(
"doing polysemous training for PQ\n");
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];
150 const float * x, uint8_t * xcodes,
151 bool compute_keys)
const
157 float *residuals =
new float [n *
d];
160 for (
size_t i = 0; i < n; i++)
169 const uint8_t * xcodes,
float * x)
const
173 std::vector<float> centroid (
d);
174 for (
size_t i = 0; i < n; i++) {
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)
197 for (
idx_t i0 = 0; i0 < n; i0 += bs) {
198 idx_t i1 = std::min(i0 + bs, n);
200 printf(
"IndexIVFPQ::add_core_o: adding %ld:%ld / %ld\n",
204 xids ? xids + i0 :
nullptr,
205 residuals_2 ? residuals_2 + i0 * d :
nullptr,
206 precomputed_idx ? precomputed_idx + i0 :
nullptr);
216 if (precomputed_idx) {
217 idx = precomputed_idx;
219 long * idx0 =
new long [n];
226 uint8_t * xcodes =
new uint8_t [n *
code_size];
229 const float *to_encode =
nullptr;
233 float *residuals =
new float [n *
d];
235 for (
size_t i = 0; i < n; i++) {
237 memset (residuals + i *
d, 0,
sizeof(*residuals) * d);
240 x + i * d, residuals + i * d, idx[i]);
242 to_encode = residuals;
243 del_to_encode.set (to_encode);
252 for (
size_t i = 0; i < n; i++) {
257 memset (residuals_2, 0,
sizeof(*residuals_2) *
d);
261 ids[key].push_back (
id);
264 codes[key].push_back (code[j]);
267 float *res2 = residuals_2 + i *
d;
268 const float *xi = to_encode + i *
d;
270 for (
int j = 0; j <
d; j++)
271 res2[j] = xi[j] - res2[j];
275 direct_map.push_back (key << 32 | (
ids[key].size() - 1));
281 char comment[100] = {0};
283 snprintf (comment, 100,
"(%ld vectors ignored)", n_ignore);
284 printf(
" add_core times: %.3f %.3f %.3f %s\n",
285 t1 - t0, t2 - t1, t3 - t2, comment);
294 const uint8_t* code = &(codes[list_no][offset *
code_size]);
297 std::vector<float> centroid(
d);
301 for (
int i = 0; i <
d; ++i) {
302 recons[i] += centroid[i];
348 fprintf(stderr,
"IndexIVFPQ::precompute_table: WARN precomputed "
349 "tables not needed for inner product quantizers\n");
354 if (miq &&
pq.
M % miq->pq.
M == 0)
361 printf (
"precomputing IVFPQ tables type %d\n",
366 std::vector<float> r_norms (
pq.
M *
pq.
ksub, NAN);
367 for (
int m = 0; m <
pq.
M; m++)
368 for (
int j = 0; j <
pq.
ksub; j++)
369 r_norms [m *
pq.
ksub + j] =
375 std::vector<float> centroid (
d);
377 for (
size_t i = 0; i <
nlist; i++) {
381 pq.compute_inner_prod_table (centroid.data(), tab);
387 FAISS_THROW_IF_NOT (miq);
389 FAISS_THROW_IF_NOT (
pq.
M % cpq.
M == 0);
394 std::vector<float> centroids (
d * cpq.
ksub, NAN);
396 for (
int m = 0; m < cpq.
M; m++) {
397 for (
size_t i = 0; i < cpq.
ksub; i++) {
398 memcpy (centroids.data() + i *
d + m * cpq.
dsub,
400 sizeof (*centroids.data()) * cpq.
dsub);
404 pq.compute_inner_prod_tables (cpq.
ksub, centroids.data (),
407 for (
size_t i = 0; i < cpq.
ksub; i++) {
417 static uint64_t get_cycles () {
419 asm volatile(
"rdtsc \n\t"
422 return ((uint64_t)high << 32) | (low);
425 #define TIC t0 = get_cycles()
426 #define TOC get_cycles () - t0
443 const IndexIVFPQ & ivfpq;
447 const ProductQuantizer & pq;
450 int use_precomputed_table;
453 float * sim_table, * sim_table_2;
454 float * residual_vec, *decoded_vec;
457 std::vector<float> mem;
460 std::vector<const float *> sim_table_ptrs;
462 explicit QueryTables (
const IndexIVFPQ & ivfpq):
466 metric_type (ivfpq.metric_type),
467 by_residual (ivfpq.by_residual),
468 use_precomputed_table (ivfpq.use_precomputed_table)
470 mem.resize (pq.ksub * pq.M * 2 + d *2);
471 sim_table = mem.data();
472 sim_table_2 = sim_table + pq.ksub * pq.M;
473 residual_vec = sim_table_2 + pq.ksub * pq.M;
474 decoded_vec = residual_vec + d;
477 if (ivfpq.polysemous_ht != 0) {
478 q_code.resize (pq.code_size);
480 init_list_cycles = 0;
481 sim_table_ptrs.resize (pq.M);
492 void init_query (
const float * qi) {
494 if (metric_type == METRIC_INNER_PRODUCT)
498 if (!by_residual && ivfpq.polysemous_ht != 0)
499 pq.compute_code (qi, q_code.data());
502 void init_query_IP () {
504 pq.compute_inner_prod_table (qi, sim_table);
506 for (
int i = 0; i < pq.ksub * pq.M; i++) {
507 sim_table[i] = - sim_table[i];
511 void init_query_L2 () {
513 pq.compute_distance_table (qi, sim_table);
514 }
else if (use_precomputed_table) {
515 pq.compute_inner_prod_table (qi, sim_table_2);
526 std::vector<uint8_t> q_code;
528 uint64_t init_list_cycles;
533 float precompute_list_tables () {
537 if (metric_type == METRIC_INNER_PRODUCT)
538 dis0 = precompute_list_tables_IP ();
540 dis0 = precompute_list_tables_L2 ();
542 init_list_cycles += TOC;
546 float precompute_list_table_pointers () {
550 if (metric_type == METRIC_INNER_PRODUCT)
551 FAISS_THROW_MSG (
"not implemented");
553 dis0 = precompute_list_table_pointers_L2 ();
555 init_list_cycles += TOC;
563 float precompute_list_tables_IP ()
567 ivfpq.quantizer->reconstruct (key, decoded_vec);
569 float dis0 = -fvec_inner_product (qi, decoded_vec, d);
571 if (ivfpq.polysemous_ht) {
572 for (
int i = 0; i < d; i++) {
573 residual_vec [i] = qi[i] - decoded_vec[i];
575 pq.compute_code (residual_vec, q_code.data());
585 float precompute_list_tables_L2 ()
589 if (use_precomputed_table == 0) {
590 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
591 pq.compute_distance_table (residual_vec, sim_table);
592 }
else if (use_precomputed_table == 1) {
596 &ivfpq.precomputed_table [key * pq.ksub * pq.M],
599 }
else if (use_precomputed_table == 2) {
602 const MultiIndexQuantizer *miq =
603 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
604 FAISS_THROW_IF_NOT (miq);
605 const ProductQuantizer &cpq = miq->pq;
606 int Mf = pq.M / cpq.M;
608 const float *qtab = sim_table_2;
609 float *ltab = sim_table;
612 for (
int cm = 0; cm < cpq.M; cm++) {
614 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
618 const float *pc = &ivfpq.precomputed_table
619 [(ki * pq.M + cm * Mf) * pq.ksub];
621 if (ivfpq.polysemous_ht == 0) {
628 ltab += Mf * pq.ksub;
629 qtab += Mf * pq.ksub;
631 for (
int m = cm * Mf; m < (cm + 1) * Mf; m++) {
633 (pq.ksub, pc, -2, qtab, ltab);
646 float precompute_list_table_pointers_L2 ()
650 if (use_precomputed_table == 1) {
653 const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M];
654 for (
int m = 0; m < pq.M; m++) {
655 sim_table_ptrs [m] = s;
658 }
else if (use_precomputed_table == 2) {
661 const MultiIndexQuantizer *miq =
662 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
663 FAISS_THROW_IF_NOT (miq);
664 const ProductQuantizer &cpq = miq->pq;
665 int Mf = pq.M / cpq.M;
669 for (
int cm = 0; cm < cpq.M; cm++) {
670 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
673 const float *pc = &ivfpq.precomputed_table
674 [(ki * pq.M + cm * Mf) * pq.ksub];
676 for (
int m = m0; m < m0 + Mf; m++) {
677 sim_table_ptrs [m] = pc;
683 FAISS_THROW_MSG (
"need precomputed tables");
686 if (ivfpq.polysemous_ht) {
687 FAISS_THROW_MSG (
"not implemented");
705 template <
typename IDType>
706 struct InvertedListScanner: QueryTables {
708 const uint8_t * __restrict list_codes;
709 const IDType * list_ids;
712 explicit InvertedListScanner (
const IndexIVFPQ & ivfpq):
715 FAISS_THROW_IF_NOT (pq.byte_per_idx == 1);
721 size_t list_size_in,
const IDType *list_ids_in,
722 const uint8_t *list_codes_in) {
724 this->coarse_dis = coarse_dis;
725 list_size = list_size_in;
726 list_codes = list_codes_in;
727 list_ids = list_ids_in;
735 void scan_list_with_table (
736 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
738 float dis0 = precompute_list_tables ();
740 for (
size_t j = 0; j < list_size; j++) {
743 const float *tab = sim_table;
745 for (
size_t m = 0; m < pq.M; m++) {
746 dis += tab[*list_codes++];
750 if (dis < heap_sim[0]) {
751 maxheap_pop (k, heap_sim, heap_ids);
752 long id = store_pairs ? (key << 32 | j) : list_ids[j];
753 maxheap_push (k, heap_sim, heap_ids, dis,
id);
761 void scan_list_with_pointer (
762 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
765 float dis0 = precompute_list_table_pointers ();
767 for (
size_t j = 0; j < list_size; j++) {
770 const float *tab = sim_table_2;
772 for (
size_t m = 0; m < pq.M; m++) {
773 int ci = *list_codes++;
774 dis += sim_table_ptrs [m][ci] - 2 * tab [ci];
778 if (dis < heap_sim[0]) {
779 maxheap_pop (k, heap_sim, heap_ids);
780 long id = store_pairs ? (key << 32 | j) : list_ids[j];
781 maxheap_push (k, heap_sim, heap_ids, dis,
id);
788 void scan_on_the_fly_dist (
789 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
792 if (by_residual && use_precomputed_table) {
793 scan_list_with_pointer (k, heap_sim, heap_ids, store_pairs);
801 if (metric_type == METRIC_INNER_PRODUCT) {
802 ivfpq.quantizer->reconstruct (key, residual_vec);
803 dis0 = fvec_inner_product (residual_vec, qi, d);
805 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
813 for (
size_t j = 0; j < list_size; j++) {
815 pq.decode (list_codes, decoded_vec);
816 list_codes += pq.code_size;
819 if (metric_type == METRIC_INNER_PRODUCT) {
820 dis = -dis0 - fvec_inner_product (decoded_vec, qi, d);
825 if (dis < heap_sim[0]) {
826 maxheap_pop (k, heap_sim, heap_ids);
827 long id = store_pairs ? (key << 32 | j) : list_ids[j];
828 maxheap_push (k, heap_sim, heap_ids, dis,
id);
838 size_t n_hamming_pass;
841 template <
class HammingComputer>
842 void scan_list_polysemous_hc (
843 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
845 float dis0 = precompute_list_tables ();
846 int ht = ivfpq.polysemous_ht;
848 int code_size = pq.code_size;
850 HammingComputer hc (q_code.data(), code_size);
852 for (
size_t j = 0; j < list_size; j++) {
853 const uint8_t *b_code = list_codes;
854 int hd = hc.hamming (b_code);
859 const float *tab = sim_table;
861 for (
size_t m = 0; m < pq.M; m++) {
862 dis += tab[*b_code++];
866 if (dis < heap_sim[0]) {
867 maxheap_pop (k, heap_sim, heap_ids);
868 long id = store_pairs ? (key << 32 | j) : list_ids[j];
869 maxheap_push (k, heap_sim, heap_ids, dis,
id);
872 list_codes += code_size;
876 void scan_list_polysemous (
877 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
879 switch (pq.code_size) {
880 #define HANDLE_CODE_SIZE(cs) \
882 scan_list_polysemous_hc <HammingComputer ## cs> \
883 (k, heap_sim, heap_ids, store_pairs); \
887 HANDLE_CODE_SIZE(16);
888 HANDLE_CODE_SIZE(20);
889 HANDLE_CODE_SIZE(32);
890 HANDLE_CODE_SIZE(64);
891 #undef HANDLE_CODE_SIZE
893 if (pq.code_size % 8 == 0)
894 scan_list_polysemous_hc <HammingComputerM8>
895 (k, heap_sim, heap_ids, store_pairs);
897 scan_list_polysemous_hc <HammingComputerM4>
898 (k, heap_sim, heap_ids, store_pairs);
911 IndexIVFPQStats indexIVFPQ_stats;
913 void IndexIVFPQStats::reset () {
914 memset (
this, 0,
sizeof (*
this));
921 const float *coarse_dis,
922 float *distances,
idx_t *labels,
923 bool store_pairs)
const
926 size_t(nx), size_t(k),
932 InvertedListScanner<long> qt (*
this);
933 size_t stats_nlist = 0;
934 size_t stats_ncode = 0;
935 uint64_t init_query_cycles = 0;
936 uint64_t scan_cycles = 0;
937 uint64_t heap_cycles = 0;
940 for (
size_t i = 0; i < nx; i++) {
941 const float *qi = qx + i *
d;
942 const long * keysi = keys + i *
nprobe;
943 const float *coarse_dis_i = coarse_dis + i *
nprobe;
944 float * heap_sim = res.
get_val (i);
945 long * heap_ids = res.
get_ids (i);
949 maxheap_heapify (k, heap_sim, heap_ids);
954 init_query_cycles += TOC;
958 for (
size_t ik = 0; ik <
nprobe; ik++) {
959 long key = keysi[ik];
964 FAISS_THROW_IF_NOT_FMT (
966 "Invalid key=%ld at ik=%ld nlist=%ld\n",
969 size_t list_size =
ids[key].size();
973 if (list_size == 0)
continue;
975 qt.init_list (key, coarse_dis_i[ik],
976 list_size,
ids[key].data(),
981 qt.scan_list_polysemous
982 (k, heap_sim, heap_ids, store_pairs);
984 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
986 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
992 stats_ncode += nscan;
994 maxheap_reorder (k, heap_sim, heap_ids);
997 for (
size_t j = 0; j < k; j++)
998 heap_sim[j] = -heap_sim[j];
1003 #pragma omp critical
1005 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1006 indexIVFPQ_stats.nlist += stats_nlist;
1007 indexIVFPQ_stats.ncode += stats_ncode;
1009 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1010 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1011 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1012 indexIVFPQ_stats.heap_cycles += heap_cycles;
1016 indexIVFPQ_stats.nq += nx;
1020 IndexIVFPQ::IndexIVFPQ ()
1034 bool operator () (
int a,
int b)
const {
1035 return cmp (a, b) > 0;
1037 int cmp (
int a,
int b)
const {
1038 return memcmp (tab + a * code_size, tab + b * code_size,
1048 for (
size_t list_no = 0; list_no <
nlist; list_no++) {
1049 size_t n =
ids[list_no].size();
1050 std::vector<int> ord (n);
1051 for (
int i = 0; i < n; i++) ord[i] = i;
1052 CodeCmp cs = { codes[list_no].data(), code_size };
1053 std::sort (ord.begin(), ord.end(), cs);
1055 const idx_t *list_ids =
ids[list_no].data();
1057 for (
int i = 0; i < n; i++) {
1058 if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) {
1060 if (prev + 1 == i) {
1062 lims[ngroup] = lims[ngroup - 1];
1063 dup_ids [lims [ngroup]++] = list_ids [ord [prev]];
1065 dup_ids [lims [ngroup]++] = list_ids [ord [i]];
1081 IndexIVFPQR::IndexIVFPQR (
1082 Index * quantizer,
size_t d,
size_t nlist,
1083 size_t M,
size_t nbits_per_idx,
1084 size_t M_refine,
size_t nbits_per_idx_refine):
1085 IndexIVFPQ (quantizer, d, nlist, M, nbits_per_idx),
1086 refine_pq (d, M_refine, nbits_per_idx_refine),
1092 IndexIVFPQR::IndexIVFPQR ():
1112 float * residual_2 =
new float [n *
d];
1118 printf (
"training %zdx%zd 2nd level PQ quantizer on %ld %dD-vectors\n",
1134 const long *precomputed_idx) {
1136 float * residual_2 =
new float [n *
d];
1141 add_core_o (n, x, xids, residual_2, precomputed_idx);
1154 const float *L1_dis,
1155 float *distances,
idx_t *labels,
1156 bool store_pairs)
const
1160 size_t k_coarse = long(k *
k_factor);
1161 idx_t *coarse_labels =
new idx_t [k_coarse * n];
1164 float *coarse_distances =
new float [k_coarse * n];
1168 idx, L1_dis, coarse_distances, coarse_labels,
1173 indexIVFPQ_stats.search_cycles += TOC;
1178 size_t n_refine = 0;
1179 #pragma omp parallel reduction(+ : n_refine)
1182 float *residual_1 =
new float [2 *
d];
1184 float *residual_2 = residual_1 +
d;
1186 for (
idx_t i = 0; i < n; i++) {
1187 const float *xq = x + i *
d;
1188 const long * shortlist = coarse_labels + k_coarse * i;
1189 float * heap_sim = distances + k * i;
1190 long * heap_ids = labels + k * i;
1191 maxheap_heapify (k, heap_sim, heap_ids);
1193 for (
int j = 0; j < k_coarse; j++) {
1194 long sl = shortlist[j];
1196 if (sl == -1)
continue;
1198 int list_no = sl >> 32;
1199 int ofs = sl & 0xffffffff;
1201 assert (list_no >= 0 && list_no <
nlist);
1202 assert (ofs >= 0 && ofs <
ids[list_no].size());
1208 const uint8_t * l2code = &codes[list_no][ofs *
pq.
code_size];
1210 for (
int l = 0; l <
d; l++)
1211 residual_2[l] = residual_1[l] - residual_2[l];
1215 assert (0 <=
id &&
id <
ntotal);
1219 float dis =
fvec_L2sqr (residual_1, residual_2, d);
1221 if (dis < heap_sim[0]) {
1222 maxheap_pop (k, heap_sim, heap_ids);
1223 long id_or_pair = store_pairs ? sl : id;
1224 maxheap_push (k, heap_sim, heap_ids, dis, id_or_pair);
1228 maxheap_reorder (k, heap_sim, heap_ids);
1231 indexIVFPQ_stats.nrefine += n_refine;
1232 indexIVFPQ_stats.refine_cycles += TOC;
1236 float* recons)
const
1241 assert (0 <=
id &&
id <
ntotal);
1243 std::vector<float> r3(d);
1245 for (
int i = 0; i <
d; ++i) {
1253 FAISS_THROW_IF_NOT(other);
1264 FAISS_THROW_MSG(
"not implemented");
1273 Index2Layer::Index2Layer (
Index * quantizer,
size_t nlist,
1276 Index (quantizer->d, metric),
1277 q1 (quantizer, nlist),
1278 pq (quantizer->d, M, 8)
1281 for (
int nbyte = 0; nbyte < 7; nbyte++) {
1282 if ((1L << (8 * nbyte)) >= nlist) {
1283 code_size_1 = nbyte;
1288 code_size = code_size_1 + code_size_2;
1291 Index2Layer::Index2Layer ()
1296 Index2Layer::~Index2Layer ()
1302 printf (
"training level-1 quantizer %ld vectors in %dD\n",
1309 printf(
"computing residuals\n");
1312 const float * x_in = x;
1320 std::vector<idx_t>
assign(n);
1322 std::vector<float> residuals(n * d);
1323 for (
idx_t i = 0; i < n; i++) {
1325 x + i * d, residuals.data() + i *
d, assign[i]);
1329 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
1332 pq.train (n, residuals.data());
1341 for (
idx_t i0 = 0; i0 < n; i0 += bs) {
1342 idx_t i1 = std::min(i0 + bs, n);
1344 printf(
"Index2Layer::add: adding %ld:%ld / %ld\n",
1347 add (i1 - i0, x + i0 * d);
1352 std::vector<idx_t> codes1 (n);
1354 std::vector<float> residuals(n * d);
1355 for (
idx_t i = 0; i < n; i++) {
1357 x + i * d, residuals.data() + i *
d, codes1[i]);
1368 const char *ip = (
char*)&i;
1369 FAISS_THROW_IF_NOT_MSG (ip[0] == 0x44,
1370 "works only on a little-endian CPU");
1374 for (
idx_t i = 0; i < n; i++) {
1377 memcpy (wp, &codes2[i * code_size_2], code_size_2);
1390 idx_t* labels)
const
1392 FAISS_THROW_MSG (
"not implemented");
1399 FAISS_THROW_IF_NOT (i0 >= 0 && i0 + ni <=
ntotal);
1402 for (
idx_t i = 0; i < ni; i++) {
1408 for (
idx_t j = 0; j <
d; j++) {
1409 recons[j] += recons1[j];
1420 FAISS_THROW_IF_NOT (other.
ntotal == 0);
1422 const uint8_t *rp =
codes.data();
1427 other.
ids[key].push_back (i);
1429 std::vector<uint8_t> & list = other.codes[key];
1430 size_t len = list.size();
1472 IndexIVFPQCompact::IndexIVFPQCompact ()
1481 IndexIVFPQCompact::IndexIVFPQCompact (
const IndexIVFPQ &other)
1483 FAISS_THROW_IF_NOT_MSG (other.ntotal < (1UL << 31),
1484 "IndexIVFPQCompact cannot store more than 2G images");
1498 nlist = other.nlist;
1500 quantizer = other.quantizer;
1503 direct_map = other.direct_map;
1509 code_size = other.code_size;
1520 limits =
new uint32_t [nlist + 1];
1527 for (
size_t i = 0; i <
nlist; i++) {
1529 const std::vector<long> &other_ids = other.ids[i];
1530 for (
size_t j = 0; j < other_ids.size(); j++) {
1531 long id = other_ids[j];
1532 FAISS_THROW_IF_NOT_MSG (
id < (1UL << 31),
1533 "IndexIVFPQCompact cannot store ids > 2G");
1537 other.codes[i].data(),
1538 other.codes[i].size());
1539 ofs += other_ids.size();
1541 FAISS_THROW_IF_NOT (ofs ==
ntotal);
1547 FAISS_THROW_MSG (
"cannot add to an IndexIVFPQCompact");
1551 FAISS_THROW_MSG (
"cannot reset an IndexIVFPQCompact");
1555 FAISS_THROW_MSG (
"cannot train an IndexIVFPQCompact");
1561 IndexIVFPQCompact::~IndexIVFPQCompact ()
1568 munmap (mmap_buffer, mmap_length);
1578 const float *coarse_dis,
1579 float *distances,
idx_t *labels,
1580 bool store_pairs)
const
1583 size_t(nx), size_t(k),
1587 #pragma omp parallel
1589 InvertedListScanner<uint32_t> qt (*
this);
1590 size_t stats_nlist = 0;
1591 size_t stats_ncode = 0;
1592 uint64_t init_query_cycles = 0;
1593 uint64_t scan_cycles = 0;
1594 uint64_t heap_cycles = 0;
1597 for (
size_t i = 0; i < nx; i++) {
1598 const float *qi = qx + i *
d;
1599 const long * keysi = keys + i *
nprobe;
1600 const float *coarse_dis_i = coarse_dis + i *
nprobe;
1601 float * heap_sim = res.
get_val (i);
1602 long * heap_ids = res.
get_ids (i);
1606 maxheap_heapify (k, heap_sim, heap_ids);
1611 init_query_cycles += TOC;
1615 for (
size_t ik = 0; ik <
nprobe; ik++) {
1616 long key = keysi[ik];
1621 if (key >= (
long) nlist) {
1622 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
1629 if (list_size == 0)
continue;
1631 qt.init_list (key, coarse_dis_i[ik],
1637 qt.scan_list_polysemous
1638 (k, heap_sim, heap_ids, store_pairs);
1640 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1642 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1648 stats_ncode += nscan;
1650 maxheap_reorder (k, heap_sim, heap_ids);
1653 for (
size_t j = 0; j < k; j++) {
1654 heap_sim[i] = -heap_sim[i];
1660 #pragma omp critical
1662 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1663 indexIVFPQ_stats.nlist += stats_nlist;
1664 indexIVFPQ_stats.ncode += stats_ncode;
1666 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1667 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1668 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1669 indexIVFPQ_stats.heap_cycles += heap_cycles;
1673 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 merge_from(IndexIVF &other, idx_t add_id) override
void transfer_to_IVFPQ(IndexIVFPQ &other) const
transfer the flat codes to an IVFPQ index
size_t code_size_2
size of the code for the second level
void reconstruct_from_offset(long list_no, long offset, float *recons) const override
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
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 reconstruct_n(idx_t i0, idx_t ni, float *recons) const override
size_t code_size
code_size_1 + code_size_2
void add(idx_t, const float *) override
the three following functions will fail at runtime
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
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 reset() override
removes all elements from the database.
void assign(idx_t n, const float *x, idx_t *labels, idx_t k=1)
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?
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
Level1Quantizer q1
first level quantizer
void fvec_madd(size_t n, const float *a, float bf, const float *b, float *c)
int polysemous_ht
Hamming thresh for polysemous filtering.
void reset() override
removes all elements from the database.
std::vector< uint8_t > codes
Codes. Size ntotal * code_size.
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
std::vector< uint8_t > refine_codes
corresponding codes
size_t code_size
byte per indexed vector
void train_residual(idx_t n, const float *x) override
trains the product quantizer
void reconstruct_from_offset(long list_no, long offset, float *recons) const override
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.
char quantizer_trains_alone
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 train(idx_t n, const float *x) override
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
void add(idx_t n, const float *x) override
float fvec_norm_L2sqr(const float *x, size_t d)
void train_q1(size_t n, const float *x, bool verbose, MetricType metric_type)
Trains the quantizer and calls train_residual to train sub-quantizers.
ClusteringParameters cp
parameters used during clustering
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
not implemented
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 code_size_1
size of the code for the first level (ceil(log8(q1.nlist)))
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)
long remove_ids(const IDSelector &sel) override
Dataset manipulation functions.
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 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
void compute_residual(const float *x, float *residual, idx_t key) const
float * get_centroids(size_t m, size_t i)
return the centroids associated with subvector m
size_t max_codes
max nb of codes to visit to do a query
virtual void reconstruct(idx_t key, float *recons) const
ProductQuantizer pq
second level quantizer is always a PQ
bool maintain_direct_map
map for direct access to the elements. Enables reconstruct().
bool own_fields
whether object owns the quantizer
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 reconstruct(idx_t key, float *recons) const override
virtual void merge_from(IndexIVF &other, idx_t add_id)
size_t nlist
number of possible key values
size_t code_size
code size per vector in bytes
size_t find_duplicates(idx_t *ids, size_t *lims) const
MetricType
Some algorithms support both an inner product version 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