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
@@ -0,0 +1,127 @@
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_ON_DISK_INVERTED_LISTS_H
11
+ #define FAISS_ON_DISK_INVERTED_LISTS_H
12
+
13
+ #include <vector>
14
+ #include <list>
15
+
16
+ #include <faiss/IndexIVF.h>
17
+
18
+ namespace faiss {
19
+
20
+
21
+ struct LockLevels;
22
+
23
+ /** On-disk storage of inverted lists.
24
+ *
25
+ * The data is stored in a mmapped chunk of memory (base ptointer ptr,
26
+ * size totsize). Each list is a range of memory that contains (object
27
+ * List) that contains:
28
+ *
29
+ * - uint8_t codes[capacity * code_size]
30
+ * - followed by idx_t ids[capacity]
31
+ *
32
+ * in each of the arrays, the size <= capacity first elements are
33
+ * used, the rest is not initialized.
34
+ *
35
+ * Addition and resize are supported by:
36
+ * - roundind up the capacity of the lists to a power of two
37
+ * - maintaining a list of empty slots, sorted by size.
38
+ * - resizing the mmapped block is adjusted as needed.
39
+ *
40
+ * An OnDiskInvertedLists is compact if the size == capacity for all
41
+ * lists and there are no available slots.
42
+ *
43
+ * Addition to the invlists is slow. For incremental add it is better
44
+ * to use a default ArrayInvertedLists object and convert it to an
45
+ * OnDisk with merge_from.
46
+ *
47
+ * When it is known that a set of lists will be accessed, it is useful
48
+ * to call prefetch_lists, that launches a set of threads to read the
49
+ * lists in parallel.
50
+ */
51
+ struct OnDiskInvertedLists: InvertedLists {
52
+
53
+ struct List {
54
+ size_t size; // size of inverted list (entries)
55
+ size_t capacity; // allocated size (entries)
56
+ size_t offset; // offset in buffer (bytes)
57
+ List ();
58
+ };
59
+
60
+ // size nlist
61
+ std::vector<List> lists;
62
+
63
+ struct Slot {
64
+ size_t offset; // bytes
65
+ size_t capacity; // bytes
66
+ Slot (size_t offset, size_t capacity);
67
+ Slot ();
68
+ };
69
+
70
+ // size whatever space remains
71
+ std::list<Slot> slots;
72
+
73
+ std::string filename;
74
+ size_t totsize;
75
+ uint8_t *ptr; // mmap base pointer
76
+ bool read_only; /// are inverted lists mapped read-only
77
+
78
+ OnDiskInvertedLists (size_t nlist, size_t code_size,
79
+ const char *filename);
80
+
81
+ size_t list_size(size_t list_no) const override;
82
+ const uint8_t * get_codes (size_t list_no) const override;
83
+ const idx_t * get_ids (size_t list_no) const override;
84
+
85
+ size_t add_entries (
86
+ size_t list_no, size_t n_entry,
87
+ const idx_t* ids, const uint8_t *code) override;
88
+
89
+ void update_entries (size_t list_no, size_t offset, size_t n_entry,
90
+ const idx_t *ids, const uint8_t *code) override;
91
+
92
+ void resize (size_t list_no, size_t new_size) override;
93
+
94
+ // copy all inverted lists into *this, in compact form (without
95
+ // allocating slots)
96
+ size_t merge_from (const InvertedLists **ils, int n_il, bool verbose=false);
97
+
98
+ /// restrict the inverted lists to l0:l1 without touching the mmapped region
99
+ void crop_invlists(size_t l0, size_t l1);
100
+
101
+ void prefetch_lists (const idx_t *list_nos, int nlist) const override;
102
+
103
+ virtual ~OnDiskInvertedLists ();
104
+
105
+ // private
106
+
107
+ LockLevels * locks;
108
+
109
+ // encapsulates the threads that are busy prefeteching
110
+ struct OngoingPrefetch;
111
+ OngoingPrefetch *pf;
112
+ int prefetch_nthread;
113
+
114
+ void do_mmap ();
115
+ void update_totsize (size_t new_totsize);
116
+ void resize_locked (size_t list_no, size_t new_size);
117
+ size_t allocate_slot (size_t capacity);
118
+ void free_slot (size_t offset, size_t capacity);
119
+
120
+ // empty constructor for the I/O functions
121
+ OnDiskInvertedLists ();
122
+ };
123
+
124
+
125
+ } // namespace faiss
126
+
127
+ #endif
@@ -0,0 +1,1157 @@
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/VectorTransform.h>
11
+
12
+ #include <cstdio>
13
+ #include <cmath>
14
+ #include <cstring>
15
+ #include <memory>
16
+
17
+ #include <faiss/utils/distances.h>
18
+ #include <faiss/utils/random.h>
19
+ #include <faiss/utils/utils.h>
20
+ #include <faiss/impl/FaissAssert.h>
21
+ #include <faiss/IndexPQ.h>
22
+
23
+ using namespace faiss;
24
+
25
+
26
+ extern "C" {
27
+
28
+ // this is to keep the clang syntax checker happy
29
+ #ifndef FINTEGER
30
+ #define FINTEGER int
31
+ #endif
32
+
33
+
34
+ /* declare BLAS functions, see http://www.netlib.org/clapack/cblas/ */
35
+
36
+ int sgemm_ (
37
+ const char *transa, const char *transb, FINTEGER *m, FINTEGER *
38
+ n, FINTEGER *k, const float *alpha, const float *a,
39
+ FINTEGER *lda, const float *b,
40
+ FINTEGER *ldb, float *beta,
41
+ float *c, FINTEGER *ldc);
42
+
43
+ int dgemm_ (
44
+ const char *transa, const char *transb, FINTEGER *m, FINTEGER *
45
+ n, FINTEGER *k, const double *alpha, const double *a,
46
+ FINTEGER *lda, const double *b,
47
+ FINTEGER *ldb, double *beta,
48
+ double *c, FINTEGER *ldc);
49
+
50
+ int ssyrk_ (
51
+ const char *uplo, const char *trans, FINTEGER *n, FINTEGER *k,
52
+ float *alpha, float *a, FINTEGER *lda,
53
+ float *beta, float *c, FINTEGER *ldc);
54
+
55
+ /* Lapack functions from http://www.netlib.org/clapack/old/single/ */
56
+
57
+ int ssyev_ (
58
+ const char *jobz, const char *uplo, FINTEGER *n, float *a,
59
+ FINTEGER *lda, float *w, float *work, FINTEGER *lwork,
60
+ FINTEGER *info);
61
+
62
+ int dsyev_ (
63
+ const char *jobz, const char *uplo, FINTEGER *n, double *a,
64
+ FINTEGER *lda, double *w, double *work, FINTEGER *lwork,
65
+ FINTEGER *info);
66
+
67
+ int sgesvd_(
68
+ const char *jobu, const char *jobvt, FINTEGER *m, FINTEGER *n,
69
+ float *a, FINTEGER *lda, float *s, float *u, FINTEGER *ldu, float *vt,
70
+ FINTEGER *ldvt, float *work, FINTEGER *lwork, FINTEGER *info);
71
+
72
+
73
+ int dgesvd_(
74
+ const char *jobu, const char *jobvt, FINTEGER *m, FINTEGER *n,
75
+ double *a, FINTEGER *lda, double *s, double *u, FINTEGER *ldu, double *vt,
76
+ FINTEGER *ldvt, double *work, FINTEGER *lwork, FINTEGER *info);
77
+
78
+ }
79
+
80
+ /*********************************************
81
+ * VectorTransform
82
+ *********************************************/
83
+
84
+
85
+
86
+ float * VectorTransform::apply (Index::idx_t n, const float * x) const
87
+ {
88
+ float * xt = new float[n * d_out];
89
+ apply_noalloc (n, x, xt);
90
+ return xt;
91
+ }
92
+
93
+
94
+ void VectorTransform::train (idx_t, const float *) {
95
+ // does nothing by default
96
+ }
97
+
98
+
99
+ void VectorTransform::reverse_transform (
100
+ idx_t , const float *,
101
+ float *) const
102
+ {
103
+ FAISS_THROW_MSG ("reverse transform not implemented");
104
+ }
105
+
106
+
107
+
108
+
109
+ /*********************************************
110
+ * LinearTransform
111
+ *********************************************/
112
+
113
+ /// both d_in > d_out and d_out < d_in are supported
114
+ LinearTransform::LinearTransform (int d_in, int d_out,
115
+ bool have_bias):
116
+ VectorTransform (d_in, d_out), have_bias (have_bias),
117
+ is_orthonormal (false), verbose (false)
118
+ {
119
+ is_trained = false; // will be trained when A and b are initialized
120
+ }
121
+
122
+ void LinearTransform::apply_noalloc (Index::idx_t n, const float * x,
123
+ float * xt) const
124
+ {
125
+ FAISS_THROW_IF_NOT_MSG(is_trained, "Transformation not trained yet");
126
+
127
+ float c_factor;
128
+ if (have_bias) {
129
+ FAISS_THROW_IF_NOT_MSG (b.size() == d_out, "Bias not initialized");
130
+ float * xi = xt;
131
+ for (int i = 0; i < n; i++)
132
+ for(int j = 0; j < d_out; j++)
133
+ *xi++ = b[j];
134
+ c_factor = 1.0;
135
+ } else {
136
+ c_factor = 0.0;
137
+ }
138
+
139
+ FAISS_THROW_IF_NOT_MSG (A.size() == d_out * d_in,
140
+ "Transformation matrix not initialized");
141
+
142
+ float one = 1;
143
+ FINTEGER nbiti = d_out, ni = n, di = d_in;
144
+ sgemm_ ("Transposed", "Not transposed",
145
+ &nbiti, &ni, &di,
146
+ &one, A.data(), &di, x, &di, &c_factor, xt, &nbiti);
147
+
148
+ }
149
+
150
+
151
+ void LinearTransform::transform_transpose (idx_t n, const float * y,
152
+ float *x) const
153
+ {
154
+ if (have_bias) { // allocate buffer to store bias-corrected data
155
+ float *y_new = new float [n * d_out];
156
+ const float *yr = y;
157
+ float *yw = y_new;
158
+ for (idx_t i = 0; i < n; i++) {
159
+ for (int j = 0; j < d_out; j++) {
160
+ *yw++ = *yr++ - b [j];
161
+ }
162
+ }
163
+ y = y_new;
164
+ }
165
+
166
+ {
167
+ FINTEGER dii = d_in, doi = d_out, ni = n;
168
+ float one = 1.0, zero = 0.0;
169
+ sgemm_ ("Not", "Not", &dii, &ni, &doi,
170
+ &one, A.data (), &dii, y, &doi, &zero, x, &dii);
171
+ }
172
+
173
+ if (have_bias) delete [] y;
174
+ }
175
+
176
+ void LinearTransform::set_is_orthonormal ()
177
+ {
178
+ if (d_out > d_in) {
179
+ // not clear what we should do in this case
180
+ is_orthonormal = false;
181
+ return;
182
+ }
183
+ if (d_out == 0) { // borderline case, unnormalized matrix
184
+ is_orthonormal = true;
185
+ return;
186
+ }
187
+
188
+ double eps = 4e-5;
189
+ FAISS_ASSERT(A.size() >= d_out * d_in);
190
+ {
191
+ std::vector<float> ATA(d_out * d_out);
192
+ FINTEGER dii = d_in, doi = d_out;
193
+ float one = 1.0, zero = 0.0;
194
+
195
+ sgemm_ ("Transposed", "Not", &doi, &doi, &dii,
196
+ &one, A.data (), &dii,
197
+ A.data(), &dii,
198
+ &zero, ATA.data(), &doi);
199
+
200
+ is_orthonormal = true;
201
+ for (long i = 0; i < d_out; i++) {
202
+ for (long j = 0; j < d_out; j++) {
203
+ float v = ATA[i + j * d_out];
204
+ if (i == j) v-= 1;
205
+ if (fabs(v) > eps) {
206
+ is_orthonormal = false;
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ }
213
+
214
+
215
+ void LinearTransform::reverse_transform (idx_t n, const float * xt,
216
+ float *x) const
217
+ {
218
+ if (is_orthonormal) {
219
+ transform_transpose (n, xt, x);
220
+ } else {
221
+ FAISS_THROW_MSG ("reverse transform not implemented for non-orthonormal matrices");
222
+ }
223
+ }
224
+
225
+
226
+ void LinearTransform::print_if_verbose (
227
+ const char*name, const std::vector<double> &mat,
228
+ int n, int d) const
229
+ {
230
+ if (!verbose) return;
231
+ printf("matrix %s: %d*%d [\n", name, n, d);
232
+ FAISS_THROW_IF_NOT (mat.size() >= n * d);
233
+ for (int i = 0; i < n; i++) {
234
+ for (int j = 0; j < d; j++) {
235
+ printf("%10.5g ", mat[i * d + j]);
236
+ }
237
+ printf("\n");
238
+ }
239
+ printf("]\n");
240
+ }
241
+
242
+ /*********************************************
243
+ * RandomRotationMatrix
244
+ *********************************************/
245
+
246
+ void RandomRotationMatrix::init (int seed)
247
+ {
248
+
249
+ if(d_out <= d_in) {
250
+ A.resize (d_out * d_in);
251
+ float *q = A.data();
252
+ float_randn(q, d_out * d_in, seed);
253
+ matrix_qr(d_in, d_out, q);
254
+ } else {
255
+ // use tight-frame transformation
256
+ A.resize (d_out * d_out);
257
+ float *q = A.data();
258
+ float_randn(q, d_out * d_out, seed);
259
+ matrix_qr(d_out, d_out, q);
260
+ // remove columns
261
+ int i, j;
262
+ for (i = 0; i < d_out; i++) {
263
+ for(j = 0; j < d_in; j++) {
264
+ q[i * d_in + j] = q[i * d_out + j];
265
+ }
266
+ }
267
+ A.resize(d_in * d_out);
268
+ }
269
+ is_orthonormal = true;
270
+ is_trained = true;
271
+ }
272
+
273
+ void RandomRotationMatrix::train (Index::idx_t /*n*/, const float */*x*/)
274
+ {
275
+ // initialize with some arbitrary seed
276
+ init (12345);
277
+ }
278
+
279
+
280
+ /*********************************************
281
+ * PCAMatrix
282
+ *********************************************/
283
+
284
+ PCAMatrix::PCAMatrix (int d_in, int d_out,
285
+ float eigen_power, bool random_rotation):
286
+ LinearTransform(d_in, d_out, true),
287
+ eigen_power(eigen_power), random_rotation(random_rotation)
288
+ {
289
+ is_trained = false;
290
+ max_points_per_d = 1000;
291
+ balanced_bins = 0;
292
+ }
293
+
294
+
295
+ namespace {
296
+
297
+ /// Compute the eigenvalue decomposition of symmetric matrix cov,
298
+ /// dimensions d_in-by-d_in. Output eigenvectors in cov.
299
+
300
+ void eig(size_t d_in, double *cov, double *eigenvalues, int verbose)
301
+ {
302
+ { // compute eigenvalues and vectors
303
+ FINTEGER info = 0, lwork = -1, di = d_in;
304
+ double workq;
305
+
306
+ dsyev_ ("Vectors as well", "Upper",
307
+ &di, cov, &di, eigenvalues, &workq, &lwork, &info);
308
+ lwork = FINTEGER(workq);
309
+ double *work = new double[lwork];
310
+
311
+ dsyev_ ("Vectors as well", "Upper",
312
+ &di, cov, &di, eigenvalues, work, &lwork, &info);
313
+
314
+ delete [] work;
315
+
316
+ if (info != 0) {
317
+ fprintf (stderr, "WARN ssyev info returns %d, "
318
+ "a very bad PCA matrix is learnt\n",
319
+ int(info));
320
+ // do not throw exception, as the matrix could still be useful
321
+ }
322
+
323
+
324
+ if(verbose && d_in <= 10) {
325
+ printf("info=%ld new eigvals=[", long(info));
326
+ for(int j = 0; j < d_in; j++) printf("%g ", eigenvalues[j]);
327
+ printf("]\n");
328
+
329
+ double *ci = cov;
330
+ printf("eigenvecs=\n");
331
+ for(int i = 0; i < d_in; i++) {
332
+ for(int j = 0; j < d_in; j++)
333
+ printf("%10.4g ", *ci++);
334
+ printf("\n");
335
+ }
336
+ }
337
+
338
+ }
339
+
340
+ // revert order of eigenvectors & values
341
+
342
+ for(int i = 0; i < d_in / 2; i++) {
343
+
344
+ std::swap(eigenvalues[i], eigenvalues[d_in - 1 - i]);
345
+ double *v1 = cov + i * d_in;
346
+ double *v2 = cov + (d_in - 1 - i) * d_in;
347
+ for(int j = 0; j < d_in; j++)
348
+ std::swap(v1[j], v2[j]);
349
+ }
350
+
351
+ }
352
+
353
+
354
+ }
355
+
356
+ void PCAMatrix::train (Index::idx_t n, const float *x)
357
+ {
358
+ const float * x_in = x;
359
+
360
+ x = fvecs_maybe_subsample (d_in, (size_t*)&n,
361
+ max_points_per_d * d_in, x, verbose);
362
+
363
+ ScopeDeleter<float> del_x (x != x_in ? x : nullptr);
364
+
365
+ // compute mean
366
+ mean.clear(); mean.resize(d_in, 0.0);
367
+ if (have_bias) { // we may want to skip the bias
368
+ const float *xi = x;
369
+ for (int i = 0; i < n; i++) {
370
+ for(int j = 0; j < d_in; j++)
371
+ mean[j] += *xi++;
372
+ }
373
+ for(int j = 0; j < d_in; j++)
374
+ mean[j] /= n;
375
+ }
376
+ if(verbose) {
377
+ printf("mean=[");
378
+ for(int j = 0; j < d_in; j++) printf("%g ", mean[j]);
379
+ printf("]\n");
380
+ }
381
+
382
+ if(n >= d_in) {
383
+ // compute covariance matrix, store it in PCA matrix
384
+ PCAMat.resize(d_in * d_in);
385
+ float * cov = PCAMat.data();
386
+ { // initialize with mean * mean^T term
387
+ float *ci = cov;
388
+ for(int i = 0; i < d_in; i++) {
389
+ for(int j = 0; j < d_in; j++)
390
+ *ci++ = - n * mean[i] * mean[j];
391
+ }
392
+ }
393
+ {
394
+ FINTEGER di = d_in, ni = n;
395
+ float one = 1.0;
396
+ ssyrk_ ("Up", "Non transposed",
397
+ &di, &ni, &one, (float*)x, &di, &one, cov, &di);
398
+
399
+ }
400
+ if(verbose && d_in <= 10) {
401
+ float *ci = cov;
402
+ printf("cov=\n");
403
+ for(int i = 0; i < d_in; i++) {
404
+ for(int j = 0; j < d_in; j++)
405
+ printf("%10g ", *ci++);
406
+ printf("\n");
407
+ }
408
+ }
409
+
410
+ std::vector<double> covd (d_in * d_in);
411
+ for (size_t i = 0; i < d_in * d_in; i++) covd [i] = cov [i];
412
+
413
+ std::vector<double> eigenvaluesd (d_in);
414
+
415
+ eig (d_in, covd.data (), eigenvaluesd.data (), verbose);
416
+
417
+ for (size_t i = 0; i < d_in * d_in; i++) PCAMat [i] = covd [i];
418
+ eigenvalues.resize (d_in);
419
+
420
+ for (size_t i = 0; i < d_in; i++)
421
+ eigenvalues [i] = eigenvaluesd [i];
422
+
423
+
424
+ } else {
425
+
426
+ std::vector<float> xc (n * d_in);
427
+
428
+ for (size_t i = 0; i < n; i++)
429
+ for(size_t j = 0; j < d_in; j++)
430
+ xc [i * d_in + j] = x [i * d_in + j] - mean[j];
431
+
432
+ // compute Gram matrix
433
+ std::vector<float> gram (n * n);
434
+ {
435
+ FINTEGER di = d_in, ni = n;
436
+ float one = 1.0, zero = 0.0;
437
+ ssyrk_ ("Up", "Transposed",
438
+ &ni, &di, &one, xc.data(), &di, &zero, gram.data(), &ni);
439
+ }
440
+
441
+ if(verbose && d_in <= 10) {
442
+ float *ci = gram.data();
443
+ printf("gram=\n");
444
+ for(int i = 0; i < n; i++) {
445
+ for(int j = 0; j < n; j++)
446
+ printf("%10g ", *ci++);
447
+ printf("\n");
448
+ }
449
+ }
450
+
451
+ std::vector<double> gramd (n * n);
452
+ for (size_t i = 0; i < n * n; i++)
453
+ gramd [i] = gram [i];
454
+
455
+ std::vector<double> eigenvaluesd (n);
456
+
457
+ // eig will fill in only the n first eigenvals
458
+
459
+ eig (n, gramd.data (), eigenvaluesd.data (), verbose);
460
+
461
+ PCAMat.resize(d_in * n);
462
+
463
+ for (size_t i = 0; i < n * n; i++)
464
+ gram [i] = gramd [i];
465
+
466
+ eigenvalues.resize (d_in);
467
+ // fill in only the n first ones
468
+ for (size_t i = 0; i < n; i++)
469
+ eigenvalues [i] = eigenvaluesd [i];
470
+
471
+ { // compute PCAMat = x' * v
472
+ FINTEGER di = d_in, ni = n;
473
+ float one = 1.0;
474
+
475
+ sgemm_ ("Non", "Non Trans",
476
+ &di, &ni, &ni,
477
+ &one, xc.data(), &di, gram.data(), &ni,
478
+ &one, PCAMat.data(), &di);
479
+ }
480
+
481
+ if(verbose && d_in <= 10) {
482
+ float *ci = PCAMat.data();
483
+ printf("PCAMat=\n");
484
+ for(int i = 0; i < n; i++) {
485
+ for(int j = 0; j < d_in; j++)
486
+ printf("%10g ", *ci++);
487
+ printf("\n");
488
+ }
489
+ }
490
+ fvec_renorm_L2 (d_in, n, PCAMat.data());
491
+
492
+ }
493
+
494
+ prepare_Ab();
495
+ is_trained = true;
496
+ }
497
+
498
+ void PCAMatrix::copy_from (const PCAMatrix & other)
499
+ {
500
+ FAISS_THROW_IF_NOT (other.is_trained);
501
+ mean = other.mean;
502
+ eigenvalues = other.eigenvalues;
503
+ PCAMat = other.PCAMat;
504
+ prepare_Ab ();
505
+ is_trained = true;
506
+ }
507
+
508
+ void PCAMatrix::prepare_Ab ()
509
+ {
510
+ FAISS_THROW_IF_NOT_FMT (
511
+ d_out * d_in <= PCAMat.size(),
512
+ "PCA matrix cannot output %d dimensions from %d ",
513
+ d_out, d_in);
514
+
515
+ if (!random_rotation) {
516
+ A = PCAMat;
517
+ A.resize(d_out * d_in); // strip off useless dimensions
518
+
519
+ // first scale the components
520
+ if (eigen_power != 0) {
521
+ float *ai = A.data();
522
+ for (int i = 0; i < d_out; i++) {
523
+ float factor = pow(eigenvalues[i], eigen_power);
524
+ for(int j = 0; j < d_in; j++)
525
+ *ai++ *= factor;
526
+ }
527
+ }
528
+
529
+ if (balanced_bins != 0) {
530
+ FAISS_THROW_IF_NOT (d_out % balanced_bins == 0);
531
+ int dsub = d_out / balanced_bins;
532
+ std::vector <float> Ain;
533
+ std::swap(A, Ain);
534
+ A.resize(d_out * d_in);
535
+
536
+ std::vector <float> accu(balanced_bins);
537
+ std::vector <int> counter(balanced_bins);
538
+
539
+ // greedy assignment
540
+ for (int i = 0; i < d_out; i++) {
541
+ // find best bin
542
+ int best_j = -1;
543
+ float min_w = 1e30;
544
+ for (int j = 0; j < balanced_bins; j++) {
545
+ if (counter[j] < dsub && accu[j] < min_w) {
546
+ min_w = accu[j];
547
+ best_j = j;
548
+ }
549
+ }
550
+ int row_dst = best_j * dsub + counter[best_j];
551
+ accu[best_j] += eigenvalues[i];
552
+ counter[best_j] ++;
553
+ memcpy (&A[row_dst * d_in], &Ain[i * d_in],
554
+ d_in * sizeof (A[0]));
555
+ }
556
+
557
+ if (verbose) {
558
+ printf(" bin accu=[");
559
+ for (int i = 0; i < balanced_bins; i++)
560
+ printf("%g ", accu[i]);
561
+ printf("]\n");
562
+ }
563
+ }
564
+
565
+
566
+ } else {
567
+ FAISS_THROW_IF_NOT_MSG (balanced_bins == 0,
568
+ "both balancing bins and applying a random rotation "
569
+ "does not make sense");
570
+ RandomRotationMatrix rr(d_out, d_out);
571
+
572
+ rr.init(5);
573
+
574
+ // apply scaling on the rotation matrix (right multiplication)
575
+ if (eigen_power != 0) {
576
+ for (int i = 0; i < d_out; i++) {
577
+ float factor = pow(eigenvalues[i], eigen_power);
578
+ for(int j = 0; j < d_out; j++)
579
+ rr.A[j * d_out + i] *= factor;
580
+ }
581
+ }
582
+
583
+ A.resize(d_in * d_out);
584
+ {
585
+ FINTEGER dii = d_in, doo = d_out;
586
+ float one = 1.0, zero = 0.0;
587
+
588
+ sgemm_ ("Not", "Not", &dii, &doo, &doo,
589
+ &one, PCAMat.data(), &dii, rr.A.data(), &doo, &zero,
590
+ A.data(), &dii);
591
+
592
+ }
593
+
594
+ }
595
+
596
+ b.clear(); b.resize(d_out);
597
+
598
+ for (int i = 0; i < d_out; i++) {
599
+ float accu = 0;
600
+ for (int j = 0; j < d_in; j++)
601
+ accu -= mean[j] * A[j + i * d_in];
602
+ b[i] = accu;
603
+ }
604
+
605
+ is_orthonormal = eigen_power == 0;
606
+
607
+ }
608
+
609
+ /*********************************************
610
+ * ITQMatrix
611
+ *********************************************/
612
+
613
+ ITQMatrix::ITQMatrix (int d):
614
+ LinearTransform(d, d, false),
615
+ max_iter (50),
616
+ seed (123)
617
+ {
618
+ }
619
+
620
+
621
+ /** translated from fbcode/deeplearning/catalyzer/catalyzer/quantizers.py */
622
+ void ITQMatrix::train (Index::idx_t n, const float* xf)
623
+ {
624
+ size_t d = d_in;
625
+ std::vector<double> rotation (d * d);
626
+
627
+ if (init_rotation.size() == d * d) {
628
+ memcpy (rotation.data(), init_rotation.data(),
629
+ d * d * sizeof(rotation[0]));
630
+ } else {
631
+ RandomRotationMatrix rrot (d, d);
632
+ rrot.init (seed);
633
+ for (size_t i = 0; i < d * d; i++) {
634
+ rotation[i] = rrot.A[i];
635
+ }
636
+ }
637
+
638
+ std::vector<double> x (n * d);
639
+
640
+ for (size_t i = 0; i < n * d; i++) {
641
+ x[i] = xf[i];
642
+ }
643
+
644
+ std::vector<double> rotated_x (n * d), cov_mat (d * d);
645
+ std::vector<double> u (d * d), vt (d * d), singvals (d);
646
+
647
+ for (int i = 0; i < max_iter; i++) {
648
+ print_if_verbose ("rotation", rotation, d, d);
649
+ { // rotated_data = np.dot(training_data, rotation)
650
+ FINTEGER di = d, ni = n;
651
+ double one = 1, zero = 0;
652
+ dgemm_ ("N", "N", &di, &ni, &di,
653
+ &one, rotation.data(), &di, x.data(), &di,
654
+ &zero, rotated_x.data(), &di);
655
+ }
656
+ print_if_verbose ("rotated_x", rotated_x, n, d);
657
+ // binarize
658
+ for (size_t j = 0; j < n * d; j++) {
659
+ rotated_x[j] = rotated_x[j] < 0 ? -1 : 1;
660
+ }
661
+ // covariance matrix
662
+ { // rotated_data = np.dot(training_data, rotation)
663
+ FINTEGER di = d, ni = n;
664
+ double one = 1, zero = 0;
665
+ dgemm_ ("N", "T", &di, &di, &ni,
666
+ &one, rotated_x.data(), &di, x.data(), &di,
667
+ &zero, cov_mat.data(), &di);
668
+ }
669
+ print_if_verbose ("cov_mat", cov_mat, d, d);
670
+ // SVD
671
+ {
672
+
673
+ FINTEGER di = d;
674
+ FINTEGER lwork = -1, info;
675
+ double lwork1;
676
+
677
+ // workspace query
678
+ dgesvd_ ("A", "A", &di, &di, cov_mat.data(), &di,
679
+ singvals.data(), u.data(), &di,
680
+ vt.data(), &di,
681
+ &lwork1, &lwork, &info);
682
+
683
+ FAISS_THROW_IF_NOT (info == 0);
684
+ lwork = size_t (lwork1);
685
+ std::vector<double> work (lwork);
686
+ dgesvd_ ("A", "A", &di, &di, cov_mat.data(), &di,
687
+ singvals.data(), u.data(), &di,
688
+ vt.data(), &di,
689
+ work.data(), &lwork, &info);
690
+ FAISS_THROW_IF_NOT_FMT (info == 0, "sgesvd returned info=%d", info);
691
+
692
+ }
693
+ print_if_verbose ("u", u, d, d);
694
+ print_if_verbose ("vt", vt, d, d);
695
+ // update rotation
696
+ {
697
+ FINTEGER di = d;
698
+ double one = 1, zero = 0;
699
+ dgemm_ ("N", "T", &di, &di, &di,
700
+ &one, u.data(), &di, vt.data(), &di,
701
+ &zero, rotation.data(), &di);
702
+ }
703
+ print_if_verbose ("final rot", rotation, d, d);
704
+
705
+ }
706
+ A.resize (d * d);
707
+ for (size_t i = 0; i < d; i++) {
708
+ for (size_t j = 0; j < d; j++) {
709
+ A[i + d * j] = rotation[j + d * i];
710
+ }
711
+ }
712
+ is_trained = true;
713
+
714
+ }
715
+
716
+ ITQTransform::ITQTransform (int d_in, int d_out, bool do_pca):
717
+ VectorTransform (d_in, d_out),
718
+ do_pca (do_pca),
719
+ itq (d_out),
720
+ pca_then_itq (d_in, d_out, false)
721
+ {
722
+ if (!do_pca) {
723
+ FAISS_THROW_IF_NOT (d_in == d_out);
724
+ }
725
+ max_train_per_dim = 10;
726
+ is_trained = false;
727
+ }
728
+
729
+
730
+
731
+
732
+ void ITQTransform::train (idx_t n, const float *x)
733
+ {
734
+ FAISS_THROW_IF_NOT (!is_trained);
735
+
736
+ const float * x_in = x;
737
+ size_t max_train_points = std::max(d_in * max_train_per_dim, 32768);
738
+ x = fvecs_maybe_subsample (d_in, (size_t*)&n, max_train_points, x);
739
+
740
+ ScopeDeleter<float> del_x (x != x_in ? x : nullptr);
741
+
742
+ std::unique_ptr<float []> x_norm(new float[n * d_in]);
743
+ { // normalize
744
+ int d = d_in;
745
+
746
+ mean.resize (d, 0);
747
+ for (idx_t i = 0; i < n; i++) {
748
+ for (idx_t j = 0; j < d; j++) {
749
+ mean[j] += x[i * d + j];
750
+ }
751
+ }
752
+ for (idx_t j = 0; j < d; j++) {
753
+ mean[j] /= n;
754
+ }
755
+ for (idx_t i = 0; i < n; i++) {
756
+ for (idx_t j = 0; j < d; j++) {
757
+ x_norm[i * d + j] = x[i * d + j] - mean[j];
758
+ }
759
+ }
760
+ fvec_renorm_L2 (d_in, n, x_norm.get());
761
+ }
762
+
763
+ // train PCA
764
+
765
+ PCAMatrix pca (d_in, d_out);
766
+ float *x_pca;
767
+ std::unique_ptr<float []> x_pca_del;
768
+ if (do_pca) {
769
+ pca.have_bias = false; // for consistency with reference implem
770
+ pca.train (n, x_norm.get());
771
+ x_pca = pca.apply (n, x_norm.get());
772
+ x_pca_del.reset(x_pca);
773
+ } else {
774
+ x_pca = x_norm.get();
775
+ }
776
+
777
+ // train ITQ
778
+ itq.train (n, x_pca);
779
+
780
+ // merge PCA and ITQ
781
+ if (do_pca) {
782
+ FINTEGER di = d_out, dini = d_in;
783
+ float one = 1, zero = 0;
784
+ pca_then_itq.A.resize(d_in * d_out);
785
+ sgemm_ ("N", "N", &dini, &di, &di,
786
+ &one, pca.A.data(), &dini,
787
+ itq.A.data(), &di,
788
+ &zero, pca_then_itq.A.data(), &dini);
789
+ } else {
790
+ pca_then_itq.A = itq.A;
791
+ }
792
+ pca_then_itq.is_trained = true;
793
+ is_trained = true;
794
+ }
795
+
796
+ void ITQTransform::apply_noalloc (Index::idx_t n, const float * x,
797
+ float * xt) const
798
+ {
799
+ FAISS_THROW_IF_NOT_MSG(is_trained, "Transformation not trained yet");
800
+
801
+ std::unique_ptr<float []> x_norm(new float[n * d_in]);
802
+ { // normalize
803
+ int d = d_in;
804
+ for (idx_t i = 0; i < n; i++) {
805
+ for (idx_t j = 0; j < d; j++) {
806
+ x_norm[i * d + j] = x[i * d + j] - mean[j];
807
+ }
808
+ }
809
+ // this is not really useful if we are going to binarize right
810
+ // afterwards but OK
811
+ fvec_renorm_L2 (d_in, n, x_norm.get());
812
+ }
813
+
814
+ pca_then_itq.apply_noalloc (n, x_norm.get(), xt);
815
+ }
816
+
817
+ /*********************************************
818
+ * OPQMatrix
819
+ *********************************************/
820
+
821
+
822
+ OPQMatrix::OPQMatrix (int d, int M, int d2):
823
+ LinearTransform (d, d2 == -1 ? d : d2, false), M(M),
824
+ niter (50),
825
+ niter_pq (4), niter_pq_0 (40),
826
+ verbose(false),
827
+ pq(nullptr)
828
+ {
829
+ is_trained = false;
830
+ // OPQ is quite expensive to train, so set this right.
831
+ max_train_points = 256 * 256;
832
+ pq = nullptr;
833
+ }
834
+
835
+
836
+
837
+ void OPQMatrix::train (Index::idx_t n, const float *x)
838
+ {
839
+
840
+ const float * x_in = x;
841
+
842
+ x = fvecs_maybe_subsample (d_in, (size_t*)&n,
843
+ max_train_points, x, verbose);
844
+
845
+ ScopeDeleter<float> del_x (x != x_in ? x : nullptr);
846
+
847
+ // To support d_out > d_in, we pad input vectors with 0s to d_out
848
+ size_t d = d_out <= d_in ? d_in : d_out;
849
+ size_t d2 = d_out;
850
+
851
+ #if 0
852
+ // what this test shows: the only way of getting bit-exact
853
+ // reproducible results with sgeqrf and sgesvd seems to be forcing
854
+ // single-threading.
855
+ { // test repro
856
+ std::vector<float> r (d * d);
857
+ float * rotation = r.data();
858
+ float_randn (rotation, d * d, 1234);
859
+ printf("CS0: %016lx\n",
860
+ ivec_checksum (128*128, (int*)rotation));
861
+ matrix_qr (d, d, rotation);
862
+ printf("CS1: %016lx\n",
863
+ ivec_checksum (128*128, (int*)rotation));
864
+ return;
865
+ }
866
+ #endif
867
+
868
+ if (verbose) {
869
+ printf ("OPQMatrix::train: training an OPQ rotation matrix "
870
+ "for M=%d from %ld vectors in %dD -> %dD\n",
871
+ M, n, d_in, d_out);
872
+ }
873
+
874
+ std::vector<float> xtrain (n * d);
875
+ // center x
876
+ {
877
+ std::vector<float> sum (d);
878
+ const float *xi = x;
879
+ for (size_t i = 0; i < n; i++) {
880
+ for (int j = 0; j < d_in; j++)
881
+ sum [j] += *xi++;
882
+ }
883
+ for (int i = 0; i < d; i++) sum[i] /= n;
884
+ float *yi = xtrain.data();
885
+ xi = x;
886
+ for (size_t i = 0; i < n; i++) {
887
+ for (int j = 0; j < d_in; j++)
888
+ *yi++ = *xi++ - sum[j];
889
+ yi += d - d_in;
890
+ }
891
+ }
892
+ float *rotation;
893
+
894
+ if (A.size () == 0) {
895
+ A.resize (d * d);
896
+ rotation = A.data();
897
+ if (verbose)
898
+ printf(" OPQMatrix::train: making random %ld*%ld rotation\n",
899
+ d, d);
900
+ float_randn (rotation, d * d, 1234);
901
+ matrix_qr (d, d, rotation);
902
+ // we use only the d * d2 upper part of the matrix
903
+ A.resize (d * d2);
904
+ } else {
905
+ FAISS_THROW_IF_NOT (A.size() == d * d2);
906
+ rotation = A.data();
907
+ }
908
+
909
+ std::vector<float>
910
+ xproj (d2 * n), pq_recons (d2 * n), xxr (d * n),
911
+ tmp(d * d * 4);
912
+
913
+
914
+ ProductQuantizer pq_default (d2, M, 8);
915
+ ProductQuantizer &pq_regular = pq ? *pq : pq_default;
916
+ std::vector<uint8_t> codes (pq_regular.code_size * n);
917
+
918
+ double t0 = getmillisecs();
919
+ for (int iter = 0; iter < niter; iter++) {
920
+
921
+ { // torch.mm(xtrain, rotation:t())
922
+ FINTEGER di = d, d2i = d2, ni = n;
923
+ float zero = 0, one = 1;
924
+ sgemm_ ("Transposed", "Not transposed",
925
+ &d2i, &ni, &di,
926
+ &one, rotation, &di,
927
+ xtrain.data(), &di,
928
+ &zero, xproj.data(), &d2i);
929
+ }
930
+
931
+ pq_regular.cp.max_points_per_centroid = 1000;
932
+ pq_regular.cp.niter = iter == 0 ? niter_pq_0 : niter_pq;
933
+ pq_regular.verbose = verbose;
934
+ pq_regular.train (n, xproj.data());
935
+
936
+ if (verbose) {
937
+ printf(" encode / decode\n");
938
+ }
939
+ if (pq_regular.assign_index) {
940
+ pq_regular.compute_codes_with_assign_index
941
+ (xproj.data(), codes.data(), n);
942
+ } else {
943
+ pq_regular.compute_codes (xproj.data(), codes.data(), n);
944
+ }
945
+ pq_regular.decode (codes.data(), pq_recons.data(), n);
946
+
947
+ float pq_err = fvec_L2sqr (pq_recons.data(), xproj.data(), n * d2) / n;
948
+
949
+ if (verbose)
950
+ printf (" Iteration %d (%d PQ iterations):"
951
+ "%.3f s, obj=%g\n", iter, pq_regular.cp.niter,
952
+ (getmillisecs () - t0) / 1000.0, pq_err);
953
+
954
+ {
955
+ float *u = tmp.data(), *vt = &tmp [d * d];
956
+ float *sing_val = &tmp [2 * d * d];
957
+ FINTEGER di = d, d2i = d2, ni = n;
958
+ float one = 1, zero = 0;
959
+
960
+ if (verbose) {
961
+ printf(" X * recons\n");
962
+ }
963
+ // torch.mm(xtrain:t(), pq_recons)
964
+ sgemm_ ("Not", "Transposed",
965
+ &d2i, &di, &ni,
966
+ &one, pq_recons.data(), &d2i,
967
+ xtrain.data(), &di,
968
+ &zero, xxr.data(), &d2i);
969
+
970
+
971
+ FINTEGER lwork = -1, info = -1;
972
+ float worksz;
973
+ // workspace query
974
+ sgesvd_ ("All", "All",
975
+ &d2i, &di, xxr.data(), &d2i,
976
+ sing_val,
977
+ vt, &d2i, u, &di,
978
+ &worksz, &lwork, &info);
979
+
980
+ lwork = int(worksz);
981
+ std::vector<float> work (lwork);
982
+ // u and vt swapped
983
+ sgesvd_ ("All", "All",
984
+ &d2i, &di, xxr.data(), &d2i,
985
+ sing_val,
986
+ vt, &d2i, u, &di,
987
+ work.data(), &lwork, &info);
988
+
989
+ sgemm_ ("Transposed", "Transposed",
990
+ &di, &d2i, &d2i,
991
+ &one, u, &di, vt, &d2i,
992
+ &zero, rotation, &di);
993
+
994
+ }
995
+ pq_regular.train_type = ProductQuantizer::Train_hot_start;
996
+ }
997
+
998
+ // revert A matrix
999
+ if (d > d_in) {
1000
+ for (long i = 0; i < d_out; i++)
1001
+ memmove (&A[i * d_in], &A[i * d], sizeof(A[0]) * d_in);
1002
+ A.resize (d_in * d_out);
1003
+ }
1004
+
1005
+ is_trained = true;
1006
+ is_orthonormal = true;
1007
+ }
1008
+
1009
+
1010
+ /*********************************************
1011
+ * NormalizationTransform
1012
+ *********************************************/
1013
+
1014
+ NormalizationTransform::NormalizationTransform (int d, float norm):
1015
+ VectorTransform (d, d), norm (norm)
1016
+ {
1017
+ }
1018
+
1019
+ NormalizationTransform::NormalizationTransform ():
1020
+ VectorTransform (-1, -1), norm (-1)
1021
+ {
1022
+ }
1023
+
1024
+ void NormalizationTransform::apply_noalloc
1025
+ (idx_t n, const float* x, float* xt) const
1026
+ {
1027
+ if (norm == 2.0) {
1028
+ memcpy (xt, x, sizeof (x[0]) * n * d_in);
1029
+ fvec_renorm_L2 (d_in, n, xt);
1030
+ } else {
1031
+ FAISS_THROW_MSG ("not implemented");
1032
+ }
1033
+ }
1034
+
1035
+ void NormalizationTransform::reverse_transform (idx_t n, const float* xt,
1036
+ float* x) const
1037
+ {
1038
+ memcpy (x, xt, sizeof (xt[0]) * n * d_in);
1039
+ }
1040
+
1041
+ /*********************************************
1042
+ * CenteringTransform
1043
+ *********************************************/
1044
+
1045
+ CenteringTransform::CenteringTransform (int d):
1046
+ VectorTransform (d, d)
1047
+ {
1048
+ is_trained = false;
1049
+ }
1050
+
1051
+ void CenteringTransform::train(Index::idx_t n, const float *x) {
1052
+ FAISS_THROW_IF_NOT_MSG(n > 0, "need at least one training vector");
1053
+ mean.resize (d_in, 0);
1054
+ for (idx_t i = 0; i < n; i++) {
1055
+ for (size_t j = 0; j < d_in; j++) {
1056
+ mean[j] += *x++;
1057
+ }
1058
+ }
1059
+
1060
+ for (size_t j = 0; j < d_in; j++) {
1061
+ mean[j] /= n;
1062
+ }
1063
+ is_trained = true;
1064
+ }
1065
+
1066
+
1067
+ void CenteringTransform::apply_noalloc
1068
+ (idx_t n, const float* x, float* xt) const
1069
+ {
1070
+ FAISS_THROW_IF_NOT (is_trained);
1071
+
1072
+ for (idx_t i = 0; i < n; i++) {
1073
+ for (size_t j = 0; j < d_in; j++) {
1074
+ *xt++ = *x++ - mean[j];
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ void CenteringTransform::reverse_transform (idx_t n, const float* xt,
1080
+ float* x) const
1081
+ {
1082
+ FAISS_THROW_IF_NOT (is_trained);
1083
+
1084
+ for (idx_t i = 0; i < n; i++) {
1085
+ for (size_t j = 0; j < d_in; j++) {
1086
+ *x++ = *xt++ + mean[j];
1087
+ }
1088
+ }
1089
+
1090
+ }
1091
+
1092
+
1093
+
1094
+
1095
+
1096
+ /*********************************************
1097
+ * RemapDimensionsTransform
1098
+ *********************************************/
1099
+
1100
+
1101
+ RemapDimensionsTransform::RemapDimensionsTransform (
1102
+ int d_in, int d_out, const int *map_in):
1103
+ VectorTransform (d_in, d_out)
1104
+ {
1105
+ map.resize (d_out);
1106
+ for (int i = 0; i < d_out; i++) {
1107
+ map[i] = map_in[i];
1108
+ FAISS_THROW_IF_NOT (map[i] == -1 || (map[i] >= 0 && map[i] < d_in));
1109
+ }
1110
+ }
1111
+
1112
+ RemapDimensionsTransform::RemapDimensionsTransform (
1113
+ int d_in, int d_out, bool uniform): VectorTransform (d_in, d_out)
1114
+ {
1115
+ map.resize (d_out, -1);
1116
+
1117
+ if (uniform) {
1118
+ if (d_in < d_out) {
1119
+ for (int i = 0; i < d_in; i++) {
1120
+ map [i * d_out / d_in] = i;
1121
+ }
1122
+ } else {
1123
+ for (int i = 0; i < d_out; i++) {
1124
+ map [i] = i * d_in / d_out;
1125
+ }
1126
+ }
1127
+ } else {
1128
+ for (int i = 0; i < d_in && i < d_out; i++)
1129
+ map [i] = i;
1130
+ }
1131
+ }
1132
+
1133
+
1134
+ void RemapDimensionsTransform::apply_noalloc (idx_t n, const float * x,
1135
+ float *xt) const
1136
+ {
1137
+ for (idx_t i = 0; i < n; i++) {
1138
+ for (int j = 0; j < d_out; j++) {
1139
+ xt[j] = map[j] < 0 ? 0 : x[map[j]];
1140
+ }
1141
+ x += d_in;
1142
+ xt += d_out;
1143
+ }
1144
+ }
1145
+
1146
+ void RemapDimensionsTransform::reverse_transform (idx_t n, const float * xt,
1147
+ float *x) const
1148
+ {
1149
+ memset (x, 0, sizeof (*x) * n * d_in);
1150
+ for (idx_t i = 0; i < n; i++) {
1151
+ for (int j = 0; j < d_out; j++) {
1152
+ if (map[j] >= 0) x[map[j]] = xt[j];
1153
+ }
1154
+ x += d_in;
1155
+ xt += d_out;
1156
+ }
1157
+ }