faiss 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (226) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/README.md +103 -3
  4. data/ext/faiss/ext.cpp +99 -32
  5. data/ext/faiss/extconf.rb +12 -2
  6. data/lib/faiss/ext.bundle +0 -0
  7. data/lib/faiss/index.rb +3 -3
  8. data/lib/faiss/index_binary.rb +3 -3
  9. data/lib/faiss/kmeans.rb +1 -1
  10. data/lib/faiss/pca_matrix.rb +2 -2
  11. data/lib/faiss/product_quantizer.rb +3 -3
  12. data/lib/faiss/version.rb +1 -1
  13. data/vendor/faiss/AutoTune.cpp +719 -0
  14. data/vendor/faiss/AutoTune.h +212 -0
  15. data/vendor/faiss/Clustering.cpp +261 -0
  16. data/vendor/faiss/Clustering.h +101 -0
  17. data/vendor/faiss/IVFlib.cpp +339 -0
  18. data/vendor/faiss/IVFlib.h +132 -0
  19. data/vendor/faiss/Index.cpp +171 -0
  20. data/vendor/faiss/Index.h +261 -0
  21. data/vendor/faiss/Index2Layer.cpp +437 -0
  22. data/vendor/faiss/Index2Layer.h +85 -0
  23. data/vendor/faiss/IndexBinary.cpp +77 -0
  24. data/vendor/faiss/IndexBinary.h +163 -0
  25. data/vendor/faiss/IndexBinaryFlat.cpp +83 -0
  26. data/vendor/faiss/IndexBinaryFlat.h +54 -0
  27. data/vendor/faiss/IndexBinaryFromFloat.cpp +78 -0
  28. data/vendor/faiss/IndexBinaryFromFloat.h +52 -0
  29. data/vendor/faiss/IndexBinaryHNSW.cpp +325 -0
  30. data/vendor/faiss/IndexBinaryHNSW.h +56 -0
  31. data/vendor/faiss/IndexBinaryIVF.cpp +671 -0
  32. data/vendor/faiss/IndexBinaryIVF.h +211 -0
  33. data/vendor/faiss/IndexFlat.cpp +508 -0
  34. data/vendor/faiss/IndexFlat.h +175 -0
  35. data/vendor/faiss/IndexHNSW.cpp +1090 -0
  36. data/vendor/faiss/IndexHNSW.h +170 -0
  37. data/vendor/faiss/IndexIVF.cpp +909 -0
  38. data/vendor/faiss/IndexIVF.h +353 -0
  39. data/vendor/faiss/IndexIVFFlat.cpp +502 -0
  40. data/vendor/faiss/IndexIVFFlat.h +118 -0
  41. data/vendor/faiss/IndexIVFPQ.cpp +1207 -0
  42. data/vendor/faiss/IndexIVFPQ.h +161 -0
  43. data/vendor/faiss/IndexIVFPQR.cpp +219 -0
  44. data/vendor/faiss/IndexIVFPQR.h +65 -0
  45. data/vendor/faiss/IndexIVFSpectralHash.cpp +331 -0
  46. data/vendor/faiss/IndexIVFSpectralHash.h +75 -0
  47. data/vendor/faiss/IndexLSH.cpp +225 -0
  48. data/vendor/faiss/IndexLSH.h +87 -0
  49. data/vendor/faiss/IndexLattice.cpp +143 -0
  50. data/vendor/faiss/IndexLattice.h +68 -0
  51. data/vendor/faiss/IndexPQ.cpp +1188 -0
  52. data/vendor/faiss/IndexPQ.h +199 -0
  53. data/vendor/faiss/IndexPreTransform.cpp +288 -0
  54. data/vendor/faiss/IndexPreTransform.h +91 -0
  55. data/vendor/faiss/IndexReplicas.cpp +123 -0
  56. data/vendor/faiss/IndexReplicas.h +76 -0
  57. data/vendor/faiss/IndexScalarQuantizer.cpp +317 -0
  58. data/vendor/faiss/IndexScalarQuantizer.h +127 -0
  59. data/vendor/faiss/IndexShards.cpp +317 -0
  60. data/vendor/faiss/IndexShards.h +100 -0
  61. data/vendor/faiss/InvertedLists.cpp +623 -0
  62. data/vendor/faiss/InvertedLists.h +334 -0
  63. data/vendor/faiss/LICENSE +21 -0
  64. data/vendor/faiss/MatrixStats.cpp +252 -0
  65. data/vendor/faiss/MatrixStats.h +62 -0
  66. data/vendor/faiss/MetaIndexes.cpp +351 -0
  67. data/vendor/faiss/MetaIndexes.h +126 -0
  68. data/vendor/faiss/OnDiskInvertedLists.cpp +674 -0
  69. data/vendor/faiss/OnDiskInvertedLists.h +127 -0
  70. data/vendor/faiss/VectorTransform.cpp +1157 -0
  71. data/vendor/faiss/VectorTransform.h +322 -0
  72. data/vendor/faiss/c_api/AutoTune_c.cpp +83 -0
  73. data/vendor/faiss/c_api/AutoTune_c.h +64 -0
  74. data/vendor/faiss/c_api/Clustering_c.cpp +139 -0
  75. data/vendor/faiss/c_api/Clustering_c.h +117 -0
  76. data/vendor/faiss/c_api/IndexFlat_c.cpp +140 -0
  77. data/vendor/faiss/c_api/IndexFlat_c.h +115 -0
  78. data/vendor/faiss/c_api/IndexIVFFlat_c.cpp +64 -0
  79. data/vendor/faiss/c_api/IndexIVFFlat_c.h +58 -0
  80. data/vendor/faiss/c_api/IndexIVF_c.cpp +92 -0
  81. data/vendor/faiss/c_api/IndexIVF_c.h +135 -0
  82. data/vendor/faiss/c_api/IndexLSH_c.cpp +37 -0
  83. data/vendor/faiss/c_api/IndexLSH_c.h +40 -0
  84. data/vendor/faiss/c_api/IndexShards_c.cpp +44 -0
  85. data/vendor/faiss/c_api/IndexShards_c.h +42 -0
  86. data/vendor/faiss/c_api/Index_c.cpp +105 -0
  87. data/vendor/faiss/c_api/Index_c.h +183 -0
  88. data/vendor/faiss/c_api/MetaIndexes_c.cpp +49 -0
  89. data/vendor/faiss/c_api/MetaIndexes_c.h +49 -0
  90. data/vendor/faiss/c_api/clone_index_c.cpp +23 -0
  91. data/vendor/faiss/c_api/clone_index_c.h +32 -0
  92. data/vendor/faiss/c_api/error_c.h +42 -0
  93. data/vendor/faiss/c_api/error_impl.cpp +27 -0
  94. data/vendor/faiss/c_api/error_impl.h +16 -0
  95. data/vendor/faiss/c_api/faiss_c.h +58 -0
  96. data/vendor/faiss/c_api/gpu/GpuAutoTune_c.cpp +96 -0
  97. data/vendor/faiss/c_api/gpu/GpuAutoTune_c.h +56 -0
  98. data/vendor/faiss/c_api/gpu/GpuClonerOptions_c.cpp +52 -0
  99. data/vendor/faiss/c_api/gpu/GpuClonerOptions_c.h +68 -0
  100. data/vendor/faiss/c_api/gpu/GpuIndex_c.cpp +17 -0
  101. data/vendor/faiss/c_api/gpu/GpuIndex_c.h +30 -0
  102. data/vendor/faiss/c_api/gpu/GpuIndicesOptions_c.h +38 -0
  103. data/vendor/faiss/c_api/gpu/GpuResources_c.cpp +86 -0
  104. data/vendor/faiss/c_api/gpu/GpuResources_c.h +66 -0
  105. data/vendor/faiss/c_api/gpu/StandardGpuResources_c.cpp +54 -0
  106. data/vendor/faiss/c_api/gpu/StandardGpuResources_c.h +53 -0
  107. data/vendor/faiss/c_api/gpu/macros_impl.h +42 -0
  108. data/vendor/faiss/c_api/impl/AuxIndexStructures_c.cpp +220 -0
  109. data/vendor/faiss/c_api/impl/AuxIndexStructures_c.h +149 -0
  110. data/vendor/faiss/c_api/index_factory_c.cpp +26 -0
  111. data/vendor/faiss/c_api/index_factory_c.h +30 -0
  112. data/vendor/faiss/c_api/index_io_c.cpp +42 -0
  113. data/vendor/faiss/c_api/index_io_c.h +50 -0
  114. data/vendor/faiss/c_api/macros_impl.h +110 -0
  115. data/vendor/faiss/clone_index.cpp +147 -0
  116. data/vendor/faiss/clone_index.h +38 -0
  117. data/vendor/faiss/demos/demo_imi_flat.cpp +151 -0
  118. data/vendor/faiss/demos/demo_imi_pq.cpp +199 -0
  119. data/vendor/faiss/demos/demo_ivfpq_indexing.cpp +146 -0
  120. data/vendor/faiss/demos/demo_sift1M.cpp +252 -0
  121. data/vendor/faiss/gpu/GpuAutoTune.cpp +95 -0
  122. data/vendor/faiss/gpu/GpuAutoTune.h +27 -0
  123. data/vendor/faiss/gpu/GpuCloner.cpp +403 -0
  124. data/vendor/faiss/gpu/GpuCloner.h +82 -0
  125. data/vendor/faiss/gpu/GpuClonerOptions.cpp +28 -0
  126. data/vendor/faiss/gpu/GpuClonerOptions.h +53 -0
  127. data/vendor/faiss/gpu/GpuDistance.h +52 -0
  128. data/vendor/faiss/gpu/GpuFaissAssert.h +29 -0
  129. data/vendor/faiss/gpu/GpuIndex.h +148 -0
  130. data/vendor/faiss/gpu/GpuIndexBinaryFlat.h +89 -0
  131. data/vendor/faiss/gpu/GpuIndexFlat.h +190 -0
  132. data/vendor/faiss/gpu/GpuIndexIVF.h +89 -0
  133. data/vendor/faiss/gpu/GpuIndexIVFFlat.h +85 -0
  134. data/vendor/faiss/gpu/GpuIndexIVFPQ.h +143 -0
  135. data/vendor/faiss/gpu/GpuIndexIVFScalarQuantizer.h +100 -0
  136. data/vendor/faiss/gpu/GpuIndicesOptions.h +30 -0
  137. data/vendor/faiss/gpu/GpuResources.cpp +52 -0
  138. data/vendor/faiss/gpu/GpuResources.h +73 -0
  139. data/vendor/faiss/gpu/StandardGpuResources.cpp +295 -0
  140. data/vendor/faiss/gpu/StandardGpuResources.h +114 -0
  141. data/vendor/faiss/gpu/impl/RemapIndices.cpp +43 -0
  142. data/vendor/faiss/gpu/impl/RemapIndices.h +24 -0
  143. data/vendor/faiss/gpu/perf/IndexWrapper-inl.h +71 -0
  144. data/vendor/faiss/gpu/perf/IndexWrapper.h +39 -0
  145. data/vendor/faiss/gpu/perf/PerfClustering.cpp +115 -0
  146. data/vendor/faiss/gpu/perf/PerfIVFPQAdd.cpp +139 -0
  147. data/vendor/faiss/gpu/perf/WriteIndex.cpp +102 -0
  148. data/vendor/faiss/gpu/test/TestGpuIndexBinaryFlat.cpp +130 -0
  149. data/vendor/faiss/gpu/test/TestGpuIndexFlat.cpp +371 -0
  150. data/vendor/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +550 -0
  151. data/vendor/faiss/gpu/test/TestGpuIndexIVFPQ.cpp +450 -0
  152. data/vendor/faiss/gpu/test/TestGpuMemoryException.cpp +84 -0
  153. data/vendor/faiss/gpu/test/TestUtils.cpp +315 -0
  154. data/vendor/faiss/gpu/test/TestUtils.h +93 -0
  155. data/vendor/faiss/gpu/test/demo_ivfpq_indexing_gpu.cpp +159 -0
  156. data/vendor/faiss/gpu/utils/DeviceMemory.cpp +77 -0
  157. data/vendor/faiss/gpu/utils/DeviceMemory.h +71 -0
  158. data/vendor/faiss/gpu/utils/DeviceUtils.h +185 -0
  159. data/vendor/faiss/gpu/utils/MemorySpace.cpp +89 -0
  160. data/vendor/faiss/gpu/utils/MemorySpace.h +44 -0
  161. data/vendor/faiss/gpu/utils/StackDeviceMemory.cpp +239 -0
  162. data/vendor/faiss/gpu/utils/StackDeviceMemory.h +129 -0
  163. data/vendor/faiss/gpu/utils/StaticUtils.h +83 -0
  164. data/vendor/faiss/gpu/utils/Timer.cpp +60 -0
  165. data/vendor/faiss/gpu/utils/Timer.h +52 -0
  166. data/vendor/faiss/impl/AuxIndexStructures.cpp +305 -0
  167. data/vendor/faiss/impl/AuxIndexStructures.h +246 -0
  168. data/vendor/faiss/impl/FaissAssert.h +95 -0
  169. data/vendor/faiss/impl/FaissException.cpp +66 -0
  170. data/vendor/faiss/impl/FaissException.h +71 -0
  171. data/vendor/faiss/impl/HNSW.cpp +818 -0
  172. data/vendor/faiss/impl/HNSW.h +275 -0
  173. data/vendor/faiss/impl/PolysemousTraining.cpp +953 -0
  174. data/vendor/faiss/impl/PolysemousTraining.h +158 -0
  175. data/vendor/faiss/impl/ProductQuantizer.cpp +876 -0
  176. data/vendor/faiss/impl/ProductQuantizer.h +242 -0
  177. data/vendor/faiss/impl/ScalarQuantizer.cpp +1628 -0
  178. data/vendor/faiss/impl/ScalarQuantizer.h +120 -0
  179. data/vendor/faiss/impl/ThreadedIndex-inl.h +192 -0
  180. data/vendor/faiss/impl/ThreadedIndex.h +80 -0
  181. data/vendor/faiss/impl/index_read.cpp +793 -0
  182. data/vendor/faiss/impl/index_write.cpp +558 -0
  183. data/vendor/faiss/impl/io.cpp +142 -0
  184. data/vendor/faiss/impl/io.h +98 -0
  185. data/vendor/faiss/impl/lattice_Zn.cpp +712 -0
  186. data/vendor/faiss/impl/lattice_Zn.h +199 -0
  187. data/vendor/faiss/index_factory.cpp +392 -0
  188. data/vendor/faiss/index_factory.h +25 -0
  189. data/vendor/faiss/index_io.h +75 -0
  190. data/vendor/faiss/misc/test_blas.cpp +84 -0
  191. data/vendor/faiss/tests/test_binary_flat.cpp +64 -0
  192. data/vendor/faiss/tests/test_dealloc_invlists.cpp +183 -0
  193. data/vendor/faiss/tests/test_ivfpq_codec.cpp +67 -0
  194. data/vendor/faiss/tests/test_ivfpq_indexing.cpp +98 -0
  195. data/vendor/faiss/tests/test_lowlevel_ivf.cpp +566 -0
  196. data/vendor/faiss/tests/test_merge.cpp +258 -0
  197. data/vendor/faiss/tests/test_omp_threads.cpp +14 -0
  198. data/vendor/faiss/tests/test_ondisk_ivf.cpp +220 -0
  199. data/vendor/faiss/tests/test_pairs_decoding.cpp +189 -0
  200. data/vendor/faiss/tests/test_params_override.cpp +231 -0
  201. data/vendor/faiss/tests/test_pq_encoding.cpp +98 -0
  202. data/vendor/faiss/tests/test_sliding_ivf.cpp +240 -0
  203. data/vendor/faiss/tests/test_threaded_index.cpp +253 -0
  204. data/vendor/faiss/tests/test_transfer_invlists.cpp +159 -0
  205. data/vendor/faiss/tutorial/cpp/1-Flat.cpp +98 -0
  206. data/vendor/faiss/tutorial/cpp/2-IVFFlat.cpp +81 -0
  207. data/vendor/faiss/tutorial/cpp/3-IVFPQ.cpp +93 -0
  208. data/vendor/faiss/tutorial/cpp/4-GPU.cpp +119 -0
  209. data/vendor/faiss/tutorial/cpp/5-Multiple-GPUs.cpp +99 -0
  210. data/vendor/faiss/utils/Heap.cpp +122 -0
  211. data/vendor/faiss/utils/Heap.h +495 -0
  212. data/vendor/faiss/utils/WorkerThread.cpp +126 -0
  213. data/vendor/faiss/utils/WorkerThread.h +61 -0
  214. data/vendor/faiss/utils/distances.cpp +765 -0
  215. data/vendor/faiss/utils/distances.h +243 -0
  216. data/vendor/faiss/utils/distances_simd.cpp +809 -0
  217. data/vendor/faiss/utils/extra_distances.cpp +336 -0
  218. data/vendor/faiss/utils/extra_distances.h +54 -0
  219. data/vendor/faiss/utils/hamming-inl.h +472 -0
  220. data/vendor/faiss/utils/hamming.cpp +792 -0
  221. data/vendor/faiss/utils/hamming.h +220 -0
  222. data/vendor/faiss/utils/random.cpp +192 -0
  223. data/vendor/faiss/utils/random.h +60 -0
  224. data/vendor/faiss/utils/utils.cpp +783 -0
  225. data/vendor/faiss/utils/utils.h +181 -0
  226. metadata +216 -2
