Faiss
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
GpuIndexIVF.cu
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 
11 #include "GpuIndexIVF.h"
12 #include "../FaissAssert.h"
13 #include "../IndexFlat.h"
14 #include "../IndexIVF.h"
15 #include "GpuIndexFlat.h"
16 #include "utils/DeviceUtils.h"
17 #include "utils/Float16.cuh"
18 
19 namespace faiss { namespace gpu {
20 
21 GpuIndexIVF::GpuIndexIVF(GpuResources* resources,
22  int dims,
23  faiss::MetricType metric,
24  int nlist,
25  GpuIndexIVFConfig config) :
26  GpuIndex(resources, dims, metric, config),
27  ivfConfig_(std::move(config)),
28  nlist_(nlist),
29  nprobe_(1),
30  quantizer_(nullptr) {
31 #ifndef FAISS_USE_FLOAT16
32  FAISS_THROW_IF_NOT_MSG(!ivfConfig_.flatConfig.useFloat16CoarseQuantizer,
33  "float16 unsupported; need CUDA SDK >= 7.5");
34 #endif
35 
36  init_();
37 }
38 
39 void
40 GpuIndexIVF::init_() {
41  FAISS_ASSERT(nlist_ > 0);
42 
43  // Spherical by default if the metric is inner_product
44  if (this->metric_type == faiss::METRIC_INNER_PRODUCT) {
45  this->cp.spherical = true;
46  }
47 
48  // here we set a low # iterations because this is typically used
49  // for large clusterings
50  this->cp.niter = 10;
51  this->cp.verbose = this->verbose;
52 
53  if (!quantizer_) {
54  // Construct an empty quantizer
55  GpuIndexFlatConfig config = ivfConfig_.flatConfig;
56  // FIXME: inherit our same device
57  config.device = device_;
58 
59  if (this->metric_type == faiss::METRIC_L2) {
60  quantizer_ = new GpuIndexFlatL2(resources_, this->d, config);
61  } else if (this->metric_type == faiss::METRIC_INNER_PRODUCT) {
62  quantizer_ = new GpuIndexFlatIP(resources_, this->d, config);
63  } else {
64  // unknown metric type
65  FAISS_ASSERT_MSG(false, "unknown metric type");
66  }
67  }
68 }
69 
70 GpuIndexIVF::~GpuIndexIVF() {
71  delete quantizer_;
72 }
73 
74 GpuIndexFlat*
76  return quantizer_;
77 }
78 
79 void
81  DeviceScope scope(device_);
82 
83  this->d = index->d;
84  this->metric_type = index->metric_type;
85 
86  FAISS_ASSERT(index->nlist > 0);
87  FAISS_THROW_IF_NOT_FMT(index->nlist <=
88  (faiss::Index::idx_t) std::numeric_limits<int>::max(),
89  "GPU index only supports %zu inverted lists",
90  (size_t) std::numeric_limits<int>::max());
91  nlist_ = index->nlist;
92  nprobe_ = index->nprobe;
93 
94  // The metric type may have changed as well, so we might have to
95  // change our quantizer
96  delete quantizer_;
97  quantizer_ = nullptr;
98 
99  // Construct an empty quantizer
100  GpuIndexFlatConfig config = ivfConfig_.flatConfig;
101  // FIXME: inherit our same device
102  config.device = device_;
103 
104  if (index->metric_type == faiss::METRIC_L2) {
105  // FIXME: 2 different float16 options?
106  quantizer_ = new GpuIndexFlatL2(resources_, this->d, config);
107  } else if (index->metric_type == faiss::METRIC_INNER_PRODUCT) {
108  // FIXME: 2 different float16 options?
109  quantizer_ = new GpuIndexFlatIP(resources_, this->d, config);
110  } else {
111  // unknown metric type
112  FAISS_ASSERT(false);
113  }
114 
115  if (!index->is_trained) {
116  this->is_trained = false;
117  this->ntotal = 0;
118  return;
119  }
120 
121  // Otherwise, we can populate ourselves from the other index
122  this->is_trained = true;
123 
124  // ntotal can exceed max int, but the number of vectors per inverted
125  // list cannot exceed this. We check this in the subclasses.
126  this->ntotal = index->ntotal;
127 
128  // Since we're trained, the quantizer must have data
129  FAISS_ASSERT(index->quantizer->ntotal > 0);
130 
131  if (index->metric_type == faiss::METRIC_L2) {
132  auto q = dynamic_cast<faiss::IndexFlatL2*>(index->quantizer);
133  FAISS_ASSERT(q);
134 
135  quantizer_->copyFrom(q);
136  } else if (index->metric_type == faiss::METRIC_INNER_PRODUCT) {
137  auto q = dynamic_cast<faiss::IndexFlatIP*>(index->quantizer);
138  FAISS_ASSERT(q);
139 
140  quantizer_->copyFrom(q);
141  } else {
142  // unknown metric type
143  FAISS_ASSERT(false);
144  }
145 }
146 
147 void
149  DeviceScope scope(device_);
150 
151  //
152  // Index information
153  //
154  index->ntotal = this->ntotal;
155  index->d = this->d;
156  index->metric_type = this->metric_type;
157  index->is_trained = this->is_trained;
158 
159  //
160  // IndexIVF information
161  //
162  index->nlist = nlist_;
163  index->nprobe = nprobe_;
164 
165  // Construct and copy the appropriate quantizer
166  faiss::IndexFlat* q = nullptr;
167 
168  if (this->metric_type == faiss::METRIC_L2) {
169  q = new faiss::IndexFlatL2(this->d);
170 
171  } else if (this->metric_type == faiss::METRIC_INNER_PRODUCT) {
172  q = new faiss::IndexFlatIP(this->d);
173 
174  } else {
175  // unknown metric type
176  FAISS_ASSERT(false);
177  }
178 
179  FAISS_ASSERT(quantizer_);
180  quantizer_->copyTo(q);
181 
182  if (index->own_fields) {
183  delete index->quantizer;
184  }
185 
186  index->quantizer = q;
187  index->quantizer_trains_alone = false;
188  index->own_fields = true;
189  index->cp = this->cp;
190  index->ids.clear();
191  index->ids.resize(nlist_);
192  index->maintain_direct_map = false;
193  index->direct_map.clear();
194 }
195 
196 int
198  return nlist_;
199 }
200 
201 void
203  FAISS_THROW_IF_NOT_FMT(nprobe > 0 && nprobe <= 1024,
204  "nprobe must be from 1 to 1024; passed %d",
205  nprobe);
206  nprobe_ = nprobe;
207 }
208 
209 int
211  return nprobe_;
212 }
213 
214 void
215 GpuIndexIVF::add(Index::idx_t n, const float* x) {
216  // FIXME: GPU-ize
217  std::vector<Index::idx_t> ids(n);
218  for (Index::idx_t i = 0; i < n; ++i) {
219  ids[i] = this->ntotal + i;
220  }
221 
222  add_with_ids(n, x, ids.data());
223 }
224 
225 void
226 GpuIndexIVF::trainQuantizer_(faiss::Index::idx_t n, const float* x) {
227  if (n == 0) {
228  // nothing to do
229  return;
230  }
231 
232  if (quantizer_->is_trained && (quantizer_->ntotal == nlist_)) {
233  if (this->verbose) {
234  printf ("IVF quantizer does not need training.\n");
235  }
236 
237  return;
238  }
239 
240  if (this->verbose) {
241  printf ("Training IVF quantizer on %ld vectors in %dD\n", n, d);
242  }
243 
244  DeviceScope scope(device_);
245 
246  // leverage the CPU-side k-means code, which works for the GPU
247  // flat index as well
248  quantizer_->reset();
249  Clustering clus(this->d, nlist_, this->cp);
250  clus.verbose = verbose;
251  clus.train(n, x, *quantizer_);
252  quantizer_->is_trained = true;
253 
254  FAISS_ASSERT(quantizer_->ntotal == nlist_);
255 }
256 
257 } } // namespace
int getNumProbes() const
Returns our current number of list probes per query.
Definition: GpuIndexIVF.cu:210
void setNumProbes(int nprobe)
Sets the number of list probes per query.
Definition: GpuIndexIVF.cu:202
int niter
clustering iterations
Definition: Clustering.h:25
int getNumLists() const
Returns the number of inverted lists we&#39;re managing.
Definition: GpuIndexIVF.cu:197
void copyTo(faiss::IndexFlat *index) const
size_t nprobe
number of probes at query time
Definition: IndexIVF.h:47
bool quantizer_trains_alone
just pass over the trainset to quantizer
Definition: IndexIVF.h:50
int device
GPU device on which the index is resident.
Definition: GpuIndex.h:27
std::vector< std::vector< long > > ids
Inverted lists for indexes.
Definition: IndexIVF.h:55
void add_with_ids(Index::idx_t n, const float *x, const Index::idx_t *ids) override
Definition: GpuIndex.cu:66
int d
vector dimension
Definition: Index.h:64
Index * quantizer
quantizer that maps vectors to inverted lists
Definition: IndexIVF.h:49
GpuIndexFlatConfig flatConfig
Configuration for the coarse quantizer object.
Definition: GpuIndexIVF.h:34
GpuIndexFlat * getQuantizer()
Return the quantizer we&#39;re using.
Definition: GpuIndexIVF.cu:75
ClusteringParameters cp
to override default clustering params
Definition: IndexIVF.h:53
int nprobe_
Number of inverted list probes per query.
Definition: GpuIndexIVF.h:91
bool own_fields
whether object owns the quantizer
Definition: IndexIVF.h:51
const int device_
The GPU device we are resident on.
Definition: GpuIndex.h:94
GpuResources * resources_
Manages streans, cuBLAS handles and scratch memory for devices.
Definition: GpuIndex.h:91
void copyTo(faiss::IndexIVF *index) const
Copy what we have to the CPU equivalent.
Definition: GpuIndexIVF.cu:148
long idx_t
all indices are this type
Definition: Index.h:62
int nlist_
Number of inverted lists that we manage.
Definition: GpuIndexIVF.h:88
idx_t ntotal
total nb of indexed vectors
Definition: Index.h:65
bool verbose
verbosity level
Definition: Index.h:66
void copyFrom(const faiss::IndexFlat *index)
Definition: GpuIndexFlat.cu:87
GpuIndexFlat * quantizer_
Quantizer for inverted lists.
Definition: GpuIndexIVF.h:94
MetricType metric_type
type of metric this index uses for search
Definition: Index.h:72
size_t nlist
number of possible key values
Definition: IndexIVF.h:46
void reset() override
Clears all vectors from this index.
void copyFrom(const faiss::IndexIVF *index)
Copy what we need from the CPU equivalent.
Definition: GpuIndexIVF.cu:80
bool is_trained
set if the Index does not require training, or if training is done already
Definition: Index.h:69
void add(Index::idx_t n, const float *x) override
Definition: GpuIndexIVF.cu:215
bool maintain_direct_map
map for direct access to the elements. Enables reconstruct().
Definition: IndexIVF.h:58
bool spherical
do we want normalized centroids?
Definition: Clustering.h:29
ClusteringParameters cp
Definition: GpuIndexIVF.h:82
MetricType
Some algorithms support both an inner product vetsion and a L2 search version.
Definition: Index.h:43