10 #include "../test/TestUtils.h"
11 #include "../../utils.h"
13 #include <gtest/gtest.h>
17 #include <unordered_map>
19 namespace faiss {
namespace gpu {
21 inline float relativeError(
float a,
float b) {
22 return std::abs(a - b) / (0.5f * (std::abs(a) + std::abs(b)));
31 clock_gettime(CLOCK_REALTIME, &t);
33 setTestSeed(t.tv_nsec);
36 void setTestSeed(
long seed) {
37 printf(
"testing with random seed %ld\n", seed);
43 int randVal(
int a,
int b) {
47 return a + (lrand48() % (b + 1 - a));
51 return randSelect<bool>({
true,
false});
54 std::vector<float> randVecs(
size_t num,
size_t dim) {
55 std::vector<float> v(num * dim);
57 faiss::float_rand(v.data(), v.size(), s_seed);
65 std::vector<unsigned char> randBinaryVecs(
size_t num,
size_t dim) {
66 std::vector<unsigned char> v(num * (dim / 8));
68 faiss::byte_rand(v.data(), v.size(), s_seed);
77 const std::vector<float>& queryVecs,
83 const std::string& configMsg,
84 float maxRelativeError,
88 std::vector<float> refDistance(numQuery * k, 0);
89 std::vector<faiss::Index::idx_t> refIndices(numQuery * k, -1);
90 refIndex.
search(numQuery, queryVecs.data(),
91 k, refDistance.data(), refIndices.data());
93 std::vector<float> testDistance(numQuery * k, 0);
94 std::vector<faiss::Index::idx_t> testIndices(numQuery * k, -1);
95 testIndex.
search(numQuery, queryVecs.data(),
96 k, testDistance.data(), testIndices.data());
98 faiss::gpu::compareLists(refDistance.data(),
105 maxRelativeError, pctMaxDiff1, pctMaxDiffN);
110 int numQuery,
int dim,
int k,
111 const std::string& configMsg,
112 float maxRelativeError,
115 auto queryVecs = faiss::gpu::randVecs(numQuery, dim);
117 compareIndices(queryVecs,
127 template <
typename T>
128 inline T lookup(
const T* p,
int i,
int j,
int ,
int dim2) {
129 return p[i * dim2 + j];
132 void compareLists(
const float* refDist,
134 const float* testDist,
137 const std::string& configMsg,
138 bool printBasicStats,
bool printDiffs,
bool assertOnErr,
139 float maxRelativeError,
143 float maxAbsErr = 0.0f;
144 for (
int i = 0; i < dim1 * dim2; ++i) {
145 maxAbsErr = std::max(maxAbsErr, std::abs(refDist[i] - testDist[i]));
147 int numResults = dim1 * dim2;
150 std::vector<std::unordered_map<faiss::Index::idx_t, int>> refIndexMap;
152 for (
int query = 0; query < dim1; ++query) {
153 std::unordered_map<faiss::Index::idx_t, int> indices;
155 for (
int result = 0; result < dim2; ++result) {
156 indices[lookup(refInd, query, result, dim1, dim2)] = result;
159 refIndexMap.emplace_back(std::move(indices));
164 std::vector<std::vector<int>> indexDiffs;
169 int nonUniqueIndices = 0;
171 double avgDiff = 0.0;
173 float maxRelErr = 0.0f;
175 for (
int query = 0; query < dim1; ++query) {
176 std::vector<int> diffs;
177 std::set<faiss::Index::idx_t> uniqueIndices;
179 auto& indices = refIndexMap[query];
181 for (
int result = 0; result < dim2; ++result) {
182 auto t = lookup(testInd, query, result, dim1, dim2);
186 bool uniqueIndex = uniqueIndices.count(t) == 0;
188 EXPECT_TRUE(uniqueIndex) << configMsg
197 uniqueIndices.insert(t);
200 auto it = indices.find(t);
201 if (it != indices.end()) {
202 int diff = std::abs(result - it->second);
203 diffs.push_back(diff);
207 maxDiff = std::max(diff, maxDiff);
208 }
else if (diff > 1) {
210 maxDiff = std::max(diff, maxDiff);
213 avgDiff += (double) diff;
220 auto refD = lookup(refDist, query, result, dim1, dim2);
221 auto testD = lookup(testDist, query, result, dim1, dim2);
223 float relErr = relativeError(refD, testD);
226 EXPECT_LE(relErr, maxRelativeError) << configMsg
227 <<
" (" << query <<
", " << result
228 <<
") refD: " << refD
229 <<
" testD: " << testD;
232 maxRelErr = std::max(maxRelErr, relErr);
235 indexDiffs.emplace_back(std::move(diffs));
239 EXPECT_LE((
float) (diff1 + diffN + diffInf),
240 (
float) numResults * pctMaxDiff1) << configMsg;
244 EXPECT_LE((
float) diffN, (
float) numResults * pctMaxDiffN) << configMsg;
247 avgDiff /= (double) numResults;
249 if (printBasicStats) {
250 if (!configMsg.empty()) {
252 "----------------------------\n"
257 printf(
"Result error and differences\n"
258 "----------------------------\n"
259 "max abs diff %.7f rel diff %.7f\n"
260 "idx diff avg: %.5g max: %d\n"
261 "idx diff of 1: %d (%.3f%% of queries)\n"
262 "idx diff of >1: %d (%.3f%% of queries)\n"
263 "idx diff not found: %d (%.3f%% of queries)"
264 " [typically a last element inversion]\n"
265 "non-unique indices: %d (a serious error if >0)\n",
266 maxAbsErr, maxRelErr,
268 diff1, 100.0f * (
float) diff1 / (
float) numResults,
269 diffN, 100.0f * (
float) diffN / (
float) numResults,
270 diffInf, 100.0f * (
float) diffInf / (
float) numResults,
275 printf(
"differences:\n");
276 printf(
"==================\n");
277 for (
int query = 0; query < dim1; ++query) {
278 for (
int result = 0; result < dim2; ++result) {
279 long refI = lookup(refInd, query, result, dim1, dim2);
280 long testI = lookup(testInd, query, result, dim1, dim2);
283 float refD = lookup(refDist, query, result, dim1, dim2);
284 float testD = lookup(testDist, query, result, dim1, dim2);
286 float maxDist = std::max(refD, testD);
287 float delta = std::abs(refD - testD);
289 float relErr = delta / maxDist;
292 printf(
"(%d, %d [%d]) (ref %ld tst %ld dist ==)\n",
294 indexDiffs[query][result],
297 printf(
"(%d, %d [%d]) (ref %ld tst %ld abs %.8f "
298 "rel %.8f ref %a tst %a)\n",
300 indexDiffs[query][result],
301 refI, testI, delta, relErr, refD, testD);
long idx_t
all indices are this type
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const =0