Coverage Report

Created: 2026-03-31 18:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
contrib/faiss/faiss/impl/ProductQuantizer.h
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and 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_PRODUCT_QUANTIZER_H
11
#define FAISS_PRODUCT_QUANTIZER_H
12
13
#include <stdint.h>
14
15
#include <vector>
16
17
#include <faiss/Clustering.h>
18
#include <faiss/impl/Quantizer.h>
19
#include <faiss/impl/platform_macros.h>
20
#include <faiss/utils/Heap.h>
21
22
namespace faiss {
23
24
/** Product Quantizer.
25
 * PQ is trained using k-means, minimizing the L2 distance to centroids.
26
 * PQ supports L2 and Inner Product search, however the quantization error is
27
 * biased towards L2 distance.
28
 */
29
struct ProductQuantizer : Quantizer {
30
    size_t M;     ///< number of subquantizers
31
    size_t nbits; ///< number of bits per quantization index
32
33
    // values derived from the above
34
    size_t dsub;  ///< dimensionality of each subvector
35
    size_t ksub;  ///< number of centroids for each subquantizer
36
    bool verbose; ///< verbose during training?
37
38
    /// initialization
39
    enum train_type_t {
40
        Train_default,
41
        Train_hot_start,     ///< the centroids are already initialized
42
        Train_shared,        ///< share dictionary across PQ segments
43
        Train_hypercube,     ///< initialize centroids with nbits-D hypercube
44
        Train_hypercube_pca, ///< initialize centroids with nbits-D hypercube
45
    };
46
    train_type_t train_type;
47
48
    ClusteringParameters cp; ///< parameters used during clustering
49
50
    /// if non-NULL, use this index for assignment (should be of size
51
    /// d / M)
52
    Index* assign_index;
53
54
    /// Centroid table, size M * ksub * dsub.
55
    /// Layout: (M, ksub, dsub)
56
    std::vector<float> centroids;
57
58
    /// Transposed centroid table, size M * ksub * dsub.
59
    /// Layout: (dsub, M, ksub)
60
    std::vector<float> transposed_centroids;
61
62
    /// Squared lengths of centroids, size M * ksub
63
    /// Layout: (M, ksub)
64
    std::vector<float> centroids_sq_lengths;
65
66
    /// return the centroids associated with subvector m
67
0
    float* get_centroids(size_t m, size_t i) {
68
0
        return &centroids[(m * ksub + i) * dsub];
69
0
    }
70
16
    const float* get_centroids(size_t m, size_t i) const {
71
16
        return &centroids[(m * ksub + i) * dsub];
72
16
    }
73
74
    // Train the product quantizer on a set of points. A clustering
75
    // can be set on input to define non-default clustering parameters
76
    void train(size_t n, const float* x) override;
77
78
    ProductQuantizer(
79
            size_t d,      /* dimensionality of the input vectors */
80
            size_t M,      /* number of subquantizers */
81
            size_t nbits); /* number of bit per subvector index */
82
83
    ProductQuantizer();
84
85
    /// compute derived values when d, M and nbits have been set
86
    void set_derived_values();
87
88
    /// Define the centroids for subquantizer m
89
    void set_params(const float* centroids, int m);
90
91
    /// Quantize one vector with the product quantizer
92
    void compute_code(const float* x, uint8_t* code) const;
93
94
    /// same as compute_code for several vectors
95
    void compute_codes(const float* x, uint8_t* codes, size_t n) const override;
96
97
    /// speed up code assignment using assign_index
98
    /// (non-const because the index is changed)
99
    void compute_codes_with_assign_index(
100
            const float* x,
101
            uint8_t* codes,
102
            size_t n);
103
104
    /// decode a vector from a given code (or n vectors if third argument)
105
    void decode(const uint8_t* code, float* x) const;
106
    void decode(const uint8_t* code, float* x, size_t n) const override;
107
108
    /// If we happen to have the distance tables precomputed, this is
109
    /// more efficient to compute the codes.
110
    void compute_code_from_distance_table(const float* tab, uint8_t* code)
111
            const;
112
113
    /** Compute distance table for one vector.
114
     *
115
     * The distance table for x = [x_0 x_1 .. x_(M-1)] is a M * ksub
116
     * matrix that contains
117
     *
118
     *   dis_table (m, j) = || x_m - c_(m, j)||^2
119
     *   for m = 0..M-1 and j = 0 .. ksub - 1
120
     *
121
     * where c_(m, j) is the centroid no j of sub-quantizer m.
122
     *
123
     * @param x         input vector size d
124
     * @param dis_table output table, size M * ksub
125
     */
126
    void compute_distance_table(const float* x, float* dis_table) const;
127
128
    void compute_inner_prod_table(const float* x, float* dis_table) const;
129
130
    /** compute distance table for several vectors
131
     * @param nx        nb of input vectors
132
     * @param x         input vector size nx * d
133
     * @param dis_table output table, size nx * M * ksub
134
     */
135
    void compute_distance_tables(size_t nx, const float* x, float* dis_tables)
136
            const;
137
138
    void compute_inner_prod_tables(size_t nx, const float* x, float* dis_tables)
139
            const;
140
141
    /** perform a search (L2 distance)
142
     * @param x        query vectors, size nx * d
143
     * @param nx       nb of queries
144
     * @param codes    database codes, size ncodes * code_size
145
     * @param ncodes   nb of nb vectors
146
     * @param res      heap array to store results (nh == nx)
147
     * @param init_finalize_heap  initialize heap (input) and sort (output)?
148
     */
149
    void search(
150
            const float* x,
151
            size_t nx,
152
            const uint8_t* codes,
153
            const size_t ncodes,
154
            float_maxheap_array_t* res,
155
            bool init_finalize_heap = true) const;
156
157
    /** same as search, but with inner product similarity */
158
    void search_ip(
159
            const float* x,
160
            size_t nx,
161
            const uint8_t* codes,
162
            const size_t ncodes,
163
            float_minheap_array_t* res,
164
            bool init_finalize_heap = true) const;
165
166
    /// Symmetric Distance Table
167
    std::vector<float> sdc_table;
168
169
    // intitialize the SDC table from the centroids
170
    void compute_sdc_table();
171
172
    void search_sdc(
173
            const uint8_t* qcodes,
174
            size_t nq,
175
            const uint8_t* bcodes,
176
            const size_t ncodes,
177
            float_maxheap_array_t* res,
178
            bool init_finalize_heap = true) const;
179
180
    /// Sync transposed centroids with regular centroids. This call
181
    /// is needed if centroids were edited directly.
182
    void sync_transposed_centroids();
183
184
    /// Clear transposed centroids table so ones are no longer used.
185
    void clear_transposed_centroids();
186
};
187
188
// block size used in ProductQuantizer::compute_codes
189
FAISS_API extern int product_quantizer_compute_codes_bs;
190
191
/*************************************************
192
 * Objects to encode / decode strings of bits
193
 *************************************************/
194
195
struct PQEncoderGeneric {
196
    uint8_t* code; ///< code for this vector
197
    uint8_t offset;
198
    const int nbits; ///< number of bits per subquantizer index
199
200
    uint8_t reg;
201
202
    PQEncoderGeneric(uint8_t* code, int nbits, uint8_t offset = 0);
203
204
    void encode(uint64_t x);
205
206
    ~PQEncoderGeneric();
207
};
208
209
struct PQEncoder8 {
210
    uint8_t* code;
211
    PQEncoder8(uint8_t* code, int nbits);
212
    void encode(uint64_t x);
213
};
214
215
struct PQEncoder16 {
216
    uint16_t* code;
217
    PQEncoder16(uint8_t* code, int nbits);
218
    void encode(uint64_t x);
219
};
220
221
struct PQDecoderGeneric {
222
    const uint8_t* code;
223
    uint8_t offset;
224
    const int nbits;
225
    const uint64_t mask;
226
    uint8_t reg;
227
    PQDecoderGeneric(const uint8_t* code, int nbits);
228
    uint64_t decode();
229
};
230
231
struct PQDecoder8 {
232
    static const int nbits = 8;
233
    const uint8_t* code;
234
    PQDecoder8(const uint8_t* code, int nbits);
235
    uint64_t decode();
236
};
237
238
struct PQDecoder16 {
239
    static const int nbits = 16;
240
    const uint16_t* code;
241
    PQDecoder16(const uint8_t* code, int nbits);
242
    uint64_t decode();
243
};
244
245
} // namespace faiss
246
247
#include <faiss/impl/ProductQuantizer-inl.h>
248
249
#endif