Faiss

- Main Page
- Namespaces
- Classes
- Files

- File List

All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
/data/users/hoss/faiss/hamming.h
1 /**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 // -*- c++ -*-
9
10 /*
11 * Hamming distances. The binary vector dimensionality should be a multiple
12 * of 64, as the elementary operations operate on words. If you really need
13 * other sizes, just pad with 0s (this is done by function fvecs2bitvecs).
14 *
15 * User-defined type hamdis_t is used for distances because at this time
16 * it is still uncler clear how we will need to balance
17 * - flexibility in vector size (may need 16- or even 8-bit vectors)
18 * - memory usage
19 * - cache-misses when dealing with large volumes of data (fewer bits is better)
20 *
21 * hamdis_t should optimally be compatibe with one of the Torch Storage
22 * (Byte,Short,Long) and therefore should be signed for 2-bytes and 4-bytes.
23 */
24
25 #ifndef FAISS_hamming_h
26 #define FAISS_hamming_h
27
28
29 #include <stdint.h>
30
31 #include "Heap.h"
32
33
34 /* The Hamming distance type should be exportable to Lua Tensor, which
35 excludes most unsigned type */
36 typedef int32_t hamdis_t;
37
38 namespace faiss {
39
40
41 extern size_t hamming_batch_size;
42
43 inline int popcount64(uint64_t x) {
44 return __builtin_popcountl(x);
45 }
46
47
48 /** Compute a set of Hamming distances between na and nb binary vectors
49 *
50 * @param a size na * nbytespercode
51 * @param b size nb * nbytespercode
52 * @param nbytespercode should be multiple of 8
53 * @param dis output distances, size na * nb
54 */
55 void hammings (
56 const uint8_t * a,
57 const uint8_t * b,
58 size_t na, size_t nb,
59 size_t nbytespercode,
60 hamdis_t * dis);
61
62 void bitvec_print (const uint8_t * b, size_t d);
63
64
65 /* Functions for casting vectors of regular types to compact bits.
66 They assume proper allocation done beforehand, meaning that b
67 should be be able to receive as many bits as x may produce. */
68
69 /* Makes an array of bits from the signs of a float array. The length
70 of the output array b is rounded up to byte size (allocate
71 accordingly) */
72 void fvecs2bitvecs (
73 const float * x,
74 uint8_t * b,
75 size_t d,
76 size_t n);
77
78
79 void fvec2bitvec (const float * x, uint8_t * b, size_t d);
80
81
82
83 /** Return the k smallest Hamming distances for a set of binary query vectors,
84 * using a max heap.
85 * @param a queries, size ha->nh * ncodes
86 * @param b database, size nb * ncodes
87 * @param nb number of database vectors
88 * @param ncodes size of the binary codes (bytes)
89 * @param ordered if != 0: order the results by decreasing distance
90 * (may be bottleneck for k/n > 0.01) */
91 void hammings_knn_hc (
92 int_maxheap_array_t * ha,
93 const uint8_t * a,
94 const uint8_t * b,
95 size_t nb,
96 size_t ncodes,
97 int ordered);
98
99 /* Legacy alias to hammings_knn_hc. */
100 void hammings_knn (
101 int_maxheap_array_t * ha,
102 const uint8_t * a,
103 const uint8_t * b,
104 size_t nb,
105 size_t ncodes,
106 int ordered);
107
108 /** Return the k smallest Hamming distances for a set of binary query vectors,
109 * using counting max.
110 * @param a queries, size na * ncodes
111 * @param b database, size nb * ncodes
112 * @param na number of query vectors
113 * @param nb number of database vectors
114 * @param k number of vectors/distances to return
115 * @param ncodes size of the binary codes (bytes)
116 * @param distances output distances from each query vector to its k nearest
117 * neighbors
118 * @param labels output ids of the k nearest neighbors to each query vector
119 */
120 void hammings_knn_mc (
121 const uint8_t * a,
122 const uint8_t * b,
123 size_t na,
124 size_t nb,
125 size_t k,
126 size_t ncodes,
127 int32_t *distances,
128 long *labels);
129
130 /* Counting the number of matches or of cross-matches (without returning them)
131 For use with function that assume pre-allocated memory */
132 void hamming_count_thres (
133 const uint8_t * bs1,
134 const uint8_t * bs2,
135 size_t n1,
136 size_t n2,
137 hamdis_t ht,
138 size_t ncodes,
139 size_t * nptr);
140
141 /* Return all Hamming distances/index passing a thres. Pre-allocation of output
142 is required. Use hamming_count_thres to determine the proper size. */
143 size_t match_hamming_thres (
144 const uint8_t * bs1,
145 const uint8_t * bs2,
146 size_t n1,
147 size_t n2,
148 hamdis_t ht,
149 size_t ncodes,
150 long * idx,
151 hamdis_t * dis);
152
153 /* Cross-matching in a set of vectors */
154 void crosshamming_count_thres (
155 const uint8_t * dbs,
156 size_t n,
157 hamdis_t ht,
158 size_t ncodes,
159 size_t * nptr);
160
161
162 /* compute the Hamming distances between two codewords of nwords*64 bits */
163 hamdis_t hamming (
164 const uint64_t * bs1,
165 const uint64_t * bs2,
166 size_t nwords);
167
168
169
170
171 /******************************************************************
172 * The HammingComputer series of classes compares a single code of
173 * size 4 to 32 to incoming codes. They are intended for use as a
174 * template class where it would be inefficient to switch on the code
175 * size in the inner loop. Hopefully the compiler will inline the
176 * hamming() functions and put the a0, a1, ... in registers.
177 ******************************************************************/
178
179
180 struct HammingComputer4 {
181 uint32_t a0;
182
183 HammingComputer4 () {}
184
185 HammingComputer4 (const uint8_t *a, int code_size) {
186 set (a, code_size);
187 }
188
189 void set (const uint8_t *a, int code_size) {
190 assert (code_size == 4);
191 a0 = *(uint32_t *)a;
192 }
193
194 inline int hamming (const uint8_t *b) const {
195 return popcount64 (*(uint32_t *)b ^ a0);
196 }
197
198 };
199
200 struct HammingComputer8 {
201 uint64_t a0;
202
203 HammingComputer8 () {}
204
205 HammingComputer8 (const uint8_t *a, int code_size) {
206 set (a, code_size);
207 }
208
209 void set (const uint8_t *a, int code_size) {
210 assert (code_size == 8);
211 a0 = *(uint64_t *)a;
212 }
213
214 inline int hamming (const uint8_t *b) const {
215 return popcount64 (*(uint64_t *)b ^ a0);
216 }
217
218 };
219
220
221 struct HammingComputer16 {
222 uint64_t a0, a1;
223
224 HammingComputer16 () {}
225
226 HammingComputer16 (const uint8_t *a8, int code_size) {
227 set (a8, code_size);
228 }
229
230 void set (const uint8_t *a8, int code_size) {
231 assert (code_size == 16);
232 const uint64_t *a = (uint64_t *)a8;
233 a0 = a[0]; a1 = a[1];
234 }
235
236 inline int hamming (const uint8_t *b8) const {
237 const uint64_t *b = (uint64_t *)b8;
238 return popcount64 (b[0] ^ a0) + popcount64 (b[1] ^ a1);
239 }
240
241 };
242
243 // when applied to an array, 1/2 of the 64-bit accesses are unaligned.
244 // This incurs a penalty of ~10% wrt. fully aligned accesses.
245 struct HammingComputer20 {
246 uint64_t a0, a1;
247 uint32_t a2;
248
249 HammingComputer20 () {}
250
251 HammingComputer20 (const uint8_t *a8, int code_size) {
252 set (a8, code_size);
253 }
254
255 void set (const uint8_t *a8, int code_size) {
256 assert (code_size == 20);
257 const uint64_t *a = (uint64_t *)a8;
258 a0 = a[0]; a1 = a[1]; a2 = a[2];
259 }
260
261 inline int hamming (const uint8_t *b8) const {
262 const uint64_t *b = (uint64_t *)b8;
263 return popcount64 (b[0] ^ a0) + popcount64 (b[1] ^ a1) +
264 popcount64 (*(uint32_t*)(b + 2) ^ a2);
265 }
266 };
267
268 struct HammingComputer32 {
269 uint64_t a0, a1, a2, a3;
270
271 HammingComputer32 () {}
272
273 HammingComputer32 (const uint8_t *a8, int code_size) {
274 set (a8, code_size);
275 }
276
277 void set (const uint8_t *a8, int code_size) {
278 assert (code_size == 32);
279 const uint64_t *a = (uint64_t *)a8;
280 a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3];
281 }
282
283 inline int hamming (const uint8_t *b8) const {
284 const uint64_t *b = (uint64_t *)b8;
285 return popcount64 (b[0] ^ a0) + popcount64 (b[1] ^ a1) +
286 popcount64 (b[2] ^ a2) + popcount64 (b[3] ^ a3);
287 }
288
289 };
290
291 struct HammingComputer64 {
292 uint64_t a0, a1, a2, a3, a4, a5, a6, a7;
293
294 HammingComputer64 () {}
295
296 HammingComputer64 (const uint8_t *a8, int code_size) {
297 set (a8, code_size);
298 }
299
300 void set (const uint8_t *a8, int code_size) {
301 assert (code_size == 64);
302 const uint64_t *a = (uint64_t *)a8;
303 a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3];
304 a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7];
305 }
306
307 inline int hamming (const uint8_t *b8) const {
308 const uint64_t *b = (uint64_t *)b8;
309 return popcount64 (b[0] ^ a0) + popcount64 (b[1] ^ a1) +
310 popcount64 (b[2] ^ a2) + popcount64 (b[3] ^ a3) +
311 popcount64 (b[4] ^ a4) + popcount64 (b[5] ^ a5) +
312 popcount64 (b[6] ^ a6) + popcount64 (b[7] ^ a7);
313 }
314
315 };
316
317 // very inefficient...
318 struct HammingComputerDefault {
319 const uint8_t *a;
320 int n;
321
322 HammingComputerDefault () {}
323
324 HammingComputerDefault (const uint8_t *a8, int code_size) {
325 set (a8, code_size);
326 }
327
328 void set (const uint8_t *a8, int code_size) {
329 a = a8;
330 n = code_size;
331 }
332
333 int hamming (const uint8_t *b8) const {
334 int accu = 0;
335 for (int i = 0; i < n; i++)
336 accu += popcount64 (a[i] ^ b8[i]);
337 return accu;
338 }
339
340 };
341
342
343 struct HammingComputerM8 {
344 const uint64_t *a;
345 int n;
346
347 HammingComputerM8 () {}
348
349 HammingComputerM8 (const uint8_t *a8, int code_size) {
350 set (a8, code_size);
351 }
352
353 void set (const uint8_t *a8, int code_size) {
354 assert (code_size % 8 == 0);
355 a = (uint64_t *)a8;
356 n = code_size / 8;
357 }
358
359 int hamming (const uint8_t *b8) const {
360 const uint64_t *b = (uint64_t *)b8;
361 int accu = 0;
362 for (int i = 0; i < n; i++)
363 accu += popcount64 (a[i] ^ b[i]);
364 return accu;
365 }
366
367 };
368
369 // even more inefficient!
370 struct HammingComputerM4 {
371 const uint32_t *a;
372 int n;
373
374 HammingComputerM4 () {}
375
376 HammingComputerM4 (const uint8_t *a4, int code_size) {
377 set (a4, code_size);
378 }
379
380 void set (const uint8_t *a4, int code_size) {
381 assert (code_size % 4 == 0);
382 a = (uint32_t *)a4;
383 n = code_size / 4;
384 }
385
386 int hamming (const uint8_t *b8) const {
387 const uint32_t *b = (uint32_t *)b8;
388 int accu = 0;
389 for (int i = 0; i < n; i++)
390 accu += popcount64 (a[i] ^ b[i]);
391 return accu;
392 }
393
394 };
395
396 /***************************************************************************
397 * Equivalence with a template class when code size is known at compile time
398 **************************************************************************/
399
400 // default template
401 template<int CODE_SIZE>
402 struct HammingComputer: HammingComputerM8 {
403 HammingComputer (const uint8_t *a, int code_size):
404 HammingComputerM8(a, code_size) {}
405 };
406
407 #define SPECIALIZED_HC(CODE_SIZE) \
408 template<> struct HammingComputer<CODE_SIZE>: \
409 HammingComputer ## CODE_SIZE { \
410 HammingComputer (const uint8_t *a): \
411 HammingComputer ## CODE_SIZE(a, CODE_SIZE) {} \
412 }
413
414 SPECIALIZED_HC(4);
415 SPECIALIZED_HC(8);
416 SPECIALIZED_HC(16);
417 SPECIALIZED_HC(20);
418 SPECIALIZED_HC(32);
419 SPECIALIZED_HC(64);
420
421 #undef SPECIALIZED_HC
422
423
424 /***************************************************************************
425 * generalized Hamming = number of bytes that are different between
426 * two codes.
427 ***************************************************************************/
428
429
430 inline int generalized_hamming_64 (uint64_t a) {
431 a |= a >> 1;
432 a |= a >> 2;
433 a |= a >> 4;
434 a &= 0x0101010101010101UL;
435 return popcount64 (a);
436 }
437
438
439 struct GenHammingComputer8 {
440 uint64_t a0;
441
442 GenHammingComputer8 (const uint8_t *a, int code_size) {
443 assert (code_size == 8);
444 a0 = *(uint64_t *)a;
445 }
446
447 inline int hamming (const uint8_t *b) const {
448 return generalized_hamming_64 (*(uint64_t *)b ^ a0);
449 }
450
451 };
452
453
454 struct GenHammingComputer16 {
455 uint64_t a0, a1;
456 GenHammingComputer16 (const uint8_t *a8, int code_size) {
457 assert (code_size == 16);
458 const uint64_t *a = (uint64_t *)a8;
459 a0 = a[0]; a1 = a[1];
460 }
461
462 inline int hamming (const uint8_t *b8) const {
463 const uint64_t *b = (uint64_t *)b8;
464 return generalized_hamming_64 (b[0] ^ a0) +
465 generalized_hamming_64 (b[1] ^ a1);
466 }
467
468 };
469
470 struct GenHammingComputer32 {
471 uint64_t a0, a1, a2, a3;
472
473 GenHammingComputer32 (const uint8_t *a8, int code_size) {
474 assert (code_size == 32);
475 const uint64_t *a = (uint64_t *)a8;
476 a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3];
477 }
478
479 inline int hamming (const uint8_t *b8) const {
480 const uint64_t *b = (uint64_t *)b8;
481 return generalized_hamming_64 (b[0] ^ a0) +
482 generalized_hamming_64 (b[1] ^ a1) +
483 generalized_hamming_64 (b[2] ^ a2) +
484 generalized_hamming_64 (b[3] ^ a3);
485 }
486
487 };
488
489 struct GenHammingComputerM8 {
490 const uint64_t *a;
491 int n;
492
493 GenHammingComputerM8 (const uint8_t *a8, int code_size) {
494 assert (code_size % 8 == 0);
495 a = (uint64_t *)a8;
496 n = code_size / 8;
497 }
498
499 int hamming (const uint8_t *b8) const {
500 const uint64_t *b = (uint64_t *)b8;
501 int accu = 0;
502 for (int i = 0; i < n; i++)
503 accu += generalized_hamming_64 (a[i] ^ b[i]);
504 return accu;
505 }
506
507 };
508
509
510 /** generalized Hamming distances (= count number of code bytes that
511 are the same) */
512 void generalized_hammings_knn_hc (
513 int_maxheap_array_t * ha,
514 const uint8_t * a,
515 const uint8_t * b,
516 size_t nb,
517 size_t code_size,
518 int ordered = true);
519
520
521
522 /** This class maintains a list of best distances seen so far.
523 *
524 * Since the distances are in a limited range (0 to nbit), the
525 * object maintains one list per possible distance, and fills
526 * in only the n-first lists, such that the sum of sizes of the
527 * n lists is below k.
528 */
529 template<class HammingComputer>
530 struct HCounterState {
531 int *counters;
532 long *ids_per_dis;
533
534 HammingComputer hc;
535 int thres;
536 int count_lt;
537 int count_eq;
538 int k;
539
540 HCounterState(int *counters, long *ids_per_dis,
541 const uint8_t *x, int d, int k)
542 : counters(counters),
543 ids_per_dis(ids_per_dis),
544 hc(x, d / 8),
545 thres(d + 1),
546 count_lt(0),
547 count_eq(0),
548 k(k) {}
549
550 void update_counter(const uint8_t *y, size_t j) {
551 int32_t dis = hc.hamming(y);
552
553 if (dis <= thres) {
554 if (dis < thres) {
555 ids_per_dis[dis * k + counters[dis]++] = j;
556 ++count_lt;
557 while (count_lt == k && thres > 0) {
558 --thres;
559 count_eq = counters[thres];
560 count_lt -= count_eq;
561 }
562 } else if (count_eq < k) {
563 ids_per_dis[dis * k + count_eq++] = j;
564 counters[dis] = count_eq;
565 }
566 }
567 }
568 };
569
570
571 } // namespace faiss
572
573
574 #endif /* FAISS_hamming_h */
faiss::hammings_knn_hc
void hammings_knn_hc(int_maxheap_array_t *ha, const uint8_t *a, const uint8_t *b, size_t nb, size_t ncodes, int order)
Definition: hamming.cpp:517
faiss::HammingComputerM4
Definition: hamming.h:370
faiss::HammingComputer
Definition: hamming.h:402
faiss::HammingComputer20
Definition: hamming.h:245
faiss::GenHammingComputer16
Definition: hamming.h:454
faiss::GenHammingComputer8
Definition: hamming.h:439
faiss::HCounterState
Definition: hamming.h:530
faiss::hammings_knn_mc
void hammings_knn_mc(const uint8_t *a, const uint8_t *b, size_t na, size_t nb, size_t k, size_t ncodes, int32_t *distances, long *labels)
Definition: hamming.cpp:555
faiss::HammingComputer8
Definition: hamming.h:200
faiss::HammingComputer64
Definition: hamming.h:291
faiss::HammingComputer16
Definition: hamming.h:221
faiss::HammingComputer4
Definition: hamming.h:180
faiss::GenHammingComputerM8
Definition: hamming.h:489
faiss::HeapArray
Definition: Heap.h:349
faiss::HammingComputer32
Definition: hamming.h:268
faiss::GenHammingComputer32
Definition: hamming.h:470
faiss::HammingComputerDefault
Definition: hamming.h:318
faiss::HammingComputerM8
Definition: hamming.h:343
faiss::generalized_hammings_knn_hc
void generalized_hammings_knn_hc(int_maxheap_array_t *ha, const uint8_t *a, const uint8_t *b, size_t nb, size_t code_size, int ordered)
Definition: hamming.cpp:728