Faiss

- Main Page
- Namespaces
- Classes
- Files

- File List

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