File: VectorTransform.h

package info (click to toggle)
faiss 1.13.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,228 kB
  • sloc: cpp: 91,727; python: 31,865; sh: 874; ansic: 425; makefile: 41
file content (313 lines) | stat: -rw-r--r-- 9,641 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// -*- c++ -*-

#ifndef FAISS_VECTOR_TRANSFORM_H
#define FAISS_VECTOR_TRANSFORM_H

/** Defines a few objects that apply transformations to a set of
 * vectors Often these are pre-processing steps.
 */

#include <stdint.h>
#include <vector>

#include <faiss/Index.h>

namespace faiss {

/** Any transformation applied on a set of vectors */
struct VectorTransform {
    int d_in;  ///! input dimension
    int d_out; ///! output dimension

    explicit VectorTransform(int d_in = 0, int d_out = 0)
            : d_in(d_in), d_out(d_out), is_trained(true) {}

    /// set if the VectorTransform does not require training, or if
    /// training is done already
    bool is_trained;

    /** Perform training on a representative set of vectors. Does
     * nothing by default.
     *
     * @param n      nb of training vectors
     * @param x      training vectors, size n * d
     */
    virtual void train(idx_t n, const float* x);

    /** apply the transformation and return the result in an allocated pointer
     * @param     n number of vectors to transform
     * @param     x input vectors, size n * d_in
     * @return    output vectors, size n * d_out
     */
    float* apply(idx_t n, const float* x) const;

    /** apply the transformation and return the result in a provided matrix
     * @param     n number of vectors to transform
     * @param     x input vectors, size n * d_in
     * @param    xt output vectors, size n * d_out
     */
    virtual void apply_noalloc(idx_t n, const float* x, float* xt) const = 0;

    /// reverse transformation. May not be implemented or may return
    /// approximate result
    virtual void reverse_transform(idx_t n, const float* xt, float* x) const;

    // check that the two transforms are identical (to merge indexes)
    virtual void check_identical(const VectorTransform& other) const = 0;

    virtual ~VectorTransform() {}
};

/** Generic linear transformation, with bias term applied on output
 * y = A * x + b
 */
struct LinearTransform : VectorTransform {
    bool have_bias; ///! whether to use the bias term

    /// check if matrix A is orthonormal (enables reverse_transform)
    bool is_orthonormal;

    /// Transformation matrix, size d_out * d_in
    std::vector<float> A;

    /// bias vector, size d_out
    std::vector<float> b;

    /// both d_in > d_out and d_out < d_in are supported
    explicit LinearTransform(
            int d_in = 0,
            int d_out = 0,
            bool have_bias = false);

    /// same as apply, but result is pre-allocated
    void apply_noalloc(idx_t n, const float* x, float* xt) const override;

    /// compute x = A^T * (x - b)
    /// is reverse transform if A has orthonormal lines
    void transform_transpose(idx_t n, const float* y, float* x) const;

    /// works only if is_orthonormal
    void reverse_transform(idx_t n, const float* xt, float* x) const override;

    /// compute A^T * A to set the is_orthonormal flag
    void set_is_orthonormal();

    bool verbose;
    void print_if_verbose(
            const char* name,
            const std::vector<double>& mat,
            int n,
            int d) const;

    void check_identical(const VectorTransform& other) const override;

    ~LinearTransform() override {}
};

/// Randomly rotate a set of vectors
struct RandomRotationMatrix : LinearTransform {
    /// both d_in > d_out and d_out < d_in are supported
    RandomRotationMatrix(int d_in, int d_out)
            : LinearTransform(d_in, d_out, false) {}

    /// must be called before the transform is used
    void init(int seed);

    // initializes with an arbitrary seed
    void train(idx_t n, const float* x) override;

    RandomRotationMatrix() {}
};

/** Applies a principal component analysis on a set of vectors,
 *  with optionally whitening and random rotation. */
struct PCAMatrix : LinearTransform {
    /** after transformation the components are multiplied by
     * eigenvalues^eigen_power
     *
     * =0: no whitening
     * =-0.5: full whitening
     */
    float eigen_power;

    /// value added to eigenvalues to avoid division by 0 when whitening
    float epsilon;

    /// random rotation after PCA
    bool random_rotation;

    /// ratio between # training vectors and dimension
    size_t max_points_per_d;

    /// try to distribute output eigenvectors in this many bins
    int balanced_bins;

    /// Mean, size d_in
    std::vector<float> mean;

    /// eigenvalues of covariance matrix (= squared singular values)
    std::vector<float> eigenvalues;

    /// PCA matrix, size d_in * d_in
    std::vector<float> PCAMat;

