Faiss
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
/data/users/matthijs/github_faiss/faiss/AutoTune.cpp
1 /**
2  * Copyright (c) 2015-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD+Patents license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
9 /* Copyright 2004-present Facebook. All Rights Reserved.
10  implementation of Hyper-parameter auto-tuning
11 */
12 
13 #include "AutoTune.h"
14 
15 #include "FaissAssert.h"
16 #include "utils.h"
17 
18 #include "IndexFlat.h"
19 #include "VectorTransform.h"
20 #include "IndexLSH.h"
21 #include "IndexPQ.h"
22 #include "IndexIVF.h"
23 #include "IndexIVFPQ.h"
24 #include "MetaIndexes.h"
25 #include "IndexScalarQuantizer.h"
26 #include "IndexHNSW.h"
27 
28 
29 namespace faiss {
30 
31 
32 AutoTuneCriterion::AutoTuneCriterion (idx_t nq, idx_t nnn):
33  nq (nq), nnn (nnn), gt_nnn (0)
34 {}
35 
36 
38  int gt_nnn, const float *gt_D_in, const idx_t *gt_I_in)
39 {
40  this->gt_nnn = gt_nnn;
41  if (gt_D_in) { // allow null for this, as it is often not used
42  gt_D.resize (nq * gt_nnn);
43  memcpy (gt_D.data(), gt_D_in, sizeof (gt_D[0]) * nq * gt_nnn);
44  }
45  gt_I.resize (nq * gt_nnn);
46  memcpy (gt_I.data(), gt_I_in, sizeof (gt_I[0]) * nq * gt_nnn);
47 }
48 
49 
50 
51 OneRecallAtRCriterion::OneRecallAtRCriterion (idx_t nq, idx_t R):
52  AutoTuneCriterion(nq, R), R(R)
53 {}
54 
55 double OneRecallAtRCriterion::evaluate(const float* /*D*/, const idx_t* I)
56  const {
57  FAISS_THROW_IF_NOT_MSG(
58  (gt_I.size() == gt_nnn * nq && gt_nnn >= 1 && nnn >= R),
59  "ground truth not initialized");
60  idx_t n_ok = 0;
61  for (idx_t q = 0; q < nq; q++) {
62  idx_t gt_nn = gt_I[q * gt_nnn];
63  const idx_t* I_line = I + q * nnn;
64  for (int i = 0; i < R; i++) {
65  if (I_line[i] == gt_nn) {
66  n_ok++;
67  break;
68  }
69  }
70  }
71  return n_ok / double(nq);
72 }
73 
74 
75 IntersectionCriterion::IntersectionCriterion (idx_t nq, idx_t R):
76  AutoTuneCriterion(nq, R), R(R)
77 {}
78 
79 double IntersectionCriterion::evaluate(const float* /*D*/, const idx_t* I)
80  const {
81  FAISS_THROW_IF_NOT_MSG(
82  (gt_I.size() == gt_nnn * nq && gt_nnn >= R && nnn >= R),
83  "ground truth not initialized");
84  long n_ok = 0;
85 #pragma omp parallel for reduction(+: n_ok)
86  for (idx_t q = 0; q < nq; q++) {
88  R, &gt_I [q * gt_nnn],
89  R, I + q * nnn);
90  }
91  return n_ok / double (nq * R);
92 }
93 
94 /***************************************************************
95  * OperatingPoints
96  ***************************************************************/
97 
98 OperatingPoints::OperatingPoints ()
99 {
100  clear();
101 }
102 
104 {
105  all_pts.clear();
106  optimal_pts.clear();
107  /// default point: doing nothing gives 0 performance and takes 0 time
108  OperatingPoint op = {0, 0, "", -1};
109  optimal_pts.push_back(op);
110 }
111 
112 /// add a performance measure
113 bool OperatingPoints::add (double perf, double t, const std::string & key,
114  size_t cno)
115 {
116  OperatingPoint op = {perf, t, key, long(cno)};
117  all_pts.push_back (op);
118  if (perf == 0) {
119  return false; // no method for 0 accuracy is faster than doing nothing
120  }
121  std::vector<OperatingPoint> & a = optimal_pts;
122  if (perf > a.back().perf) {
123  // keep unconditionally
124  a.push_back (op);
125  } else if (perf == a.back().perf) {
126  if (t < a.back ().t) {
127  a.back() = op;
128  } else {
129  return false;
130  }
131  } else {
132  int i;
133  // stricto sensu this should be a bissection
134  for (i = 0; i < a.size(); i++) {
135  if (a[i].perf >= perf) break;
136  }
137  assert (i < a.size());
138  if (t < a[i].t) {
139  if (a[i].perf == perf) {
140  a[i] = op;
141  } else {
142  a.insert (a.begin() + i, op);
143  }
144  } else {
145  return false;
146  }
147  }
148  { // remove non-optimal points from array
149  int i = a.size() - 1;
150  while (i > 0) {
151  if (a[i].t < a[i - 1].t)
152  a.erase (a.begin() + (i - 1));
153  i--;
154  }
155  }
156  return true;
157 }
158 
159 
161  const std::string & prefix)
162 {
163  int n_add = 0;
164  for (int i = 0; i < other.all_pts.size(); i++) {
165  const OperatingPoint & op = other.all_pts[i];
166  if (add (op.perf, op.t, prefix + op.key, op.cno))
167  n_add++;
168  }
169  return n_add;
170 }
171 
172 
173 
174 /// get time required to obtain a given performance measure
175 double OperatingPoints::t_for_perf (double perf) const
176 {
177  const std::vector<OperatingPoint> & a = optimal_pts;
178  if (perf > a.back().perf) return 1e50;
179  int i0 = -1, i1 = a.size() - 1;
180  while (i0 + 1 < i1) {
181  int imed = (i0 + i1 + 1) / 2;
182  if (a[imed].perf < perf) i0 = imed;
183  else i1 = imed;
184  }
185  return a[i1].t;
186 }
187 
188 
189 void OperatingPoints::all_to_gnuplot (const char *fname) const
190 {
191  FILE *f = fopen(fname, "w");
192  if (!f) {
193  fprintf (stderr, "cannot open %s", fname);
194  perror("");
195  abort();
196  }
197  for (int i = 0; i < all_pts.size(); i++) {
198  const OperatingPoint & op = all_pts[i];
199  fprintf (f, "%g %g %s\n", op.perf, op.t, op.key.c_str());
200  }
201  fclose(f);
202 }
203 
204 void OperatingPoints::optimal_to_gnuplot (const char *fname) const
205 {
206  FILE *f = fopen(fname, "w");
207  if (!f) {
208  fprintf (stderr, "cannot open %s", fname);
209  perror("");
210  abort();
211  }
212  double prev_perf = 0.0;
213  for (int i = 0; i < optimal_pts.size(); i++) {
214  const OperatingPoint & op = optimal_pts[i];
215  fprintf (f, "%g %g\n", prev_perf, op.t);
216  fprintf (f, "%g %g %s\n", op.perf, op.t, op.key.c_str());
217  prev_perf = op.perf;
218  }
219  fclose(f);
220 }
221 
222 void OperatingPoints::display (bool only_optimal) const
223 {
224  const std::vector<OperatingPoint> &pts =
225  only_optimal ? optimal_pts : all_pts;
226  printf("Tested %ld operating points, %ld ones are optimal:\n",
227  all_pts.size(), optimal_pts.size());
228 
229  for (int i = 0; i < pts.size(); i++) {
230  const OperatingPoint & op = pts[i];
231  const char *star = "";
232  if (!only_optimal) {
233  for (int j = 0; j < optimal_pts.size(); j++) {
234  if (op.cno == optimal_pts[j].cno) {
235  star = "*";
236  break;
237  }
238  }
239  }
240  printf ("cno=%ld key=%s perf=%.4f t=%.3f %s\n",
241  op.cno, op.key.c_str(), op.perf, op.t, star);
242  }
243 
244 }
245 
246 /***************************************************************
247  * ParameterSpace
248  ***************************************************************/
249 
250 ParameterSpace::ParameterSpace ():
251  verbose (1), n_experiments (500),
252  batchsize (1<<30), thread_over_batches (false)
253 {
254 }
255 
256 /* not keeping this constructor as inheritors will call the parent
257  initialize()
258  */
259 
260 #if 0
261 ParameterSpace::ParameterSpace (Index *index):
262  verbose (1), n_experiments (500),
263  batchsize (1<<30), thread_over_batches (false)
264 {
265  initialize(index);
266 }
267 #endif
268 
270 {
271  size_t n = 1;
272  for (int i = 0; i < parameter_ranges.size(); i++)
273  n *= parameter_ranges[i].values.size();
274  return n;
275 }
276 
277 /// get string representation of the combination
278 std::string ParameterSpace::combination_name (size_t cno) const {
279  char buf[1000], *wp = buf;
280  *wp = 0;
281  for (int i = 0; i < parameter_ranges.size(); i++) {
282  const ParameterRange & pr = parameter_ranges[i];
283  size_t j = cno % pr.values.size();
284  cno /= pr.values.size();
285  wp += snprintf (
286  wp, buf + 1000 - wp, "%s%s=%g", i == 0 ? "" : ",",
287  pr.name.c_str(), pr.values[j]);
288  }
289  return std::string (buf);
290 }
291 
292 
293 bool ParameterSpace::combination_ge (size_t c1, size_t c2) const
294 {
295  for (int i = 0; i < parameter_ranges.size(); i++) {
296  int nval = parameter_ranges[i].values.size();
297  size_t j1 = c1 % nval;
298  size_t j2 = c2 % nval;
299  if (!(j1 >= j2)) return false;
300  c1 /= nval;
301  c2 /= nval;
302  }
303  return true;
304 }
305 
306 
307 
308 #define DC(classname) \
309  const classname *ix = dynamic_cast<const classname *>(index)
310 
311 static void init_pq_ParameterRange (const ProductQuantizer & pq,
312  ParameterRange & pr)
313 {
314  if (pq.code_size % 4 == 0) {
315  // Polysemous not supported for code sizes that are not a
316  // multiple of 4
317  for (int i = 2; i <= pq.code_size * 8 / 2; i+= 2)
318  pr.values.push_back(i);
319  }
320  pr.values.push_back (pq.code_size * 8);
321 }
322 
324 {
325  for (auto & pr : parameter_ranges) {
326  if (pr.name == name) {
327  return pr;
328  }
329  }
330  parameter_ranges.push_back (ParameterRange ());
331  parameter_ranges.back ().name = name;
332  return parameter_ranges.back ();
333 }
334 
335 
336 /// initialize with reasonable parameters for the index
337 void ParameterSpace::initialize (const Index * index)
338 {
339  if (DC (IndexPreTransform)) {
340  index = ix->index;
341  }
342  if (DC (IndexRefineFlat)) {
343  ParameterRange & pr = add_range("k_factor_rf");
344  for (int i = 0; i <= 6; i++) {
345  pr.values.push_back (1 << i);
346  }
347  index = ix->base_index;
348  }
349  if (DC (IndexPreTransform)) {
350  index = ix->index;
351  }
352 
353  if (DC (IndexIVF)) {
354  {
355  ParameterRange & pr = add_range("nprobe");
356  for (int i = 0; i < 13; i++) {
357  size_t nprobe = 1 << i;
358  if (nprobe >= ix->nlist) break;
359  pr.values.push_back (nprobe);
360  }
361  }
362  if (dynamic_cast<const IndexHNSW*>(ix->quantizer)) {
363  ParameterRange & pr = add_range("efSearch");
364  for (int i = 2; i <= 9; i++) {
365  pr.values.push_back (1 << i);
366  }
367  }
368  }
369  if (DC (IndexPQ)) {
370  ParameterRange & pr = add_range("ht");
371  init_pq_ParameterRange (ix->pq, pr);
372  }
373  if (DC (IndexIVFPQ)) {
374  ParameterRange & pr = add_range("ht");
375  init_pq_ParameterRange (ix->pq, pr);
376  }
377 
378  if (DC (IndexIVF)) {
379  const MultiIndexQuantizer *miq =
380  dynamic_cast<const MultiIndexQuantizer *> (ix->quantizer);
381  if (miq) {
382  ParameterRange & pr_max_codes = add_range("max_codes");
383  for (int i = 8; i < 20; i++) {
384  pr_max_codes.values.push_back (1 << i);
385  }
386  pr_max_codes.values.push_back (1.0 / 0.0);
387  }
388  }
389  if (DC (IndexIVFPQR)) {
390  ParameterRange & pr = add_range("k_factor");
391  for (int i = 0; i <= 6; i++) {
392  pr.values.push_back (1 << i);
393  }
394  }
395  if (dynamic_cast<const IndexHNSW*>(index)) {
396  ParameterRange & pr = add_range("efSearch");
397  for (int i = 2; i <= 9; i++) {
398  pr.values.push_back (1 << i);
399  }
400  }
401 }
402 
403 #undef DC
404 
405 // non-const version
406 #define DC(classname) classname *ix = dynamic_cast<classname *>(index)
407 
408 
409 /// set a combination of parameters on an index
410 void ParameterSpace::set_index_parameters (Index *index, size_t cno) const
411 {
412 
413  for (int i = 0; i < parameter_ranges.size(); i++) {
414  const ParameterRange & pr = parameter_ranges[i];
415  size_t j = cno % pr.values.size();
416  cno /= pr.values.size();
417  double val = pr.values [j];
418  set_index_parameter (index, pr.name, val);
419  }
420 }
421 
422 /// set a combination of parameters on an index
424  Index *index, const char *description_in) const
425 {
426  char description[strlen(description_in) + 1];
427  char *ptr;
428  memcpy (description, description_in, strlen(description_in) + 1);
429 
430  for (char *tok = strtok_r (description, " ,", &ptr);
431  tok;
432  tok = strtok_r (nullptr, " ,", &ptr)) {
433  char name[100];
434  double val;
435  int ret = sscanf (tok, "%100[^=]=%lf", name, &val);
436  FAISS_THROW_IF_NOT_FMT (
437  ret == 2, "could not interpret parameters %s", tok);
438  set_index_parameter (index, name, val);
439  }
440 
441 }
442 
444  Index * index, const std::string & name, double val) const
445 {
446  if (verbose > 1)
447  printf(" set %s=%g\n", name.c_str(), val);
448 
449  if (name == "verbose") {
450  index->verbose = int(val);
451  // and fall through to also enable it on sub-indexes
452  }
453  if (DC (IndexPreTransform)) {
454  set_index_parameter (ix->index, name, val);
455  return;
456  }
457  if (DC (IndexShards)) {
458  // call on all sub-indexes
459  for (auto & shard_index : ix->shard_indexes) {
460  set_index_parameter (shard_index, name, val);
461  }
462  return;
463  }
464  if (DC (IndexRefineFlat)) {
465  if (name == "k_factor_rf") {
466  ix->k_factor = int(val);
467  return;
468  }
469  // otherwise it is for the sub-index
470  set_index_parameter (&ix->refine_index, name, val);
471  return;
472  }
473 
474  if (name == "verbose") {
475  index->verbose = int(val);
476  return; // last verbose that we could find
477  }
478 
479  if (name == "nprobe") {
480  if ( DC(IndexIVF)) {
481  ix->nprobe = int(val);
482  return;
483  }
484  }
485 
486  if (name == "ht") {
487  if (DC (IndexPQ)) {
488  if (val >= ix->pq.code_size * 8) {
489  ix->search_type = IndexPQ::ST_PQ;
490  } else {
491  ix->search_type = IndexPQ::ST_polysemous;
492  ix->polysemous_ht = int(val);
493  }
494  return;
495  } else if (DC (IndexIVFPQ)) {
496  if (val >= ix->pq.code_size * 8) {
497  ix->polysemous_ht = 0;
498  } else {
499  ix->polysemous_ht = int(val);
500  }
501  return;
502  }
503  }
504 
505  if (name == "k_factor") {
506  if (DC (IndexIVFPQR)) {
507  ix->k_factor = val;
508  return;
509  }
510  }
511  if (name == "max_codes") {
512  if (DC (IndexIVF)) {
513  ix->max_codes = finite(val) ? size_t(val) : 0;
514  return;
515  }
516  }
517  FAISS_THROW_FMT ("ParameterSpace::set_index_parameter:"
518  "could not set parameter %s",
519  name.c_str());
520 }
521 
523 {
524  printf ("ParameterSpace, %ld parameters, %ld combinations:\n",
525  parameter_ranges.size (), n_combinations ());
526  for (int i = 0; i < parameter_ranges.size(); i++) {
527  const ParameterRange & pr = parameter_ranges[i];
528  printf (" %s: ", pr.name.c_str ());
529  char sep = '[';
530  for (int j = 0; j < pr.values.size(); j++) {
531  printf ("%c %g", sep, pr.values [j]);
532  sep = ',';
533  }
534  printf ("]\n");
535  }
536 }
537 
538 
539 
540 void ParameterSpace::update_bounds (size_t cno, const OperatingPoint & op,
541  double *upper_bound_perf,
542  double *lower_bound_t) const
543 {
544  if (combination_ge (cno, op.cno)) {
545  if (op.t > *lower_bound_t) *lower_bound_t = op.t;
546  }
547  if (combination_ge (op.cno, cno)) {
548  if (op.perf < *upper_bound_perf) *upper_bound_perf = op.perf;
549  }
550 }
551 
552 
553 
555  size_t nq, const float *xq,
556  const AutoTuneCriterion & crit,
557  OperatingPoints * ops) const
558 {
559  FAISS_THROW_IF_NOT_MSG (nq == crit.nq,
560  "criterion does not have the same nb of queries");
561 
562  size_t n_comb = n_combinations ();
563 
564  if (n_experiments == 0) {
565 
566  for (size_t cno = 0; cno < n_comb; cno++) {
567  set_index_parameters (index, cno);
568  std::vector<Index::idx_t> I(nq * crit.nnn);
569  std::vector<float> D(nq * crit.nnn);
570 
571  double t0 = getmillisecs ();
572  index->search (nq, xq, crit.nnn, D.data(), I.data());
573  double t_search = (getmillisecs() - t0) / 1e3;
574 
575  double perf = crit.evaluate (D.data(), I.data());
576 
577  bool keep = ops->add (perf, t_search, combination_name (cno), cno);
578 
579  if (verbose)
580  printf(" %ld/%ld: %s perf=%.3f t=%.3f s %s\n", cno, n_comb,
581  combination_name (cno).c_str(), perf, t_search,
582  keep ? "*" : "");
583  }
584  return;
585  }
586 
587  int n_exp = n_experiments;
588 
589  if (n_exp > n_comb) n_exp = n_comb;
590  FAISS_THROW_IF_NOT (n_comb == 1 || n_exp > 2);
591  std::vector<int> perm (n_comb);
592  // make sure the slowest and fastest experiment are run
593  perm[0] = 0;
594  if (n_comb > 1) {
595  perm[1] = n_comb - 1;
596  rand_perm (&perm[2], n_comb - 2, 1234);
597  for (int i = 2; i < perm.size(); i++) perm[i] ++;
598  }
599 
600  for (size_t xp = 0; xp < n_exp; xp++) {
601  size_t cno = perm[xp];
602 
603  if (verbose)
604  printf(" %ld/%d: cno=%ld %s ", xp, n_exp, cno,
605  combination_name (cno).c_str());
606 
607  {
608  double lower_bound_t = 0.0;
609  double upper_bound_perf = 1.0;
610  for (int i = 0; i < ops->all_pts.size(); i++) {
611  update_bounds (cno, ops->all_pts[i],
612  &upper_bound_perf, &lower_bound_t);
613  }
614  double best_t = ops->t_for_perf (upper_bound_perf);
615  if (verbose)
616  printf ("bounds [perf<=%.3f t>=%.3f] %s",
617  upper_bound_perf, lower_bound_t,
618  best_t <= lower_bound_t ? "skip\n" : "");
619  if (best_t <= lower_bound_t) continue;
620  }
621 
622  set_index_parameters (index, cno);
623  std::vector<Index::idx_t> I(nq * crit.nnn);
624  std::vector<float> D(nq * crit.nnn);
625 
626  double t0 = getmillisecs ();
627 
628  if (thread_over_batches) {
629 #pragma omp parallel for
630  for (size_t q0 = 0; q0 < nq; q0 += batchsize) {
631  size_t q1 = q0 + batchsize;
632  if (q1 > nq) q1 = nq;
633  index->search (q1 - q0, xq + q0 * index->d,
634  crit.nnn,
635  D.data() + q0 * crit.nnn,
636  I.data() + q0 * crit.nnn);
637  }
638  } else {
639  for (size_t q0 = 0; q0 < nq; q0 += batchsize) {
640  size_t q1 = q0 + batchsize;
641  if (q1 > nq) q1 = nq;
642  index->search (q1 - q0, xq + q0 * index->d,
643  crit.nnn,
644  D.data() + q0 * crit.nnn,
645  I.data() + q0 * crit.nnn);
646  }
647  }
648 
649  double t_search = (getmillisecs() - t0) / 1e3;
650 
651  double perf = crit.evaluate (D.data(), I.data());
652 
653  bool keep = ops->add (perf, t_search, combination_name (cno), cno);
654 
655  if (verbose)
656  printf(" perf %.3f t %.3f %s\n", perf, t_search,
657  keep ? "*" : "");
658  }
659 }
660 
661 /***************************************************************
662  * index_factory
663  ***************************************************************/
664 
665 namespace {
666 
667 struct VTChain {
668  std::vector<VectorTransform *> chain;
669  ~VTChain () {
670  for (int i = 0; i < chain.size(); i++) {
671  delete chain[i];
672  }
673  }
674 };
675 
676 
677 /// what kind of training does this coarse quantizer require?
678 char get_trains_alone(const Index *coarse_quantizer) {
679  return
680  dynamic_cast<const MultiIndexQuantizer*>(coarse_quantizer) ? 1 :
681  0;
682 }
683 
684 
685 }
686 
687 Index *index_factory (int d, const char *description_in, MetricType metric)
688 {
689  VTChain vts;
690  Index *coarse_quantizer = nullptr;
691  Index *index = nullptr;
692  bool add_idmap = false;
693  bool make_IndexRefineFlat = false;
694 
695  ScopeDeleter1<Index> del_coarse_quantizer, del_index;
696 
697  char description[strlen(description_in) + 1];
698  char *ptr;
699  memcpy (description, description_in, strlen(description_in) + 1);
700 
701  int ncentroids = -1;
702 
703  for (char *tok = strtok_r (description, " ,", &ptr);
704  tok;
705  tok = strtok_r (nullptr, " ,", &ptr)) {
706  int d_out, opq_M, nbit, M, M2, pq_m, ncent;
707  std::string stok(tok);
708 
709  // to avoid mem leaks with exceptions:
710  // do all tests before any instanciation
711 
712  VectorTransform *vt_1 = nullptr;
713  Index *coarse_quantizer_1 = nullptr;
714  Index *index_1 = nullptr;
715 
716  // VectorTransforms
717  if (sscanf (tok, "PCA%d", &d_out) == 1) {
718  vt_1 = new PCAMatrix (d, d_out);
719  d = d_out;
720  } else if (sscanf (tok, "PCAR%d", &d_out) == 1) {
721  vt_1 = new PCAMatrix (d, d_out, 0, true);
722  d = d_out;
723  } else if (sscanf (tok, "PCAW%d", &d_out) == 1) {
724  vt_1 = new PCAMatrix (d, d_out, -0.5, false);
725  d = d_out;
726  } else if (sscanf (tok, "PCAWR%d", &d_out) == 1) {
727  vt_1 = new PCAMatrix (d, d_out, -0.5, true);
728  d = d_out;
729  } else if (sscanf (tok, "OPQ%d_%d", &opq_M, &d_out) == 2) {
730  vt_1 = new OPQMatrix (d, opq_M, d_out);
731  d = d_out;
732  } else if (sscanf (tok, "OPQ%d", &opq_M) == 1) {
733  vt_1 = new OPQMatrix (d, opq_M);
734  } else if (stok == "L2norm") {
735  vt_1 = new NormalizationTransform (d, 2.0);
736 
737 
738  } else if (!coarse_quantizer &&
739  sscanf (tok, "IVF%d", &ncentroids) == 1) {
740  if (metric == METRIC_L2) {
741  coarse_quantizer_1 = new IndexFlatL2 (d);
742  } else { // if (metric == METRIC_IP)
743  coarse_quantizer_1 = new IndexFlatIP (d);
744  }
745  } else if (!coarse_quantizer && sscanf (tok, "IMI2x%d", &nbit) == 1) {
746  FAISS_THROW_IF_NOT_MSG (metric == METRIC_L2,
747  "MultiIndex not implemented for inner prod search");
748  coarse_quantizer_1 = new MultiIndexQuantizer (d, 2, nbit);
749  ncentroids = 1 << (2 * nbit);
750  } else if (stok == "IDMap") {
751  add_idmap = true;
752 
753  // IVFs
754  } else if (!index && stok == "Flat") {
755  if (coarse_quantizer) {
756  // if there was an IVF in front, then it is an IVFFlat
757  IndexIVF *index_ivf = new IndexIVFFlat (
758  coarse_quantizer, d, ncentroids, metric);
759  index_ivf->quantizer_trains_alone =
760  get_trains_alone (coarse_quantizer);
761  index_ivf->cp.spherical = metric == METRIC_INNER_PRODUCT;
762  del_coarse_quantizer.release ();
763  index_ivf->own_fields = true;
764  index_1 = index_ivf;
765  } else {
766  index_1 = new IndexFlat (d, metric);
767  }
768  } else if (!index && (stok == "SQ8" || stok == "SQ4")) {
770  stok == "SQ8" ? ScalarQuantizer::QT_8bit :
771  stok == "SQ4" ? ScalarQuantizer::QT_4bit :
773  if (coarse_quantizer) {
774  IndexIVFScalarQuantizer *index_ivf =
776  coarse_quantizer, d, ncentroids, qt, metric);
777  index_ivf->quantizer_trains_alone =
778  get_trains_alone (coarse_quantizer);
779  del_coarse_quantizer.release ();
780  index_ivf->own_fields = true;
781  index_1 = index_ivf;
782  } else {
783  index_1 = new IndexScalarQuantizer (d, qt, metric);
784  }
785  } else if (!index && sscanf (tok, "PQ%d+%d", &M, &M2) == 2) {
786  FAISS_THROW_IF_NOT_MSG(coarse_quantizer,
787  "PQ with + works only with an IVF");
788  FAISS_THROW_IF_NOT_MSG(metric == METRIC_L2,
789  "IVFPQR not implemented for inner product search");
790  IndexIVFPQR *index_ivf = new IndexIVFPQR (
791  coarse_quantizer, d, ncentroids, M, 8, M2, 8);
792  index_ivf->quantizer_trains_alone =
793  get_trains_alone (coarse_quantizer);
794  del_coarse_quantizer.release ();
795  index_ivf->own_fields = true;
796  index_1 = index_ivf;
797  } else if (!index && (sscanf (tok, "PQ%d", &M) == 1 ||
798  sscanf (tok, "PQ%dnp", &M) == 1)) {
799  bool do_polysemous_training = stok.find("np") == std::string::npos;
800  if (coarse_quantizer) {
801  IndexIVFPQ *index_ivf = new IndexIVFPQ (
802  coarse_quantizer, d, ncentroids, M, 8);
803  index_ivf->quantizer_trains_alone =
804  get_trains_alone (coarse_quantizer);
805  index_ivf->metric_type = metric;
806  index_ivf->cp.spherical = metric == METRIC_INNER_PRODUCT;
807  del_coarse_quantizer.release ();
808  index_ivf->own_fields = true;
809  index_ivf->do_polysemous_training = do_polysemous_training;
810  index_1 = index_ivf;
811  } else {
812  IndexPQ *index_pq = new IndexPQ (d, M, 8, metric);
813  index_pq->do_polysemous_training = do_polysemous_training;
814  index_1 = index_pq;
815  }
816  } else if (stok == "RFlat") {
817  make_IndexRefineFlat = true;
818  } else {
819  FAISS_THROW_FMT( "could not parse token \"%s\" in %s\n",
820  tok, description_in);
821  }
822 
823  if (index_1 && add_idmap) {
824  IndexIDMap *idmap = new IndexIDMap(index_1);
825  del_index.set (idmap);
826  idmap->own_fields = true;
827  index_1 = idmap;
828  add_idmap = false;
829  }
830 
831  if (vt_1) {
832  vts.chain.push_back (vt_1);
833  }
834 
835  if (coarse_quantizer_1) {
836  coarse_quantizer = coarse_quantizer_1;
837  del_coarse_quantizer.set (coarse_quantizer);
838  }
839 
840  if (index_1) {
841  index = index_1;
842  del_index.set (index);
843  }
844  }
845 
846  FAISS_THROW_IF_NOT_FMT(index, "descrption %s did not generate an index",
847  description_in);
848 
849  // nothing can go wrong now
850  del_index.release ();
851  del_coarse_quantizer.release ();
852 
853  if (add_idmap) {
854  fprintf(stderr, "index_factory: WARNING: "
855  "IDMap option not used\n");
856  }
857 
858  if (vts.chain.size() > 0) {
859  IndexPreTransform *index_pt = new IndexPreTransform (index);
860  index_pt->own_fields = true;
861  // add from back
862  while (vts.chain.size() > 0) {
863  index_pt->prepend_transform (vts.chain.back ());
864  vts.chain.pop_back ();
865  }
866  index = index_pt;
867  }
868 
869  if (make_IndexRefineFlat) {
870  IndexRefineFlat *index_rf = new IndexRefineFlat (index);
871  index_rf->own_fields = true;
872  index = index_rf;
873  }
874 
875  return index;
876 }
877 
878 
879 
880 
881 }; // namespace faiss
void explore(Index *index, size_t nq, const float *xq, const AutoTuneCriterion &crit, OperatingPoints *ops) const
Definition: AutoTune.cpp:554
std::vector< ParameterRange > parameter_ranges
all tunable parameters
Definition: AutoTune.h:134
std::string key
key that identifies this op pt
Definition: AutoTune.h:89
long cno
integer identifer
Definition: AutoTune.h:90
bool do_polysemous_training
false = standard PQ
Definition: IndexPQ.h:69
void display(bool only_optimal=true) const
easy-to-read output
Definition: AutoTune.cpp:222
double perf
performance measure (output of a Criterion)
Definition: AutoTune.h:87
double t_for_perf(double perf) const
get time required to obtain a given performance measure
Definition: AutoTune.cpp:175
idx_t nnn
nb of NNs that the query should request
Definition: AutoTune.h:29
bool add(double perf, double t, const std::string &key, size_t cno=0)
add a performance measure. Return whether it is an optimal point
Definition: AutoTune.cpp:113
bool do_polysemous_training
reorder PQ centroids after training?
Definition: IndexIVFPQ.h:34
size_t batchsize
maximum number of queries to submit at a time.
Definition: AutoTune.h:145
virtual double evaluate(const float *D, const idx_t *I) const =0
idx_t nq
nb of queries this criterion is evaluated on
Definition: AutoTune.h:28
std::vector< OperatingPoint > optimal_pts
optimal operating points, sorted by perf
Definition: AutoTune.h:98
void set_groundtruth(int gt_nnn, const float *gt_D_in, const idx_t *gt_I_in)
Definition: AutoTune.cpp:37
ParameterRange & add_range(const char *name)
add a new parameter (or return it if it exists)
Definition: AutoTune.cpp:323
idx_t gt_nnn
nb of GT NNs required to evaluate crterion
Definition: AutoTune.h:30
void all_to_gnuplot(const char *fname) const
output to a format easy to digest by gnuplot
Definition: AutoTune.cpp:189
bool own_fields
should the base index be deallocated?
Definition: IndexFlat.h:110
int d
vector dimension
Definition: Index.h:64
size_t code_size
byte per indexed vector
std::vector< OperatingPoint > all_pts
all operating points
Definition: AutoTune.h:95
size_t ranklist_intersection_size(size_t k1, const long *v1, size_t k2, const long *v2_in)
Definition: utils.cpp:1576
ClusteringParameters cp
to override default clustering params
Definition: IndexIVF.h:44
bool verbose
verbosity level
Definition: Index.h:66
double getmillisecs()
ms elapsed since some arbitrary epoch
Definition: utils.cpp:74
std::vector< float > gt_D
Ground-truth distances (size nq * gt_nnn)
Definition: AutoTune.h:32
std::string combination_name(size_t cno) const
get string representation of the combination
Definition: AutoTune.cpp:278
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const =0
void update_bounds(size_t cno, const OperatingPoint &op, double *upper_bound_perf, double *lower_bound_t) const
Definition: AutoTune.cpp:540
bool own_fields
! the sub-index
virtual void initialize(const Index *index)
initialize with reasonable parameters for the index
Definition: AutoTune.cpp:337
int verbose
verbosity during exploration
Definition: AutoTune.h:139
int merge_with(const OperatingPoints &other, const std::string &prefix="")
add operating points from other to this, with a prefix to the keys
Definition: AutoTune.cpp:160
virtual void set_index_parameter(Index *index, const std::string &name, double val) const
set one of the parameters
Definition: AutoTune.cpp:443
size_t n_combinations() const
nb of combinations, = product of values sizes
Definition: AutoTune.cpp:269
MetricType metric_type
type of metric this index uses for search
Definition: Index.h:72
void set_index_parameters(Index *index, size_t cno) const
set a combination of parameters on an index
Definition: AutoTune.cpp:410
asymmetric product quantizer (default)
Definition: IndexPQ.h:76
void display() const
print a description on stdout
Definition: AutoTune.cpp:522
HE filter (using ht) + PQ combination.
Definition: IndexPQ.h:80
bool combination_ge(size_t c1, size_t c2) const
returns whether combinations c1 &gt;= c2 in the tuple sense
Definition: AutoTune.cpp:293
bool spherical
do we want normalized centroids?
Definition: Clustering.h:29
bool own_fields
whether object owns the quantizer
Definition: IndexIVF.h:42
possible values of a parameter, sorted from least to most expensive/accurate
Definition: AutoTune.h:125
Index * index_factory(int d, const char *description_in, MetricType metric)
Definition: AutoTune.cpp:687
int n_experiments
nb of experiments during optimization (0 = try all combinations)
Definition: AutoTune.h:142
std::vector< idx_t > gt_I
Ground-truth indexes (size nq * gt_nnn)
Definition: AutoTune.h:33
double t
corresponding execution time (ms)
Definition: AutoTune.h:88
MetricType
Some algorithms support both an inner product version and a L2 search version.
Definition: Index.h:43
bool own_fields
! the sub-index
Definition: MetaIndexes.h:28