14 #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_ASSERT (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;
68 void IndexIVFPQ::set_typename ()
72 <<
"[" << nlist <<
":" << quantizer->index_typename <<
"]";
73 index_typename = s.str();
86 if(n > ntrain) n = ntrain;
88 const float *trainset;
90 if(
verbose) printf(
"computing residuals\n");
92 quantizer->assign (n, x, assign);
93 float *residuals =
new float [n *
d];
94 for (
idx_t i = 0; i < n; i++)
95 quantizer->compute_residual (x + i * d, residuals+i*d, assign[i]);
102 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
105 pq.train (n, trainset);
110 if (!pt) pt = &default_pt;
116 uint8_t *train_codes =
new uint8_t [
pq.
code_size * n];
119 for (
idx_t i = 0; i < n; i++) {
120 const float *xx = trainset + i *
d;
121 float * res = residuals_2 + i *
d;
123 for (
int j = 0; j <
d; j++)
124 res[j] = xx[j] - res[j];
127 delete [] train_codes;
139 void IndexIVFPQ::encode (
long key,
const float * x, uint8_t * code)
const
142 float residual_vec[
d];
143 quantizer->compute_residual (x, residual_vec, key);
154 const float * x, uint8_t * xcodes)
const
157 float *residuals =
new float [n *
d];
159 for (
size_t i = 0; i < n; i++)
160 quantizer->compute_residual (x + i * d, residuals + i * d, keys[i]);
177 float *residuals_2,
const long *precomputed_idx)
183 if (precomputed_idx) {
184 idx = precomputed_idx;
186 long * idx0 =
new long [n];
187 quantizer->assign (n, x, idx0);
192 uint8_t * xcodes =
new uint8_t [n *
code_size];
194 const float *to_encode =
nullptr;
197 float *residuals =
new float [n *
d];
199 for (
size_t i = 0; i < n; i++) {
201 memset (residuals + i * d, 0,
sizeof(*residuals) * d);
203 quantizer->compute_residual (
204 x + i * d, residuals + i * d, idx[i]);
206 to_encode = residuals;
215 for (
size_t i = 0; i < n; i++) {
220 memset (residuals_2, 0,
sizeof(*residuals_2) * d);
224 ids[key].push_back (
id);
227 codes[key].push_back (code[j]);
230 float *res2 = residuals_2 + i *
d;
231 const float *xi = to_encode + i *
d;
233 for (
int j = 0; j <
d; j++)
234 res2[j] = xi[j] - res2[j];
238 direct_map.push_back (key << 32 | (
ids[key].size() - 1));
245 if (!precomputed_idx)
249 char comment[100] = {0};
251 snprintf (comment, 100,
"(%ld vectors ignored)", n_ignore);
252 printf(
" add_core times: %.3f %.3f %.3f %s\n",
253 t1 - t0, t2 - t1, t3 - t2, comment);
260 FAISS_ASSERT (ni == 0 || (i0 >= 0 && i0 + ni <=
ntotal));
262 std::vector<float> centroid (d);
264 for (
int key = 0; key <
nlist; key++) {
265 const std::vector<long> & idlist =
ids[key];
266 const uint8_t * code_line = codes[key].data();
268 for (
long ofs = 0; ofs < idlist.size(); ofs++) {
269 long id = idlist[ofs];
270 if (!(
id >= i0 &&
id < i0 + ni))
continue;
271 float *r = recons + d * (
id - i0);
273 quantizer->reconstruct (key, centroid.data());
275 for (
int j = 0; j <
d; j++) {
289 FAISS_ASSERT (direct_map.size() ==
ntotal);
290 int list_no = direct_map[key] >> 32;
291 int ofs = direct_map[key] & 0xffffffff;
293 quantizer->reconstruct (list_no, recons);
294 const uint8_t * code = &(codes[list_no][ofs *
pq.
code_size]);
296 for (
size_t m = 0; m <
pq.
M; m++) {
297 float * out = recons + m *
pq.
dsub;
299 for (
size_t i = 0; i <
pq.
dsub; i++) {
310 for (
int i = 0; i <
nlist; i++) {
311 codes[i].insert (codes[i].end(),
312 other.codes[i].begin(), other.codes[i].end());
313 other.codes[i].clear();
318 long a1,
long a2)
const
320 FAISS_ASSERT (nlist == other.
nlist);
323 for (
long list_no = 0; list_no <
nlist; list_no++) {
324 const std::vector<idx_t> & ids_in =
ids[list_no];
325 std::vector<idx_t> & ids_out = other.
ids[list_no];
326 const std::vector<uint8_t> & codes_in = codes[list_no];
327 std::vector<uint8_t> & codes_out = other.codes[list_no];
329 for (
long i = 0; i < ids_in.size(); i++) {
330 idx_t id = ids_in[i];
331 if (subset_type == 0 && a1 <=
id &&
id < a2) {
332 ids_out.push_back (
id);
333 codes_out.insert (codes_out.end(),
335 codes_in.begin() + (i + 1) * code_size);
381 if (quantizer->metric_type == METRIC_INNER_PRODUCT) {
382 fprintf(stderr,
"IndexIVFPQ::precompute_table: WARN precomputed "
383 "tables not supported for inner product quantizers\n");
388 if (miq &&
pq.
M % miq->pq.
M == 0)
396 std::vector<float> r_norms (
pq.
M *
pq.
ksub, 0.0/0.0);
397 for (
int m = 0; m <
pq.
M; m++)
398 for (
int j = 0; j <
pq.
ksub; j++)
399 r_norms [m *
pq.
ksub + j] =
405 std::vector<float> centroid (d);
407 for (
size_t i = 0; i <
nlist; i++) {
408 quantizer->reconstruct (i, centroid.data());
411 pq.compute_inner_prod_table (centroid.data(), tab);
419 FAISS_ASSERT (
pq.
M % cpq.
M == 0);
424 std::vector<float> centroids (d * cpq.
ksub, 0.0/0.0);
426 for (
int m = 0; m < cpq.
M; m++) {
427 for (
size_t i = 0; i < cpq.
ksub; i++) {
428 memcpy (centroids.data() + i * d + m * cpq.
dsub,
430 sizeof (*centroids.data()) * cpq.
dsub);
434 pq.compute_inner_prod_tables (cpq.
ksub, centroids.data (),
437 for (
size_t i = 0; i < cpq.
ksub; i++) {
447 static uint64_t get_cycles () {
449 asm volatile(
"rdtsc \n\t"
452 return ((uint64_t)high << 32) | (low);
455 #define TIC t0 = get_cycles()
456 #define TOC get_cycles () - t0
473 const IndexIVFPQ & ivfpq;
477 const ProductQuantizer & pq;
480 int use_precomputed_table;
483 float * sim_table, * sim_table_2;
484 float * residual_vec, *decoded_vec;
487 std::vector<float> mem;
490 std::vector<const float *> sim_table_ptrs;
492 explicit QueryTables (
const IndexIVFPQ & ivfpq):
496 metric_type (ivfpq.metric_type),
497 by_residual (ivfpq.by_residual),
498 use_precomputed_table (ivfpq.use_precomputed_table)
500 mem.resize (pq.ksub * pq.M * 2 + d *2);
501 sim_table = mem.data();
502 sim_table_2 = sim_table + pq.ksub * pq.M;
503 residual_vec = sim_table_2 + pq.ksub * pq.M;
504 decoded_vec = residual_vec + d;
507 if (ivfpq.polysemous_ht != 0) {
508 q_code.resize (pq.code_size);
510 init_list_cycles = 0;
511 sim_table_ptrs.resize (pq.M);
522 void init_query (
const float * qi) {
524 if (metric_type == METRIC_INNER_PRODUCT)
528 if (!by_residual && ivfpq.polysemous_ht != 0)
529 pq.compute_code (qi, q_code.data());
532 void init_query_IP () {
534 pq.compute_inner_prod_table (qi, sim_table);
536 for (
int i = 0; i < pq.ksub * pq.M; i++) {
537 sim_table[i] = - sim_table[i];
541 void init_query_L2 () {
543 pq.compute_distance_table (qi, sim_table);
544 }
else if (use_precomputed_table) {
545 pq.compute_inner_prod_table (qi, sim_table_2);
556 std::vector<uint8_t> q_code;
558 uint64_t init_list_cycles;
563 float precompute_list_tables () {
567 if (metric_type == METRIC_INNER_PRODUCT)
568 dis0 = precompute_list_tables_IP ();
570 dis0 = precompute_list_tables_L2 ();
572 init_list_cycles += TOC;
576 float precompute_list_table_pointers () {
580 if (metric_type == METRIC_INNER_PRODUCT)
581 FAISS_ASSERT (!
"not implemented");
583 dis0 = precompute_list_table_pointers_L2 ();
585 init_list_cycles += TOC;
593 float precompute_list_tables_IP ()
597 ivfpq.quantizer->reconstruct (key, decoded_vec);
599 float dis0 = -fvec_inner_product (qi, decoded_vec, d);
601 if (ivfpq.polysemous_ht) {
602 for (
int i = 0; i < d; i++) {
603 residual_vec [i] = qi[i] - decoded_vec[i];
605 pq.compute_code (residual_vec, q_code.data());
615 float precompute_list_tables_L2 ()
619 if (use_precomputed_table == 0) {
620 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
621 pq.compute_distance_table (residual_vec, sim_table);
622 }
else if (use_precomputed_table == 1) {
626 &ivfpq.precomputed_table [key * pq.ksub * pq.M],
629 }
else if (use_precomputed_table == 2) {
632 const MultiIndexQuantizer *miq =
633 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
635 const ProductQuantizer &cpq = miq->pq;
636 int Mf = pq.M / cpq.M;
638 const float *qtab = sim_table_2;
639 float *ltab = sim_table;
642 for (
int cm = 0; cm < cpq.M; cm++) {
644 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
648 const float *pc = &ivfpq.precomputed_table
649 [(ki * pq.M + cm * Mf) * pq.ksub];
651 if (ivfpq.polysemous_ht == 0) {
658 ltab += Mf * pq.ksub;
659 qtab += Mf * pq.ksub;
661 for (
int m = cm * Mf; m < (cm + 1) * Mf; m++) {
663 (pq.ksub, pc, -2, qtab, ltab);
676 float precompute_list_table_pointers_L2 ()
680 if (use_precomputed_table == 1) {
683 const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M];
684 for (
int m = 0; m < pq.M; m++) {
685 sim_table_ptrs [m] = s;
688 }
else if (use_precomputed_table == 2) {
691 const MultiIndexQuantizer *miq =
692 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
694 const ProductQuantizer &cpq = miq->pq;
695 int Mf = pq.M / cpq.M;
699 for (
int cm = 0; cm < cpq.M; cm++) {
700 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
703 const float *pc = &ivfpq.precomputed_table
704 [(ki * pq.M + cm * Mf) * pq.ksub];
706 for (
int m = m0; m < m0 + Mf; m++) {
707 sim_table_ptrs [m] = pc;
712 }
else FAISS_ASSERT (!
"need precomputed tables");
714 if (ivfpq.polysemous_ht) {
715 FAISS_ASSERT (!
"not implemented");
733 template <
typename IDType>
734 struct InvertedListScanner: QueryTables {
736 const uint8_t * __restrict list_codes;
737 const IDType * list_ids;
740 explicit InvertedListScanner (
const IndexIVFPQ & ivfpq):
743 FAISS_ASSERT(pq.byte_per_idx == 1);
749 size_t list_size_in,
const IDType *list_ids_in,
750 const uint8_t *list_codes_in) {
752 this->coarse_dis = coarse_dis;
753 list_size = list_size_in;
754 list_codes = list_codes_in;
755 list_ids = list_ids_in;
763 void scan_list_with_table (
764 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
766 float dis0 = precompute_list_tables ();
768 for (
size_t j = 0; j < list_size; j++) {
771 const float *tab = sim_table;
773 for (
size_t m = 0; m < pq.M; m++) {
774 dis += tab[*list_codes++];
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);
789 void scan_list_with_pointer (
790 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
793 float dis0 = precompute_list_table_pointers ();
795 for (
size_t j = 0; j < list_size; j++) {
798 const float *tab = sim_table_2;
800 for (
size_t m = 0; m < pq.M; m++) {
801 int ci = *list_codes++;
802 dis += sim_table_ptrs [m][ci] - 2 * tab [ci];
806 if (dis < heap_sim[0]) {
807 maxheap_pop (k, heap_sim, heap_ids);
808 long id = store_pairs ? (key << 32 | j) : list_ids[j];
809 maxheap_push (k, heap_sim, heap_ids, dis,
id);
816 void scan_on_the_fly_dist (
817 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
820 if (by_residual && use_precomputed_table) {
821 scan_list_with_pointer (k, heap_sim, heap_ids, store_pairs);
829 if (metric_type == METRIC_INNER_PRODUCT) {
830 ivfpq.quantizer->reconstruct (key, residual_vec);
831 dis0 = fvec_inner_product (residual_vec, qi, d);
833 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
841 for (
size_t j = 0; j < list_size; j++) {
843 pq.decode (list_codes, decoded_vec);
844 list_codes += pq.code_size;
847 if (metric_type == METRIC_INNER_PRODUCT) {
848 dis = -dis0 - fvec_inner_product (decoded_vec, qi, d);
853 if (dis < heap_sim[0]) {
854 maxheap_pop (k, heap_sim, heap_ids);
855 long id = store_pairs ? (key << 32 | j) : list_ids[j];
856 maxheap_push (k, heap_sim, heap_ids, dis,
id);
866 size_t n_hamming_pass;
869 template <
class HammingComputer>
870 void scan_list_polysemous_hc (
871 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
873 float dis0 = precompute_list_tables ();
874 int ht = ivfpq.polysemous_ht;
876 int code_size = pq.code_size;
878 HammingComputer hc (q_code.data(), code_size);
880 for (
size_t j = 0; j < list_size; j++) {
881 const uint8_t *b_code = list_codes;
882 int hd = hc.hamming (b_code);
887 const float *tab = sim_table;
889 for (
size_t m = 0; m < pq.M; m++) {
890 dis += tab[*b_code++];
894 if (dis < heap_sim[0]) {
895 maxheap_pop (k, heap_sim, heap_ids);
896 long id = store_pairs ? (key << 32 | j) : list_ids[j];
897 maxheap_push (k, heap_sim, heap_ids, dis,
id);
900 list_codes += code_size;
904 void scan_list_polysemous (
905 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
907 switch (pq.code_size) {
908 #define HANDLE_CODE_SIZE(cs) \
910 scan_list_polysemous_hc <HammingComputer ## cs> \
911 (k, heap_sim, heap_ids, store_pairs); \
915 HANDLE_CODE_SIZE(16);
916 HANDLE_CODE_SIZE(20);
917 HANDLE_CODE_SIZE(32);
918 HANDLE_CODE_SIZE(64);
919 #undef HANDLE_CODE_SIZE
921 if (pq.code_size % 8 == 0)
922 scan_list_polysemous_hc <HammingComputerM8>
923 (k, heap_sim, heap_ids, store_pairs);
925 scan_list_polysemous_hc <HammingComputerM4>
926 (k, heap_sim, heap_ids, store_pairs);
939 IndexIVFPQStats indexIVFPQ_stats;
941 void IndexIVFPQStats::reset () {
942 memset (
this, 0,
sizeof (*
this));
950 const float * coarse_dis,
952 bool store_pairs)
const
954 const size_t k = res->
k;
958 InvertedListScanner<long> qt (*
this);
959 size_t stats_nlist = 0;
960 size_t stats_ncode = 0;
961 uint64_t init_query_cycles = 0;
962 uint64_t scan_cycles = 0;
963 uint64_t heap_cycles = 0;
966 for (
size_t i = 0; i < nx; i++) {
967 const float *qi = qx + i *
d;
968 const long * keysi = keys + i *
nprobe;
969 const float *coarse_dis_i = coarse_dis + i *
nprobe;
970 float * heap_sim = res->
get_val (i);
971 long * heap_ids = res->
get_ids (i);
975 maxheap_heapify (k, heap_sim, heap_ids);
980 init_query_cycles += TOC;
984 for (
size_t ik = 0; ik <
nprobe; ik++) {
985 long key = keysi[ik];
990 if (key >= (
long) nlist) {
991 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
994 size_t list_size =
ids[key].size();
998 if (list_size == 0)
continue;
1000 qt.init_list (key, coarse_dis_i[ik],
1001 list_size,
ids[key].data(),
1006 qt.scan_list_polysemous
1007 (k, heap_sim, heap_ids, store_pairs);
1009 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1011 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1017 stats_ncode += nscan;
1019 maxheap_reorder (k, heap_sim, heap_ids);
1022 for (
size_t j = 0; j < k; j++)
1023 heap_sim[j] = -heap_sim[j];
1028 #pragma omp critical
1030 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1031 indexIVFPQ_stats.nlist += stats_nlist;
1032 indexIVFPQ_stats.ncode += stats_ncode;
1034 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1035 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1036 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1037 indexIVFPQ_stats.heap_cycles += heap_cycles;
1041 indexIVFPQ_stats.nq += nx;
1046 float *distances,
idx_t *labels)
const
1048 long * idx =
new long [n *
nprobe];
1049 float * coarse_dis =
new float [n *
nprobe];
1052 quantizer->search (n, x,
nprobe, coarse_dis, idx);
1053 indexIVFPQ_stats.assign_cycles += TOC;
1060 delete [] coarse_dis;
1061 indexIVFPQ_stats.search_cycles += TOC;
1068 for (
size_t key = 0; key <
nlist; key++) {
1076 !
"direct map remove not implemented");
1078 #pragma omp parallel for reduction(+: nremove)
1079 for (
long i = 0; i <
nlist; i++) {
1080 std::vector<idx_t> & idsi =
ids[i];
1081 uint8_t * codesi = codes[i].data();
1083 long l = idsi.size(), j = 0;
1085 if (sel.is_member (idsi[j])) {
1087 idsi [j] = idsi [l];
1088 memmove (codesi + j * code_size,
1089 codesi + l * code_size, code_size);
1094 if (l < idsi.size()) {
1095 nremove += idsi.size() - l;
1097 codes[i].resize (l * code_size);
1105 IndexIVFPQ::IndexIVFPQ ()
1120 bool operator () (
int a,
int b)
const {
1121 return cmp (a, b) > 0;
1123 int cmp (
int a,
int b)
const {
1124 return memcmp (tab + a * code_size, tab + b * code_size,
1134 for (
size_t list_no = 0; list_no <
nlist; list_no++) {
1135 size_t n =
ids[list_no].size();
1136 std::vector<int> ord (n);
1137 for (
int i = 0; i < n; i++) ord[i] = i;
1138 CodeCmp cs = { codes[list_no].data(), code_size };
1139 std::sort (ord.begin(), ord.end(), cs);
1141 const idx_t *list_ids =
ids[list_no].data();
1143 for (
int i = 0; i < n; i++) {
1144 if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) {
1146 if (prev + 1 == i) {
1148 lims[ngroup] = lims[ngroup - 1];
1149 dup_ids [lims [ngroup]++] = list_ids [ord [prev]];
1151 dup_ids [lims [ngroup]++] = list_ids [ord [i]];
1167 IndexIVFPQR::IndexIVFPQR (
1168 Index * quantizer,
size_t d,
size_t nlist,
1169 size_t M,
size_t nbits_per_idx,
1170 size_t M_refine,
size_t nbits_per_idx_refine):
1171 IndexIVFPQ (quantizer, d, nlist, M, nbits_per_idx),
1172 refine_pq (d, M_refine, nbits_per_idx_refine),
1179 IndexIVFPQR::IndexIVFPQR ():
1186 void IndexIVFPQR::set_typename()
1188 std::stringstream s;
1191 <<
"[" << nlist <<
":" << quantizer->index_typename <<
"]";
1192 index_typename = s.str();
1209 float * residual_2 =
new float [n *
d];
1214 printf (
"training %zdx%zd 2nd level PQ quantizer on %ld %dD-vectors\n",
1221 delete [] residual_2;
1231 const long *precomputed_idx) {
1233 float * residual_2 =
new float [n *
d];
1237 add_core_o (n, x, xids, residual_2, precomputed_idx);
1244 delete [] residual_2;
1251 float *distances,
idx_t *labels)
const
1254 long * idx =
new long [n *
nprobe];
1255 float * L1_dis =
new float [n *
nprobe];
1258 quantizer->search (n, x,
nprobe, L1_dis, idx);
1259 indexIVFPQ_stats.assign_cycles += TOC;
1262 size_t k_coarse = long(k *
k_factor);
1263 idx_t *coarse_labels =
new idx_t [k_coarse * n];
1265 float *coarse_distances =
new float [k_coarse * n];
1268 size_t(n), k_coarse, coarse_labels, coarse_distances};
1270 delete [] coarse_distances;
1273 indexIVFPQ_stats.search_cycles += TOC;
1278 size_t n_refine = 0;
1279 #pragma omp parallel reduction(+ : n_refine)
1282 float *residual_1 =
new float [2 *
d];
1283 float *residual_2 = residual_1 +
d;
1285 for (
idx_t i = 0; i < n; i++) {
1286 const float *xq = x + i *
d;
1287 const long * shortlist = coarse_labels + k_coarse * i;
1288 float * heap_sim = distances + k * i;
1289 long * heap_ids = labels + k * i;
1290 maxheap_heapify (k, heap_sim, heap_ids);
1292 for (
int j = 0; j < k_coarse; j++) {
1293 long sl = shortlist[j];
1295 if (sl == -1)
continue;
1297 int list_no = sl >> 32;
1298 int ofs = sl & 0xffffffff;
1300 assert (list_no >= 0 && list_no < nlist);
1301 assert (ofs >= 0 && ofs <
ids[list_no].size());
1304 quantizer->compute_residual (xq, residual_1, list_no);
1307 const uint8_t * l2code = &codes[list_no][ofs *
pq.
code_size];
1309 for (
int l = 0; l <
d; l++)
1310 residual_2[l] = residual_1[l] - residual_2[l];
1314 assert (0 <=
id &&
id <
ntotal);
1318 float dis =
fvec_L2sqr (residual_1, residual_2, d);
1320 if (dis < heap_sim[0]) {
1321 maxheap_pop (k, heap_sim, heap_ids);
1322 maxheap_push (k, heap_sim, heap_ids, dis,
id);
1326 maxheap_reorder (k, heap_sim, heap_ids);
1328 delete [] residual_1;
1330 delete [] coarse_labels;
1332 indexIVFPQ_stats.nrefine += n_refine;
1333 indexIVFPQ_stats.refine_cycles += TOC;
1338 float *r3 =
new float [
d];
1342 for (
idx_t i = i0; i < i0 + ni; i++) {
1343 float *r = recons + i *
d;
1346 for (
int j = 0; j <
d; j++)
1364 FAISS_ASSERT(!
"not implemented");
1371 IndexIVFPQCompact::IndexIVFPQCompact ()
1380 IndexIVFPQCompact::IndexIVFPQCompact (
const IndexIVFPQ &other)
1382 FAISS_ASSERT (other.ntotal < (1UL << 31) ||
1383 !
"IndexIVFPQCompact cannot store more than 2G images");
1397 nlist = other.nlist;
1399 quantizer = other.quantizer;
1402 direct_map = other.direct_map;
1408 code_size = other.code_size;
1419 limits =
new uint32_t [nlist + 1];
1426 for (
size_t i = 0; i <
nlist; i++) {
1428 const std::vector<long> &other_ids = other.ids[i];
1429 for (
size_t j = 0; j < other_ids.size(); j++) {
1430 long id = other_ids[j];
1431 FAISS_ASSERT (
id < (1UL << 31) ||
1432 !
"IndexIVFPQCompact cannot store ids > 2G");
1436 other.codes[i].data(),
1437 other.codes[i].size());
1438 ofs += other_ids.size();
1440 FAISS_ASSERT (ofs ==
ntotal);
1446 FAISS_ASSERT (!
"cannot add to an IndexIVFPQCompact");
1450 FAISS_ASSERT (!
"cannot reset an IndexIVFPQCompact");
1454 FAISS_ASSERT (!
"cannot train an IndexIVFPQCompact");
1460 IndexIVFPQCompact::~IndexIVFPQCompact ()
1467 munmap (mmap_buffer, mmap_length);
1477 const float * coarse_dis,
1479 bool store_pairs)
const
1481 const size_t k = res->
k;
1483 #pragma omp parallel
1485 InvertedListScanner<uint32_t> qt (*
this);
1486 size_t stats_nlist = 0;
1487 size_t stats_ncode = 0;
1488 uint64_t init_query_cycles = 0;
1489 uint64_t scan_cycles = 0;
1490 uint64_t heap_cycles = 0;
1493 for (
size_t i = 0; i < nx; i++) {
1494 const float *qi = qx + i *
d;
1495 const long * keysi = keys + i *
nprobe;
1496 const float *coarse_dis_i = coarse_dis + i *
nprobe;
1497 float * heap_sim = res->
get_val (i);
1498 long * heap_ids = res->
get_ids (i);
1502 maxheap_heapify (k, heap_sim, heap_ids);
1507 init_query_cycles += TOC;
1511 for (
size_t ik = 0; ik <
nprobe; ik++) {
1512 long key = keysi[ik];
1517 if (key >= (
long) nlist) {
1518 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
1525 if (list_size == 0)
continue;
1527 qt.init_list (key, coarse_dis_i[ik],
1533 qt.scan_list_polysemous
1534 (k, heap_sim, heap_ids, store_pairs);
1536 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1538 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1544 stats_ncode += nscan;
1546 maxheap_reorder (k, heap_sim, heap_ids);
1549 for (
size_t j = 0; j < k; j++) {
1550 heap_sim[i] = -heap_sim[i];
1556 #pragma omp critical
1558 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1559 indexIVFPQ_stats.nlist += stats_nlist;
1560 indexIVFPQ_stats.ncode += stats_ncode;
1562 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1563 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1564 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1565 indexIVFPQ_stats.heap_cycles += heap_cycles;
1569 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
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 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
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
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
void encode_multiple(size_t n, const long *keys, const float *x, uint8_t *codes) const
same as encode, for multiple points at once
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