    // the final matrix is computed after random rotation and/or whitening
    explicit PCAMatrix(
            int d_in = 0,
            int d_out = 0,
            float eigen_power = 0,
            bool random_rotation = false);

    /// train on n vectors. If n < d_in then the eigenvector matrix
    /// will be completed with 0s
    void train(idx_t n, const float* x) override;

    /// copy pre-trained PCA matrix
    void copy_from(const PCAMatrix& other);

    /// called after mean, PCAMat and eigenvalues are computed
    void prepare_Ab();
};

/** ITQ implementation from
 *
 *     Iterative quantization: A procrustean approach to learning binary codes
 *     for large-scale image retrieval,
 *
 * Yunchao Gong, Svetlana Lazebnik, Albert Gordo, Florent Perronnin,
 * PAMI'12.
 */

struct ITQMatrix : LinearTransform {
    int max_iter;
    int seed;

    // force initialization of the rotation (for debugging)
    std::vector<double> init_rotation;

    explicit ITQMatrix(int d = 0);

    void train(idx_t n, const float* x) override;
};

/** The full ITQ transform, including normalizations and PCA transformation
 */
struct ITQTransform : VectorTransform {
    std::vector<float> mean;
    bool do_pca;
    ITQMatrix itq;

    /// max training points per dimension
    int max_train_per_dim;

    // concatenation of PCA + ITQ transformation
    LinearTransform pca_then_itq;

    explicit ITQTransform(int d_in = 0, int d_out = 0, bool do_pca = false);

    void train(idx_t n, const float* x) override;

    void apply_noalloc(idx_t n, const float* x, float* xt) const override;

    void check_identical(const VectorTransform& other) const override;
};

struct ProductQuantizer;

/** Applies a rotation to align the dimensions with a PQ to minimize
 *  the reconstruction error. Can be used before an IndexPQ or an
 *  IndexIVFPQ. The method is the non-parametric version described in:
 *
 * "Optimized Product Quantization for Approximate Nearest Neighbor Search"
 * Tiezheng Ge, Kaiming He, Qifa Ke, Jian Sun, CVPR'13
 *
 */
struct OPQMatrix : LinearTransform {
    int M;               ///< nb of subquantizers
    int niter = 50;      ///< Number of outer training iterations
    int niter_pq = 4;    ///< Number of training iterations for the PQ
    int niter_pq_0 = 40; ///< same, for the first outer iteration

    /// if there are too many training points, resample
    size_t max_train_points = 256 * 256;
    bool verbose = false;

    /// if non-NULL, use this product quantizer for training
    /// should be constructed with (d_out, M, _)
    ProductQuantizer* pq = nullptr;

    /// if d2 != -1, output vectors of this dimension
    explicit OPQMatrix(int d = 0, int M = 1, int d2 = -1);

    void train(idx_t n, const float* x) override;
};

/** remap dimensions for input vectors, possibly inserting 0s
 * strictly speaking this is also a linear transform but we don't want
 * to compute it with matrix multiplies */
struct RemapDimensionsTransform : VectorTransform {
    /// map from output dimension to input, size d_out
    /// -1 -> set output to 0
    std::vector<int> map;

    RemapDimensionsTransform(int d_in, int d_out, const int* map);

    /// remap input to output, skipping or inserting dimensions as needed
    /// if uniform: distribute dimensions uniformly
    /// otherwise just take the d_out first ones.
    RemapDimensionsTransform(int d_in, int d_out, bool uniform = true);

    void apply_noalloc(idx_t n, const float* x, float* xt) const override;

    /// reverse transform correct only when the mapping is a permutation
    void reverse_transform(idx_t n, const float* xt, float* x) const override;

    RemapDimensionsTransform() {}

    void check_identical(const VectorTransform& other) const override;
};

/** per-vector normalization */
struct NormalizationTransform : VectorTransform {
    float norm;

    explicit NormalizationTransform(int d, float norm = 2.0);
    NormalizationTransform();

    void apply_noalloc(idx_t n, const float* x, float* xt) const override;

    /// Identity transform since norm is not revertible
    void reverse_transform(idx_t n, const float* xt, float* x) const override;

    void check_identical(const VectorTransform& other) const override;
};

/** Subtract the mean of each component from the vectors. */
struct CenteringTransform : VectorTransform {
    /// Mean, size d_in = d_out
    std::vector<float> mean;

    explicit CenteringTransform(int d = 0);

    /// train on n vectors.
    void train(idx_t n, const float* x) override;

    /// subtract the mean
    void apply_noalloc(idx_t n, const float* x, float* xt) const override;

    /// add the mean
    void reverse_transform(idx_t n, const float* xt, float* x) const override;

    void check_identical(const VectorTransform& other) const override;
};

} // namespace faiss

#endif