@@ -0,0 +1,142 @@
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
+ #include <cstring>
11
+ #include <cassert>
12
+
13
+ #include <faiss/impl/io.h>
14
+ #include <faiss/impl/FaissAssert.h>
15
+
16
+
17
+ namespace faiss {
18
+
19
+
20
+ /***********************************************************************
21
+ * IO functions
22
+ ***********************************************************************/
23
+
24
+
25
+ int IOReader::fileno ()
26
+ {
27
+ FAISS_THROW_MSG ("IOReader does not support memory mapping");
28
+ }
29
+
30
+ int IOWriter::fileno ()
31
+ {
32
+ FAISS_THROW_MSG ("IOWriter does not support memory mapping");
33
+ }
34
+
35
+ /***********************************************************************
36
+ * IO Vector
37
+ ***********************************************************************/
38
+
39
+
40
+
41
+ size_t VectorIOWriter::operator()(
42
+ const void *ptr, size_t size, size_t nitems)
43
+ {
44
+ size_t bytes = size * nitems;
45
+ if (bytes > 0) {
46
+ size_t o = data.size();
47
+ data.resize(o + bytes);
48
+ memcpy (&data[o], ptr, size * nitems);
49
+ }
50
+ return nitems;
51
+ }
52
+
53
+ size_t VectorIOReader::operator()(
54
+ void *ptr, size_t size, size_t nitems)
55
+ {
56
+ if (rp >= data.size()) return 0;
57
+ size_t nremain = (data.size() - rp) / size;
58
+ if (nremain < nitems) nitems = nremain;
59
+ if (size * nitems > 0) {
60
+ memcpy (ptr, &data[rp], size * nitems);
61
+ rp += size * nitems;
62
+ }
63
+ return nitems;
64
+ }
65
+
66
+
67
+
68
+
69
+ /***********************************************************************
70
+ * IO File
71
+ ***********************************************************************/
72
+
73
+
74
+
75
+ FileIOReader::FileIOReader(FILE *rf): f(rf) {}
76
+
77
+ FileIOReader::FileIOReader(const char * fname)
78
+ {
79
+ name = fname;
80
+ f = fopen(fname, "rb");
81
+ FAISS_THROW_IF_NOT_FMT (f, "could not open %s for reading: %s",
82
+ fname, strerror(errno));
83
+ need_close = true;
84
+ }
85
+
86
+ FileIOReader::~FileIOReader() {
87
+ if (need_close) {
88
+ int ret = fclose(f);
89
+ if (ret != 0) {// we cannot raise and exception in the destructor
90
+ fprintf(stderr, "file %s close error: %s",
91
+ name.c_str(), strerror(errno));
92
+ }
93
+ }
94
+ }
95
+
96
+ size_t FileIOReader::operator()(void *ptr, size_t size, size_t nitems) {
97
+ return fread(ptr, size, nitems, f);
98
+ }
99
+
100
+ int FileIOReader::fileno() {
101
+ return ::fileno (f);
102
+ }
103
+
104
+
105
+ FileIOWriter::FileIOWriter(FILE *wf): f(wf) {}
106
+
107
+ FileIOWriter::FileIOWriter(const char * fname)
108
+ {
109
+ name = fname;
110
+ f = fopen(fname, "wb");
111
+ FAISS_THROW_IF_NOT_FMT (f, "could not open %s for writing: %s",
112
+ fname, strerror(errno));
113
+ need_close = true;
114
+ }
115
+
116
+ FileIOWriter::~FileIOWriter() {
117
+ if (need_close) {
118
+ int ret = fclose(f);
119
+ if (ret != 0) {
120
+ // we cannot raise and exception in the destructor
121
+ fprintf(stderr, "file %s close error: %s",
122
+ name.c_str(), strerror(errno));
123
+ }
124
+ }
125
+ }
126
+
127
+ size_t FileIOWriter::operator()(const void *ptr, size_t size, size_t nitems) {
128
+ return fwrite(ptr, size, nitems, f);
129
+ }
130
+
131
+ int FileIOWriter::fileno() {
132
+ return ::fileno (f);
133
+ }
134
+
135
+ uint32_t fourcc (const char sx[4]) {
136
+ assert(4 == strlen(sx));
137
+ const unsigned char *x = (unsigned char*)sx;
138
+ return x[0] | x[1] << 8 | x[2] << 16 | x[3] << 24;
139
+ }
140
+
141
+
142
+ } // namespace faiss
@@ -0,0 +1,98 @@
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
+ * Abstract I/O objects
12
+ ***********************************************************/
13
+
14
+ #pragma once
15
+
16
+ #include <string>
17
+ #include <cstdio>
18
+ #include <vector>
19
+
20
+ #include <faiss/Index.h>
21
+
22
+ namespace faiss {
23
+
24
+
25
+ struct IOReader {
26
+ // name that can be used in error messages
27
+ std::string name;
28
+
29
+ // fread
30
+ virtual size_t operator()(
31
+ void *ptr, size_t size, size_t nitems) = 0;
32
+
33
+ // return a file number that can be memory-mapped
34
+ virtual int fileno ();
35
+
36
+ virtual ~IOReader() {}
37
+ };
38
+
39
+ struct IOWriter {
40
+ // name that can be used in error messages
41
+ std::string name;
42
+
43
+ // fwrite
44
+ virtual size_t operator()(
45
+ const void *ptr, size_t size, size_t nitems) = 0;
46
+
47
+ // return a file number that can be memory-mapped
48
+ virtual int fileno ();
49
+
50
+ virtual ~IOWriter() {}
51
+ };
52
+
53
+
54
+ struct VectorIOReader:IOReader {
55
+ std::vector<uint8_t> data;
56
+ size_t rp = 0;
57
+ size_t operator()(void *ptr, size_t size, size_t nitems) override;
58
+ };
59
+
60
+ struct VectorIOWriter:IOWriter {
61
+ std::vector<uint8_t> data;
62
+ size_t operator()(const void *ptr, size_t size, size_t nitems) override;
63
+ };
64
+
65
+ struct FileIOReader: IOReader {
66
+ FILE *f = nullptr;
67
+ bool need_close = false;
68
+
69
+ FileIOReader(FILE *rf);
70
+
71
+ FileIOReader(const char * fname);
72
+
73
+ ~FileIOReader() override;
74
+
75
+ size_t operator()(void *ptr, size_t size, size_t nitems) override;
76
+
77
+ int fileno() override;
78
+ };
79
+
80
+ struct FileIOWriter: IOWriter {
81
+ FILE *f = nullptr;
82
+ bool need_close = false;
83
+
84
+ FileIOWriter(FILE *wf);
85
+
86
+ FileIOWriter(const char * fname);
87
+
88
+ ~FileIOWriter() override;
89
+
90
+ size_t operator()(const void *ptr, size_t size, size_t nitems) override;
91
+
92
+ int fileno() override;
93
+ };
94
+
95
+ /// cast a 4-character string to a uint32_t that can be written and read easily
96
+ uint32_t fourcc (const char sx[4]);
97
+
98
+ } // namespace faiss
@@ -0,0 +1,712 @@
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
+ #include <faiss/impl/lattice_Zn.h>
11
+
12
+ #include <cstdlib>
13
+ #include <cmath>
14
+ #include <cstring>
15
+ #include <cassert>
16
+
17
+ #include <queue>
18
+ #include <unordered_set>
19
+ #include <unordered_map>
20
+ #include <algorithm>
21
+
22
+ #include <faiss/utils/distances.h>
23
+
24
+ namespace faiss {
25
+
26
+ /********************************************
27
+ * small utility functions
28
+ ********************************************/
29
+
30
+ namespace {
31
+
32
+ inline float sqr(float x) {
33
+ return x * x;
34
+ }
35
+
36
+
37
+ typedef std::vector<float> point_list_t;
38
+
39
+ struct Comb {
40
+ std::vector<uint64_t> tab; // Pascal's triangle
41
+ int nmax;
42
+
43
+ explicit Comb(int nmax): nmax(nmax) {
44
+ tab.resize(nmax * nmax, 0);
45
+ tab[0] = 1;
46
+ for(int i = 1; i < nmax; i++) {
47
+ tab[i * nmax] = 1;
48
+ for(int j = 1; j <= i; j++) {
49
+ tab[i * nmax + j] =
50
+ tab[(i - 1) * nmax + j] +
51
+ tab[(i - 1) * nmax + (j - 1)];
52
+ }
53
+
54
+ }
55
+ }
56
+
57
+ uint64_t operator()(int n, int p) const {
58
+ assert (n < nmax && p < nmax);
59
+ if (p > n) return 0;
60
+ return tab[n * nmax + p];
61
+ }
62
+ };
63
+
64
+ Comb comb(100);
65
+
66
+
67
+
68
+ // compute combinations of n integer values <= v that sum up to total (squared)
69
+ point_list_t sum_of_sq (float total, int v, int n, float add = 0) {
70
+ if (total < 0) {
71
+ return point_list_t();
72
+ } else if (n == 1) {
73
+ while (sqr(v + add) > total) v--;
74
+ if (sqr(v + add) == total) {
75
+ return point_list_t(1, v + add);
76
+ } else {
77
+ return point_list_t();
78
+ }
79
+ } else {
80
+ point_list_t res;
81
+ while (v >= 0) {
82
+ point_list_t sub_points =
83
+ sum_of_sq (total - sqr(v + add), v, n - 1, add);
84
+ for (size_t i = 0; i < sub_points.size(); i += n - 1) {
85
+ res.push_back (v + add);
86
+ for (int j = 0; j < n - 1; j++) {
87
+ res.push_back(sub_points[i + j]);
88
+ }
89
+ }
90
+ v--;
91
+ }
92
+ return res;
93
+ }
94
+ }
95
+
96
+ int decode_comb_1 (uint64_t *n, int k1, int r) {
97
+ while (comb(r, k1) > *n) {
98
+ r--;
99
+ }
100
+ *n -= comb(r, k1);
101
+ return r;
102
+ }
103
+
104
+ // optimized version for < 64 bits
105
+ long repeats_encode_64 (
106
+ const std::vector<Repeat> & repeats,
107
+ int dim, const float *c)
108
+ {
109
+ uint64_t coded = 0;
110
+ int nfree = dim;
111
+ uint64_t code = 0, shift = 1;
112
+ for (auto r = repeats.begin(); r != repeats.end(); ++r) {
113
+ int rank = 0, occ = 0;
114
+ uint64_t code_comb = 0;
115
+ uint64_t tosee = ~coded;
116
+ for(;;) {
117
+ // directly jump to next available slot.
118
+ int i = __builtin_ctzl(tosee);
119
+ tosee &= ~(1UL << i) ;
120
+ if (c[i] == r->val) {
121
+ code_comb += comb(rank, occ + 1);
122
+ occ++;
123
+ coded |= 1UL << i;
124
+ if (occ == r->n) break;
125
+ }
126
+ rank++;
127
+ }
128
+ uint64_t max_comb = comb(nfree, r->n);
129
+ code += shift * code_comb;
130
+ shift *= max_comb;
131
+ nfree -= r->n;
132
+ }
133
+ return code;
134
+ }
135
+
136
+
137
+ void repeats_decode_64(
138
+ const std::vector<Repeat> & repeats,
139
+ int dim, uint64_t code, float *c)
140
+ {
141
+ uint64_t decoded = 0;
142
+ int nfree = dim;
143
+ for (auto r = repeats.begin(); r != repeats.end(); ++r) {
144
+ uint64_t max_comb = comb(nfree, r->n);
145
+ uint64_t code_comb = code % max_comb;
146
+ code /= max_comb;
147
+
148
+ int occ = 0;
149
+ int rank = nfree;
150
+ int next_rank = decode_comb_1 (&code_comb, r->n, rank);
151
+ uint64_t tosee = ((1UL << dim) - 1) ^ decoded;
152
+ for(;;) {
153
+ int i = 63 - __builtin_clzl(tosee);
154
+ tosee &= ~(1UL << i);
155
+ rank--;
156
+ if (rank == next_rank) {
157
+ decoded |= 1UL << i;
158
+ c[i] = r->val;
159
+ occ++;
160
+ if (occ == r->n) break;
161
+ next_rank = decode_comb_1 (
162
+ &code_comb, r->n - occ, next_rank);
163
+ }
164
+ }
165
+ nfree -= r->n;
166
+ }
167
+
168
+ }
169
+
170
+
171
+
172
+ } // anonymous namespace
173
+
174
+ Repeats::Repeats (int dim, const float *c): dim(dim)
175
+ {
176
+ for(int i = 0; i < dim; i++) {
177
+ int j = 0;
178
+ for(;;) {
179
+ if (j == repeats.size()) {
180
+ repeats.push_back(Repeat{c[i], 1});
181
+ break;
182
+ }
183
+ if (repeats[j].val == c[i]) {
184
+ repeats[j].n++;
185
+ break;
186
+ }
187
+ j++;
188
+ }
189
+ }
190
+ }
191
+
192
+
193
+ long Repeats::count () const
194
+ {
195
+ long accu = 1;
196
+ int remain = dim;
197
+ for (int i = 0; i < repeats.size(); i++) {
198
+ accu *= comb(remain, repeats[i].n);
199
+ remain -= repeats[i].n;
200
+ }
201
+ return accu;
202
+ }
203
+
204
+
205
+
206
+ // version with a bool vector that works for > 64 dim
207
+ long Repeats::encode(const float *c) const
208
+ {
209
+ if (dim < 64) {
210
+ return repeats_encode_64 (repeats, dim, c);
211
+ }
212
+ std::vector<bool> coded(dim, false);
213
+ int nfree = dim;
214
+ uint64_t code = 0, shift = 1;
215
+ for (auto r = repeats.begin(); r != repeats.end(); ++r) {
216
+ int rank = 0, occ = 0;
217
+ uint64_t code_comb = 0;
218
+ for (int i = 0; i < dim; i++) {
219
+ if (!coded[i]) {
220
+ if (c[i] == r->val) {
221
+ code_comb += comb(rank, occ + 1);
222
+ occ++;
223
+ coded[i] = true;
224
+ if (occ == r->n) break;
225
+ }
226
+ rank++;
227
+ }
228
+ }
229
+ uint64_t max_comb = comb(nfree, r->n);
230
+ code += shift * code_comb;
231
+ shift *= max_comb;
232
+ nfree -= r->n;
233
+ }
234
+ return code;
235
+ }
236
+
237
+
238
+
239
+ void Repeats::decode(uint64_t code, float *c) const
240
+ {
241
+ if (dim < 64) {
242
+ repeats_decode_64 (repeats, dim, code, c);
243
+ return;
244
+ }
245
+
246
+ std::vector<bool> decoded(dim, false);
247
+ int nfree = dim;
248
+ for (auto r = repeats.begin(); r != repeats.end(); ++r) {
249
+ uint64_t max_comb = comb(nfree, r->n);
250
+ uint64_t code_comb = code % max_comb;
251
+ code /= max_comb;
252
+
253
+ int occ = 0;
254
+ int rank = nfree;
255
+ int next_rank = decode_comb_1 (&code_comb, r->n, rank);
256
+ for (int i = dim - 1; i >= 0; i--) {
257
+ if (!decoded[i]) {
258
+ rank--;
259
+ if (rank == next_rank) {
260
+ decoded[i] = true;
261
+ c[i] = r->val;
262
+ occ++;
263
+ if (occ == r->n) break;
264
+ next_rank = decode_comb_1 (
265
+ &code_comb, r->n - occ, next_rank);
266
+ }
267
+ }
268
+ }
269
+ nfree -= r->n;
270
+ }
271
+
272
+ }
273
+
274
+
275
+
276
+ /********************************************
277
+ * EnumeratedVectors functions
278
+ ********************************************/
279
+
280
+
281
+ void EnumeratedVectors::encode_multi(size_t n, const float *c,
282
+ uint64_t * codes) const
283
+ {
284
+ #pragma omp parallel if (n > 1000)
285
+ {
286
+ #pragma omp for
287
+ for(int i = 0; i < n; i++) {
288
+ codes[i] = encode(c + i * dim);
289
+ }
290
+ }
291
+ }
292
+
293
+
294
+ void EnumeratedVectors::decode_multi(size_t n, const uint64_t * codes,
295
+ float *c) const
296
+ {
297
+ #pragma omp parallel if (n > 1000)
298
+ {
299
+ #pragma omp for
300
+ for(int i = 0; i < n; i++) {
301
+ decode(codes[i], c + i * dim);
302
+ }
303
+ }
304
+ }
305
+
306
+ void EnumeratedVectors::find_nn (
307
+ size_t nc, const uint64_t * codes,
308
+ size_t nq, const float *xq,
309
+ long *labels, float *distances)
310
+ {
311
+ for (long i = 0; i < nq; i++) {
312
+ distances[i] = -1e20;
313
+ labels[i] = -1;
314
+ }
315
+
316
+ float c[dim];
317
+ for(long i = 0; i < nc; i++) {
318
+ uint64_t code = codes[nc];
319
+ decode(code, c);
320
+ for (long j = 0; j < nq; j++) {
321
+ const float *x = xq + j * dim;
322
+ float dis = fvec_inner_product(x, c, dim);
323
+ if (dis > distances[j]) {
324
+ distances[j] = dis;
325
+ labels[j] = i;
326
+ }
327
+ }
328
+ }
329
+
330
+ }
331
+
332
+
333
+ /**********************************************************
334
+ * ZnSphereSearch
335
+ **********************************************************/
336
+
337
+
338
+ ZnSphereSearch::ZnSphereSearch(int dim, int r2): dimS(dim), r2(r2) {
339
+ voc = sum_of_sq(r2, int(ceil(sqrt(r2)) + 1), dim);
340
+ natom = voc.size() / dim;
341
+ }
342
+
343
+ float ZnSphereSearch::search(const float *x, float *c) const {
344
+ float tmp[dimS * 2];
345
+ int tmp_int[dimS];
346
+ return search(x, c, tmp, tmp_int);
347
+ }
348
+
349
+ float ZnSphereSearch::search(const float *x, float *c,
350
+ float *tmp, // size 2 *dim
351
+ int *tmp_int, // size dim
352
+ int *ibest_out
353
+ ) const {
354
+ int dim = dimS;
355
+ assert (natom > 0);
356
+ int *o = tmp_int;
357
+ float *xabs = tmp;
358
+ float *xperm = tmp + dim;
359
+
360
+ // argsort
361
+ for (int i = 0; i < dim; i++) {
362
+ o[i] = i;
363
+ xabs[i] = fabsf(x[i]);
364
+ }
365
+ std::sort(o, o + dim, [xabs](int a, int b) {
366
+ return xabs[a] > xabs[b];
367
+ });
368
+ for (int i = 0; i < dim; i++) {
369
+ xperm[i] = xabs[o[i]];
370
+ }
371
+ // find best
372
+ int ibest = -1;
373
+ float dpbest = -100;
374
+ for (int i = 0; i < natom; i++) {
375
+ float dp = fvec_inner_product (voc.data() + i * dim, xperm, dim);
376
+ if (dp > dpbest) {
377
+ dpbest = dp;
378
+ ibest = i;
379
+ }
380
+ }
381
+ // revert sort
382
+ const float *cin = voc.data() + ibest * dim;
383
+ for (int i = 0; i < dim; i++) {
384
+ c[o[i]] = copysignf (cin[i], x[o[i]]);
385
+ }
386
+ if (ibest_out) {
387
+ *ibest_out = ibest;
388
+ }
389
+ return dpbest;
390
+ }
391
+
392
+ void ZnSphereSearch::search_multi(int n, const float *x,
393
+ float *c_out,
394
+ float *dp_out) {
395
+ #pragma omp parallel if (n > 1000)
396
+ {
397
+ #pragma omp for
398
+ for(int i = 0; i < n; i++) {
399
+ dp_out[i] = search(x + i * dimS, c_out + i * dimS);
400
+ }
401
+ }
402
+ }
403
+
404
+
405
+ /**********************************************************
406
+ * ZnSphereCodec
407
+ **********************************************************/
408
+
409
+ ZnSphereCodec::ZnSphereCodec(int dim, int r2):
410
+ ZnSphereSearch(dim, r2),
411
+ EnumeratedVectors(dim)
412
+ {
413
+ nv = 0;
414
+ for (int i = 0; i < natom; i++) {
415
+ Repeats repeats(dim, &voc[i * dim]);
416
+ CodeSegment cs(repeats);
417
+ cs.c0 = nv;
418
+ Repeat &br = repeats.repeats.back();
419
+ cs.signbits = br.val == 0 ? dim - br.n : dim;
420
+ code_segments.push_back(cs);
421
+ nv += repeats.count() << cs.signbits;
422
+ }
423
+
424
+ uint64_t nvx = nv;
425
+ code_size = 0;
426
+ while (nvx > 0) {
427
+ nvx >>= 8;
428
+ code_size++;
429
+ }
430
+ }
431
+
432
+ uint64_t ZnSphereCodec::search_and_encode(const float *x) const {
433
+ float tmp[dim * 2];
434
+ int tmp_int[dim];
435
+ int ano; // atom number
436
+ float c[dim];
437
+ search(x, c, tmp, tmp_int, &ano);
438
+ uint64_t signs = 0;
439
+ float cabs[dim];
440
+ int nnz = 0;
441
+ for (int i = 0; i < dim; i++) {
442
+ cabs[i] = fabs(c[i]);
443
+ if (c[i] != 0) {
444
+ if (c[i] < 0) {
445
+ signs |= 1UL << nnz;
446
+ }
447
+ nnz ++;
448
+ }
449
+ }
450
+ const CodeSegment &cs = code_segments[ano];
451
+ assert(nnz == cs.signbits);
452
+ uint64_t code = cs.c0 + signs;
453
+ code += cs.encode(cabs) << cs.signbits;
454
+ return code;
455
+ }
456
+
457
+ uint64_t ZnSphereCodec::encode(const float *x) const
458
+ {
459
+ return search_and_encode(x);
460
+ }
461
+
462
+
463
+ void ZnSphereCodec::decode(uint64_t code, float *c) const {
464
+ int i0 = 0, i1 = natom;
465
+ while (i0 + 1 < i1) {
466
+ int imed = (i0 + i1) / 2;
467
+ if (code_segments[imed].c0 <= code) i0 = imed;
468
+ else i1 = imed;
469
+ }
470
+ const CodeSegment &cs = code_segments[i0];
471
+ code -= cs.c0;
472
+ uint64_t signs = code;
473
+ code >>= cs.signbits;
474
+ cs.decode(code, c);
475
+
476
+ int nnz = 0;
477
+ for (int i = 0; i < dim; i++) {
478
+ if (c[i] != 0) {
479
+ if (signs & (1UL << nnz)) {
480
+ c[i] = -c[i];
481
+ }
482
+ nnz ++;
483
+ }
484
+ }
485
+ }
486
+
487
+
488
+ /**************************************************************
489
+ * ZnSphereCodecRec
490
+ **************************************************************/
491
+
492
+ uint64_t ZnSphereCodecRec::get_nv(int ld, int r2a) const
493
+ {
494
+ return all_nv[ld * (r2 + 1) + r2a];
495
+ }
496
+
497
+
498
+ uint64_t ZnSphereCodecRec::get_nv_cum(int ld, int r2t, int r2a) const
499
+ {
500
+ return all_nv_cum[(ld * (r2 + 1) + r2t) * (r2 + 1) + r2a];
501
+ }
502
+
503
+ void ZnSphereCodecRec::set_nv_cum(int ld, int r2t, int r2a, uint64_t cum)
504
+ {
505
+ all_nv_cum[(ld * (r2 + 1) + r2t) * (r2 + 1) + r2a] = cum;
506
+ }
507
+
508
+
509
+ ZnSphereCodecRec::ZnSphereCodecRec(int dim, int r2):
510
+ EnumeratedVectors(dim), r2(r2)
511
+ {
512
+ log2_dim = 0;
513
+ while (dim > (1 << log2_dim)) {
514
+ log2_dim++;
515
+ }
516
+ assert(dim == (1 << log2_dim) ||
517
+ !"dimension must be a power of 2");
518
+
519
+ all_nv.resize((log2_dim + 1) * (r2 + 1));
520
+ all_nv_cum.resize((log2_dim + 1) * (r2 + 1) * (r2 + 1));
521
+
522
+ for (int r2a = 0; r2a <= r2; r2a++) {
523
+ int r = int(sqrt(r2a));
524
+ if (r * r == r2a) {
525
+ all_nv[r2a] = r == 0 ? 1 : 2;
526
+ } else {
527
+ all_nv[r2a] = 0;
528
+ }
529
+ }
530
+
531
+ for (int ld = 1; ld <= log2_dim; ld++) {
532
+
533
+ for (int r2sub = 0; r2sub <= r2; r2sub++) {
534
+ uint64_t nv = 0;
535
+ for (int r2a = 0; r2a <= r2sub; r2a++) {
536
+ int r2b = r2sub - r2a;
537
+ set_nv_cum(ld, r2sub, r2a, nv);
538
+ nv += get_nv(ld - 1, r2a) * get_nv(ld - 1, r2b);
539
+ }
540
+ all_nv[ld * (r2 + 1) + r2sub] = nv;
541
+ }
542
+ }
543
+ nv = get_nv(log2_dim, r2);
544
+
545
+ uint64_t nvx = nv;
546
+ code_size = 0;
547
+ while (nvx > 0) {
548
+ nvx >>= 8;
549
+ code_size++;
550
+ }
551
+
552
+ int cache_level = std::min(3, log2_dim - 1);
553
+ decode_cache_ld = 0;
554
+ assert(cache_level <= log2_dim);
555
+ decode_cache.resize((r2 + 1));
556
+
557
+ for (int r2sub = 0; r2sub <= r2; r2sub++) {
558
+ int ld = cache_level;
559
+ uint64_t nvi = get_nv(ld, r2sub);
560
+ std::vector<float> &cache = decode_cache[r2sub];
561
+ int dimsub = (1 << cache_level);
562
+ cache.resize (nvi * dimsub);
563
+ float c[dim];
564
+ uint64_t code0 = get_nv_cum(cache_level + 1, r2,
565
+ r2 - r2sub);
566
+ for (int i = 0; i < nvi; i++) {
567
+ decode(i + code0, c);
568
+ memcpy(&cache[i * dimsub], c + dim - dimsub,
569
+ dimsub * sizeof(*c));
570
+ }
571
+ }
572
+ decode_cache_ld = cache_level;
573
+ }
574
+
575
+ uint64_t ZnSphereCodecRec::encode(const float *c) const
576
+ {
577
+ return encode_centroid(c);
578
+ }
579
+
580
+
581
+
582
+ uint64_t ZnSphereCodecRec::encode_centroid(const float *c) const
583
+ {
584
+ uint64_t codes[dim];
585
+ int norm2s[dim];
586
+ for(int i = 0; i < dim; i++) {
587
+ if (c[i] == 0) {
588
+ codes[i] = 0;
589
+ norm2s[i] = 0;
590
+ } else {
591
+ int r2i = int(c[i] * c[i]);
592
+ norm2s[i] = r2i;
593
+ codes[i] = c[i] >= 0 ? 0 : 1;
594
+ }
595
+ }
596
+ int dim2 = dim / 2;
597
+ for(int ld = 1; ld <= log2_dim; ld++) {
598
+ for (int i = 0; i < dim2; i++) {
599
+ int r2a = norm2s[2 * i];
600
+ int r2b = norm2s[2 * i + 1];
601
+
602
+ uint64_t code_a = codes[2 * i];
603
+ uint64_t code_b = codes[2 * i + 1];
604
+
605
+ codes[i] =
606
+ get_nv_cum(ld, r2a + r2b, r2a) +
607
+ code_a * get_nv(ld - 1, r2b) +
608
+ code_b;
609
+ norm2s[i] = r2a + r2b;
610
+ }
611
+ dim2 /= 2;
612
+ }
613
+ return codes[0];
614
+ }
615
+
616
+
617
+
618
+ void ZnSphereCodecRec::decode(uint64_t code, float *c) const
619
+ {
620
+ uint64_t codes[dim];
621
+ int norm2s[dim];
622
+ codes[0] = code;
623
+ norm2s[0] = r2;
624
+
625
+ int dim2 = 1;
626
+ for(int ld = log2_dim; ld > decode_cache_ld; ld--) {
627
+ for (int i = dim2 - 1; i >= 0; i--) {
628
+ int r2sub = norm2s[i];
629
+ int i0 = 0, i1 = r2sub + 1;
630
+ uint64_t codei = codes[i];
631
+ const uint64_t *cum =
632
+ &all_nv_cum[(ld * (r2 + 1) + r2sub) * (r2 + 1)];
633
+ while (i1 > i0 + 1) {
634
+ int imed = (i0 + i1) / 2;
635
+ if (cum[imed] <= codei)
636
+ i0 = imed;
637
+ else
638
+ i1 = imed;
639
+ }
640
+ int r2a = i0, r2b = r2sub - i0;
641
+ codei -= cum[r2a];
642
+ norm2s[2 * i] = r2a;
643
+ norm2s[2 * i + 1] = r2b;
644
+
645
+ uint64_t code_a = codei / get_nv(ld - 1, r2b);
646
+ uint64_t code_b = codei % get_nv(ld - 1, r2b);
647
+
648
+ codes[2 * i] = code_a;
649
+ codes[2 * i + 1] = code_b;
650
+
651
+ }
652
+ dim2 *= 2;
653
+ }
654
+
655
+ if (decode_cache_ld == 0) {
656
+ for(int i = 0; i < dim; i++) {
657
+ if (norm2s[i] == 0) {
658
+ c[i] = 0;
659
+ } else {
660
+ float r = sqrt(norm2s[i]);
661
+ assert(r * r == norm2s[i]);
662
+ c[i] = codes[i] == 0 ? r : -r;
663
+ }
664
+ }
665
+ } else {
666
+ int subdim = 1 << decode_cache_ld;
667
+ assert ((dim2 * subdim) == dim);
668
+
669
+ for(int i = 0; i < dim2; i++) {
670
+
671
+ const std::vector<float> & cache =
672
+ decode_cache[norm2s[i]];
673
+ assert(codes[i] < cache.size());
674
+ memcpy(c + i * subdim,
675
+ &cache[codes[i] * subdim],
676
+ sizeof(*c)* subdim);
677
+ }
678
+ }
679
+ }
680
+
681
+ // if not use_rec, instanciate an arbitrary harmless znc_rec
682
+ ZnSphereCodecAlt::ZnSphereCodecAlt (int dim, int r2):
683
+ ZnSphereCodec (dim, r2),
684
+ use_rec ((dim & (dim - 1)) == 0),
685
+ znc_rec (use_rec ? dim : 8,
686
+ use_rec ? r2 : 14)
687
+ {}
688
+
689
+ uint64_t ZnSphereCodecAlt::encode(const float *x) const
690
+ {
691
+ if (!use_rec) {
692
+ // it's ok if the vector is not normalized
693
+ return ZnSphereCodec::encode(x);
694
+ } else {
695
+ // find nearest centroid
696
+ std::vector<float> centroid(dim);
697
+ search (x, centroid.data());
698
+ return znc_rec.encode(centroid.data());
699
+ }
700
+ }
701
+
702
+ void ZnSphereCodecAlt::decode(uint64_t code, float *c) const
703
+ {
704
+ if (!use_rec) {
705
+ ZnSphereCodec::decode (code, c);
706
+ } else {
707
+ znc_rec.decode (code, c);
708
+ }
709
+ }
710
+
711
+
712
+ } // namespace faiss