10 #include "IndexIVFPQ.h"
21 #include "Clustering.h"
22 #include "IndexFlat.h"
26 #include "FaissAssert.h"
28 #include "AuxIndexStructures.h"
37 IndexIVFPQ::IndexIVFPQ (Index * quantizer,
size_t d,
size_t nlist,
38 size_t M,
size_t nbits_per_idx):
39 IndexIVF (quantizer, d, nlist, 0, METRIC_L2),
40 pq (d, M, nbits_per_idx)
42 FAISS_THROW_IF_NOT (nbits_per_idx <= 8);
47 use_precomputed_table = 0;
48 scan_table_threshold = 0;
50 polysemous_training =
nullptr;
51 do_polysemous_training =
false;
68 const float * x_in = x;
76 const float *trainset;
79 if(
verbose) printf(
"computing residuals\n");
83 float *residuals =
new float [n *
d];
84 del_residuals.set (residuals);
85 for (
idx_t i = 0; i < n; i++)
93 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
96 pq.train (n, trainset);
100 printf(
"doing polysemous training for PQ\n");
103 if (!pt) pt = &default_pt;
109 uint8_t *train_codes =
new uint8_t [
pq.
code_size * n];
113 for (
idx_t i = 0; i < n; i++) {
114 const float *xx = trainset + i *
d;
115 float * res = residuals_2 + i *
d;
117 for (
int j = 0; j <
d; j++)
118 res[j] = xx[j] - res[j];
139 void IndexIVFPQ::encode (
long key,
const float * x, uint8_t * code)
const
142 float residual_vec[
d];
150 const float * x, uint8_t * xcodes,
151 bool compute_keys)
const
160 const uint8_t * xcodes,
float * x)
const
164 std::vector<float> centroid (
d);
165 for (
size_t i = 0; i < n; i++) {
167 float *xi = x + i *
d;
168 for (
size_t j = 0; j <
d; j++) {
169 xi [j] += centroid [j];
188 static float * compute_residuals (
189 const Index *quantizer,
193 size_t d = quantizer->
d;
194 float *residuals =
new float [n * d];
196 for (
size_t i = 0; i < n; i++) {
198 memset (residuals + i * d, 0,
sizeof(*residuals) * d);
201 x + i * d, residuals + i * d, list_nos[i]);
207 const idx_t *list_nos,
208 uint8_t * codes)
const
211 float *to_encode = compute_residuals (quantizer, n, x, list_nos);
221 float *residuals_2,
const long *precomputed_idx)
226 for (
idx_t i0 = 0; i0 < n; i0 += bs) {
227 idx_t i1 = std::min(i0 + bs, n);
229 printf(
"IndexIVFPQ::add_core_o: adding %ld:%ld / %ld\n",
233 xids ? xids + i0 :
nullptr,
234 residuals_2 ? residuals_2 + i0 * d :
nullptr,
235 precomputed_idx ? precomputed_idx + i0 :
nullptr);
245 if (precomputed_idx) {
246 idx = precomputed_idx;
248 long * idx0 =
new long [n];
250 quantizer->
assign (n, x, idx0);
255 uint8_t * xcodes =
new uint8_t [n *
code_size];
258 const float *to_encode =
nullptr;
262 to_encode = compute_residuals (quantizer, n, x, idx);
263 del_to_encode.set (to_encode);
272 for (
size_t i = 0; i < n; i++) {
277 memset (residuals_2, 0,
sizeof(*residuals_2) * d);
286 float *res2 = residuals_2 + i *
d;
287 const float *xi = to_encode + i *
d;
289 for (
int j = 0; j <
d; j++)
290 res2[j] = xi[j] - res2[j];
294 direct_map.push_back (key << 32 | offset);
300 char comment[100] = {0};
302 snprintf (comment, 100,
"(%ld vectors ignored)", n_ignore);
303 printf(
" add_core times: %.3f %.3f %.3f %s\n",
304 t1 - t0, t2 - t1, t3 - t2, comment);
316 std::vector<float> centroid(d);
320 for (
int i = 0; i <
d; ++i) {
321 recons[i] += centroid[i];
369 if (quantizer->
metric_type == METRIC_INNER_PRODUCT) {
371 printf(
"IndexIVFPQ::precompute_table: precomputed "
372 "tables not needed for inner product quantizers\n");
378 if (miq &&
pq.
M % miq->pq.
M == 0)
385 "IndexIVFPQ::precompute_table: not precomputing table, "
386 "it would be too big: %ld bytes (max %ld)\n",
397 printf (
"precomputing IVFPQ tables type %d\n",
402 std::vector<float> r_norms (
pq.
M *
pq.
ksub, NAN);
403 for (
int m = 0; m <
pq.
M; m++)
404 for (
int j = 0; j <
pq.
ksub; j++)
405 r_norms [m *
pq.
ksub + j] =
411 std::vector<float> centroid (d);
413 for (
size_t i = 0; i <
nlist; i++) {
417 pq.compute_inner_prod_table (centroid.data(), tab);
423 FAISS_THROW_IF_NOT (miq);
425 FAISS_THROW_IF_NOT (
pq.
M % cpq.
M == 0);
430 std::vector<float> centroids (d * cpq.
ksub, NAN);
432 for (
int m = 0; m < cpq.
M; m++) {
433 for (
size_t i = 0; i < cpq.
ksub; i++) {
434 memcpy (centroids.data() + i * d + m * cpq.
dsub,
436 sizeof (*centroids.data()) * cpq.
dsub);
440 pq.compute_inner_prod_tables (cpq.
ksub, centroids.data (),
443 for (
size_t i = 0; i < cpq.
ksub; i++) {
456 static uint64_t get_cycles () {
459 asm volatile(
"rdtsc \n\t"
462 return ((uint64_t)high << 32) | (low);
468 #define TIC t0 = get_cycles()
469 #define TOC get_cycles () - t0
486 const IndexIVFPQ & ivfpq;
487 const IVFSearchParameters *params;
491 const ProductQuantizer & pq;
494 int use_precomputed_table;
498 float * sim_table, * sim_table_2;
499 float * residual_vec, *decoded_vec;
502 std::vector<float> mem;
505 std::vector<const float *> sim_table_ptrs;
507 explicit QueryTables (
const IndexIVFPQ & ivfpq,
508 const IVFSearchParameters *params):
512 metric_type (ivfpq.metric_type),
513 by_residual (ivfpq.by_residual),
514 use_precomputed_table (ivfpq.use_precomputed_table)
516 mem.resize (pq.ksub * pq.M * 2 + d * 2);
517 sim_table = mem.data ();
518 sim_table_2 = sim_table + pq.ksub * pq.M;
519 residual_vec = sim_table_2 + pq.ksub * pq.M;
520 decoded_vec = residual_vec + d;
523 polysemous_ht = ivfpq.polysemous_ht;
524 if (
auto ivfpq_params =
525 dynamic_cast<const IVFPQSearchParameters *>(params)) {
526 polysemous_ht = ivfpq_params->polysemous_ht;
528 if (polysemous_ht != 0) {
529 q_code.resize (pq.code_size);
531 init_list_cycles = 0;
532 sim_table_ptrs.resize (pq.M);
543 void init_query (
const float * qi) {
545 if (metric_type == METRIC_INNER_PRODUCT)
549 if (!by_residual && polysemous_ht != 0)
550 pq.compute_code (qi, q_code.data());
553 void init_query_IP () {
555 pq.compute_inner_prod_table (qi, sim_table);
558 void init_query_L2 () {
560 pq.compute_distance_table (qi, sim_table);
561 }
else if (use_precomputed_table) {
562 pq.compute_inner_prod_table (qi, sim_table_2);
573 std::vector<uint8_t> q_code;
575 uint64_t init_list_cycles;
580 float precompute_list_tables () {
584 if (metric_type == METRIC_INNER_PRODUCT)
585 dis0 = precompute_list_tables_IP ();
587 dis0 = precompute_list_tables_L2 ();
589 init_list_cycles += TOC;
593 float precompute_list_table_pointers () {
597 if (metric_type == METRIC_INNER_PRODUCT)
598 FAISS_THROW_MSG (
"not implemented");
600 dis0 = precompute_list_table_pointers_L2 ();
602 init_list_cycles += TOC;
610 float precompute_list_tables_IP ()
614 ivfpq.quantizer->reconstruct (key, decoded_vec);
616 float dis0 = fvec_inner_product (qi, decoded_vec, d);
619 for (
int i = 0; i < d; i++) {
620 residual_vec [i] = qi[i] - decoded_vec[i];
622 pq.compute_code (residual_vec, q_code.data());
632 float precompute_list_tables_L2 ()
636 if (use_precomputed_table == 0 || use_precomputed_table == -1) {
637 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
638 pq.compute_distance_table (residual_vec, sim_table);
640 if (polysemous_ht != 0) {
641 pq.compute_code (residual_vec, q_code.data());
644 }
else if (use_precomputed_table == 1) {
648 &ivfpq.precomputed_table [key * pq.ksub * pq.M],
653 if (polysemous_ht != 0) {
654 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
655 pq.compute_code (residual_vec, q_code.data());
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;
667 const float *qtab = sim_table_2;
668 float *ltab = sim_table;
671 for (
int cm = 0; cm < cpq.M; cm++) {
673 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
677 const float *pc = &ivfpq.precomputed_table
678 [(ki * pq.M + cm * Mf) * pq.ksub];
680 if (polysemous_ht == 0) {
687 ltab += Mf * pq.ksub;
688 qtab += Mf * pq.ksub;
690 for (
int m = cm * Mf; m < (cm + 1) * Mf; m++) {
692 (pq.ksub, pc, -2, qtab, ltab);
705 float precompute_list_table_pointers_L2 ()
709 if (use_precomputed_table == 1) {
712 const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M];
713 for (
int m = 0; m < pq.M; m++) {
714 sim_table_ptrs [m] = s;
717 }
else if (use_precomputed_table == 2) {
720 const MultiIndexQuantizer *miq =
721 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
722 FAISS_THROW_IF_NOT (miq);
723 const ProductQuantizer &cpq = miq->pq;
724 int Mf = pq.M / cpq.M;
728 for (
int cm = 0; cm < cpq.M; cm++) {
729 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
732 const float *pc = &ivfpq.precomputed_table
733 [(ki * pq.M + cm * Mf) * pq.ksub];
735 for (
int m = m0; m < m0 + Mf; m++) {
736 sim_table_ptrs [m] = pc;
742 FAISS_THROW_MSG (
"need precomputed tables");
746 FAISS_THROW_MSG (
"not implemented");
761 struct KnnSearchResults {
772 inline void add (idx_t j,
float dis) {
773 if (C::cmp (heap_sim[0], dis)) {
774 heap_pop<C> (k, heap_sim, heap_ids);
775 long id = ids ? ids[j] : (key << 32 | j);
776 heap_push<C> (k, heap_sim, heap_ids, dis, id);
784 struct RangeSearchResults {
790 RangeQueryResult & rres;
792 inline void add (idx_t j,
float dis) {
793 if (C::cmp (radius, dis)) {
794 long id = ids ? ids[j] : (key << 32 | j);
807 template <
typename IDType, MetricType METRIC_TYPE>
808 struct IVFPQScannerT: QueryTables {
810 const uint8_t * list_codes;
811 const IDType * list_ids;
814 IVFPQScannerT (
const IndexIVFPQ & ivfpq,
const IVFSearchParameters *params):
815 QueryTables (ivfpq, params)
817 FAISS_THROW_IF_NOT (pq.nbits == 8);
818 assert(METRIC_TYPE == metric_type);
823 void init_list (idx_t list_no,
float coarse_dis,
826 this->coarse_dis = coarse_dis;
829 dis0 = precompute_list_tables ();
830 }
else if (mode == 1) {
831 dis0 = precompute_list_table_pointers ();
840 template<
class SearchResultType>
841 void scan_list_with_table (
size_t ncode,
const uint8_t *codes,
842 SearchResultType & res)
const
844 for (
size_t j = 0; j < ncode; j++) {
847 const float *tab = sim_table;
849 for (
size_t m = 0; m < pq.M; m++) {
850 dis += tab[*codes++];
861 template<
class SearchResultType>
862 void scan_list_with_pointer (
size_t ncode,
const uint8_t *codes,
863 SearchResultType & res)
const
865 for (
size_t j = 0; j < ncode; j++) {
868 const float *tab = sim_table_2;
870 for (
size_t m = 0; m < pq.M; m++) {
872 dis += sim_table_ptrs [m][ci] - 2 * tab [ci];
881 template<
class SearchResultType>
882 void scan_on_the_fly_dist (
size_t ncode,
const uint8_t *codes,
883 SearchResultType &res)
const
888 if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
889 ivfpq.quantizer->reconstruct (key, residual_vec);
890 dis0 = fvec_inner_product (residual_vec, qi, d);
892 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
900 for (
size_t j = 0; j < ncode; j++) {
902 pq.decode (codes, decoded_vec);
903 codes += pq.code_size;
906 if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
907 dis = dis0 + fvec_inner_product (decoded_vec, qi, d);
919 template <
class HammingComputer,
class SearchResultType>
920 void scan_list_polysemous_hc (
921 size_t ncode,
const uint8_t *codes,
922 SearchResultType & res)
const
924 int ht = ivfpq.polysemous_ht;
925 size_t n_hamming_pass = 0, nup = 0;
927 int code_size = pq.code_size;
929 HammingComputer hc (q_code.data(), code_size);
931 for (
size_t j = 0; j < ncode; j++) {
932 const uint8_t *b_code = codes;
933 int hd = hc.hamming (b_code);
938 const float *tab = sim_table;
940 for (
size_t m = 0; m < pq.M; m++) {
941 dis += tab[*b_code++];
951 indexIVFPQ_stats.n_hamming_pass += n_hamming_pass;
955 template<
class SearchResultType>
956 void scan_list_polysemous (
957 size_t ncode,
const uint8_t *codes,
958 SearchResultType &res)
const
960 switch (pq.code_size) {
961 #define HANDLE_CODE_SIZE(cs) \
963 scan_list_polysemous_hc \
964 <HammingComputer ## cs, SearchResultType> \
965 (ncode, codes, res); \
969 HANDLE_CODE_SIZE(16);
970 HANDLE_CODE_SIZE(20);
971 HANDLE_CODE_SIZE(32);
972 HANDLE_CODE_SIZE(64);
973 #undef HANDLE_CODE_SIZE
975 if (pq.code_size % 8 == 0)
976 scan_list_polysemous_hc
977 <HammingComputerM8, SearchResultType>
980 scan_list_polysemous_hc
981 <HammingComputerM4, SearchResultType>
996 template<MetricType METRIC_TYPE,
class C,
int precompute_mode>
998 IVFPQScannerT<Index::idx_t, METRIC_TYPE>,
1003 IVFPQScanner(
const IndexIVFPQ & ivfpq,
bool store_pairs):
1004 IVFPQScannerT<Index::idx_t, METRIC_TYPE>(ivfpq, nullptr),
1005 store_pairs(store_pairs)
1009 void set_query (
const float *query)
override {
1010 this->init_query (query);
1013 void set_list (idx_t list_no,
float coarse_dis)
override {
1014 this->init_list (list_no, coarse_dis, precompute_mode);
1017 float distance_to_code (
const uint8_t *code)
const override {
1018 assert(precompute_mode == 2);
1019 float dis = this->dis0;
1020 const float *tab = this->sim_table;
1022 for (
size_t m = 0; m < this->pq.M; m++) {
1023 dis += tab[*code++];
1024 tab += this->pq.ksub;
1029 size_t scan_codes (
size_t ncode,
1030 const uint8_t *codes,
1032 float *heap_sim, idx_t *heap_ids,
1033 size_t k)
const override
1035 KnnSearchResults<C> res = {
1037 this->store_pairs ?
nullptr : ids,
1044 if (this->polysemous_ht > 0) {
1045 assert(precompute_mode == 2);
1046 this->scan_list_polysemous (ncode, codes, res);
1047 }
else if (precompute_mode == 2) {
1048 this->scan_list_with_table (ncode, codes, res);
1049 }
else if (precompute_mode == 1) {
1050 this->scan_list_with_pointer (ncode, codes, res);
1051 }
else if (precompute_mode == 0) {
1052 this->scan_on_the_fly_dist (ncode, codes, res);
1054 FAISS_THROW_MSG(
"bad precomp mode");
1059 void scan_codes_range (
size_t ncode,
1060 const uint8_t *codes,
1063 RangeQueryResult & rres)
const override
1065 RangeSearchResults<C> res = {
1067 this->store_pairs ?
nullptr : ids,
1072 if (this->polysemous_ht > 0) {
1073 assert(precompute_mode == 2);
1074 this->scan_list_polysemous (ncode, codes, res);
1075 }
else if (precompute_mode == 2) {
1076 this->scan_list_with_table (ncode, codes, res);
1077 }
else if (precompute_mode == 1) {
1078 this->scan_list_with_pointer (ncode, codes, res);
1079 }
else if (precompute_mode == 0) {
1080 this->scan_on_the_fly_dist (ncode, codes, res);
1082 FAISS_THROW_MSG(
"bad precomp mode");
1093 InvertedListScanner *
1097 return new IVFPQScanner<METRIC_INNER_PRODUCT, CMin<float, long>, 2>
1098 (*
this, store_pairs);
1100 return new IVFPQScanner<METRIC_L2, CMax<float, long>, 2>
1101 (*
this, store_pairs);
1111 void IndexIVFPQStats::reset () {
1112 memset (
this, 0,
sizeof (*
this));
1117 IndexIVFPQ::IndexIVFPQ ()
1131 bool operator () (
int a,
int b)
const {
1132 return cmp (a, b) > 0;
1134 int cmp (
int a,
int b)
const {
1135 return memcmp (tab + a * code_size, tab + b * code_size,
1145 for (
size_t list_no = 0; list_no <
nlist; list_no++) {
1147 std::vector<int> ord (n);
1148 for (
int i = 0; i < n; i++) ord[i] = i;
1150 CodeCmp cs = { codes.get(), code_size };
1151 std::sort (ord.begin(), ord.end(), cs);
1155 for (
int i = 0; i < n; i++) {
1156 if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) {
1158 if (prev + 1 == i) {
1160 lims[ngroup] = lims[ngroup - 1];
1161 dup_ids [lims [ngroup]++] = list_ids [ord [prev]];
1163 dup_ids [lims [ngroup]++] = list_ids [ord [i]];
1179 IndexIVFPQR::IndexIVFPQR (
1180 Index * quantizer,
size_t d,
size_t nlist,
1181 size_t M,
size_t nbits_per_idx,
1182 size_t M_refine,
size_t nbits_per_idx_refine):
1183 IndexIVFPQ (quantizer, d, nlist, M, nbits_per_idx),
1184 refine_pq (d, M_refine, nbits_per_idx_refine),
1190 IndexIVFPQR::IndexIVFPQR ():
1210 float * residual_2 =
new float [n *
d];
1216 printf (
"training %zdx%zd 2nd level PQ quantizer on %ld %dD-vectors\n",
1232 const long *precomputed_idx) {
1234 float * residual_2 =
new float [n *
d];
1239 add_core_o (n, x, xids, residual_2, precomputed_idx);
1252 const float *L1_dis,
1253 float *distances,
idx_t *labels,
1260 size_t k_coarse = long(k *
k_factor);
1261 idx_t *coarse_labels =
new idx_t [k_coarse * n];
1264 float *coarse_distances =
new float [k_coarse * n];
1269 idx, L1_dis, coarse_distances, coarse_labels,
1274 indexIVFPQ_stats.search_cycles += TOC;
1279 size_t n_refine = 0;
1280 #pragma omp parallel reduction(+ : n_refine)
1283 float *residual_1 =
new float [2 *
d];
1285 float *residual_2 = residual_1 +
d;
1287 for (
idx_t i = 0; i < n; i++) {
1288 const float *xq = x + i *
d;
1289 const long * shortlist = coarse_labels + k_coarse * i;
1290 float * heap_sim = distances + k * i;
1291 long * heap_ids = labels + k * i;
1292 maxheap_heapify (k, heap_sim, heap_ids);
1294 for (
int j = 0; j < k_coarse; j++) {
1295 long sl = shortlist[j];
1297 if (sl == -1)
continue;
1299 int list_no = sl >> 32;
1300 int ofs = sl & 0xffffffff;
1302 assert (list_no >= 0 && list_no <
nlist);
1303 assert (ofs >= 0 && ofs < invlists->list_size (list_no));
1309 const uint8_t * l2code =
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 long id_or_pair = store_pairs ? sl : id;
1327 maxheap_push (k, heap_sim, heap_ids, dis, id_or_pair);
1331 maxheap_reorder (k, heap_sim, heap_ids);
1334 indexIVFPQ_stats.nrefine += n_refine;
1335 indexIVFPQ_stats.refine_cycles += TOC;
1339 float* recons)
const
1344 assert (0 <=
id &&
id <
ntotal);
1346 std::vector<float> r3(d);
1348 for (
int i = 0; i <
d; ++i) {
1356 FAISS_THROW_IF_NOT(other);
1367 FAISS_THROW_MSG(
"not implemented");
1376 Index2Layer::Index2Layer (
Index * quantizer,
size_t nlist,
1379 Index (quantizer->d, metric),
1380 q1 (quantizer, nlist),
1381 pq (quantizer->d, M, 8)
1384 for (
int nbyte = 0; nbyte < 7; nbyte++) {
1385 if ((1L << (8 * nbyte)) >= nlist) {
1386 code_size_1 = nbyte;
1391 code_size = code_size_1 + code_size_2;
1394 Index2Layer::Index2Layer ()
1399 Index2Layer::~Index2Layer ()
1405 printf (
"training level-1 quantizer %ld vectors in %dD\n",
1412 printf(
"computing residuals\n");
1415 const float * x_in = x;
1423 std::vector<idx_t>
assign(n);
1425 std::vector<float> residuals(n * d);
1426 for (
idx_t i = 0; i < n; i++) {
1428 x + i * d, residuals.data() + i *
d, assign[i]);
1432 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
1435 pq.train (n, residuals.data());
1444 for (
idx_t i0 = 0; i0 < n; i0 += bs) {
1445 idx_t i1 = std::min(i0 + bs, n);
1447 printf(
"Index2Layer::add: adding %ld:%ld / %ld\n",
1450 add (i1 - i0, x + i0 * d);
1455 std::vector<idx_t> codes1 (n);
1457 std::vector<float> residuals(n * d);
1458 for (
idx_t i = 0; i < n; i++) {
1460 x + i * d, residuals.data() + i *
d, codes1[i]);
1466 codes.resize ((
ntotal + n) * code_size);
1471 const char *ip = (
char*)&i;
1472 FAISS_THROW_IF_NOT_MSG (ip[0] == 0x44,
1473 "works only on a little-endian CPU");
1477 for (
idx_t i = 0; i < n; i++) {
1480 memcpy (wp, &codes2[i * code_size_2], code_size_2);
1494 FAISS_THROW_MSG(
"not implemented");
1501 FAISS_THROW_IF_NOT (i0 >= 0 && i0 + ni <=
ntotal);
1502 const uint8_t *rp = &codes[i0 *
code_size];
1504 for (
idx_t i = 0; i < ni; i++) {
1510 for (
idx_t j = 0; j <
d; j++) {
1511 recons[j] += recons1[j];
1522 FAISS_THROW_IF_NOT (other.
ntotal == 0);
1524 const uint8_t *rp = codes.data();
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
virtual 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 IVFSearchParameters *params=nullptr) const
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
void reconstruct_n(idx_t i0, idx_t ni, float *recons) const override
size_t code_size
code_size_1 + code_size_2
const float * fvecs_maybe_subsample(size_t d, size_t *n, size_t nmax, const float *x, bool verbose, long seed)
void reset() override
removes all elements from the database.
void assign(idx_t n, const float *x, idx_t *labels, idx_t k=1)
virtual size_t list_size(size_t list_no) const =0
get the size of a list
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
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)
void encode_vectors(idx_t n, const float *x, const idx_t *list_nos, uint8_t *codes) const override
int polysemous_ht
Hamming thresh for polysemous filtering.
InvertedListScanner * get_InvertedListScanner(bool store_pairs) const override
get a scanner for this index (store_pairs means ignore labels)
void reset() override
removes all elements from the database.
void add_with_ids(idx_t n, const float *x, const long *xids=nullptr) override
default implementation that calls encode_vectors
void compute_codes(const float *x, uint8_t *codes, size_t n) const
same as compute_code for several vectors
virtual idx_t get_single_id(size_t list_no, size_t offset) const
long idx_t
all indices are this type
size_t code_size
code size per vector in bytes
virtual const uint8_t * get_single_code(size_t list_no, size_t offset) const
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
static size_t precomputed_table_max_bytes
2G by default, accommodates tables up to PQ32 w/ 65536 centroids
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
virtual size_t add_entry(size_t list_no, idx_t theid, const uint8_t *code)
add one entry to an inverted list
size_t ksub
number of centroids for each subquantizer
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?
MetricType metric_type
type of metric this index uses for search
ProductQuantizer pq
produces the codes
InvertedLists * invlists
Acess to the actual data.
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)
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 IVFSearchParameters *params=nullptr) const override
long remove_ids(const IDSelector &sel) override
Dataset manipulation functions.
Index * quantizer
quantizer that maps vectors to inverted lists
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
ProductQuantizer pq
second level quantizer is always a PQ
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
default implementation that calls encode_vectors
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