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