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();
85 const float * x_in = x;
91 const float *trainset;
93 if(
verbose) printf(
"computing residuals\n");
95 quantizer->assign (n, x, assign);
96 float *residuals =
new float [n *
d];
97 for (
idx_t i = 0; i < n; i++)
98 quantizer->compute_residual (x + i * d, residuals+i*d, assign[i]);
100 trainset = residuals;
105 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
108 pq.train (n, trainset);
113 if (!pt) pt = &default_pt;
119 uint8_t *train_codes =
new uint8_t [
pq.
code_size * n];
122 for (
idx_t i = 0; i < n; i++) {
123 const float *xx = trainset + i *
d;
124 float * res = residuals_2 + i *
d;
126 for (
int j = 0; j <
d; j++)
127 res[j] = xx[j] - res[j];
130 delete [] train_codes;
138 if (x_in != x)
delete [] x;
143 void IndexIVFPQ::encode (
long key,
const float * x, uint8_t * code)
const
146 float residual_vec[
d];
147 quantizer->compute_residual (x, residual_vec, key);
158 const float * x, uint8_t * xcodes)
const
161 float *residuals =
new float [n *
d];
163 for (
size_t i = 0; i < n; i++)
164 quantizer->compute_residual (x + i * d, residuals + i * d, keys[i]);
181 float *residuals_2,
const long *precomputed_idx)
187 if (precomputed_idx) {
188 idx = precomputed_idx;
190 long * idx0 =
new long [n];
191 quantizer->assign (n, x, idx0);
196 uint8_t * xcodes =
new uint8_t [n *
code_size];
198 const float *to_encode =
nullptr;
201 float *residuals =
new float [n *
d];
203 for (
size_t i = 0; i < n; i++) {
205 memset (residuals + i * d, 0,
sizeof(*residuals) * d);
207 quantizer->compute_residual (
208 x + i * d, residuals + i * d, idx[i]);
210 to_encode = residuals;
219 for (
size_t i = 0; i < n; i++) {
224 memset (residuals_2, 0,
sizeof(*residuals_2) * d);
228 ids[key].push_back (
id);
231 codes[key].push_back (code[j]);
234 float *res2 = residuals_2 + i *
d;
235 const float *xi = to_encode + i *
d;
237 for (
int j = 0; j <
d; j++)
238 res2[j] = xi[j] - res2[j];
242 direct_map.push_back (key << 32 | (
ids[key].size() - 1));
249 if (!precomputed_idx)
253 char comment[100] = {0};
255 snprintf (comment, 100,
"(%ld vectors ignored)", n_ignore);
256 printf(
" add_core times: %.3f %.3f %.3f %s\n",
257 t1 - t0, t2 - t1, t3 - t2, comment);
264 FAISS_ASSERT (ni == 0 || (i0 >= 0 && i0 + ni <=
ntotal));
266 std::vector<float> centroid (d);
268 for (
int key = 0; key <
nlist; key++) {
269 const std::vector<long> & idlist =
ids[key];
270 const uint8_t * code_line = codes[key].data();
272 for (
long ofs = 0; ofs < idlist.size(); ofs++) {
273 long id = idlist[ofs];
274 if (!(
id >= i0 &&
id < i0 + ni))
continue;
275 float *r = recons + d * (
id - i0);
277 quantizer->reconstruct (key, centroid.data());
279 for (
int j = 0; j <
d; j++) {
293 FAISS_ASSERT (direct_map.size() ==
ntotal);
294 int list_no = direct_map[key] >> 32;
295 int ofs = direct_map[key] & 0xffffffff;
297 quantizer->reconstruct (list_no, recons);
298 const uint8_t * code = &(codes[list_no][ofs *
pq.
code_size]);
300 for (
size_t m = 0; m <
pq.
M; m++) {
301 float * out = recons + m *
pq.
dsub;
303 for (
size_t i = 0; i <
pq.
dsub; i++) {
314 for (
int i = 0; i <
nlist; i++) {
315 codes[i].insert (codes[i].end(),
316 other.codes[i].begin(), other.codes[i].end());
317 other.codes[i].clear();
322 long a1,
long a2)
const
324 FAISS_ASSERT (nlist == other.
nlist);
327 for (
long list_no = 0; list_no <
nlist; list_no++) {
328 const std::vector<idx_t> & ids_in =
ids[list_no];
329 std::vector<idx_t> & ids_out = other.
ids[list_no];
330 const std::vector<uint8_t> & codes_in = codes[list_no];
331 std::vector<uint8_t> & codes_out = other.codes[list_no];
333 for (
long i = 0; i < ids_in.size(); i++) {
334 idx_t id = ids_in[i];
335 if (subset_type == 0 && a1 <=
id &&
id < a2) {
336 ids_out.push_back (
id);
337 codes_out.insert (codes_out.end(),
339 codes_in.begin() + (i + 1) * code_size);
385 if (quantizer->metric_type == METRIC_INNER_PRODUCT) {
386 fprintf(stderr,
"IndexIVFPQ::precompute_table: WARN precomputed "
387 "tables not supported for inner product quantizers\n");
392 if (miq &&
pq.
M % miq->pq.
M == 0)
400 std::vector<float> r_norms (
pq.
M *
pq.
ksub, 0.0/0.0);
401 for (
int m = 0; m <
pq.
M; m++)
402 for (
int j = 0; j <
pq.
ksub; j++)
403 r_norms [m *
pq.
ksub + j] =
409 std::vector<float> centroid (d);
411 for (
size_t i = 0; i <
nlist; i++) {
412 quantizer->reconstruct (i, centroid.data());
415 pq.compute_inner_prod_table (centroid.data(), tab);
423 FAISS_ASSERT (
pq.
M % cpq.
M == 0);
428 std::vector<float> centroids (d * cpq.
ksub, 0.0/0.0);
430 for (
int m = 0; m < cpq.
M; m++) {
431 for (
size_t i = 0; i < cpq.
ksub; i++) {
432 memcpy (centroids.data() + i * d + m * cpq.
dsub,
434 sizeof (*centroids.data()) * cpq.
dsub);
438 pq.compute_inner_prod_tables (cpq.
ksub, centroids.data (),
441 for (
size_t i = 0; i < cpq.
ksub; i++) {
451 static uint64_t get_cycles () {
453 asm volatile(
"rdtsc \n\t"
456 return ((uint64_t)high << 32) | (low);
459 #define TIC t0 = get_cycles()
460 #define TOC get_cycles () - t0
477 const IndexIVFPQ & ivfpq;
481 const ProductQuantizer & pq;
484 int use_precomputed_table;
487 float * sim_table, * sim_table_2;
488 float * residual_vec, *decoded_vec;
491 std::vector<float> mem;
494 std::vector<const float *> sim_table_ptrs;
496 explicit QueryTables (
const IndexIVFPQ & ivfpq):
500 metric_type (ivfpq.metric_type),
501 by_residual (ivfpq.by_residual),
502 use_precomputed_table (ivfpq.use_precomputed_table)
504 mem.resize (pq.ksub * pq.M * 2 + d *2);
505 sim_table = mem.data();
506 sim_table_2 = sim_table + pq.ksub * pq.M;
507 residual_vec = sim_table_2 + pq.ksub * pq.M;
508 decoded_vec = residual_vec + d;
511 if (ivfpq.polysemous_ht != 0) {
512 q_code.resize (pq.code_size);
514 init_list_cycles = 0;
515 sim_table_ptrs.resize (pq.M);
526 void init_query (
const float * qi) {
528 if (metric_type == METRIC_INNER_PRODUCT)
532 if (!by_residual && ivfpq.polysemous_ht != 0)
533 pq.compute_code (qi, q_code.data());
536 void init_query_IP () {
538 pq.compute_inner_prod_table (qi, sim_table);
540 for (
int i = 0; i < pq.ksub * pq.M; i++) {
541 sim_table[i] = - sim_table[i];
545 void init_query_L2 () {
547 pq.compute_distance_table (qi, sim_table);
548 }
else if (use_precomputed_table) {
549 pq.compute_inner_prod_table (qi, sim_table_2);
560 std::vector<uint8_t> q_code;
562 uint64_t init_list_cycles;
567 float precompute_list_tables () {
571 if (metric_type == METRIC_INNER_PRODUCT)
572 dis0 = precompute_list_tables_IP ();
574 dis0 = precompute_list_tables_L2 ();
576 init_list_cycles += TOC;
580 float precompute_list_table_pointers () {
584 if (metric_type == METRIC_INNER_PRODUCT)
585 FAISS_ASSERT (!
"not implemented");
587 dis0 = precompute_list_table_pointers_L2 ();
589 init_list_cycles += TOC;
597 float precompute_list_tables_IP ()
601 ivfpq.quantizer->reconstruct (key, decoded_vec);
603 float dis0 = -fvec_inner_product (qi, decoded_vec, d);
605 if (ivfpq.polysemous_ht) {
606 for (
int i = 0; i < d; i++) {
607 residual_vec [i] = qi[i] - decoded_vec[i];
609 pq.compute_code (residual_vec, q_code.data());
619 float precompute_list_tables_L2 ()
623 if (use_precomputed_table == 0) {
624 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
625 pq.compute_distance_table (residual_vec, sim_table);
626 }
else if (use_precomputed_table == 1) {
630 &ivfpq.precomputed_table [key * pq.ksub * pq.M],
633 }
else if (use_precomputed_table == 2) {
636 const MultiIndexQuantizer *miq =
637 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
639 const ProductQuantizer &cpq = miq->pq;
640 int Mf = pq.M / cpq.M;
642 const float *qtab = sim_table_2;
643 float *ltab = sim_table;
646 for (
int cm = 0; cm < cpq.M; cm++) {
648 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
652 const float *pc = &ivfpq.precomputed_table
653 [(ki * pq.M + cm * Mf) * pq.ksub];
655 if (ivfpq.polysemous_ht == 0) {
662 ltab += Mf * pq.ksub;
663 qtab += Mf * pq.ksub;
665 for (
int m = cm * Mf; m < (cm + 1) * Mf; m++) {
667 (pq.ksub, pc, -2, qtab, ltab);
680 float precompute_list_table_pointers_L2 ()
684 if (use_precomputed_table == 1) {
687 const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M];
688 for (
int m = 0; m < pq.M; m++) {
689 sim_table_ptrs [m] = s;
692 }
else if (use_precomputed_table == 2) {
695 const MultiIndexQuantizer *miq =
696 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
698 const ProductQuantizer &cpq = miq->pq;
699 int Mf = pq.M / cpq.M;
703 for (
int cm = 0; cm < cpq.M; cm++) {
704 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
707 const float *pc = &ivfpq.precomputed_table
708 [(ki * pq.M + cm * Mf) * pq.ksub];
710 for (
int m = m0; m < m0 + Mf; m++) {
711 sim_table_ptrs [m] = pc;
716 }
else FAISS_ASSERT (!
"need precomputed tables");
718 if (ivfpq.polysemous_ht) {
719 FAISS_ASSERT (!
"not implemented");
737 template <
typename IDType>
738 struct InvertedListScanner: QueryTables {
740 const uint8_t * __restrict list_codes;
741 const IDType * list_ids;
744 explicit InvertedListScanner (
const IndexIVFPQ & ivfpq):
747 FAISS_ASSERT(pq.byte_per_idx == 1);
753 size_t list_size_in,
const IDType *list_ids_in,
754 const uint8_t *list_codes_in) {
756 this->coarse_dis = coarse_dis;
757 list_size = list_size_in;
758 list_codes = list_codes_in;
759 list_ids = list_ids_in;
767 void scan_list_with_table (
768 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
770 float dis0 = precompute_list_tables ();
772 for (
size_t j = 0; j < list_size; j++) {
775 const float *tab = sim_table;
777 for (
size_t m = 0; m < pq.M; m++) {
778 dis += tab[*list_codes++];
782 if (dis < heap_sim[0]) {
783 maxheap_pop (k, heap_sim, heap_ids);
784 long id = store_pairs ? (key << 32 | j) : list_ids[j];
785 maxheap_push (k, heap_sim, heap_ids, dis,
id);
793 void scan_list_with_pointer (
794 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
797 float dis0 = precompute_list_table_pointers ();
799 for (
size_t j = 0; j < list_size; j++) {
802 const float *tab = sim_table_2;
804 for (
size_t m = 0; m < pq.M; m++) {
805 int ci = *list_codes++;
806 dis += sim_table_ptrs [m][ci] - 2 * tab [ci];
810 if (dis < heap_sim[0]) {
811 maxheap_pop (k, heap_sim, heap_ids);
812 long id = store_pairs ? (key << 32 | j) : list_ids[j];
813 maxheap_push (k, heap_sim, heap_ids, dis,
id);
820 void scan_on_the_fly_dist (
821 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
824 if (by_residual && use_precomputed_table) {
825 scan_list_with_pointer (k, heap_sim, heap_ids, store_pairs);
833 if (metric_type == METRIC_INNER_PRODUCT) {
834 ivfpq.quantizer->reconstruct (key, residual_vec);
835 dis0 = fvec_inner_product (residual_vec, qi, d);
837 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
845 for (
size_t j = 0; j < list_size; j++) {
847 pq.decode (list_codes, decoded_vec);
848 list_codes += pq.code_size;
851 if (metric_type == METRIC_INNER_PRODUCT) {
852 dis = -dis0 - fvec_inner_product (decoded_vec, qi, d);
857 if (dis < heap_sim[0]) {
858 maxheap_pop (k, heap_sim, heap_ids);
859 long id = store_pairs ? (key << 32 | j) : list_ids[j];
860 maxheap_push (k, heap_sim, heap_ids, dis,
id);
870 size_t n_hamming_pass;
873 template <
class HammingComputer>
874 void scan_list_polysemous_hc (
875 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
877 float dis0 = precompute_list_tables ();
878 int ht = ivfpq.polysemous_ht;
880 int code_size = pq.code_size;
882 HammingComputer hc (q_code.data(), code_size);
884 for (
size_t j = 0; j < list_size; j++) {
885 const uint8_t *b_code = list_codes;
886 int hd = hc.hamming (b_code);
891 const float *tab = sim_table;
893 for (
size_t m = 0; m < pq.M; m++) {
894 dis += tab[*b_code++];
898 if (dis < heap_sim[0]) {
899 maxheap_pop (k, heap_sim, heap_ids);
900 long id = store_pairs ? (key << 32 | j) : list_ids[j];
901 maxheap_push (k, heap_sim, heap_ids, dis,
id);
904 list_codes += code_size;
908 void scan_list_polysemous (
909 size_t k,
float * heap_sim,
long * heap_ids,
bool store_pairs)
911 switch (pq.code_size) {
912 #define HANDLE_CODE_SIZE(cs) \
914 scan_list_polysemous_hc <HammingComputer ## cs> \
915 (k, heap_sim, heap_ids, store_pairs); \
919 HANDLE_CODE_SIZE(16);
920 HANDLE_CODE_SIZE(20);
921 HANDLE_CODE_SIZE(32);
922 HANDLE_CODE_SIZE(64);
923 #undef HANDLE_CODE_SIZE
925 if (pq.code_size % 8 == 0)
926 scan_list_polysemous_hc <HammingComputerM8>
927 (k, heap_sim, heap_ids, store_pairs);
929 scan_list_polysemous_hc <HammingComputerM4>
930 (k, heap_sim, heap_ids, store_pairs);
943 IndexIVFPQStats indexIVFPQ_stats;
945 void IndexIVFPQStats::reset () {
946 memset (
this, 0,
sizeof (*
this));
954 const float * coarse_dis,
956 bool store_pairs)
const
958 const size_t k = res->
k;
962 InvertedListScanner<long> qt (*
this);
963 size_t stats_nlist = 0;
964 size_t stats_ncode = 0;
965 uint64_t init_query_cycles = 0;
966 uint64_t scan_cycles = 0;
967 uint64_t heap_cycles = 0;
970 for (
size_t i = 0; i < nx; i++) {
971 const float *qi = qx + i *
d;
972 const long * keysi = keys + i *
nprobe;
973 const float *coarse_dis_i = coarse_dis + i *
nprobe;
974 float * heap_sim = res->
get_val (i);
975 long * heap_ids = res->
get_ids (i);
979 maxheap_heapify (k, heap_sim, heap_ids);
984 init_query_cycles += TOC;
988 for (
size_t ik = 0; ik <
nprobe; ik++) {
989 long key = keysi[ik];
994 if (key >= (
long) nlist) {
995 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
998 size_t list_size =
ids[key].size();
1002 if (list_size == 0)
continue;
1004 qt.init_list (key, coarse_dis_i[ik],
1005 list_size,
ids[key].data(),
1010 qt.scan_list_polysemous
1011 (k, heap_sim, heap_ids, store_pairs);
1013 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1015 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1021 stats_ncode += nscan;
1023 maxheap_reorder (k, heap_sim, heap_ids);
1026 for (
size_t j = 0; j < k; j++)
1027 heap_sim[j] = -heap_sim[j];
1032 #pragma omp critical
1034 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1035 indexIVFPQ_stats.nlist += stats_nlist;
1036 indexIVFPQ_stats.ncode += stats_ncode;
1038 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1039 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1040 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1041 indexIVFPQ_stats.heap_cycles += heap_cycles;
1045 indexIVFPQ_stats.nq += nx;
1050 float *distances,
idx_t *labels)
const
1052 long * idx =
new long [n *
nprobe];
1053 float * coarse_dis =
new float [n *
nprobe];
1056 quantizer->search (n, x,
nprobe, coarse_dis, idx);
1057 indexIVFPQ_stats.assign_cycles += TOC;
1064 delete [] coarse_dis;
1065 indexIVFPQ_stats.search_cycles += TOC;
1072 for (
size_t key = 0; key <
nlist; key++) {
1080 !
"direct map remove not implemented");
1082 #pragma omp parallel for reduction(+: nremove)
1083 for (
long i = 0; i <
nlist; i++) {
1084 std::vector<idx_t> & idsi =
ids[i];
1085 uint8_t * codesi = codes[i].data();
1087 long l = idsi.size(), j = 0;
1089 if (sel.is_member (idsi[j])) {
1091 idsi [j] = idsi [l];
1092 memmove (codesi + j * code_size,
1093 codesi + l * code_size, code_size);
1098 if (l < idsi.size()) {
1099 nremove += idsi.size() - l;
1101 codes[i].resize (l * code_size);
1109 IndexIVFPQ::IndexIVFPQ ()
1124 bool operator () (
int a,
int b)
const {
1125 return cmp (a, b) > 0;
1127 int cmp (
int a,
int b)
const {
1128 return memcmp (tab + a * code_size, tab + b * code_size,
1138 for (
size_t list_no = 0; list_no <
nlist; list_no++) {
1139 size_t n =
ids[list_no].size();
1140 std::vector<int> ord (n);
1141 for (
int i = 0; i < n; i++) ord[i] = i;
1142 CodeCmp cs = { codes[list_no].data(), code_size };
1143 std::sort (ord.begin(), ord.end(), cs);
1145 const idx_t *list_ids =
ids[list_no].data();
1147 for (
int i = 0; i < n; i++) {
1148 if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) {
1150 if (prev + 1 == i) {
1152 lims[ngroup] = lims[ngroup - 1];
1153 dup_ids [lims [ngroup]++] = list_ids [ord [prev]];
1155 dup_ids [lims [ngroup]++] = list_ids [ord [i]];
1171 IndexIVFPQR::IndexIVFPQR (
1172 Index * quantizer,
size_t d,
size_t nlist,
1173 size_t M,
size_t nbits_per_idx,
1174 size_t M_refine,
size_t nbits_per_idx_refine):
1175 IndexIVFPQ (quantizer, d, nlist, M, nbits_per_idx),
1176 refine_pq (d, M_refine, nbits_per_idx_refine),
1183 IndexIVFPQR::IndexIVFPQR ():
1190 void IndexIVFPQR::set_typename()
1192 std::stringstream s;
1195 <<
"[" << nlist <<
":" << quantizer->index_typename <<
"]";
1196 index_typename = s.str();
1213 float * residual_2 =
new float [n *
d];
1218 printf (
"training %zdx%zd 2nd level PQ quantizer on %ld %dD-vectors\n",
1225 delete [] residual_2;
1235 const long *precomputed_idx) {
1237 float * residual_2 =
new float [n *
d];
1241 add_core_o (n, x, xids, residual_2, precomputed_idx);
1248 delete [] residual_2;
1255 float *distances,
idx_t *labels)
const
1258 long * idx =
new long [n *
nprobe];
1259 float * L1_dis =
new float [n *
nprobe];
1262 quantizer->search (n, x,
nprobe, L1_dis, idx);
1263 indexIVFPQ_stats.assign_cycles += TOC;
1266 size_t k_coarse = long(k *
k_factor);
1267 idx_t *coarse_labels =
new idx_t [k_coarse * n];
1269 float *coarse_distances =
new float [k_coarse * n];
1272 size_t(n), k_coarse, coarse_labels, coarse_distances};
1274 delete [] coarse_distances;
1277 indexIVFPQ_stats.search_cycles += TOC;
1282 size_t n_refine = 0;
1283 #pragma omp parallel reduction(+ : n_refine)
1286 float *residual_1 =
new float [2 *
d];
1287 float *residual_2 = residual_1 +
d;
1289 for (
idx_t i = 0; i < n; i++) {
1290 const float *xq = x + i *
d;
1291 const long * shortlist = coarse_labels + k_coarse * i;
1292 float * heap_sim = distances + k * i;
1293 long * heap_ids = labels + k * i;
1294 maxheap_heapify (k, heap_sim, heap_ids);
1296 for (
int j = 0; j < k_coarse; j++) {
1297 long sl = shortlist[j];
1299 if (sl == -1)
continue;
1301 int list_no = sl >> 32;
1302 int ofs = sl & 0xffffffff;
1304 assert (list_no >= 0 && list_no < nlist);
1305 assert (ofs >= 0 && ofs <
ids[list_no].size());
1308 quantizer->compute_residual (xq, residual_1, list_no);
1311 const uint8_t * l2code = &codes[list_no][ofs *
pq.
code_size];
1313 for (
int l = 0; l <
d; l++)
1314 residual_2[l] = residual_1[l] - residual_2[l];
1318 assert (0 <=
id &&
id <
ntotal);
1322 float dis =
fvec_L2sqr (residual_1, residual_2, d);
1324 if (dis < heap_sim[0]) {
1325 maxheap_pop (k, heap_sim, heap_ids);
1326 maxheap_push (k, heap_sim, heap_ids, dis,
id);
1330 maxheap_reorder (k, heap_sim, heap_ids);
1332 delete [] residual_1;
1334 delete [] coarse_labels;
1336 indexIVFPQ_stats.nrefine += n_refine;
1337 indexIVFPQ_stats.refine_cycles += TOC;
1342 float *r3 =
new float [
d];
1346 for (
idx_t i = i0; i < i0 + ni; i++) {
1347 float *r = recons + i *
d;
1350 for (
int j = 0; j <
d; j++)
1368 FAISS_ASSERT(!
"not implemented");
1375 IndexIVFPQCompact::IndexIVFPQCompact ()
1384 IndexIVFPQCompact::IndexIVFPQCompact (
const IndexIVFPQ &other)
1386 FAISS_ASSERT (other.ntotal < (1UL << 31) ||
1387 !
"IndexIVFPQCompact cannot store more than 2G images");
1401 nlist = other.nlist;
1403 quantizer = other.quantizer;
1406 direct_map = other.direct_map;
1412 code_size = other.code_size;
1423 limits =
new uint32_t [nlist + 1];
1430 for (
size_t i = 0; i <
nlist; i++) {
1432 const std::vector<long> &other_ids = other.ids[i];
1433 for (
size_t j = 0; j < other_ids.size(); j++) {
1434 long id = other_ids[j];
1435 FAISS_ASSERT (
id < (1UL << 31) ||
1436 !
"IndexIVFPQCompact cannot store ids > 2G");
1440 other.codes[i].data(),
1441 other.codes[i].size());
1442 ofs += other_ids.size();
1444 FAISS_ASSERT (ofs ==
ntotal);
1450 FAISS_ASSERT (!
"cannot add to an IndexIVFPQCompact");
1454 FAISS_ASSERT (!
"cannot reset an IndexIVFPQCompact");
1458 FAISS_ASSERT (!
"cannot train an IndexIVFPQCompact");
1464 IndexIVFPQCompact::~IndexIVFPQCompact ()
1471 munmap (mmap_buffer, mmap_length);
1481 const float * coarse_dis,
1483 bool store_pairs)
const
1485 const size_t k = res->
k;
1487 #pragma omp parallel
1489 InvertedListScanner<uint32_t> qt (*
this);
1490 size_t stats_nlist = 0;
1491 size_t stats_ncode = 0;
1492 uint64_t init_query_cycles = 0;
1493 uint64_t scan_cycles = 0;
1494 uint64_t heap_cycles = 0;
1497 for (
size_t i = 0; i < nx; i++) {
1498 const float *qi = qx + i *
d;
1499 const long * keysi = keys + i *
nprobe;
1500 const float *coarse_dis_i = coarse_dis + i *
nprobe;
1501 float * heap_sim = res->
get_val (i);
1502 long * heap_ids = res->
get_ids (i);
1506 maxheap_heapify (k, heap_sim, heap_ids);
1511 init_query_cycles += TOC;
1515 for (
size_t ik = 0; ik <
nprobe; ik++) {
1516 long key = keysi[ik];
1521 if (key >= (
long) nlist) {
1522 fprintf (stderr,
"Invalid key=%ld nlist=%ld\n", key, nlist);
1529 if (list_size == 0)
continue;
1531 qt.init_list (key, coarse_dis_i[ik],
1537 qt.scan_list_polysemous
1538 (k, heap_sim, heap_ids, store_pairs);
1540 qt.scan_list_with_table (k, heap_sim, heap_ids, store_pairs);
1542 qt.scan_on_the_fly_dist (k, heap_sim, heap_ids, store_pairs);
1548 stats_ncode += nscan;
1550 maxheap_reorder (k, heap_sim, heap_ids);
1553 for (
size_t j = 0; j < k; j++) {
1554 heap_sim[i] = -heap_sim[i];
1560 #pragma omp critical
1562 indexIVFPQ_stats.n_hamming_pass += qt.n_hamming_pass;
1563 indexIVFPQ_stats.nlist += stats_nlist;
1564 indexIVFPQ_stats.ncode += stats_ncode;
1566 indexIVFPQ_stats.init_query_cycles += init_query_cycles;
1567 indexIVFPQ_stats.init_list_cycles += qt.init_list_cycles;
1568 indexIVFPQ_stats.scan_cycles += scan_cycles - qt.init_list_cycles;
1569 indexIVFPQ_stats.heap_cycles += heap_cycles;
1573 indexIVFPQ_stats.nq += nx;
uint32_t * compact_ids
size ntotal
uint8_t * compact_codes
size ntotal * code_size
void precompute_table()
build precomputed table
void copy_subset_to(IndexIVFPQ &other, int subset_type, long a1, long a2) const
size_t nbits
number of bits per quantization index
virtual void reconstruct(idx_t key, float *recons) const override
void decode(const uint8_t *code, float *x) const
decode a vector from a given code (or n vectors if third argument)
ProductQuantizer refine_pq
3rd level quantizer
float fvec_L2sqr(const float *x, const float *y, size_t d)
Squared L2 distance between two vectors.
PolysemousTraining * polysemous_training
if NULL, use default
T * get_val(size_t key)
Return the list of values for a heap.
virtual void add(idx_t, const float *) override
the three following functions will fail at runtime
virtual void search_knn_with_key(size_t nx, const float *qx, const long *keys, const float *coarse_dis, float_maxheap_array_t *res, bool store_pairs=false) const override
virtual void reconstruct_n(idx_t i0, idx_t ni, float *recons) const override
const float * fvecs_maybe_subsample(size_t d, size_t *n, size_t nmax, const float *x, bool verbose, long seed)
size_t nprobe
number of probes at query time
void assign(idx_t n, const float *x, idx_t *labels, idx_t k=1)
bool quantizer_trains_alone
just pass over the trainset to quantizer
virtual void set_typename() override
virtual void merge_from_residuals(IndexIVF &other) override
used to implement merging
void train_residual_o(idx_t n, const float *x, float *residuals_2)
same as train_residual, also output 2nd level residuals
bool do_polysemous_training
reorder PQ centroids after training?
size_t scan_table_threshold
use table computation or on-the-fly?
size_t k
allocated size per heap
virtual void train_residual(idx_t n, const float *x) override
trains the two product quantizers
void add_core(idx_t n, const float *x, const long *xids, const long *precomputed_idx=nullptr)
same as add_with_ids, but optionally use the precomputed list ids
uint32_t * limits
size nlist + 1
size_t dsub
dimensionality of each subvector
int seed
seed for the random number generator
std::vector< float > precomputed_table
void fvec_madd(size_t n, const float *a, float bf, const float *b, float *c)
int polysemous_ht
Hamming thresh for polysemous filtering.
virtual void search_knn_with_key(size_t nx, const float *qx, const long *keys, const float *coarse_dis, float_maxheap_array_t *res, bool store_pairs=false) const
virtual void reset() override
removes all elements from the database.
virtual void add_with_ids(idx_t n, const float *x, const long *xids=nullptr) override
std::vector< std::vector< long > > ids
Inverted lists for indexes.
void compute_codes(const float *x, uint8_t *codes, size_t n) const
same as compute_code for several vectors
Index * quantizer
quantizer that maps vectors to inverted lists
size_t max_codes
max nb of codes to visit to do a query
std::vector< uint8_t > refine_codes
corresponding codes
size_t code_size
byte per indexed vector
virtual long remove_ids(const IDSelector &sel) override
virtual void train_residual(idx_t n, const float *x) override
trains the product quantizer
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