11 #include "IndexIVFPQ.h"
22 #include "Clustering.h"
23 #include "IndexFlat.h"
27 #include "FaissAssert.h"
29 #include "AuxIndexStructures.h"
38 IndexIVFPQ::IndexIVFPQ (Index * quantizer,
size_t d,
size_t nlist,
39 size_t M,
size_t nbits_per_idx):
40 IndexIVF (quantizer, d, nlist, 0, METRIC_L2),
41 pq (d, M, nbits_per_idx)
43 FAISS_THROW_IF_NOT (nbits_per_idx <= 8);
48 use_precomputed_table = 0;
49 scan_table_threshold = 0;
51 polysemous_training =
nullptr;
52 do_polysemous_training =
false;
69 const float * x_in = x;
77 const float *trainset;
80 if(
verbose) printf(
"computing residuals\n");
84 float *residuals =
new float [n *
d];
85 del_residuals.set (residuals);
86 for (
idx_t i = 0; i < n; i++)
94 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
97 pq.train (n, trainset);
101 printf(
"doing polysemous training for PQ\n");
104 if (!pt) pt = &default_pt;
110 uint8_t *train_codes =
new uint8_t [
pq.
code_size * n];
114 for (
idx_t i = 0; i < n; i++) {
115 const float *xx = trainset + i *
d;
116 float * res = residuals_2 + i *
d;
118 for (
int j = 0; j <
d; j++)
119 res[j] = xx[j] - res[j];
140 void IndexIVFPQ::encode (
long key,
const float * x, uint8_t * code)
const
143 float residual_vec[
d];
151 const float * x, uint8_t * xcodes,
152 bool compute_keys)
const
161 const uint8_t * xcodes,
float * x)
const
165 std::vector<float> centroid (
d);
166 for (
size_t i = 0; i < n; i++) {
168 float *xi = x + i *
d;
169 for (
size_t j = 0; j <
d; j++) {
170 xi [j] += centroid [j];
189 static float * compute_residuals (
190 const Index *quantizer,
194 size_t d = quantizer->
d;
195 float *residuals =
new float [n * d];
197 for (
size_t i = 0; i < n; i++) {
199 memset (residuals + i * d, 0,
sizeof(*residuals) * d);
202 x + i * d, residuals + i * d, list_nos[i]);
208 const idx_t *list_nos,
209 uint8_t * codes)
const
212 float *to_encode = compute_residuals (quantizer, n, x, list_nos);
222 float *residuals_2,
const long *precomputed_idx)
227 for (
idx_t i0 = 0; i0 < n; i0 += bs) {
228 idx_t i1 = std::min(i0 + bs, n);
230 printf(
"IndexIVFPQ::add_core_o: adding %ld:%ld / %ld\n",
234 xids ? xids + i0 :
nullptr,
235 residuals_2 ? residuals_2 + i0 * d :
nullptr,
236 precomputed_idx ? precomputed_idx + i0 :
nullptr);
246 if (precomputed_idx) {
247 idx = precomputed_idx;
249 long * idx0 =
new long [n];
251 quantizer->
assign (n, x, idx0);
256 uint8_t * xcodes =
new uint8_t [n *
code_size];
259 const float *to_encode =
nullptr;
263 to_encode = compute_residuals (quantizer, n, x, idx);
264 del_to_encode.set (to_encode);
273 for (
size_t i = 0; i < n; i++) {
278 memset (residuals_2, 0,
sizeof(*residuals_2) * d);
287 float *res2 = residuals_2 + i *
d;
288 const float *xi = to_encode + i *
d;
290 for (
int j = 0; j <
d; j++)
291 res2[j] = xi[j] - res2[j];
295 direct_map.push_back (key << 32 | offset);
301 char comment[100] = {0};
303 snprintf (comment, 100,
"(%ld vectors ignored)", n_ignore);
304 printf(
" add_core times: %.3f %.3f %.3f %s\n",
305 t1 - t0, t2 - t1, t3 - t2, comment);
317 std::vector<float> centroid(d);
321 for (
int i = 0; i <
d; ++i) {
322 recons[i] += centroid[i];
370 if (quantizer->
metric_type == METRIC_INNER_PRODUCT) {
372 printf(
"IndexIVFPQ::precompute_table: precomputed "
373 "tables not needed for inner product quantizers\n");
379 if (miq &&
pq.
M % miq->pq.
M == 0)
386 "IndexIVFPQ::precompute_table: not precomputing table, "
387 "it would be too big: %ld bytes (max %ld)\n",
398 printf (
"precomputing IVFPQ tables type %d\n",
403 std::vector<float> r_norms (
pq.
M *
pq.
ksub, NAN);
404 for (
int m = 0; m <
pq.
M; m++)
405 for (
int j = 0; j <
pq.
ksub; j++)
406 r_norms [m *
pq.
ksub + j] =
412 std::vector<float> centroid (d);
414 for (
size_t i = 0; i <
nlist; i++) {
418 pq.compute_inner_prod_table (centroid.data(), tab);
424 FAISS_THROW_IF_NOT (miq);
426 FAISS_THROW_IF_NOT (
pq.
M % cpq.
M == 0);
431 std::vector<float> centroids (d * cpq.
ksub, NAN);
433 for (
int m = 0; m < cpq.
M; m++) {
434 for (
size_t i = 0; i < cpq.
ksub; i++) {
435 memcpy (centroids.data() + i * d + m * cpq.
dsub,
437 sizeof (*centroids.data()) * cpq.
dsub);
441 pq.compute_inner_prod_tables (cpq.
ksub, centroids.data (),
444 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);
639 }
else if (use_precomputed_table == 1) {
643 &ivfpq.precomputed_table [key * pq.ksub * pq.M],
646 }
else if (use_precomputed_table == 2) {
649 const MultiIndexQuantizer *miq =
650 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
651 FAISS_THROW_IF_NOT (miq);
652 const ProductQuantizer &cpq = miq->pq;
653 int Mf = pq.M / cpq.M;
655 const float *qtab = sim_table_2;
656 float *ltab = sim_table;
659 for (
int cm = 0; cm < cpq.M; cm++) {
661 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
665 const float *pc = &ivfpq.precomputed_table
666 [(ki * pq.M + cm * Mf) * pq.ksub];
668 if (polysemous_ht == 0) {
675 ltab += Mf * pq.ksub;
676 qtab += Mf * pq.ksub;
678 for (
int m = cm * Mf; m < (cm + 1) * Mf; m++) {
680 (pq.ksub, pc, -2, qtab, ltab);
693 float precompute_list_table_pointers_L2 ()
697 if (use_precomputed_table == 1) {
700 const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M];
701 for (
int m = 0; m < pq.M; m++) {
702 sim_table_ptrs [m] = s;
705 }
else if (use_precomputed_table == 2) {
708 const MultiIndexQuantizer *miq =
709 dynamic_cast<const MultiIndexQuantizer *
> (ivfpq.quantizer);
710 FAISS_THROW_IF_NOT (miq);
711 const ProductQuantizer &cpq = miq->pq;
712 int Mf = pq.M / cpq.M;
716 for (
int cm = 0; cm < cpq.M; cm++) {
717 int ki = k & ((uint64_t(1) << cpq.nbits) - 1);
720 const float *pc = &ivfpq.precomputed_table
721 [(ki * pq.M + cm * Mf) * pq.ksub];
723 for (
int m = m0; m < m0 + Mf; m++) {
724 sim_table_ptrs [m] = pc;
730 FAISS_THROW_MSG (
"need precomputed tables");
734 FAISS_THROW_MSG (
"not implemented");
752 template <
typename IDType,
bool store_pairs,
class C, MetricType METRIC_TYPE>
753 struct IVFPQScannerT: QueryTables {
755 const uint8_t * list_codes;
756 const IDType * list_ids;
759 explicit IVFPQScannerT (
const IndexIVFPQ & ivfpq,
760 const IVFSearchParameters *params):
761 QueryTables (ivfpq, params)
763 FAISS_THROW_IF_NOT (pq.byte_per_idx == 1);
764 assert(METRIC_TYPE == metric_type);
769 void init_list (idx_t list_no,
float coarse_dis,
772 this->coarse_dis = coarse_dis;
775 dis0 = precompute_list_tables ();
776 }
else if (mode == 1) {
777 dis0 = precompute_list_table_pointers ();
786 size_t scan_list_with_table (
787 size_t ncode,
const uint8_t *codes,
const idx_t *ids,
788 size_t k,
float * heap_sim,
long * heap_ids)
const
791 for (
size_t j = 0; j < ncode; j++) {
794 const float *tab = sim_table;
796 for (
size_t m = 0; m < pq.M; m++) {
797 dis += tab[*codes++];
801 if (C::cmp (heap_sim[0], dis)) {
802 heap_pop<C> (k, heap_sim, heap_ids);
803 long id = store_pairs ? (key << 32 | j) : ids[j];
804 heap_push<C> (k, heap_sim, heap_ids, dis, id);
814 size_t scan_list_with_pointer (
815 size_t ncode,
const uint8_t *codes,
const idx_t *ids,
816 size_t k,
float * heap_sim,
long * heap_ids)
const
819 for (
size_t j = 0; j < ncode; j++) {
822 const float *tab = sim_table_2;
824 for (
size_t m = 0; m < pq.M; m++) {
826 dis += sim_table_ptrs [m][ci] - 2 * tab [ci];
830 if (C::cmp (heap_sim[0], dis)) {
831 heap_pop<C> (k, heap_sim, heap_ids);
832 long id = store_pairs ? (key << 32 | j) : ids[j];
833 heap_push<C> (k, heap_sim, heap_ids, dis, id);
842 size_t scan_on_the_fly_dist (
843 size_t ncode,
const uint8_t *codes,
const idx_t *ids,
844 size_t k,
float * heap_sim,
long * heap_ids)
const
850 if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
851 ivfpq.quantizer->reconstruct (key, residual_vec);
852 dis0 = fvec_inner_product (residual_vec, qi, d);
854 ivfpq.quantizer->compute_residual (qi, residual_vec, key);
862 for (
size_t j = 0; j < ncode; j++) {
864 pq.decode (codes, decoded_vec);
865 codes += pq.code_size;
868 if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
869 dis = dis0 + fvec_inner_product (decoded_vec, qi, d);
874 if (C::cmp (heap_sim[0], dis)) {
875 heap_pop<C> (k, heap_sim, heap_ids);
876 long id = store_pairs ? (key << 32 | j) : ids[j];
877 heap_push<C> (k, heap_sim, heap_ids, dis, id);
888 template <
class HammingComputer>
889 size_t scan_list_polysemous_hc (
890 size_t ncode,
const uint8_t *codes,
const idx_t *ids,
891 size_t k,
float * heap_sim,
long * heap_ids)
const
893 int ht = ivfpq.polysemous_ht;
894 size_t n_hamming_pass = 0, nup = 0;
896 int code_size = pq.code_size;
898 HammingComputer hc (q_code.data(), code_size);
900 for (
size_t j = 0; j < ncode; j++) {
901 const uint8_t *b_code = codes;
902 int hd = hc.hamming (b_code);
907 const float *tab = sim_table;
909 for (
size_t m = 0; m < pq.M; m++) {
910 dis += tab[*b_code++];
914 if (C::cmp (heap_sim[0], dis)) {
915 heap_pop<C> (k, heap_sim, heap_ids);
916 long id = store_pairs ? (key << 32 | j) : ids[j];
917 heap_push<C> (k, heap_sim, heap_ids, dis, id);
925 indexIVFPQ_stats.n_hamming_pass += n_hamming_pass;
930 size_t scan_list_polysemous (
931 size_t ncode,
const uint8_t *codes,
const idx_t *ids,
932 size_t k,
float * heap_sim,
long * heap_ids)
const
934 switch (pq.code_size) {
935 #define HANDLE_CODE_SIZE(cs) \
937 return scan_list_polysemous_hc <HammingComputer ## cs> \
938 (ncode, codes, ids, k, heap_sim, heap_ids); \
942 HANDLE_CODE_SIZE(16);
943 HANDLE_CODE_SIZE(20);
944 HANDLE_CODE_SIZE(32);
945 HANDLE_CODE_SIZE(64);
946 #undef HANDLE_CODE_SIZE
948 if (pq.code_size % 8 == 0)
949 return scan_list_polysemous_hc <HammingComputerM8>
950 (ncode, codes, ids, k, heap_sim, heap_ids);
952 return scan_list_polysemous_hc <HammingComputerM4>
953 (ncode, codes, ids, k, heap_sim, heap_ids);
967 template<
MetricType METRIC_TYPE,
bool store_pairs,
class C,
970 IVFPQScannerT<Index::idx_t, store_pairs, C, METRIC_TYPE>,
974 IVFPQScanner(
const IndexIVFPQ & ivfpq):
975 IVFPQScannerT<Index::idx_t, store_pairs, C, METRIC_TYPE>(ivfpq, nullptr)
979 void set_query (
const float *query)
override {
980 this->init_query (query);
983 void set_list (idx_t list_no,
float coarse_dis)
override {
984 this->init_list (list_no, coarse_dis, precompute_mode);
987 float distance_to_code (
const uint8_t *code)
const override {
988 assert(precompute_mode == 2);
989 float dis = this->dis0;
990 const float *tab = this->sim_table;
992 for (
size_t m = 0; m < this->pq.M; m++) {
994 tab += this->pq.ksub;
999 size_t scan_codes (
size_t ncode,
1000 const uint8_t *codes,
1002 float *heap_sim, idx_t *heap_ids,
1003 size_t k)
const override
1005 if (this->polysemous_ht > 0) {
1006 assert(precompute_mode == 2);
1007 this->scan_list_polysemous
1008 (ncode, codes, ids, k, heap_sim, heap_ids);
1009 }
else if (precompute_mode == 2) {
1010 this->scan_list_with_table
1011 (ncode, codes, ids, k, heap_sim, heap_ids);
1012 }
else if (precompute_mode == 1) {
1013 this->scan_list_with_pointer
1014 (ncode, codes, ids, k, heap_sim, heap_ids);
1015 }
else if (precompute_mode == 0) {
1016 this->scan_on_the_fly_dist
1017 (ncode, codes, ids, k, heap_sim, heap_ids);
1019 FAISS_THROW_MSG(
"bad precomp mode");
1031 InvertedListScanner *
1036 return new IVFPQScanner<
1039 return new IVFPQScanner<
1044 return new IVFPQScanner<
1047 return new IVFPQScanner<
1059 void IndexIVFPQStats::reset () {
1060 memset (
this, 0,
sizeof (*
this));
1065 IndexIVFPQ::IndexIVFPQ ()
1079 bool operator () (
int a,
int b)
const {
1080 return cmp (a, b) > 0;
1082 int cmp (
int a,
int b)
const {
1083 return memcmp (tab + a * code_size, tab + b * code_size,
1093 for (
size_t list_no = 0; list_no <
nlist; list_no++) {
1095 std::vector<int> ord (n);
1096 for (
int i = 0; i < n; i++) ord[i] = i;
1098 CodeCmp cs = { codes.get(), code_size };
1099 std::sort (ord.begin(), ord.end(), cs);
1103 for (
int i = 0; i < n; i++) {
1104 if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) {
1106 if (prev + 1 == i) {
1108 lims[ngroup] = lims[ngroup - 1];
1109 dup_ids [lims [ngroup]++] = list_ids [ord [prev]];
1111 dup_ids [lims [ngroup]++] = list_ids [ord [i]];
1127 IndexIVFPQR::IndexIVFPQR (
1128 Index * quantizer,
size_t d,
size_t nlist,
1129 size_t M,
size_t nbits_per_idx,
1130 size_t M_refine,
size_t nbits_per_idx_refine):
1131 IndexIVFPQ (quantizer, d, nlist, M, nbits_per_idx),
1132 refine_pq (d, M_refine, nbits_per_idx_refine),
1138 IndexIVFPQR::IndexIVFPQR ():
1158 float * residual_2 =
new float [n *
d];
1164 printf (
"training %zdx%zd 2nd level PQ quantizer on %ld %dD-vectors\n",
1180 const long *precomputed_idx) {
1182 float * residual_2 =
new float [n *
d];
1187 add_core_o (n, x, xids, residual_2, precomputed_idx);
1200 const float *L1_dis,
1201 float *distances,
idx_t *labels,
1208 size_t k_coarse = long(k *
k_factor);
1209 idx_t *coarse_labels =
new idx_t [k_coarse * n];
1212 float *coarse_distances =
new float [k_coarse * n];
1217 idx, L1_dis, coarse_distances, coarse_labels,
1222 indexIVFPQ_stats.search_cycles += TOC;
1227 size_t n_refine = 0;
1228 #pragma omp parallel reduction(+ : n_refine)
1231 float *residual_1 =
new float [2 *
d];
1233 float *residual_2 = residual_1 +
d;
1235 for (
idx_t i = 0; i < n; i++) {
1236 const float *xq = x + i *
d;
1237 const long * shortlist = coarse_labels + k_coarse * i;
1238 float * heap_sim = distances + k * i;
1239 long * heap_ids = labels + k * i;
1240 maxheap_heapify (k, heap_sim, heap_ids);
1242 for (
int j = 0; j < k_coarse; j++) {
1243 long sl = shortlist[j];
1245 if (sl == -1)
continue;
1247 int list_no = sl >> 32;
1248 int ofs = sl & 0xffffffff;
1250 assert (list_no >= 0 && list_no <
nlist);
1251 assert (ofs >= 0 && ofs < invlists->list_size (list_no));
1257 const uint8_t * l2code =
1261 for (
int l = 0; l <
d; l++)
1262 residual_2[l] = residual_1[l] - residual_2[l];
1266 assert (0 <=
id &&
id <
ntotal);
1270 float dis =
fvec_L2sqr (residual_1, residual_2, d);
1272 if (dis < heap_sim[0]) {
1273 maxheap_pop (k, heap_sim, heap_ids);
1274 long id_or_pair = store_pairs ? sl : id;
1275 maxheap_push (k, heap_sim, heap_ids, dis, id_or_pair);
1279 maxheap_reorder (k, heap_sim, heap_ids);
1282 indexIVFPQ_stats.nrefine += n_refine;
1283 indexIVFPQ_stats.refine_cycles += TOC;
1287 float* recons)
const
1292 assert (0 <=
id &&
id <
ntotal);
1294 std::vector<float> r3(d);
1296 for (
int i = 0; i <
d; ++i) {
1304 FAISS_THROW_IF_NOT(other);
1315 FAISS_THROW_MSG(
"not implemented");
1324 Index2Layer::Index2Layer (
Index * quantizer,
size_t nlist,
1327 Index (quantizer->d, metric),
1328 q1 (quantizer, nlist),
1329 pq (quantizer->d, M, 8)
1332 for (
int nbyte = 0; nbyte < 7; nbyte++) {
1333 if ((1L << (8 * nbyte)) >= nlist) {
1334 code_size_1 = nbyte;
1339 code_size = code_size_1 + code_size_2;
1342 Index2Layer::Index2Layer ()
1347 Index2Layer::~Index2Layer ()
1353 printf (
"training level-1 quantizer %ld vectors in %dD\n",
1360 printf(
"computing residuals\n");
1363 const float * x_in = x;
1371 std::vector<idx_t>
assign(n);
1373 std::vector<float> residuals(n * d);
1374 for (
idx_t i = 0; i < n; i++) {
1376 x + i * d, residuals.data() + i *
d, assign[i]);
1380 printf (
"training %zdx%zd product quantizer on %ld vectors in %dD\n",
1383 pq.train (n, residuals.data());
1392 for (
idx_t i0 = 0; i0 < n; i0 += bs) {
1393 idx_t i1 = std::min(i0 + bs, n);
1395 printf(
"Index2Layer::add: adding %ld:%ld / %ld\n",
1398 add (i1 - i0, x + i0 * d);
1403 std::vector<idx_t> codes1 (n);
1405 std::vector<float> residuals(n * d);
1406 for (
idx_t i = 0; i < n; i++) {
1408 x + i * d, residuals.data() + i *
d, codes1[i]);
1414 codes.resize ((
ntotal + n) * code_size);
1419 const char *ip = (
char*)&i;
1420 FAISS_THROW_IF_NOT_MSG (ip[0] == 0x44,
1421 "works only on a little-endian CPU");
1425 for (
idx_t i = 0; i < n; i++) {
1428 memcpy (wp, &codes2[i * code_size_2], code_size_2);
1442 FAISS_THROW_MSG(
"not implemented");
1449 FAISS_THROW_IF_NOT (i0 >= 0 && i0 + ni <=
ntotal);
1450 const uint8_t *rp = &codes[i0 *
code_size];
1452 for (
idx_t i = 0; i < ni; i++) {
1458 for (
idx_t j = 0; j <
d; j++) {
1459 recons[j] += recons1[j];
1470 FAISS_THROW_IF_NOT (other.
ntotal == 0);
1472 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
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
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
long idx_t
all indices are this type
void compute_code(const float *x, uint8_t *code) const
Quantize one vector with the product quantizer.
idx_t ntotal
total nb of indexed vectors
void train(idx_t n, const float *x) override
bool verbose
verbosity level
void reset() override
removes all elements from the database.
double getmillisecs()
ms elapsed since some arbitrary epoch
optimizes the order of indices in a ProductQuantizer
void add(idx_t n, const float *x) override
float fvec_norm_L2sqr(const float *x, size_t d)
void train_q1(size_t n, const float *x, bool verbose, MetricType metric_type)
Trains the quantizer and calls train_residual to train sub-quantizers.
ClusteringParameters cp
parameters used during clustering
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
not implemented
bool by_residual
Encode residual or plain vector?
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
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