Skip to content

Commit e0aaa42

Browse files
junjieqifacebook-github-bot
authored andcommittedApr 3, 2024
Implement reconstruct_n for GPU IVFFlat indexes (#3338)
Summary: add reconstruct_n for GPU IVFFlat Differential Revision: D55577561
1 parent da9f292 commit e0aaa42

10 files changed

+168
-2
lines changed
 

‎faiss/gpu/GpuIndexIVFFlat.cu

+23
Original file line numberDiff line numberDiff line change
@@ -356,5 +356,28 @@ void GpuIndexIVFFlat::setIndex_(
356356
}
357357
}
358358

359+
void GpuIndexIVFFlat::reconstruct_n(idx_t i0, idx_t ni, float* out) const {
360+
DeviceScope scope(config_.device);
361+
FAISS_ASSERT(index_);
362+
363+
if (ni == 0) {
364+
// nothing to do
365+
return;
366+
}
367+
368+
FAISS_THROW_IF_NOT_FMT(
369+
i0 < this->ntotal,
370+
"start index (%zu) out of bounds (ntotal %zu)",
371+
i0,
372+
this->ntotal);
373+
FAISS_THROW_IF_NOT_FMT(
374+
i0 + ni - 1 < this->ntotal,
375+
"max index requested (%zu) out of bounds (ntotal %zu)",
376+
i0 + ni - 1,
377+
this->ntotal);
378+
379+
index_->reconstruct_n(i0, ni, out);
380+
}
381+
359382
} // namespace gpu
360383
} // namespace faiss

‎faiss/gpu/GpuIndexIVFFlat.h

