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;
58 polysemous_training =
nullptr;
59 do_polysemous_training =
false;
73 const float * x_in = x;
81 const float *trainset;
84 if(
verbose) printf(
"computing residuals\n");
88 float *residuals =
new float [n *
d];
89 del_residuals.set (residuals);
90 for (
idx_t i = 0; i < n; i++)
98 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
101 pq.train (n, trainset);
105 printf(
"doing polysemous training for PQ\n");
108 if (!pt) pt = &default_pt;
114 uint8_t *train_codes =
new uint8_t [
pq.
code_size * n];
118 for (
idx_t i = 0; i < n; i++) {
119 const float *xx = trainset + i *
d;
120 float * res = residuals_2 + i *
d;
122 for (
int j = 0; j <
d; j++)
123 res[j] = xx[j] - res[j];
136 void IndexIVFPQ::encode (
long key,
const float * x, uint8_t * code)
const
139 float residual_vec[
d];
151 const float * x, uint8_t * xcodes,
152 bool compute_keys)
const
158 float *residuals =
new float [n *
d];
161 for (
size_t i = 0; i < n; i++)
170 const uint8_t * xcodes,
float * x)
const
174 std::vector<float> centroid (
d);
175 for (
size_t i = 0; i < n; i++) {
177 float *xi = x + i *
d;
178 for (
size_t j = 0; j <
d; j++) {
179 xi [j] += centroid [j];
193 float *residuals_2,
const long *precomputed_idx)
200 if (precomputed_idx) {
201 idx = precomputed_idx;
203 long * idx0 =
new long [n];
210 uint8_t * xcodes =
new uint8_t [n *
code_size];
213 const float *to_encode =
nullptr;
217 float *residuals =
new float [n *
d];
219 for (
size_t i = 0; i < n; i++) {
221 memset (residuals + i *
d, 0,
sizeof(*residuals) * d);
224 x + i * d, residuals + i * d, idx[i]);
226 to_encode = residuals;
227 del_to_encode.set (to_encode);
236 for (
size_t i = 0; i < n; i++) {
241 memset (residuals_2, 0,
sizeof(*residuals_2) *
d);
245 ids[key].push_back (
id);
248 codes[key].push_back (code[j]);
251 float *res2 = residuals_2 + i *
d;
252 const float *xi = to_encode + i *
d;
254 for (
int j = 0; j <
d; j++)
255 res2[j] = xi[j] - res2[j];
259 direct_map.push_back (key << 32 | (
ids[key].size() - 1));
265 char comment[100] = {0};
267 snprintf (comment, 100,
"(%ld vectors ignored)", n_ignore);
268 printf(
" add_core times: %.3f %.3f %.3f %s\n",
269 t1 - t0, t2 - t1, t3 - t2, comment);
276 FAISS_THROW_IF_NOT (ni == 0 || (i0 >= 0 && i0 + ni <=
ntotal));
278 std::vector<float> centroid (
d);
280 for (
int key = 0; key <
nlist; key++) {
281 const std::vector<long> & idlist =
ids[key];
282 const uint8_t * code_line = codes[key].data();
284 for (
long ofs = 0; ofs < idlist.size(); ofs++) {
285 long id = idlist[ofs];
286 if (!(
id >= i0 &&
id < i0 + ni))
continue;
287 float *r = recons +
d * (
id - i0);
291 for (
int j = 0; j <
d; j++) {
304 FAISS_THROW_IF_NOT (direct_map.size() ==
ntotal);
306 int list_no = direct_map[key] >> 32;
307 int ofs = direct_map[key] & 0xffffffff;
310 const uint8_t * code = &(codes[list_no][ofs *
pq.
code_size]);
312 for (
size_t m = 0; m <
pq.
M; m++) {
313 float * out = recons + m *
pq.
dsub;
315 for (
size_t i = 0; i <
pq.
dsub; i++) {
360 fprintf(stderr,
"IndexIVFPQ::precompute_table: WARN precomputed "
361 "tables not needed for inner product quantizers\n");
366 if (miq &&
pq.
M % miq->pq.
M == 0)
373 printf (
"precomputing IVFPQ tables type %d\n",
378 std::vector<float> r_norms (
pq.
M *
pq.
ksub, NAN);
379 for (
int m = 0; m <
pq.
M; m++)
380 for (
int j = 0; j <
pq.
ksub; j++)
381 r_norms [m *
pq.
ksub + j] =
387 std::vector<float> centroid (
d);
389 for (
size_t i = 0; i <
nlist; i++) {
393 pq.compute_inner_prod_table (centroid.data(), tab);
399 FAISS_THROW_IF_NOT (miq);
401 FAISS_THROW_IF_NOT (
pq.
M % cpq.
M == 0);
406 std::vector<float> centroids (
d * cpq.
ksub, NAN);
408 for (
int m = 0; m < cpq.
M; m++) {
409 for (
size_t i = 0; i < cpq.
ksub; i++) {
410 memcpy (centroids.data() + i *
d + m * cpq.
dsub,
412 sizeof (*centroids.data()) * cpq.
dsub);
416 pq.compute_inner_prod_tables (cpq.
ksub, centroids.data (),
419 for (
size_t i = 0; i < cpq.
ksub; i++) {
429 static uint64_t get_cycles () {
431 asm volatile(
"rdtsc \n\t"
434 return ((uint64_t)high << 32) | (low);
437 #define TIC t0 = get_cycles()
438 #define TOC get_cycles () - t0
455 const IndexIVFPQ & ivfpq;
459 const ProductQuantizer & pq;
462 int use_precomputed_table;
465 float * sim_table, * sim_table_2;
466 float * residual_vec, *decoded_vec;
469 std::vector<float> mem;
472 std::vector<const float *> sim_table_ptrs;
474 explicit QueryTables (
const IndexIVFPQ & ivfpq):
478 metric_type (ivfpq.metric_type),
479 by_residual (ivfpq.by_residual),
480 use_precomputed_table (ivfpq.use_precomputed_table)
482 mem.resize (pq.ksub * pq.M * 2 + d *2);
483 sim_table = mem.data();
484 sim_table_2 = sim_table + pq.ksub * pq.M;
485 residual_vec = sim_table_2 + pq.ksub * pq.M;
486 decoded_vec = residual_vec + d;
489 if (ivfpq.polysemous_ht != 0) {
490 q_code.resize (pq.code_size);
492 init_list_cycles = 0;
493 sim_table_ptrs.resize (pq.M);
504 void init_query (
const float * qi) {
506 if (metric_type == METRIC_INNER_PRODUCT)
510 if (!by_residual && ivfpq.polysemous_ht != 0)
511 pq.compute_code (qi, q_code.data());
514 void init_query_IP () {
516 pq.compute_inner_prod_table (qi, sim_table);
518 for (
int i = 0; i < pq.ksub * pq.M; i++) {
519 sim_table[i] = - sim_table[i];
523 void init_query_L2 () {
525 pq.compute_distance_table (qi, sim_table);
526 }
else if (use_precomputed_table) {
527 pq.compute_inner_prod_table (qi, sim_table_2);
538 std::vector<uint8_t> q_code;
540 uint64_t init_list_cycles;
545 float precompute_list_tables () {
549 if (metric_type == METRIC_INNER_PRODUCT)
550 dis0 = precompute_list_tables_IP ();
552 dis0 = precompute_list_tables_L2 ();
554 init_list_cycles += TOC;
558 float precompute_list_table_pointers () {
562 if (metric_type == METRIC_INNER_PRODUCT)
563 FAISS_THROW_MSG (
"not implemented");
565 dis0 = precompute_list_table_pointers_L2 ();
567 init_list_cycles += TOC;
575 float precompute_list_tables_IP ()
579 ivfpq.quantizer->reconstruct (key, decoded_vec);
581 float dis0 = -fvec_inner_product (qi, decoded_vec, d);
583 if (ivfpq.polysemous_ht) {
584 for (
int i = 0; i < d; i++) {
585 residual_vec [i] = qi[i] - decoded_vec[i];
587 pq.compute_code (residual_vec, q_code.data());
597 float precompute_list_tables_L2 ()
601 if (use_precomputed_table == 0) {
602 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
603 pq.compute_distance_table (residual_vec, sim_table);
604 }
else if (use_precomputed_table == 1) {
608 &ivfpq.precomputed_table [key * pq.ksub * pq.M],
611 }
else if (use_precomputed_table == 2) {
614 const MultiIndexQuantizer *miq =
615 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
616 FAISS_THROW_IF_NOT (miq);
617 const ProductQuantizer &cpq = miq->pq;
618 int Mf = pq.M / cpq.M;
620 const float *qtab = sim_table_2;
621 float *ltab = sim_table;
624 for (
int cm = 0; cm < cpq.M; cm++) {
626 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
630 const float *pc = &ivfpq.precomputed_table
631 [(ki * pq.M + cm * Mf) * pq.ksub];
633 if (ivfpq.polysemous_ht == 0) {
640 ltab += Mf * pq.ksub;
641 qtab += Mf * pq.ksub;
643 for (
int m = cm * Mf; m < (cm + 1) * Mf; m++) {
645 (pq.ksub, pc, -2, qtab, ltab);
658 float precompute_list_table_pointers_L2 ()
662 if (use_precomputed_table == 1) {
665 const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M];
666 for (
int m = 0; m < pq.M; m++) {
667 sim_table_ptrs [m] = s;
670 }
else if (use_precomputed_table == 2) {
673 const MultiIndexQuantizer *miq =
674 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
675 FAISS_THROW_IF_NOT (miq);
676 const ProductQuantizer &cpq = miq->pq;
677 int Mf = pq.M / cpq.M;
681 for (
int cm = 0; cm < cpq.M; cm++) {
682 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
685 const float *pc = &ivfpq.precomputed_table
686 [(ki * pq.M + cm * Mf) * pq.ksub];
688 for (
int m = m0; m < m0 + Mf; m++) {
689 sim_table_ptrs [m] = pc;
695 FAISS_THROW_MSG (
"need precomputed tables");
698 if (ivfpq.polysemous_ht) {
699 FAISS_THROW_MSG (
"not implemented");
717 template <
typename IDType>
718 struct InvertedListScanner: QueryTables {
720 const uint8_t * __restrict list_codes;
721 const IDType * list_ids;
724 explicit InvertedListScanner (
const IndexIVFPQ & ivfpq):
727 FAISS_THROW_IF_NOT (pq.byte_per_idx == 1);
733 size_t list_size_in,
const IDType *list_ids_in,
734 const uint8_t *list_codes_in) {
736 this->coarse_dis = coarse_dis;
737 list_size = list_size_in;
738 list_codes = list_codes_in;
739 list_ids = list_ids_in;
747 void scan_list_with_table (
748 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
750 float dis0 = precompute_list_tables ();
752 for (
size_t j = 0; j < list_size; j++) {
755 const float *tab = sim_table;
757 for (
size_t m = 0; m < pq.M; m++) {
758 dis += tab[*list_codes++];
762 if (dis < heap_sim[0]) {
763 maxheap_pop (k, heap_sim, heap_ids);
764 long id = store_pairs ? (key << 32 | j) : list_ids[j];
765 maxheap_push (k, heap_sim, heap_ids, dis,
id);
773 void scan_list_with_pointer (
774 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
777 float dis0 = precompute_list_table_pointers ();
779 for (
size_t j = 0; j < list_size; j++) {
782 const float *tab = sim_table_2;
784 for (
size_t m = 0; m < pq.M; m++) {
785 int ci = *list_codes++;
786 dis += sim_table_ptrs [m][ci] - 2 * tab [ci];
790 if (dis < heap_sim[0]) {
791 maxheap_pop (k, heap_sim, heap_ids);
792 long id = store_pairs ? (key << 32 | j) : list_ids[j];
793 maxheap_push (k, heap_sim, heap_ids, dis,
id);
800 void scan_on_the_fly_dist (
801 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
804 if (by_residual && use_precomputed_table) {
805 scan_list_with_pointer (k, heap_sim, heap_ids, store_pairs);
813 if (metric_type == METRIC_INNER_PRODUCT) {
814 ivfpq.quantizer->reconstruct (key, residual_vec);
815 dis0 = fvec_inner_product (residual_vec, qi, d);
817 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
825 for (
size_t j = 0; j < list_size; j++) {
827 pq.decode (list_codes, decoded_vec);
828 list_codes += pq.code_size;
831 if (metric_type == METRIC_INNER_PRODUCT) {
832 dis = -dis0 - fvec_inner_product (decoded_vec, qi, d);
837 if (dis < heap_sim[0]) {
838 maxheap_pop (k, heap_sim, heap_ids);
839 long id = store_pairs ? (key << 32 | j) : list_ids[j];
840 maxheap_push (k, heap_sim, heap_ids, dis,
id);
850 size_t n_hamming_pass;
853 template <
class HammingComputer>
854 void scan_list_polysemous_hc (
855 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
857 float dis0 = precompute_list_tables ();
858 int ht = ivfpq.polysemous_ht;
860 int code_size = pq.code_size;
862 HammingComputer hc (q_code.data(), code_size);
864 for (
size_t j = 0; j < list_size; j++) {
865 const uint8_t *b_code = list_codes;
866 int hd = hc.hamming (b_code);
871 const float *tab = sim_table;
873 for (
size_t m = 0; m < pq.M; m++) {
874 dis += tab[*b_code++];
878 if (dis < heap_sim[0]) {
879 maxheap_pop (k, heap_sim, heap_ids);
880 long id = store_pairs ? (key << 32 | j) : list_ids[j];
881 maxheap_push (k, heap_sim, heap_ids, dis,
id);
884 list_codes += code_size;
888 void scan_list_polysemous (
889 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
891 switch (pq.code_size) {
892 #define HANDLE_CODE_SIZE(cs) \
894 scan_list_polysemous_hc <HammingComputer ## cs> \
895 (k, heap_sim, heap_ids, store_pairs); \
899 HANDLE_CODE_SIZE(16);
900 HANDLE_CODE_SIZE(20);
901 HANDLE_CODE_SIZE(32);
902 HANDLE_CODE_SIZE(64);
903 #undef HANDLE_CODE_SIZE
905 if (pq.code_size % 8 == 0)
906 scan_list_polysemous_hc <HammingComputerM8>
907 (k, heap_sim, heap_ids, store_pairs);
909 scan_list_polysemous_hc <HammingComputerM4>
910 (k, heap_sim, heap_ids, store_pairs);
923 IndexIVFPQStats indexIVFPQ_stats;
925 void IndexIVFPQStats::reset () {
926 memset (
this, 0,
sizeof (*
this));
933 const float *coarse_dis,
934 float *distances,
idx_t *labels,
935 bool store_pairs)
const
938 size_t(nx), size_t(k),
944 InvertedListScanner<long> qt (*
this);
945 size_t stats_nlist = 0;
946 size_t stats_ncode = 0;
947 uint64_t init_query_cycles = 0;
948 uint64_t scan_cycles = 0;
949 uint64_t heap_cycles = 0;
952 for (
size_t i = 0; i < nx; i++) {
953 const float *qi = qx + i *
d;
954 const long * keysi = keys + i *
nprobe;
955 const float *coarse_dis_i = coarse_dis + i *
nprobe;
956 float * heap_sim = res.
get_val (i);
957 long * heap_ids = res.
get_ids (i);
961 maxheap_heapify (k, heap_sim, heap_ids);
966 init_query_cycles += TOC;
970 for (
size_t ik = 0; ik <
nprobe; ik++) {
971 long key = keysi[ik];
976 FAISS_THROW_IF_NOT_FMT (
978 "Invalid key=%ld at ik=%ld nlist=%ld\n",
981 size_t list_size =
ids[key].size();
985 if (list_size == 0)
continue;
987 qt.init_list (key, coarse_dis_i[ik],
988 list_size,
ids[key].data(),
993 qt.scan_list_polysemous
994 (k, heap_sim, heap_ids, store_pairs);
996 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
998 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1004 stats_ncode += nscan;
1006 maxheap_reorder (k, heap_sim, heap_ids);
1009 for (
size_t j = 0; j < k; j++)
1010 heap_sim[j] = -heap_sim[j];
1015 #pragma omp critical
1017 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1018 indexIVFPQ_stats.nlist += stats_nlist;
1019 indexIVFPQ_stats.ncode += stats_ncode;
1021 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1022 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1023 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1024 indexIVFPQ_stats.heap_cycles += heap_cycles;
1028 indexIVFPQ_stats.nq += nx;
1033 float *distances,
idx_t *labels,
1034 float *reconstructed)
1036 long * idx =
new long [n *
nprobe];
1038 float * coarse_dis =
new float [n *
nprobe];
1044 distances, labels,
true);
1046 for (
long i = 0; i < n; i++) {
1047 for (
long j = 0; j < k; j++) {
1048 long ij = i * k + j;
1049 idx_t res = labels[ij];
1050 float *recons = reconstructed + d * (ij);
1053 memset(recons, -1,
sizeof(*recons) * d);
1055 int list_no = res >> 32;
1056 int ofs = res & 0xffffffff;
1057 labels[ij] =
ids[list_no][ofs];
1060 const uint8_t * code = &(codes[list_no][ofs *
pq.
code_size]);
1062 for (
size_t m = 0; m <
pq.
M; m++) {
1063 float * out = recons + m *
pq.
dsub;
1065 for (
size_t l = 0; l <
pq.
dsub; l++) {
1079 IndexIVFPQ::IndexIVFPQ ()
1094 bool operator () (
int a,
int b)
const {
1095 return cmp (a, b) > 0;
1097 int cmp (
int a,
int b)
const {
1098 return memcmp (tab + a * code_size, tab + b * code_size,
1108 for (
size_t list_no = 0; list_no <
nlist; list_no++) {
1109 size_t n =
ids[list_no].size();
1110 std::vector<int> ord (n);
1111 for (
int i = 0; i < n; i++) ord[i] = i;
1112 CodeCmp cs = { codes[list_no].data(), code_size };
1113 std::sort (ord.begin(), ord.end(), cs);
1115 const idx_t *list_ids =
ids[list_no].data();
1117 for (
int i = 0; i < n; i++) {
1118 if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) {
1120 if (prev + 1 == i) {
1122 lims[ngroup] = lims[ngroup - 1];
1123 dup_ids [lims [ngroup]++] = list_ids [ord [prev]];
1125 dup_ids [lims [ngroup]++] = list_ids [ord [i]];
1141 IndexIVFPQR::IndexIVFPQR (
1142 Index * quantizer,
size_t d,
size_t nlist,
1143 size_t M,
size_t nbits_per_idx,
1144 size_t M_refine,
size_t nbits_per_idx_refine):
1145 IndexIVFPQ (quantizer, d, nlist, M, nbits_per_idx),
1146 refine_pq (d, M_refine, nbits_per_idx_refine),
1152 IndexIVFPQR::IndexIVFPQR ():
1172 float * residual_2 =
new float [n *
d];
1178 printf (
"training %zdx%zd 2nd level PQ quantizer on %ld %dD-vectors\n",
1194 const long *precomputed_idx) {
1196 float * residual_2 =
new float [n *
d];
1201 add_core_o (n, x, xids, residual_2, precomputed_idx);
1214 float *distances,
idx_t *labels)
const
1217 long * idx =
new long [n *
nprobe];
1219 float * L1_dis =
new float [n *
nprobe];
1224 indexIVFPQ_stats.assign_cycles += TOC;
1227 size_t k_coarse = long(k *
k_factor);
1228 idx_t *coarse_labels =
new idx_t [k_coarse * n];
1231 float *coarse_distances =
new float [k_coarse * n];
1235 idx, L1_dis, coarse_distances, coarse_labels,
1240 indexIVFPQ_stats.search_cycles += TOC;
1245 size_t n_refine = 0;
1246 #pragma omp parallel reduction(+ : n_refine)
1249 float *residual_1 =
new float [2 *
d];
1251 float *residual_2 = residual_1 +
d;
1253 for (
idx_t i = 0; i < n; i++) {
1254 const float *xq = x + i *
d;
1255 const long * shortlist = coarse_labels + k_coarse * i;
1256 float * heap_sim = distances + k * i;
1257 long * heap_ids = labels + k * i;
1258 maxheap_heapify (k, heap_sim, heap_ids);
1260 for (
int j = 0; j < k_coarse; j++) {
1261 long sl = shortlist[j];
1263 if (sl == -1)
continue;
1265 int list_no = sl >> 32;
1266 int ofs = sl & 0xffffffff;
1268 assert (list_no >= 0 && list_no <
nlist);
1269 assert (ofs >= 0 && ofs <
ids[list_no].size());
1275 const uint8_t * l2code = &codes[list_no][ofs *
pq.
code_size];
1277 for (
int l = 0; l <
d; l++)
1278 residual_2[l] = residual_1[l] - residual_2[l];
1282 assert (0 <=
id &&
id <
ntotal);
1286 float dis =
fvec_L2sqr (residual_1, residual_2, d);
1288 if (dis < heap_sim[0]) {
1289 maxheap_pop (k, heap_sim, heap_ids);
1290 maxheap_push (k, heap_sim, heap_ids, dis,
id);
1294 maxheap_reorder (k, heap_sim, heap_ids);
1297 indexIVFPQ_stats.nrefine += n_refine;
1298 indexIVFPQ_stats.refine_cycles += TOC;
1303 std::vector<float> r3 (d);
1307 for (
idx_t i = i0; i < i0 + ni; i++) {
1308 float *r = recons + i *
d;
1311 for (
int j = 0; j <
d; j++)
1323 FAISS_THROW_IF_NOT(other);
1334 FAISS_THROW_MSG(
"not implemented");
1342 IndexIVFPQCompact::IndexIVFPQCompact ()
1351 IndexIVFPQCompact::IndexIVFPQCompact (
const IndexIVFPQ &other)
1353 FAISS_THROW_IF_NOT_MSG (other.ntotal < (1UL << 31),
1354 "IndexIVFPQCompact cannot store more than 2G images");
1368 nlist = other.nlist;
1373 direct_map = other.direct_map;
1379 code_size = other.code_size;
1397 for (
size_t i = 0; i <
nlist; i++) {
1399 const std::vector<long> &other_ids = other.ids[i];
1400 for (
size_t j = 0; j < other_ids.size(); j++) {
1401 long id = other_ids[j];
1402 FAISS_THROW_IF_NOT_MSG (
id < (1UL << 31),
1403 "IndexIVFPQCompact cannot store ids > 2G");
1407 other.codes[i].data(),
1408 other.codes[i].size());
1409 ofs += other_ids.size();
1411 FAISS_THROW_IF_NOT (ofs ==
ntotal);
1417 FAISS_THROW_MSG (
"cannot add to an IndexIVFPQCompact");
1421 FAISS_THROW_MSG (
"cannot reset an IndexIVFPQCompact");
1425 FAISS_THROW_MSG (
"cannot train an IndexIVFPQCompact");
1431 IndexIVFPQCompact::~IndexIVFPQCompact ()
1438 munmap (mmap_buffer, mmap_length);
1448 const float *coarse_dis,
1449 float *distances,
idx_t *labels,
1450 bool store_pairs)
const
1453 size_t(nx), size_t(k),
1457 #pragma omp parallel
1459 InvertedListScanner<uint32_t> qt (*
this);
1460 size_t stats_nlist = 0;
1461 size_t stats_ncode = 0;
1462 uint64_t init_query_cycles = 0;
1463 uint64_t scan_cycles = 0;
1464 uint64_t heap_cycles = 0;
1467 for (
size_t i = 0; i < nx; i++) {
1468 const float *qi = qx + i *
d;
1469 const long * keysi = keys + i *
nprobe;
1470 const float *coarse_dis_i = coarse_dis + i *
nprobe;
1471 float * heap_sim = res.
get_val (i);
1472 long * heap_ids = res.
get_ids (i);
1476 maxheap_heapify (k, heap_sim, heap_ids);
1481 init_query_cycles += TOC;
1485 for (
size_t ik = 0; ik <
nprobe; ik++) {
1486 long key = keysi[ik];
1491 if (key >= (
long) nlist) {
1492 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
1499 if (list_size == 0)
continue;
1501 qt.init_list (key, coarse_dis_i[ik],
1507 qt.scan_list_polysemous
1508 (k, heap_sim, heap_ids, store_pairs);
1510 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1512 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1518 stats_ncode += nscan;
1520 maxheap_reorder (k, heap_sim, heap_ids);
1523 for (
size_t j = 0; j < k; j++) {
1524 heap_sim[i] = -heap_sim[i];
1530 #pragma omp critical
1532 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1533 indexIVFPQ_stats.nlist += stats_nlist;
1534 indexIVFPQ_stats.ncode += stats_ncode;
1536 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1537 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1538 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1539 indexIVFPQ_stats.heap_cycles += heap_cycles;
1543 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 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 reconstruct_n(idx_t i0, idx_t ni, 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
char quantizer_trains_alone
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)
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
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.
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
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
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
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const =0
float fvec_norm_L2sqr(const float *x, size_t d)
ClusteringParameters cp
parameters used during clustering
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
void search_and_reconstruct(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels, float *reconstructed)
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)
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
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
virtual void reconstruct(idx_t key, float *recons) const
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
virtual void merge_from(IndexIVF &other, idx_t add_id)
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