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,353 @@
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
+ #ifndef FAISS_INDEX_IVF_H
11
+ #define FAISS_INDEX_IVF_H
12
+
13
+
14
+ #include <vector>
15
+ #include <stdint.h>
16
+
17
+ #include <faiss/Index.h>
18
+ #include <faiss/InvertedLists.h>
19
+ #include <faiss/Clustering.h>
20
+ #include <faiss/utils/Heap.h>
21
+
22
+
23
+ namespace faiss {
24
+
25
+
26
+ /** Encapsulates a quantizer object for the IndexIVF
27
+ *
28
+ * The class isolates the fields that are independent of the storage
29
+ * of the lists (especially training)
30
+ */
31
+ struct Level1Quantizer {
32
+ Index * quantizer; ///< quantizer that maps vectors to inverted lists
33
+ size_t nlist; ///< number of possible key values
34
+
35
+
36
+ /**
37
+ * = 0: use the quantizer as index in a kmeans training
38
+ * = 1: just pass on the training set to the train() of the quantizer
39
+ * = 2: kmeans training on a flat index + add the centroids to the quantizer
40
+ */
41
+ char quantizer_trains_alone;
42
+ bool own_fields; ///< whether object owns the quantizer
43
+
44
+ ClusteringParameters cp; ///< to override default clustering params
45
+ Index *clustering_index; ///< to override index used during clustering
46
+
47
+ /// Trains the quantizer and calls train_residual to train sub-quantizers
48
+ void train_q1 (size_t n, const float *x, bool verbose,
49
+ MetricType metric_type);
50
+
51
+
52
+ /// compute the number of bytes required to store list ids
53
+ size_t coarse_code_size () const;
54
+ void encode_listno (Index::idx_t list_no, uint8_t *code) const;
55
+ Index::idx_t decode_listno (const uint8_t *code) const;
56
+
57
+ Level1Quantizer (Index * quantizer, size_t nlist);
58
+
59
+ Level1Quantizer ();
60
+
61
+ ~Level1Quantizer ();
62
+
63
+ };
64
+
65
+
66
+
67
+ struct IVFSearchParameters {
68
+ size_t nprobe; ///< number of probes at query time
69
+ size_t max_codes; ///< max nb of codes to visit to do a query
70
+ virtual ~IVFSearchParameters () {}
71
+ };
72
+
73
+
74
+
75
+ struct InvertedListScanner;
76
+
77
+ /** Index based on a inverted file (IVF)
78
+ *
79
+ * In the inverted file, the quantizer (an Index instance) provides a
80
+ * quantization index for each vector to be added. The quantization
81
+ * index maps to a list (aka inverted list or posting list), where the
82
+ * id of the vector is stored.
83
+ *
84
+ * The inverted list object is required only after trainng. If none is
85
+ * set externally, an ArrayInvertedLists is used automatically.
86
+ *
87
+ * At search time, the vector to be searched is also quantized, and
88
+ * only the list corresponding to the quantization index is
89
+ * searched. This speeds up the search by making it
90
+ * non-exhaustive. This can be relaxed using multi-probe search: a few
91
+ * (nprobe) quantization indices are selected and several inverted
92
+ * lists are visited.
93
+ *
94
+ * Sub-classes implement a post-filtering of the index that refines
95
+ * the distance estimation from the query to databse vectors.
96
+ */
97
+ struct IndexIVF: Index, Level1Quantizer {
98
+ /// Acess to the actual data
99
+ InvertedLists *invlists;
100
+ bool own_invlists;
101
+
102
+ size_t code_size; ///< code size per vector in bytes
103
+
104
+ size_t nprobe; ///< number of probes at query time
105
+ size_t max_codes; ///< max nb of codes to visit to do a query
106
+
107
+ /** Parallel mode determines how queries are parallelized with OpenMP
108
+ *
109
+ * 0 (default): parallelize over queries
110
+ * 1: parallelize over over inverted lists
111
+ * 2: parallelize over both
112
+ */
113
+ int parallel_mode;
114
+
115
+ /// map for direct access to the elements. Enables reconstruct().
116
+ bool maintain_direct_map;
117
+ std::vector <idx_t> direct_map;
118
+
119
+ /** The Inverted file takes a quantizer (an Index) on input,
120
+ * which implements the function mapping a vector to a list
121
+ * identifier. The pointer is borrowed: the quantizer should not
122
+ * be deleted while the IndexIVF is in use.
123
+ */
124
+ IndexIVF (Index * quantizer, size_t d,
125
+ size_t nlist, size_t code_size,
126
+ MetricType metric = METRIC_L2);
127
+
128
+ void reset() override;
129
+
130
+ /// Trains the quantizer and calls train_residual to train sub-quantizers
131
+ void train(idx_t n, const float* x) override;
132
+
133
+ /// Calls add_with_ids with NULL ids
134
+ void add(idx_t n, const float* x) override;
135
+
136
+ /// default implementation that calls encode_vectors
137
+ void add_with_ids(idx_t n, const float* x, const idx_t* xids) override;
138
+
139
+ /** Encodes a set of vectors as they would appear in the inverted lists
140
+ *
141
+ * @param list_nos inverted list ids as returned by the
142
+ * quantizer (size n). -1s are ignored.
143
+ * @param codes output codes, size n * code_size
144
+ * @param include_listno
145
+ * include the list ids in the code (in this case add
146
+ * ceil(log8(nlist)) to the code size)
147
+ */
148
+ virtual void encode_vectors(idx_t n, const float* x,
149
+ const idx_t *list_nos,
150
+ uint8_t * codes,
151
+ bool include_listno = false) const = 0;
152
+
153
+ /// Sub-classes that encode the residuals can train their encoders here
154
+ /// does nothing by default
155
+ virtual void train_residual (idx_t n, const float *x);
156
+
157
+ /** search a set of vectors, that are pre-quantized by the IVF
158
+ * quantizer. Fill in the corresponding heaps with the query
159
+ * results. The default implementation uses InvertedListScanners
160
+ * to do the search.
161
+ *
162
+ * @param n nb of vectors to query
163
+ * @param x query vectors, size nx * d
164
+ * @param assign coarse quantization indices, size nx * nprobe
165
+ * @param centroid_dis
166
+ * distances to coarse centroids, size nx * nprobe
167
+ * @param distance
168
+ * output distances, size n * k
169
+ * @param labels output labels, size n * k
170
+ * @param store_pairs store inv list index + inv list offset
171
+ * instead in upper/lower 32 bit of result,
172
+ * instead of ids (used for reranking).
173
+ * @param params used to override the object's search parameters
174
+ */
175
+ virtual void search_preassigned (idx_t n, const float *x, idx_t k,
176
+ const idx_t *assign,
177
+ const float *centroid_dis,
178
+ float *distances, idx_t *labels,
179
+ bool store_pairs,
180
+ const IVFSearchParameters *params=nullptr
181
+ ) const;
182
+
183
+ /** assign the vectors, then call search_preassign */
184
+ void search (idx_t n, const float *x, idx_t k,
185
+ float *distances, idx_t *labels) const override;
186
+
187
+ void range_search (idx_t n, const float* x, float radius,
188
+ RangeSearchResult* result) const override;
189
+
190
+ void range_search_preassigned(idx_t nx, const float *x, float radius,
191
+ const idx_t *keys, const float *coarse_dis,
192
+ RangeSearchResult *result) const;
193
+
194
+ /// get a scanner for this index (store_pairs means ignore labels)
195
+ virtual InvertedListScanner *get_InvertedListScanner (
196
+ bool store_pairs=false) const;
197
+
198
+ void reconstruct (idx_t key, float* recons) const override;
199
+
200
+ /** Reconstruct a subset of the indexed vectors.
201
+ *
202
+ * Overrides default implementation to bypass reconstruct() which requires
203
+ * direct_map to be maintained.
204
+ *
205
+ * @param i0 first vector to reconstruct
206
+ * @param ni nb of vectors to reconstruct
207
+ * @param recons output array of reconstructed vectors, size ni * d
208
+ */
209
+ void reconstruct_n(idx_t i0, idx_t ni, float* recons) const override;
210
+
211
+ /** Similar to search, but also reconstructs the stored vectors (or an
212
+ * approximation in the case of lossy coding) for the search results.
213
+ *
214
+ * Overrides default implementation to avoid having to maintain direct_map
215
+ * and instead fetch the code offsets through the `store_pairs` flag in
216
+ * search_preassigned().
217
+ *
218
+ * @param recons reconstructed vectors size (n, k, d)
219
+ */
220
+ void search_and_reconstruct (idx_t n, const float *x, idx_t k,
221
+ float *distances, idx_t *labels,
222
+ float *recons) const override;
223
+
224
+ /** Reconstruct a vector given the location in terms of (inv list index +
225
+ * inv list offset) instead of the id.
226
+ *
227
+ * Useful for reconstructing when the direct_map is not maintained and
228
+ * the inv list offset is computed by search_preassigned() with
229
+ * `store_pairs` set.
230
+ */
231
+ virtual void reconstruct_from_offset (int64_t list_no, int64_t offset,
232
+ float* recons) const;
233
+
234
+
235
+ /// Dataset manipulation functions
236
+
237
+ size_t remove_ids(const IDSelector& sel) override;
238
+
239
+ /** check that the two indexes are compatible (ie, they are
240
+ * trained in the same way and have the same
241
+ * parameters). Otherwise throw. */
242
+ void check_compatible_for_merge (const IndexIVF &other) const;
243
+
244
+ /** moves the entries from another dataset to self. On output,
245
+ * other is empty. add_id is added to all moved ids (for
246
+ * sequential ids, this would be this->ntotal */
247
+ virtual void merge_from (IndexIVF &other, idx_t add_id);
248
+
249
+ /** copy a subset of the entries index to the other index
250
+ *
251
+ * if subset_type == 0: copies ids in [a1, a2)
252
+ * if subset_type == 1: copies ids if id % a1 == a2
253
+ * if subset_type == 2: copies inverted lists such that a1
254
+ * elements are left before and a2 elements are after
255
+ */
256
+ virtual void copy_subset_to (IndexIVF & other, int subset_type,
257
+ idx_t a1, idx_t a2) const;
258
+
259
+ ~IndexIVF() override;
260
+
261
+ size_t get_list_size (size_t list_no) const
262
+ { return invlists->list_size(list_no); }
263
+
264
+ /** intialize a direct map
265
+ *
266
+ * @param new_maintain_direct_map if true, create a direct map,
267
+ * else clear it
268
+ */
269
+ void make_direct_map (bool new_maintain_direct_map=true);
270
+
271
+ /// replace the inverted lists, old one is deallocated if own_invlists
272
+ void replace_invlists (InvertedLists *il, bool own=false);
273
+
274
+ /* The standalone codec interface (except sa_decode that is specific) */
275
+ size_t sa_code_size () const override;
276
+
277
+ void sa_encode (idx_t n, const float *x,
278
+ uint8_t *bytes) const override;
279
+
280
+ IndexIVF ();
281
+ };
282
+
283
+ struct RangeQueryResult;
284
+
285
+ /** Object that handles a query. The inverted lists to scan are
286
+ * provided externally. The object has a lot of state, but
287
+ * distance_to_code and scan_codes can be called in multiple
288
+ * threads */
289
+ struct InvertedListScanner {
290
+
291
+ using idx_t = Index::idx_t;
292
+
293
+ /// from now on we handle this query.
294
+ virtual void set_query (const float *query_vector) = 0;
295
+
296
+ /// following codes come from this inverted list
297
+ virtual void set_list (idx_t list_no, float coarse_dis) = 0;
298
+
299
+ /// compute a single query-to-code distance
300
+ virtual float distance_to_code (const uint8_t *code) const = 0;
301
+
302
+ /** scan a set of codes, compute distances to current query and
303
+ * update heap of results if necessary.
304
+ *
305
+ * @param n number of codes to scan
306
+ * @param codes codes to scan (n * code_size)
307
+ * @param ids corresponding ids (ignored if store_pairs)
308
+ * @param distances heap distances (size k)
309
+ * @param labels heap labels (size k)
310
+ * @param k heap size
311
+ * @return number of heap updates performed
312
+ */
313
+ virtual size_t scan_codes (size_t n,
314
+ const uint8_t *codes,
315
+ const idx_t *ids,
316
+ float *distances, idx_t *labels,
317
+ size_t k) const = 0;
318
+
319
+ /** scan a set of codes, compute distances to current query and
320
+ * update results if distances are below radius
321
+ *
322
+ * (default implementation fails) */
323
+ virtual void scan_codes_range (size_t n,
324
+ const uint8_t *codes,
325
+ const idx_t *ids,
326
+ float radius,
327
+ RangeQueryResult &result) const;
328
+
329
+ virtual ~InvertedListScanner () {}
330
+
331
+ };
332
+
333
+
334
+ struct IndexIVFStats {
335
+ size_t nq; // nb of queries run
336
+ size_t nlist; // nb of inverted lists scanned
337
+ size_t ndis; // nb of distancs computed
338
+ size_t nheap_updates; // nb of times the heap was updated
339
+ double quantization_time; // time spent quantizing vectors (in ms)
340
+ double search_time; // time spent searching lists (in ms)
341
+
342
+ IndexIVFStats () {reset (); }
343
+ void reset ();
344
+ };
345
+
346
+ // global var that collects them all
347
+ extern IndexIVFStats indexIVF_stats;
348
+
349
+
350
+ } // namespace faiss
351
+
352
+
353
+ #endif
@@ -0,0 +1,502 @@
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/IndexIVFFlat.h>
11
+
12
+ #include <cstdio>
13
+
14
+ #include <faiss/IndexFlat.h>
15
+
16
+ #include <faiss/utils/distances.h>
17
+ #include <faiss/utils/utils.h>
18
+ #include <faiss/impl/FaissAssert.h>
19
+ #include <faiss/impl/AuxIndexStructures.h>
20
+
21
+
22
+ namespace faiss {
23
+
24
+
25
+ /*****************************************
26
+ * IndexIVFFlat implementation
27
+ ******************************************/
28
+
29
+ IndexIVFFlat::IndexIVFFlat (Index * quantizer,
30
+ size_t d, size_t nlist, MetricType metric):
31
+ IndexIVF (quantizer, d, nlist, sizeof(float) * d, metric)
32
+ {
33
+ code_size = sizeof(float) * d;
34
+ }
35
+
36
+
37
+ void IndexIVFFlat::add_with_ids (idx_t n, const float * x, const idx_t *xids)
38
+ {
39
+ add_core (n, x, xids, nullptr);
40
+ }
41
+
42
+ void IndexIVFFlat::add_core (idx_t n, const float * x, const int64_t *xids,
43
+ const int64_t *precomputed_idx)
44
+
45
+ {
46
+ FAISS_THROW_IF_NOT (is_trained);
47
+ assert (invlists);
48
+ FAISS_THROW_IF_NOT_MSG (!(maintain_direct_map && xids),
49
+ "cannot have direct map and add with ids");
50
+ const int64_t * idx;
51
+ ScopeDeleter<int64_t> del;
52
+
53
+ if (precomputed_idx) {
54
+ idx = precomputed_idx;
55
+ } else {
56
+ int64_t * idx0 = new int64_t [n];
57
+ del.set (idx0);
58
+ quantizer->assign (n, x, idx0);
59
+ idx = idx0;
60
+ }
61
+ int64_t n_add = 0;
62
+ for (size_t i = 0; i < n; i++) {
63
+ int64_t id = xids ? xids[i] : ntotal + i;
64
+ int64_t list_no = idx [i];
65
+
66
+ if (list_no < 0)
67
+ continue;
68
+ const float *xi = x + i * d;
69
+ size_t offset = invlists->add_entry (
70
+ list_no, id, (const uint8_t*) xi);
71
+
72
+ if (maintain_direct_map)
73
+ direct_map.push_back (list_no << 32 | offset);
74
+ n_add++;
75
+ }
76
+ if (verbose) {
77
+ printf("IndexIVFFlat::add_core: added %ld / %ld vectors\n",
78
+ n_add, n);
79
+ }
80
+ ntotal += n;
81
+ }
82
+
83
+ void IndexIVFFlat::encode_vectors(idx_t n, const float* x,
84
+ const idx_t * list_nos,
85
+ uint8_t * codes,
86
+ bool include_listnos) const
87
+ {
88
+ if (!include_listnos) {
89
+ memcpy (codes, x, code_size * n);
90
+ } else {
91
+ size_t coarse_size = coarse_code_size ();
92
+ for (size_t i = 0; i < n; i++) {
93
+ int64_t list_no = list_nos [i];
94
+ uint8_t *code = codes + i * (code_size + coarse_size);
95
+ const float *xi = x + i * d;
96
+ if (list_no >= 0) {
97
+ encode_listno (list_no, code);
98
+ memcpy (code + coarse_size, xi, code_size);
99
+ } else {
100
+ memset (code, 0, code_size + coarse_size);
101
+ }
102
+
103
+ }
104
+ }
105
+ }
106
+
107
+ void IndexIVFFlat::sa_decode (idx_t n, const uint8_t *bytes,
108
+ float *x) const
109
+ {
110
+ size_t coarse_size = coarse_code_size ();
111
+ for (size_t i = 0; i < n; i++) {
112
+ const uint8_t *code = bytes + i * (code_size + coarse_size);
113
+ float *xi = x + i * d;
114
+ memcpy (xi, code + coarse_size, code_size);
115
+ }
116
+ }
117
+
118
+
119
+ namespace {
120
+
121
+
122
+ template<MetricType metric, class C>
123
+ struct IVFFlatScanner: InvertedListScanner {
124
+ size_t d;
125
+ bool store_pairs;
126
+
127
+ IVFFlatScanner(size_t d, bool store_pairs):
128
+ d(d), store_pairs(store_pairs) {}
129
+
130
+ const float *xi;
131
+ void set_query (const float *query) override {
132
+ this->xi = query;
133
+ }
134
+
135
+ idx_t list_no;
136
+ void set_list (idx_t list_no, float /* coarse_dis */) override {
137
+ this->list_no = list_no;
138
+ }
139
+
140
+ float distance_to_code (const uint8_t *code) const override {
141
+ const float *yj = (float*)code;
142
+ float dis = metric == METRIC_INNER_PRODUCT ?
143
+ fvec_inner_product (xi, yj, d) : fvec_L2sqr (xi, yj, d);
144
+ return dis;
145
+ }
146
+
147
+ size_t scan_codes (size_t list_size,
148
+ const uint8_t *codes,
149
+ const idx_t *ids,
150
+ float *simi, idx_t *idxi,
151
+ size_t k) const override
152
+ {
153
+ const float *list_vecs = (const float*)codes;
154
+ size_t nup = 0;
155
+ for (size_t j = 0; j < list_size; j++) {
156
+ const float * yj = list_vecs + d * j;
157
+ float dis = metric == METRIC_INNER_PRODUCT ?
158
+ fvec_inner_product (xi, yj, d) : fvec_L2sqr (xi, yj, d);
159
+ if (C::cmp (simi[0], dis)) {
160
+ heap_pop<C> (k, simi, idxi);
161
+ int64_t id = store_pairs ? (list_no << 32 | j) : ids[j];
162
+ heap_push<C> (k, simi, idxi, dis, id);
163
+ nup++;
164
+ }
165
+ }
166
+ return nup;
167
+ }
168
+
169
+ void scan_codes_range (size_t list_size,
170
+ const uint8_t *codes,
171
+ const idx_t *ids,
172
+ float radius,
173
+ RangeQueryResult & res) const override
174
+ {
175
+ const float *list_vecs = (const float*)codes;
176
+ for (size_t j = 0; j < list_size; j++) {
177
+ const float * yj = list_vecs + d * j;
178
+ float dis = metric == METRIC_INNER_PRODUCT ?
179
+ fvec_inner_product (xi, yj, d) : fvec_L2sqr (xi, yj, d);
180
+ if (C::cmp (radius, dis)) {
181
+ int64_t id = store_pairs ? (list_no << 32 | j) : ids[j];
182
+ res.add (dis, id);
183
+ }
184
+ }
185
+ }
186
+
187
+
188
+ };
189
+
190
+
191
+ } // anonymous namespace
192
+
193
+
194
+
195
+ InvertedListScanner* IndexIVFFlat::get_InvertedListScanner
196
+ (bool store_pairs) const
197
+ {
198
+ if (metric_type == METRIC_INNER_PRODUCT) {
199
+ return new IVFFlatScanner<
200
+ METRIC_INNER_PRODUCT, CMin<float, int64_t> > (d, store_pairs);
201
+ } else if (metric_type == METRIC_L2) {
202
+ return new IVFFlatScanner<
203
+ METRIC_L2, CMax<float, int64_t> >(d, store_pairs);
204
+ } else {
205
+ FAISS_THROW_MSG("metric type not supported");
206
+ }
207
+ return nullptr;
208
+ }
209
+
210
+
211
+
212
+ void IndexIVFFlat::update_vectors (int n, idx_t *new_ids, const float *x)
213
+ {
214
+
215
+ FAISS_THROW_IF_NOT (maintain_direct_map);
216
+ FAISS_THROW_IF_NOT (is_trained);
217
+ std::vector<idx_t> assign (n);
218
+ quantizer->assign (n, x, assign.data());
219
+
220
+ for (size_t i = 0; i < n; i++) {
221
+ idx_t id = new_ids[i];
222
+ FAISS_THROW_IF_NOT_MSG (0 <= id && id < ntotal,
223
+ "id to update out of range");
224
+ { // remove old one
225
+ int64_t dm = direct_map[id];
226
+ int64_t ofs = dm & 0xffffffff;
227
+ int64_t il = dm >> 32;
228
+ size_t l = invlists->list_size (il);
229
+ if (ofs != l - 1) { // move l - 1 to ofs
230
+ int64_t id2 = invlists->get_single_id (il, l - 1);
231
+ direct_map[id2] = (il << 32) | ofs;
232
+ invlists->update_entry (il, ofs, id2,
233
+ invlists->get_single_code (il, l - 1));
234
+ }
235
+ invlists->resize (il, l - 1);
236
+ }
237
+ { // insert new one
238
+ int64_t il = assign[i];
239
+ size_t l = invlists->list_size (il);
240
+ int64_t dm = (il << 32) | l;
241
+ direct_map[id] = dm;
242
+ invlists->add_entry (il, id, (const uint8_t*)(x + i * d));
243
+ }
244
+ }
245
+
246
+ }
247
+
248
+ void IndexIVFFlat::reconstruct_from_offset (int64_t list_no, int64_t offset,
249
+ float* recons) const
250
+ {
251
+ memcpy (recons, invlists->get_single_code (list_no, offset), code_size);
252
+ }
253
+
254
+ /*****************************************
255
+ * IndexIVFFlatDedup implementation
256
+ ******************************************/
257
+
258
+ IndexIVFFlatDedup::IndexIVFFlatDedup (
259
+ Index * quantizer, size_t d, size_t nlist_,
260
+ MetricType metric_type):
261
+ IndexIVFFlat (quantizer, d, nlist_, metric_type)
262
+ {}
263
+
264
+
265
+ void IndexIVFFlatDedup::train(idx_t n, const float* x)
266
+ {
267
+ std::unordered_map<uint64_t, idx_t> map;
268
+ float * x2 = new float [n * d];
269
+ ScopeDeleter<float> del (x2);
270
+
271
+ int64_t n2 = 0;
272
+ for (int64_t i = 0; i < n; i++) {
273
+ uint64_t hash = hash_bytes((uint8_t *)(x + i * d), code_size);
274
+ if (map.count(hash) &&
275
+ !memcmp (x2 + map[hash] * d, x + i * d, code_size)) {
276
+ // is duplicate, skip
277
+ } else {
278
+ map [hash] = n2;
279
+ memcpy (x2 + n2 * d, x + i * d, code_size);
280
+ n2 ++;
281
+ }
282
+ }
283
+ if (verbose) {
284
+ printf ("IndexIVFFlatDedup::train: train on %ld points after dedup "
285
+ "(was %ld points)\n", n2, n);
286
+ }
287
+ IndexIVFFlat::train (n2, x2);
288
+ }
289
+
290
+
291
+
292
+ void IndexIVFFlatDedup::add_with_ids(
293
+ idx_t na, const float* x, const idx_t* xids)
294
+ {
295
+
296
+ FAISS_THROW_IF_NOT (is_trained);
297
+ assert (invlists);
298
+ FAISS_THROW_IF_NOT_MSG (
299
+ !maintain_direct_map,
300
+ "IVFFlatDedup not implemented with direct_map");
301
+ int64_t * idx = new int64_t [na];
302
+ ScopeDeleter<int64_t> del (idx);
303
+ quantizer->assign (na, x, idx);
304
+
305
+ int64_t n_add = 0, n_dup = 0;
306
+ // TODO make a omp loop with this
307
+ for (size_t i = 0; i < na; i++) {
308
+ idx_t id = xids ? xids[i] : ntotal + i;
309
+ int64_t list_no = idx [i];
310
+
311
+ if (list_no < 0) {
312
+ continue;
313
+ }
314
+ const float *xi = x + i * d;
315
+
316
+ // search if there is already an entry with that id
317
+ InvertedLists::ScopedCodes codes (invlists, list_no);
318
+
319
+ int64_t n = invlists->list_size (list_no);
320
+ int64_t offset = -1;
321
+ for (int64_t o = 0; o < n; o++) {
322
+ if (!memcmp (codes.get() + o * code_size,
323
+ xi, code_size)) {
324
+ offset = o;
325
+ break;
326
+ }
327
+ }
328
+
329
+ if (offset == -1) { // not found
330
+ invlists->add_entry (list_no, id, (const uint8_t*) xi);
331
+ } else {
332
+ // mark equivalence
333
+ idx_t id2 = invlists->get_single_id (list_no, offset);
334
+ std::pair<idx_t, idx_t> pair (id2, id);
335
+ instances.insert (pair);
336
+ n_dup ++;
337
+ }
338
+ n_add++;
339
+ }
340
+ if (verbose) {
341
+ printf("IndexIVFFlat::add_with_ids: added %ld / %ld vectors"
342
+ " (out of which %ld are duplicates)\n",
343
+ n_add, na, n_dup);
344
+ }
345
+ ntotal += n_add;
346
+ }
347
+
348
+ void IndexIVFFlatDedup::search_preassigned (
349
+ idx_t n, const float *x, idx_t k,
350
+ const idx_t *assign,
351
+ const float *centroid_dis,
352
+ float *distances, idx_t *labels,
353
+ bool store_pairs,
354
+ const IVFSearchParameters *params) const
355
+ {
356
+ FAISS_THROW_IF_NOT_MSG (
357
+ !store_pairs, "store_pairs not supported in IVFDedup");
358
+
359
+ IndexIVFFlat::search_preassigned (n, x, k, assign, centroid_dis,
360
+ distances, labels, false,
361
+ params);
362
+
363
+ std::vector <idx_t> labels2 (k);
364
+ std::vector <float> dis2 (k);
365
+
366
+ for (int64_t i = 0; i < n; i++) {
367
+ idx_t *labels1 = labels + i * k;
368
+ float *dis1 = distances + i * k;
369
+ int64_t j = 0;
370
+ for (; j < k; j++) {
371
+ if (instances.find (labels1[j]) != instances.end ()) {
372
+ // a duplicate: special handling
373
+ break;
374
+ }
375
+ }
376
+ if (j < k) {
377
+ // there are duplicates, special handling
378
+ int64_t j0 = j;
379
+ int64_t rp = j;
380
+ while (j < k) {
381
+ auto range = instances.equal_range (labels1[rp]);
382
+ float dis = dis1[rp];
383
+ labels2[j] = labels1[rp];
384
+ dis2[j] = dis;
385
+ j ++;
386
+ for (auto it = range.first; j < k && it != range.second; ++it) {
387
+ labels2[j] = it->second;
388
+ dis2[j] = dis;
389
+ j++;
390
+ }
391
+ rp++;
392
+ }
393
+ memcpy (labels1 + j0, labels2.data() + j0,
394
+ sizeof(labels1[0]) * (k - j0));
395
+ memcpy (dis1 + j0, dis2.data() + j0,
396
+ sizeof(dis2[0]) * (k - j0));
397
+ }
398
+ }
399
+
400
+ }
401
+
402
+
403
+ size_t IndexIVFFlatDedup::remove_ids(const IDSelector& sel)
404
+ {
405
+ std::unordered_map<idx_t, idx_t> replace;
406
+ std::vector<std::pair<idx_t, idx_t> > toadd;
407
+ for (auto it = instances.begin(); it != instances.end(); ) {
408
+ if (sel.is_member(it->first)) {
409
+ // then we erase this entry
410
+ if (!sel.is_member(it->second)) {
411
+ // if the second is not erased
412
+ if (replace.count(it->first) == 0) {
413
+ replace[it->first] = it->second;
414
+ } else { // remember we should add an element
415
+ std::pair<idx_t, idx_t> new_entry (
416
+ replace[it->first], it->second);
417
+ toadd.push_back(new_entry);
418
+ }
419
+ }
420
+ it = instances.erase(it);
421
+ } else {
422
+ if (sel.is_member(it->second)) {
423
+ it = instances.erase(it);
424
+ } else {
425
+ ++it;
426
+ }
427
+ }
428
+ }
429
+
430
+ instances.insert (toadd.begin(), toadd.end());
431
+
432
+ // mostly copied from IndexIVF.cpp
433
+
434
+ FAISS_THROW_IF_NOT_MSG (!maintain_direct_map,
435
+ "direct map remove not implemented");
436
+
437
+ std::vector<int64_t> toremove(nlist);
438
+
439
+ #pragma omp parallel for
440
+ for (int64_t i = 0; i < nlist; i++) {
441
+ int64_t l0 = invlists->list_size (i), l = l0, j = 0;
442
+ InvertedLists::ScopedIds idsi (invlists, i);
443
+ while (j < l) {
444
+ if (sel.is_member (idsi[j])) {
445
+ if (replace.count(idsi[j]) == 0) {
446
+ l--;
447
+ invlists->update_entry (
448
+ i, j,
449
+ invlists->get_single_id (i, l),
450
+ InvertedLists::ScopedCodes (invlists, i, l).get());
451
+ } else {
452
+ invlists->update_entry (
453
+ i, j,
454
+ replace[idsi[j]],
455
+ InvertedLists::ScopedCodes (invlists, i, j).get());
456
+ j++;
457
+ }
458
+ } else {
459
+ j++;
460
+ }
461
+ }
462
+ toremove[i] = l0 - l;
463
+ }
464
+ // this will not run well in parallel on ondisk because of possible shrinks
465
+ int64_t nremove = 0;
466
+ for (int64_t i = 0; i < nlist; i++) {
467
+ if (toremove[i] > 0) {
468
+ nremove += toremove[i];
469
+ invlists->resize(
470
+ i, invlists->list_size(i) - toremove[i]);
471
+ }
472
+ }
473
+ ntotal -= nremove;
474
+ return nremove;
475
+ }
476
+
477
+
478
+ void IndexIVFFlatDedup::range_search(
479
+ idx_t ,
480
+ const float* ,
481
+ float ,
482
+ RangeSearchResult* ) const
483
+ {
484
+ FAISS_THROW_MSG ("not implemented");
485
+ }
486
+
487
+ void IndexIVFFlatDedup::update_vectors (int , idx_t *, const float *)
488
+ {
489
+ FAISS_THROW_MSG ("not implemented");
490
+ }
491
+
492
+
493
+ void IndexIVFFlatDedup::reconstruct_from_offset (
494
+ int64_t , int64_t , float* ) const
495
+ {
496
+ FAISS_THROW_MSG ("not implemented");
497
+ }
498
+
499
+
500
+
501
+
502
+ } // namespace faiss