faiss 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
data/lib/faiss/kmeans.rb CHANGED
@@ -5,7 +5,7 @@ module Faiss
5
5
  def train(objects)
6
6
  objects = Numo::SFloat.cast(objects) unless objects.is_a?(Numo::SFloat)
7
7
  @index = IndexFlatL2.new(d)
8
- _train(objects.shape[0], objects.to_binary, @index)
8
+ _train(objects.shape[0], objects, @index)
9
9
  end
10
10
 
11
11
  def centroids
@@ -2,13 +2,13 @@ module Faiss
2
2
  class PCAMatrix
3
3
  def train(objects)
4
4
  objects = Numo::SFloat.cast(objects) unless objects.is_a?(Numo::SFloat)
5
- _train(objects.shape[0], objects.to_binary)
5
+ _train(objects.shape[0], objects)
6
6
  end
7
7
 
8
8
  def apply(objects)
9
9
  objects = Numo::SFloat.cast(objects) unless objects.is_a?(Numo::SFloat)
10
10
  n = objects.shape[0]
11
- res = _apply(n, objects.to_binary)
11
+ res = _apply(n, objects)
12
12
  Numo::SFloat.from_binary(res).reshape(n, d_out)
13
13
  end
14
14
  end
@@ -2,20 +2,20 @@ module Faiss
2
2
  class ProductQuantizer
3
3
  def train(objects)
4
4
  objects = Numo::SFloat.cast(objects) unless objects.is_a?(Numo::SFloat)
5
- _train(objects.shape[0], objects.to_binary)
5
+ _train(objects.shape[0], objects)
6
6
  end
7
7
 
8
8
  def compute_codes(objects)
9
9
  objects = Numo::SFloat.cast(objects) unless objects.is_a?(Numo::SFloat)
10
10
  n = objects.shape[0]
11
- res = _compute_codes(n, objects.to_binary)
11
+ res = _compute_codes(n, objects)
12
12
  Numo::UInt8.from_binary(res).reshape(n, m)
13
13
  end
14
14
 
15
15
  def decode(objects)
16
16
  objects = Numo::UInt8.cast(objects) unless objects.is_a?(Numo::UInt8)
17
17
  n = objects.shape[0]
18
- res = _decode(n, objects.to_binary)
18
+ res = _decode(n, objects)
19
19
  Numo::SFloat.from_binary(res).reshape(n, d)
20
20
  end
21
21
  end
