Faiss
 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 
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 
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 
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.
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 
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 
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...
319  const uint8_t *a;
320  int n;
321 
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 
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!
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>
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 
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 
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 
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 
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) */
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>
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 */
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
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
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