+10
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ class GpuIndexIVFFlat : public GpuIndexIVF {
8787
/// Trains the coarse quantizer based on the given vector data
8888
void train(idx_t n, const float* x) override;
8989

90+
/* It is used to reconstruct a given number of vectors in an Inverted File
91+
* (IVF) index
92+
* @param i0 index of the first vector to reconstruct
93+
* @param n number of vectors to reconstruct
94+
* @param out This is a pointer to a buffer where the reconstructed
95+
* vectors will be stored.
96+
*/
97+
98+
void reconstruct_n(idx_t i0, idx_t n, float* out) const override;
99+
90100
protected:
91101
/// Initialize appropriate index
92102
void setIndex_(

‎faiss/gpu/impl/IVFBase.cu

+4
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,10 @@ void IVFBase::copyInvertedListsTo(InvertedLists* ivf) {
340340
}
341341
}
342342

343+
void IVFBase::reconstruct_n(idx_t i0, idx_t n, float* out) {
344+
FAISS_THROW_MSG("not implemented");
345+
}
346+
343347
void IVFBase::addEncodedVectorsToList_(
344348
idx_t listId,
345349
const void* codes,

‎faiss/gpu/impl/IVFBase.cuh

+11-2
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,18 @@ class IVFBase {
109109
Tensor<idx_t, 2, true>& outIndices,
110110
bool storePairs) = 0;
111111

112+
/* It is used to reconstruct a given number of vectors in an Inverted File
113+
* (IVF) index
114+
* @param i0 index of the first vector to reconstruct
115+
* @param n number of vectors to reconstruct
116+
* @param out This is a pointer to a buffer where the reconstructed
117+
* vectors will be stored.
118+
*/
119+
virtual void reconstruct_n(idx_t i0, idx_t n, float* out);
120+
112121
protected:
113-
/// Adds a set of codes and indices to a list, with the representation
114-
/// coming from the CPU equivalent
122+
/// Adds a set of codes and indices to a list, with the
123+
/// representation coming from the CPU equivalent
115124
virtual void addEncodedVectorsToList_(
116125
idx_t listId,
117126
// resident on the host

‎faiss/gpu/impl/IVFFlat.cu

+28
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,34 @@ void IVFFlat::searchPreassigned(
283283
storePairs);
284284
}
285285

286+
void IVFFlat::reconstruct_n(idx_t i0, idx_t ni, float* out) {
287+
if (ni == 0) {
288+
// nothing to do
289+
return;
290+
}
291+
292+
auto stream = resources_->getDefaultStreamCurrentDevice();
293+
294+
for (idx_t list_no = 0; list_no < numLists_; list_no++) {
295+
size_t list_size = deviceListData_[list_no]->numVecs;
296+
297+
auto idlist = getListIndices(list_no);
298+
299+
for (idx_t offset = 0; offset < list_size; offset++) {
300+
idx_t id = idlist[offset];
301+
if (!(id >= i0 && id < i0 + ni)) {
302+
continue;
303+
}
304+
305+
auto listDataPtr =
306+
(float*)(deviceListData_[list_no]->data.data() + offset);
307+
308+
fromDevice<float>(
309+
listDataPtr, out + (id - i0) * dim_, dim_, stream);
310+
}
311+
}
312+
}
313+
286314
void IVFFlat::searchImpl_(
287315
Tensor<float, 2, true>& queries,
288316
Tensor<float, 2, true>& coarseDistances,

‎faiss/gpu/impl/IVFFlat.cuh

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class IVFFlat : public IVFBase {
5151
Tensor<idx_t, 2, true>& outIndices,
5252
bool storePairs) override;
5353

54+
void reconstruct_n(idx_t i0, idx_t n, float* out) override;
55+
5456
protected:
5557
/// Returns the number of bytes in which an IVF list containing numVecs
5658
/// vectors is encoded on the device. Note that due to padding this is not

‎faiss/gpu/test/TestGpuIndexIVFFlat.cpp

+65
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,71 @@ TEST(TestGpuIndexIVFFlat, LongIVFList) {
842842
#endif
843843
}
844844

845+
TEST(TestGpuIndexIVFFlat, Reconstruct_n) {
846+
Options opt;
847+
848+
std::vector<float> trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim);
849+
std::vector<float> addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim);
850+
851+
faiss::IndexFlatL2 cpuQuantizer(opt.dim);
852+
faiss::IndexIVFFlat cpuIndex(
853+
&cpuQuantizer, opt.dim, opt.numCentroids, faiss::METRIC_L2);
854+
cpuIndex.nprobe = opt.nprobe;
855+
cpuIndex.train(opt.numTrain, trainVecs.data());
856+
cpuIndex.add(opt.numAdd, addVecs.data());
857+
858+
faiss::gpu::StandardGpuResources res;
859+
res.noTempMemory();
860+
861+
faiss::gpu::GpuIndexIVFFlatConfig config;
862+
config.device = opt.device;
863+
config.indicesOptions = faiss::gpu::INDICES_64_BIT;
864+
config.use_raft = false;
865+
866+
faiss::gpu::GpuIndexIVFFlat gpuIndex(
867+
&res, opt.dim, opt.numCentroids, faiss::METRIC_L2, config);
868+
gpuIndex.nprobe = opt.nprobe;
869+
870+
gpuIndex.train(opt.numTrain, trainVecs.data());
871+
gpuIndex.add(opt.numAdd, addVecs.data());
872+
873+
std::vector<float> gpuVals(opt.numAdd * opt.dim);
874+
875+
gpuIndex.reconstruct_n(0, gpuIndex.ntotal, gpuVals.data());
876+
877+
std::vector<float> cpuVals(opt.numAdd * opt.dim);
878+
879+
cpuIndex.reconstruct_n(0, cpuIndex.ntotal, cpuVals.data());
880+
881+
EXPECT_EQ(gpuVals, cpuVals);
882+
883+
config.indicesOptions = faiss::gpu::INDICES_32_BIT;
884+
885+
faiss::gpu::GpuIndexIVFFlat gpuIndex1(
886+
&res, opt.dim, opt.numCentroids, faiss::METRIC_L2, config);
887+
gpuIndex1.nprobe = opt.nprobe;
888+
889+
gpuIndex1.train(opt.numTrain, trainVecs.data());
890+
gpuIndex1.add(opt.numAdd, addVecs.data());
891+
892+
gpuIndex1.reconstruct_n(0, gpuIndex1.ntotal, gpuVals.data());
893+
894+
EXPECT_EQ(gpuVals, cpuVals);
895+
896+
config.indicesOptions = faiss::gpu::INDICES_CPU;
897+
898+
faiss::gpu::GpuIndexIVFFlat gpuIndex2(
899+
&res, opt.dim, opt.numCentroids, faiss::METRIC_L2, config);
900+
gpuIndex2.nprobe = opt.nprobe;
901+
902+
gpuIndex2.train(opt.numTrain, trainVecs.data());
903+
gpuIndex2.add(opt.numAdd, addVecs.data());
904+
905+
gpuIndex2.reconstruct_n(0, gpuIndex2.ntotal, gpuVals.data());
906+
907+
EXPECT_EQ(gpuVals, cpuVals);
908+
}
909+
845910
int main(int argc, char** argv) {
846911
testing::InitGoogleTest(&argc, argv);
847912

‎faiss/gpu/test/test_gpu_basics.py

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import random
1212
from common_faiss_tests import get_dataset_2
1313

14+
1415
class ReferencedObject(unittest.TestCase):
1516

1617
d = 16
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright (c) Facebook, Inc. and its affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
import unittest
7+
8+
import faiss
9+
import numpy as np
10+
11+
12+
class TestGpuIndexIvfflat(unittest.TestCase):
13+
def test_reconstruct_n(self):
14+
index = faiss.index_factory(4, "IVF10,Flat")
15+
x = np.random.RandomState(123).rand(10, 4).astype('float32')
16+
index.train(x)
17+
index.add(x)
18+
res = faiss.StandardGpuResources()
19+
index2 = faiss.index_cpu_to_gpu(res, 0, index)
20+
recons = index2.reconstruct_n(0, 10)
21+
22+
np.testing.assert_arrays_equal(recons, x)

‎faiss/gpu/utils/DeviceVector.cuh

+2
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ class DeviceVector {
169169
T out;
170170
CUDA_VERIFY(cudaMemcpyAsync(
171171
&out, data() + idx, sizeof(T), cudaMemcpyDeviceToHost, stream));
172+
173+
return out;
172174
}
173175

174176
// Clean up after oversized allocations, while leaving some space to

0 commit comments

Comments
 (0)