data/lib/faiss/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Faiss
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,719 @@
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
+ * implementation of Hyper-parameter auto-tuning
12
+ */
13
+
14
+ #include <faiss/AutoTune.h>
15
+
16
+ #include <cmath>
17
+
18
+ #include <faiss/impl/FaissAssert.h>
19
+ #include <faiss/utils/utils.h>
20
+ #include <faiss/utils/random.h>
21
+
22
+ #include <faiss/IndexFlat.h>
23
+ #include <faiss/VectorTransform.h>
24
+ #include <faiss/IndexPreTransform.h>
25
+ #include <faiss/IndexLSH.h>
26
+ #include <faiss/IndexPQ.h>
27
+ #include <faiss/IndexIVF.h>
28
+ #include <faiss/IndexIVFPQ.h>
29
+ #include <faiss/IndexIVFPQR.h>
30
+ #include <faiss/IndexIVFFlat.h>
31
+ #include <faiss/MetaIndexes.h>
32
+ #include <faiss/IndexScalarQuantizer.h>
33
+ #include <faiss/IndexHNSW.h>
34
+
35
+ #include <faiss/IndexBinaryFlat.h>
36
+ #include <faiss/IndexBinaryHNSW.h>
37
+ #include <faiss/IndexBinaryIVF.h>
38
+
39
+ namespace faiss {
40
+
41
+
42
+ AutoTuneCriterion::AutoTuneCriterion (idx_t nq, idx_t nnn):
43
+ nq (nq), nnn (nnn), gt_nnn (0)
44
+ {}
45
+
46
+
47
+ void AutoTuneCriterion::set_groundtruth (
48
+ int gt_nnn, const float *gt_D_in, const idx_t *gt_I_in)
49
+ {
50
+ this->gt_nnn = gt_nnn;
51
+ if (gt_D_in) { // allow null for this, as it is often not used
52
+ gt_D.resize (nq * gt_nnn);
53
+ memcpy (gt_D.data(), gt_D_in, sizeof (gt_D[0]) * nq * gt_nnn);
54
+ }
55
+ gt_I.resize (nq * gt_nnn);
56
+ memcpy (gt_I.data(), gt_I_in, sizeof (gt_I[0]) * nq * gt_nnn);
57
+ }
58
+
59
+
60
+
61
+ OneRecallAtRCriterion::OneRecallAtRCriterion (idx_t nq, idx_t R):
62
+ AutoTuneCriterion(nq, R), R(R)
63
+ {}
64
+
65
+ double OneRecallAtRCriterion::evaluate(const float* /*D*/, const idx_t* I)
66
+ const {
67
+ FAISS_THROW_IF_NOT_MSG(
68
+ (gt_I.size() == gt_nnn * nq && gt_nnn >= 1 && nnn >= R),
69
+ "ground truth not initialized");
70
+ idx_t n_ok = 0;
71
+ for (idx_t q = 0; q < nq; q++) {
72
+ idx_t gt_nn = gt_I[q * gt_nnn];
73
+ const idx_t* I_line = I + q * nnn;
74
+ for (int i = 0; i < R; i++) {
75
+ if (I_line[i] == gt_nn) {
76
+ n_ok++;
77
+ break;
78
+ }
79
+ }
80
+ }
81
+ return n_ok / double(nq);
82
+ }
83
+
84
+
85
+ IntersectionCriterion::IntersectionCriterion (idx_t nq, idx_t R):
86
+ AutoTuneCriterion(nq, R), R(R)
87
+ {}
88
+
89
+ double IntersectionCriterion::evaluate(const float* /*D*/, const idx_t* I)
90
+ const {
91
+ FAISS_THROW_IF_NOT_MSG(
92
+ (gt_I.size() == gt_nnn * nq && gt_nnn >= R && nnn >= R),
93
+ "ground truth not initialized");
94
+ int64_t n_ok = 0;
95
+ #pragma omp parallel for reduction(+: n_ok)
96
+ for (idx_t q = 0; q < nq; q++) {
97
+ n_ok += ranklist_intersection_size (
98
+ R, &gt_I [q * gt_nnn],
99
+ R, I + q * nnn);
100
+ }
101
+ return n_ok / double (nq * R);
102
+ }
103
+
104
+ /***************************************************************
105
+ * OperatingPoints
106
+ ***************************************************************/
107
+
108
+ OperatingPoints::OperatingPoints ()
109
+ {
110
+ clear();
111
+ }
112
+
113
+ void OperatingPoints::clear ()
114
+ {
115
+ all_pts.clear();
116
+ optimal_pts.clear();
117
+ /// default point: doing nothing gives 0 performance and takes 0 time
118
+ OperatingPoint op = {0, 0, "", -1};
119
+ optimal_pts.push_back(op);
120
+ }
121
+
122
+ /// add a performance measure
123
+ bool OperatingPoints::add (double perf, double t, const std::string & key,
124
+ size_t cno)
125
+ {
126
+ OperatingPoint op = {perf, t, key, int64_t(cno)};
127
+ all_pts.push_back (op);
128
+ if (perf == 0) {
129
+ return false; // no method for 0 accuracy is faster than doing nothing
130
+ }
131
+ std::vector<OperatingPoint> & a = optimal_pts;
132
+ if (perf > a.back().perf) {
133
+ // keep unconditionally
134
+ a.push_back (op);
135
+ } else if (perf == a.back().perf) {
136
+ if (t < a.back ().t) {
137
+ a.back() = op;
138
+ } else {
139
+ return false;
140
+ }
141
+ } else {
142
+ int i;
143
+ // stricto sensu this should be a bissection
144
+ for (i = 0; i < a.size(); i++) {
145
+ if (a[i].perf >= perf) break;
146
+ }
147
+ assert (i < a.size());
148
+ if (t < a[i].t) {
149
+ if (a[i].perf == perf) {
150
+ a[i] = op;
151
+ } else {
152
+ a.insert (a.begin() + i, op);
153
+ }
154
+ } else {
155
+ return false;
156
+ }
157
+ }
158
+ { // remove non-optimal points from array
159
+ int i = a.size() - 1;
160
+ while (i > 0) {
161
+ if (a[i].t < a[i - 1].t)
162
+ a.erase (a.begin() + (i - 1));
163
+ i--;
164
+ }
165
+ }
166
+ return true;
167
+ }
168
+
169
+
170
+ int OperatingPoints::merge_with (const OperatingPoints &other,
171
+ const std::string & prefix)
172
+ {
173
+ int n_add = 0;
174
+ for (int i = 0; i < other.all_pts.size(); i++) {
175
+ const OperatingPoint & op = other.all_pts[i];
176
+ if (add (op.perf, op.t, prefix + op.key, op.cno))
177
+ n_add++;
178
+ }
179
+ return n_add;
180
+ }
181
+
182
+
183
+
184
+ /// get time required to obtain a given performance measure
185
+ double OperatingPoints::t_for_perf (double perf) const
186
+ {
187
+ const std::vector<OperatingPoint> & a = optimal_pts;
188
+ if (perf > a.back().perf) return 1e50;
189
+ int i0 = -1, i1 = a.size() - 1;
190
+ while (i0 + 1 < i1) {
191
+ int imed = (i0 + i1 + 1) / 2;
192
+ if (a[imed].perf < perf) i0 = imed;
193
+ else i1 = imed;
194
+ }
195
+ return a[i1].t;
196
+ }
197
+
198
+
199
+ void OperatingPoints::all_to_gnuplot (const char *fname) const
200
+ {
201
+ FILE *f = fopen(fname, "w");
202
+ if (!f) {
203
+ fprintf (stderr, "cannot open %s", fname);
204
+ perror("");
205
+ abort();
206
+ }
207
+ for (int i = 0; i < all_pts.size(); i++) {
208
+ const OperatingPoint & op = all_pts[i];
209
+ fprintf (f, "%g %g %s\n", op.perf, op.t, op.key.c_str());
210
+ }
211
+ fclose(f);
212
+ }
213
+
214
+ void OperatingPoints::optimal_to_gnuplot (const char *fname) const
215
+ {
216
+ FILE *f = fopen(fname, "w");
217
+ if (!f) {
218
+ fprintf (stderr, "cannot open %s", fname);
219
+ perror("");
220
+ abort();
221
+ }
222
+ double prev_perf = 0.0;
223
+ for (int i = 0; i < optimal_pts.size(); i++) {
224
+ const OperatingPoint & op = optimal_pts[i];
225
+ fprintf (f, "%g %g\n", prev_perf, op.t);
226
+ fprintf (f, "%g %g %s\n", op.perf, op.t, op.key.c_str());
227
+ prev_perf = op.perf;
228
+ }
229
+ fclose(f);
230
+ }
231
+
232
+ void OperatingPoints::display (bool only_optimal) const
233
+ {
234
+ const std::vector<OperatingPoint> &pts =
235
+ only_optimal ? optimal_pts : all_pts;
236
+ printf("Tested %ld operating points, %ld ones are optimal:\n",
237
+ all_pts.size(), optimal_pts.size());
238
+
239
+ for (int i = 0; i < pts.size(); i++) {
240
+ const OperatingPoint & op = pts[i];
241
+ const char *star = "";
242
+ if (!only_optimal) {
243
+ for (int j = 0; j < optimal_pts.size(); j++) {
244
+ if (op.cno == optimal_pts[j].cno) {
245
+ star = "*";
246
+ break;
247
+ }
248
+ }
249
+ }
250
+ printf ("cno=%ld key=%s perf=%.4f t=%.3f %s\n",
251
+ op.cno, op.key.c_str(), op.perf, op.t, star);
252
+ }
253
+
254
+ }
255
+
256
+ /***************************************************************
257
+ * ParameterSpace
258
+ ***************************************************************/
259
+
260
+ ParameterSpace::ParameterSpace ():
261
+ verbose (1), n_experiments (500),
262
+ batchsize (1<<30), thread_over_batches (false),
263
+ min_test_duration (0)
264
+ {
265
+ }
266
+
267
+ /* not keeping this constructor as inheritors will call the parent
268
+ initialize()
269
+ */
270
+
271
+ #if 0
272
+ ParameterSpace::ParameterSpace (Index *index):
273
+ verbose (1), n_experiments (500),
274
+ batchsize (1<<30), thread_over_batches (false)
275
+
276
+ {
277
+ initialize(index);
278
+ }
279
+ #endif
280
+
281
+ size_t ParameterSpace::n_combinations () const
282
+ {
283
+ size_t n = 1;
284
+ for (int i = 0; i < parameter_ranges.size(); i++)
285
+ n *= parameter_ranges[i].values.size();
286
+ return n;
287
+ }
288
+
289
+ /// get string representation of the combination
290
+ std::string ParameterSpace::combination_name (size_t cno) const {
291
+ char buf[1000], *wp = buf;
292
+ *wp = 0;
293
+ for (int i = 0; i < parameter_ranges.size(); i++) {
294
+ const ParameterRange & pr = parameter_ranges[i];
295
+ size_t j = cno % pr.values.size();
296
+ cno /= pr.values.size();
297
+ wp += snprintf (
298
+ wp, buf + 1000 - wp, "%s%s=%g", i == 0 ? "" : ",",
299
+ pr.name.c_str(), pr.values[j]);
300
+ }
301
+ return std::string (buf);
302
+ }
303
+
304
+
305
+ bool ParameterSpace::combination_ge (size_t c1, size_t c2) const
306
+ {
307
+ for (int i = 0; i < parameter_ranges.size(); i++) {
308
+ int nval = parameter_ranges[i].values.size();
309
+ size_t j1 = c1 % nval;
310
+ size_t j2 = c2 % nval;
311
+ if (!(j1 >= j2)) return false;
312
+ c1 /= nval;
313
+ c2 /= nval;
314
+ }
315
+ return true;
316
+ }
317
+
318
+
319
+
320
+ #define DC(classname) \
321
+ const classname *ix = dynamic_cast<const classname *>(index)
322
+
323
+ static void init_pq_ParameterRange (const ProductQuantizer & pq,
324
+ ParameterRange & pr)
325
+ {
326
+ if (pq.code_size % 4 == 0) {
327
+ // Polysemous not supported for code sizes that are not a
328
+ // multiple of 4
329
+ for (int i = 2; i <= pq.code_size * 8 / 2; i+= 2)
330
+ pr.values.push_back(i);
331
+ }
332
+ pr.values.push_back (pq.code_size * 8);
333
+ }
334
+
335
+ ParameterRange &ParameterSpace::add_range(const char * name)
336
+ {
337
+ for (auto & pr : parameter_ranges) {
338
+ if (pr.name == name) {
339
+ return pr;
340
+ }
341
+ }
342
+ parameter_ranges.push_back (ParameterRange ());
343
+ parameter_ranges.back ().name = name;
344
+ return parameter_ranges.back ();
345
+ }
346
+
347
+
348
+ /// initialize with reasonable parameters for the index
349
+ void ParameterSpace::initialize (const Index * index)
350
+ {
351
+ if (DC (IndexPreTransform)) {
352
+ index = ix->index;
353
+ }
354
+ if (DC (IndexRefineFlat)) {
355
+ ParameterRange & pr = add_range("k_factor_rf");
356
+ for (int i = 0; i <= 6; i++) {
357
+ pr.values.push_back (1 << i);
358
+ }
359
+ index = ix->base_index;
360
+ }
361
+ if (DC (IndexPreTransform)) {
362
+ index = ix->index;
363
+ }
364
+
365
+ if (DC (IndexIVF)) {
366
+ {
367
+ ParameterRange & pr = add_range("nprobe");
368
+ for (int i = 0; i < 13; i++) {
369
+ size_t nprobe = 1 << i;
370
+ if (nprobe >= ix->nlist) break;
371
+ pr.values.push_back (nprobe);
372
+ }
373
+ }
374
+ if (dynamic_cast<const IndexHNSW*>(ix->quantizer)) {
375
+ ParameterRange & pr = add_range("efSearch");
376
+ for (int i = 2; i <= 9; i++) {
377
+ pr.values.push_back (1 << i);
378
+ }
379
+ }
380
+ }
381
+ if (DC (IndexPQ)) {
382
+ ParameterRange & pr = add_range("ht");
383
+ init_pq_ParameterRange (ix->pq, pr);
384
+ }
385
+ if (DC (IndexIVFPQ)) {
386
+ ParameterRange & pr = add_range("ht");
387
+ init_pq_ParameterRange (ix->pq, pr);
388
+ }
389
+
390
+ if (DC (IndexIVF)) {
391
+ const MultiIndexQuantizer *miq =
392
+ dynamic_cast<const MultiIndexQuantizer *> (ix->quantizer);
393
+ if (miq) {
394
+ ParameterRange & pr_max_codes = add_range("max_codes");
395
+ for (int i = 8; i < 20; i++) {
396
+ pr_max_codes.values.push_back (1 << i);
397
+ }
398
+ pr_max_codes.values.push_back (
399
+ std::numeric_limits<double>::infinity()
400
+ );
401
+ }
402
+ }
403
+ if (DC (IndexIVFPQR)) {
404
+ ParameterRange & pr = add_range("k_factor");
405
+ for (int i = 0; i <= 6; i++) {
406
+ pr.values.push_back (1 << i);
407
+ }
408
+ }
409
+ if (dynamic_cast<const IndexHNSW*>(index)) {
410
+ ParameterRange & pr = add_range("efSearch");
411
+ for (int i = 2; i <= 9; i++) {
412
+ pr.values.push_back (1 << i);
413
+ }
414
+ }
415
+ }
416
+
417
+ #undef DC
418
+
419
+ // non-const version
420
+ #define DC(classname) classname *ix = dynamic_cast<classname *>(index)
421
+
422
+
423
+ /// set a combination of parameters on an index
424
+ void ParameterSpace::set_index_parameters (Index *index, size_t cno) const
425
+ {
426
+
427
+ for (int i = 0; i < parameter_ranges.size(); i++) {
428
+ const ParameterRange & pr = parameter_ranges[i];
429
+ size_t j = cno % pr.values.size();
430
+ cno /= pr.values.size();
431
+ double val = pr.values [j];
432
+ set_index_parameter (index, pr.name, val);
433
+ }
434
+ }
435
+
436
+ /// set a combination of parameters on an index
437
+ void ParameterSpace::set_index_parameters (
438
+ Index *index, const char *description_in) const
439
+ {
440
+ char description[strlen(description_in) + 1];
441
+ char *ptr;
442
+ memcpy (description, description_in, strlen(description_in) + 1);
443
+
444
+ for (char *tok = strtok_r (description, " ,", &ptr);
445
+ tok;
446
+ tok = strtok_r (nullptr, " ,", &ptr)) {
447
+ char name[100];
448
+ double val;
449
+ int ret = sscanf (tok, "%100[^=]=%lf", name, &val);
450
+ FAISS_THROW_IF_NOT_FMT (
451
+ ret == 2, "could not interpret parameters %s", tok);
452
+ set_index_parameter (index, name, val);
453
+ }
454
+
455
+ }
456
+
457
+ void ParameterSpace::set_index_parameter (
458
+ Index * index, const std::string & name, double val) const
459
+ {
460
+ if (verbose > 1)
461
+ printf(" set %s=%g\n", name.c_str(), val);
462
+
463
+ if (name == "verbose") {
464
+ index->verbose = int(val);
465
+ // and fall through to also enable it on sub-indexes
466
+ }
467
+ if (DC (IndexPreTransform)) {
468
+ set_index_parameter (ix->index, name, val);
469
+ return;
470
+ }
471
+ if (DC (IndexShards)) {
472
+ // call on all sub-indexes
473
+ auto fn =
474
+ [this, name, val](int, Index* subIndex) {
475
+ set_index_parameter(subIndex, name, val);
476
+ };
477
+
478
+ ix->runOnIndex(fn);
479
+ return;
480
+ }
481
+ if (DC (IndexReplicas)) {
482
+ // call on all sub-indexes
483
+ auto fn =
484
+ [this, name, val](int, Index* subIndex) {
485
+ set_index_parameter(subIndex, name, val);
486
+ };
487
+
488
+ ix->runOnIndex(fn);
489
+ return;
490
+ }
491
+ if (DC (IndexRefineFlat)) {
492
+ if (name == "k_factor_rf") {
493
+ ix->k_factor = int(val);
494
+ return;
495
+ }
496
+ // otherwise it is for the sub-index
497
+ set_index_parameter (&ix->refine_index, name, val);
498
+ return;
499
+ }
500
+
501
+ if (name == "verbose") {
502
+ index->verbose = int(val);
503
+ return; // last verbose that we could find
504
+ }
505
+
506
+ if (name == "nprobe") {
507
+ if (DC (IndexIDMap)) {
508
+ set_index_parameter (ix->index, name, val);
509
+ return;
510
+ } else if (DC (IndexIVF)) {
511
+ ix->nprobe = int(val);
512
+ return;
513
+ }
514
+ }
515
+
516
+ if (name == "ht") {
517
+ if (DC (IndexPQ)) {
518
+ if (val >= ix->pq.code_size * 8) {
519
+ ix->search_type = IndexPQ::ST_PQ;
520
+ } else {
521
+ ix->search_type = IndexPQ::ST_polysemous;
522
+ ix->polysemous_ht = int(val);
523
+ }
524
+ return;
525
+ } else if (DC (IndexIVFPQ)) {
526
+ if (val >= ix->pq.code_size * 8) {
527
+ ix->polysemous_ht = 0;
528
+ } else {
529
+ ix->polysemous_ht = int(val);
530
+ }
531
+ return;
532
+ }
533
+ }
534
+
535
+ if (name == "k_factor") {
536
+ if (DC (IndexIVFPQR)) {
537
+ ix->k_factor = val;
538
+ return;
539
+ }
540
+ }
541
+ if (name == "max_codes") {
542
+ if (DC (IndexIVF)) {
543
+ ix->max_codes = std::isfinite(val) ? size_t(val) : 0;
544
+ return;
545
+ }
546
+ }
547
+
548
+ if (name == "efSearch") {
549
+ if (DC (IndexHNSW)) {
550
+ ix->hnsw.efSearch = int(val);
551
+ return;
552
+ }
553
+ if (DC (IndexIVF)) {
554
+ if (IndexHNSW *cq =
555
+ dynamic_cast<IndexHNSW *>(ix->quantizer)) {
556
+ cq->hnsw.efSearch = int(val);
557
+ return;
558
+ }
559
+ }
560
+ }
561
+
562
+ FAISS_THROW_FMT ("ParameterSpace::set_index_parameter:"
563
+ "could not set parameter %s",
564
+ name.c_str());
565
+ }
566
+
567
+ void ParameterSpace::display () const
568
+ {
569
+ printf ("ParameterSpace, %ld parameters, %ld combinations:\n",
570
+ parameter_ranges.size (), n_combinations ());
571
+ for (int i = 0; i < parameter_ranges.size(); i++) {
572
+ const ParameterRange & pr = parameter_ranges[i];
573
+ printf (" %s: ", pr.name.c_str ());
574
+ char sep = '[';
575
+ for (int j = 0; j < pr.values.size(); j++) {
576
+ printf ("%c %g", sep, pr.values [j]);
577
+ sep = ',';
578
+ }
579
+ printf ("]\n");
580
+ }
581
+ }
582
+
583
+
584
+
585
+ void ParameterSpace::update_bounds (size_t cno, const OperatingPoint & op,
586
+ double *upper_bound_perf,
587
+ double *lower_bound_t) const
588
+ {
589
+ if (combination_ge (cno, op.cno)) {
590
+ if (op.t > *lower_bound_t) *lower_bound_t = op.t;
591
+ }
592
+ if (combination_ge (op.cno, cno)) {
593
+ if (op.perf < *upper_bound_perf) *upper_bound_perf = op.perf;
594
+ }
595
+ }
596
+
597
+
598
+
599
+ void ParameterSpace::explore (Index *index,
600
+ size_t nq, const float *xq,
601
+ const AutoTuneCriterion & crit,
602
+ OperatingPoints * ops) const
603
+ {
604
+ FAISS_THROW_IF_NOT_MSG (nq == crit.nq,
605
+ "criterion does not have the same nb of queries");
606
+
607
+ size_t n_comb = n_combinations ();
608
+
609
+ if (n_experiments == 0) {
610
+
611
+ for (size_t cno = 0; cno < n_comb; cno++) {
612
+ set_index_parameters (index, cno);
613
+ std::vector<Index::idx_t> I(nq * crit.nnn);
614
+ std::vector<float> D(nq * crit.nnn);
615
+
616
+ double t0 = getmillisecs ();
617
+ index->search (nq, xq, crit.nnn, D.data(), I.data());
618
+ double t_search = (getmillisecs() - t0) / 1e3;
619
+
620
+ double perf = crit.evaluate (D.data(), I.data());
621
+
622
+ bool keep = ops->add (perf, t_search, combination_name (cno), cno);
623
+
624
+ if (verbose)
625
+ printf(" %ld/%ld: %s perf=%.3f t=%.3f s %s\n", cno, n_comb,
626
+ combination_name (cno).c_str(), perf, t_search,
627
+ keep ? "*" : "");
628
+ }
629
+ return;
630
+ }
631
+
632
+ int n_exp = n_experiments;
633
+
634
+ if (n_exp > n_comb) n_exp = n_comb;
635
+ FAISS_THROW_IF_NOT (n_comb == 1 || n_exp > 2);
636
+ std::vector<int> perm (n_comb);
637
+ // make sure the slowest and fastest experiment are run
638
+ perm[0] = 0;
639
+ if (n_comb > 1) {
640
+ perm[1] = n_comb - 1;
641
+ rand_perm (&perm[2], n_comb - 2, 1234);
642
+ for (int i = 2; i < perm.size(); i++) perm[i] ++;
643
+ }
644
+
645
+ for (size_t xp = 0; xp < n_exp; xp++) {
646
+ size_t cno = perm[xp];
647
+
648
+ if (verbose)
649
+ printf(" %ld/%d: cno=%ld %s ", xp, n_exp, cno,
650
+ combination_name (cno).c_str());
651
+
652
+ {
653
+ double lower_bound_t = 0.0;
654
+ double upper_bound_perf = 1.0;
655
+ for (int i = 0; i < ops->all_pts.size(); i++) {
656
+ update_bounds (cno, ops->all_pts[i],
657
+ &upper_bound_perf, &lower_bound_t);
658
+ }
659
+ double best_t = ops->t_for_perf (upper_bound_perf);
660
+ if (verbose)
661
+ printf ("bounds [perf<=%.3f t>=%.3f] %s",
662
+ upper_bound_perf, lower_bound_t,
663
+ best_t <= lower_bound_t ? "skip\n" : "");
664
+ if (best_t <= lower_bound_t) continue;
665
+ }
666
+
667
+ set_index_parameters (index, cno);
668
+ std::vector<Index::idx_t> I(nq * crit.nnn);
669
+ std::vector<float> D(nq * crit.nnn);
670
+
671
+ double t0 = getmillisecs ();
672
+
673
+ int nrun = 0;
674
+ double t_search;
675
+
676
+ do {
677
+
678
+ if (thread_over_batches) {
679
+ #pragma omp parallel for
680
+ for (size_t q0 = 0; q0 < nq; q0 += batchsize) {
681
+ size_t q1 = q0 + batchsize;
682
+ if (q1 > nq) q1 = nq;
683
+ index->search (q1 - q0, xq + q0 * index->d,
684
+ crit.nnn,
685
+ D.data() + q0 * crit.nnn,
686
+ I.data() + q0 * crit.nnn);
687
+ }
688
+ } else {
689
+ for (size_t q0 = 0; q0 < nq; q0 += batchsize) {
690
+ size_t q1 = q0 + batchsize;
691
+ if (q1 > nq) q1 = nq;
692
+ index->search (q1 - q0, xq + q0 * index->d,
693
+ crit.nnn,
694
+ D.data() + q0 * crit.nnn,
695
+ I.data() + q0 * crit.nnn);
696
+ }
697
+ }
698
+ nrun ++;
699
+ t_search = (getmillisecs() - t0) / 1e3;
700
+
701
+ } while (t_search < min_test_duration);
702
+
703
+ t_search /= nrun;
704
+
705
+ double perf = crit.evaluate (D.data(), I.data());
706
+
707
+ bool keep = ops->add (perf, t_search, combination_name (cno), cno);
708
+
709
+ if (verbose)
710
+ printf(" perf %.3f t %.3f (%d runs) %s\n",
711
+ perf, t_search, nrun,
712
+ keep ? "*" : "");
713
+ }
714
+ }
715
+
716
+
717
+
718
+
719
+ } // namespace faiss