faiss 0.6.2 → 0.6.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/faiss/version.rb +1 -1
- data/vendor/faiss/faiss/AutoTune.cpp +3 -1
- data/vendor/faiss/faiss/Clustering.cpp +9 -1
- data/vendor/faiss/faiss/IVFlib.cpp +14 -3
- data/vendor/faiss/faiss/Index.h +2 -2
- data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
- data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
- data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +4 -5
- data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +2 -4
- data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
- data/vendor/faiss/faiss/IndexEDEN.h +57 -0
- data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
- data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
- data/vendor/faiss/faiss/IndexHNSW.cpp +10 -11
- data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
- data/vendor/faiss/faiss/IndexIDMap.h +2 -0
- data/vendor/faiss/faiss/IndexIVF.cpp +17 -6
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
- data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
- data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
- data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
- data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
- data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +40 -22
- data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
- data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
- data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
- data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
- data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
- data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
- data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
- data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
- data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
- data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
- data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
- data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
- data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
- data/vendor/faiss/faiss/IndexShards.cpp +2 -2
- data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
- data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
- data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
- data/vendor/faiss/faiss/SuperKMeans.h +30 -0
- data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
- data/vendor/faiss/faiss/clone_index.cpp +5 -0
- data/vendor/faiss/faiss/factory_tools.cpp +47 -4
- data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
- data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
- data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
- data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
- data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
- data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
- data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
- data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
- data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
- data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
- data/vendor/faiss/faiss/impl/HNSW.cpp +109 -152
- data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
- data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
- data/vendor/faiss/faiss/impl/Panorama.h +9 -7
- data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +34 -22
- data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +44 -36
- data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +168 -67
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
- data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
- data/vendor/faiss/faiss/impl/ResultHandler.h +25 -31
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +258 -57
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +20 -0
- data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
- data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
- data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
- data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
- data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
- data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
- data/vendor/faiss/faiss/impl/index_read.cpp +376 -36
- data/vendor/faiss/faiss/impl/index_write.cpp +55 -4
- data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
- data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
- data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
- data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
- data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
- data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
- data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
- data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
- data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +85 -23
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +136 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +16 -16
- data/vendor/faiss/faiss/impl/simd_dispatch.h +30 -9
- data/vendor/faiss/faiss/index_factory.cpp +32 -6
- data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +2 -2
- data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
- data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
- data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
- data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
- data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
- data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +31 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +15 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +1 -2
- data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
- data/vendor/faiss/faiss/utils/distances.cpp +14 -2
- data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
- data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
- data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
- data/vendor/faiss/faiss/utils/hamming.cpp +1 -1
- data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
- data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +92 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
- data/vendor/faiss/faiss/utils/simd_levels.cpp +44 -0
- data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
- data/vendor/faiss/faiss/utils/utils.cpp +9 -27
- metadata +16 -1
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
|
|
27
27
|
#include <faiss/impl/FaissAssert.h>
|
|
28
28
|
|
|
29
|
+
#include <faiss/impl/polysemous_training/dispatch.h>
|
|
30
|
+
|
|
29
31
|
/*****************************************
|
|
30
32
|
* Mixed PQ / Hamming
|
|
31
33
|
******************************************/
|
|
@@ -168,6 +170,94 @@ static inline int hamming_dis(uint64_t a, uint64_t b) {
|
|
|
168
170
|
return popcount64(a ^ b);
|
|
169
171
|
}
|
|
170
172
|
|
|
173
|
+
static inline double sqr(double x) {
|
|
174
|
+
return x * x;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Scalar (NONE) kernels for the two objectives. The dispatch boundary lives in
|
|
178
|
+
// the objective methods below, which route to these or to the AVX-512
|
|
179
|
+
// specializations (in polysemous_training/avx512.cpp) via
|
|
180
|
+
// with_selected_simd_levels.
|
|
181
|
+
namespace polysemous_training {
|
|
182
|
+
|
|
183
|
+
template <>
|
|
184
|
+
double hamming_compute_cost<SIMDLevel::NONE>(
|
|
185
|
+
int n,
|
|
186
|
+
const int* perm,
|
|
187
|
+
const double* target_dis,
|
|
188
|
+
const double* weights) {
|
|
189
|
+
double cost = 0;
|
|
190
|
+
for (int i = 0; i < n; i++) {
|
|
191
|
+
for (int j = 0; j < n; j++) {
|
|
192
|
+
double wanted = target_dis[i * n + j];
|
|
193
|
+
double w = weights[i * n + j];
|
|
194
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
195
|
+
cost += w * sqr(wanted - actual);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return cost;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
template <>
|
|
202
|
+
double hamming_cost_update<SIMDLevel::NONE>(
|
|
203
|
+
int n,
|
|
204
|
+
const int* perm,
|
|
205
|
+
int iw,
|
|
206
|
+
int jw,
|
|
207
|
+
const double* target_dis,
|
|
208
|
+
const double* weights) {
|
|
209
|
+
double delta_cost = 0;
|
|
210
|
+
|
|
211
|
+
for (int i = 0; i < n; i++) {
|
|
212
|
+
if (i == iw) {
|
|
213
|
+
for (int j = 0; j < n; j++) {
|
|
214
|
+
double wanted = target_dis[i * n + j], w = weights[i * n + j];
|
|
215
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
216
|
+
delta_cost -= w * sqr(wanted - actual);
|
|
217
|
+
double new_actual = hamming_dis(
|
|
218
|
+
perm[jw],
|
|
219
|
+
perm[j == iw ? jw
|
|
220
|
+
: j == jw ? iw
|
|
221
|
+
: j]);
|
|
222
|
+
delta_cost += w * sqr(wanted - new_actual);
|
|
223
|
+
}
|
|
224
|
+
} else if (i == jw) {
|
|
225
|
+
for (int j = 0; j < n; j++) {
|
|
226
|
+
double wanted = target_dis[i * n + j], w = weights[i * n + j];
|
|
227
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
228
|
+
delta_cost -= w * sqr(wanted - actual);
|
|
229
|
+
double new_actual = hamming_dis(
|
|
230
|
+
perm[iw],
|
|
231
|
+
perm[j == iw ? jw
|
|
232
|
+
: j == jw ? iw
|
|
233
|
+
: j]);
|
|
234
|
+
delta_cost += w * sqr(wanted - new_actual);
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
int j = iw;
|
|
238
|
+
{
|
|
239
|
+
double wanted = target_dis[i * n + j], w = weights[i * n + j];
|
|
240
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
241
|
+
delta_cost -= w * sqr(wanted - actual);
|
|
242
|
+
double new_actual = hamming_dis(perm[i], perm[jw]);
|
|
243
|
+
delta_cost += w * sqr(wanted - new_actual);
|
|
244
|
+
}
|
|
245
|
+
j = jw;
|
|
246
|
+
{
|
|
247
|
+
double wanted = target_dis[i * n + j], w = weights[i * n + j];
|
|
248
|
+
double actual = hamming_dis(perm[i], perm[j]);
|
|
249
|
+
delta_cost -= w * sqr(wanted - actual);
|
|
250
|
+
double new_actual = hamming_dis(perm[i], perm[iw]);
|
|
251
|
+
delta_cost += w * sqr(wanted - new_actual);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return delta_cost;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
} // namespace polysemous_training
|
|
260
|
+
|
|
171
261
|
namespace {
|
|
172
262
|
|
|
173
263
|
/// optimize permutation to reproduce a distance table with Hamming distances
|
|
@@ -175,10 +265,6 @@ struct ReproduceWithHammingObjective : PermutationObjective {
|
|
|
175
265
|
int nbits;
|
|
176
266
|
double dis_weight_factor;
|
|
177
267
|
|
|
178
|
-
static double sqr(double x) {
|
|
179
|
-
return x * x;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
268
|
// weighting of distances: it is more important to reproduce small
|
|
183
269
|
// distances well
|
|
184
270
|
double dis_weight(double x) const {
|
|
@@ -190,73 +276,21 @@ struct ReproduceWithHammingObjective : PermutationObjective {
|
|
|
190
276
|
|
|
191
277
|
// cost = quadratic difference between actual distance and Hamming distance
|
|
192
278
|
double compute_cost(const int* perm) const override {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
199
|
-
cost += w * sqr(wanted - actual);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return cost;
|
|
279
|
+
return with_selected_simd_levels<polysemous_training::SIMD_LEVELS>(
|
|
280
|
+
[&]<SIMDLevel SL>() {
|
|
281
|
+
return polysemous_training::hamming_compute_cost<SL>(
|
|
282
|
+
n, perm, target_dis.data(), weights.data());
|
|
283
|
+
});
|
|
203
284
|
}
|
|
204
285
|
|
|
205
286
|
// what would the cost update be if iw and jw were swapped?
|
|
206
287
|
// computed in O(n) instead of O(n^2) for the full re-computation
|
|
207
288
|
double cost_update(const int* perm, int iw, int jw) const override {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
double wanted = target_dis[i * n + j],
|
|
214
|
-
w = weights[i * n + j];
|
|
215
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
216
|
-
delta_cost -= w * sqr(wanted - actual);
|
|
217
|
-
double new_actual = hamming_dis(
|
|
218
|
-
perm[jw],
|
|
219
|
-
perm[j == iw ? jw
|
|
220
|
-
: j == jw ? iw
|
|
221
|
-
: j]);
|
|
222
|
-
delta_cost += w * sqr(wanted - new_actual);
|
|
223
|
-
}
|
|
224
|
-
} else if (i == jw) {
|
|
225
|
-
for (int j = 0; j < n; j++) {
|
|
226
|
-
double wanted = target_dis[i * n + j],
|
|
227
|
-
w = weights[i * n + j];
|
|
228
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
229
|
-
delta_cost -= w * sqr(wanted - actual);
|
|
230
|
-
double new_actual = hamming_dis(
|
|
231
|
-
perm[iw],
|
|
232
|
-
perm[j == iw ? jw
|
|
233
|
-
: j == jw ? iw
|
|
234
|
-
: j]);
|
|
235
|
-
delta_cost += w * sqr(wanted - new_actual);
|
|
236
|
-
}
|
|
237
|
-
} else {
|
|
238
|
-
int j = iw;
|
|
239
|
-
{
|
|
240
|
-
double wanted = target_dis[i * n + j],
|
|
241
|
-
w = weights[i * n + j];
|
|
242
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
243
|
-
delta_cost -= w * sqr(wanted - actual);
|
|
244
|
-
double new_actual = hamming_dis(perm[i], perm[jw]);
|
|
245
|
-
delta_cost += w * sqr(wanted - new_actual);
|
|
246
|
-
}
|
|
247
|
-
j = jw;
|
|
248
|
-
{
|
|
249
|
-
double wanted = target_dis[i * n + j],
|
|
250
|
-
w = weights[i * n + j];
|
|
251
|
-
double actual = hamming_dis(perm[i], perm[j]);
|
|
252
|
-
delta_cost -= w * sqr(wanted - actual);
|
|
253
|
-
double new_actual = hamming_dis(perm[i], perm[iw]);
|
|
254
|
-
delta_cost += w * sqr(wanted - new_actual);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return delta_cost;
|
|
289
|
+
return with_selected_simd_levels<polysemous_training::SIMD_LEVELS>(
|
|
290
|
+
[&]<SIMDLevel SL>() {
|
|
291
|
+
return polysemous_training::hamming_cost_update<SL>(
|
|
292
|
+
n, perm, iw, jw, target_dis.data(), weights.data());
|
|
293
|
+
});
|
|
260
294
|
}
|
|
261
295
|
|
|
262
296
|
ReproduceWithHammingObjective(
|
|
@@ -306,14 +340,20 @@ double ReproduceDistancesObjective::get_source_dis(int i, int j) const {
|
|
|
306
340
|
return source_dis[i * n + j];
|
|
307
341
|
}
|
|
308
342
|
|
|
343
|
+
namespace polysemous_training {
|
|
344
|
+
|
|
309
345
|
// cost = quadratic difference between actual distance and Hamming distance
|
|
310
|
-
|
|
346
|
+
template <>
|
|
347
|
+
double distances_compute_cost<SIMDLevel::NONE>(
|
|
348
|
+
const ReproduceDistancesObjective& obj,
|
|
349
|
+
const int* perm) {
|
|
350
|
+
const int n = obj.n;
|
|
311
351
|
double cost = 0;
|
|
312
352
|
for (int i = 0; i < n; i++) {
|
|
313
353
|
for (int j = 0; j < n; j++) {
|
|
314
|
-
double wanted = target_dis[i * n + j];
|
|
315
|
-
double w = weights[i * n + j];
|
|
316
|
-
double actual = get_source_dis(perm[i], perm[j]);
|
|
354
|
+
double wanted = obj.target_dis[i * n + j];
|
|
355
|
+
double w = obj.weights[i * n + j];
|
|
356
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
317
357
|
cost += w * sqr(wanted - actual);
|
|
318
358
|
}
|
|
319
359
|
}
|
|
@@ -322,16 +362,22 @@ double ReproduceDistancesObjective::compute_cost(const int* perm) const {
|
|
|
322
362
|
|
|
323
363
|
// what would the cost update be if iw and jw were swapped?
|
|
324
364
|
// computed in O(n) instead of O(n^2) for the full re-computation
|
|
325
|
-
|
|
326
|
-
|
|
365
|
+
template <>
|
|
366
|
+
double distances_cost_update<SIMDLevel::NONE>(
|
|
367
|
+
const ReproduceDistancesObjective& obj,
|
|
368
|
+
const int* perm,
|
|
369
|
+
int iw,
|
|
370
|
+
int jw) {
|
|
371
|
+
const int n = obj.n;
|
|
327
372
|
double delta_cost = 0;
|
|
328
373
|
for (int i = 0; i < n; i++) {
|
|
329
374
|
if (i == iw) {
|
|
330
375
|
for (int j = 0; j < n; j++) {
|
|
331
|
-
double wanted = target_dis[i * n + j],
|
|
332
|
-
|
|
376
|
+
double wanted = obj.target_dis[i * n + j],
|
|
377
|
+
w = obj.weights[i * n + j];
|
|
378
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
333
379
|
delta_cost -= w * sqr(wanted - actual);
|
|
334
|
-
double new_actual = get_source_dis(
|
|
380
|
+
double new_actual = obj.get_source_dis(
|
|
335
381
|
perm[jw],
|
|
336
382
|
perm[j == iw ? jw
|
|
337
383
|
: j == jw ? iw
|
|
@@ -340,10 +386,11 @@ double ReproduceDistancesObjective::cost_update(const int* perm, int iw, int jw)
|
|
|
340
386
|
}
|
|
341
387
|
} else if (i == jw) {
|
|
342
388
|
for (int j = 0; j < n; j++) {
|
|
343
|
-
double wanted = target_dis[i * n + j],
|
|
344
|
-
|
|
389
|
+
double wanted = obj.target_dis[i * n + j],
|
|
390
|
+
w = obj.weights[i * n + j];
|
|
391
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
345
392
|
delta_cost -= w * sqr(wanted - actual);
|
|
346
|
-
double new_actual = get_source_dis(
|
|
393
|
+
double new_actual = obj.get_source_dis(
|
|
347
394
|
perm[iw],
|
|
348
395
|
perm[j == iw ? jw
|
|
349
396
|
: j == jw ? iw
|
|
@@ -353,18 +400,20 @@ double ReproduceDistancesObjective::cost_update(const int* perm, int iw, int jw)
|
|
|
353
400
|
} else {
|
|
354
401
|
int j = iw;
|
|
355
402
|
{
|
|
356
|
-
double wanted = target_dis[i * n + j],
|
|
357
|
-
|
|
403
|
+
double wanted = obj.target_dis[i * n + j],
|
|
404
|
+
w = obj.weights[i * n + j];
|
|
405
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
358
406
|
delta_cost -= w * sqr(wanted - actual);
|
|
359
|
-
double new_actual = get_source_dis(perm[i], perm[jw]);
|
|
407
|
+
double new_actual = obj.get_source_dis(perm[i], perm[jw]);
|
|
360
408
|
delta_cost += w * sqr(wanted - new_actual);
|
|
361
409
|
}
|
|
362
410
|
j = jw;
|
|
363
411
|
{
|
|
364
|
-
double wanted = target_dis[i * n + j],
|
|
365
|
-
|
|
412
|
+
double wanted = obj.target_dis[i * n + j],
|
|
413
|
+
w = obj.weights[i * n + j];
|
|
414
|
+
double actual = obj.get_source_dis(perm[i], perm[j]);
|
|
366
415
|
delta_cost -= w * sqr(wanted - actual);
|
|
367
|
-
double new_actual = get_source_dis(perm[i], perm[iw]);
|
|
416
|
+
double new_actual = obj.get_source_dis(perm[i], perm[iw]);
|
|
368
417
|
delta_cost += w * sqr(wanted - new_actual);
|
|
369
418
|
}
|
|
370
419
|
}
|
|
@@ -372,6 +421,25 @@ double ReproduceDistancesObjective::cost_update(const int* perm, int iw, int jw)
|
|
|
372
421
|
return delta_cost;
|
|
373
422
|
}
|
|
374
423
|
|
|
424
|
+
} // namespace polysemous_training
|
|
425
|
+
|
|
426
|
+
double ReproduceDistancesObjective::compute_cost(const int* perm) const {
|
|
427
|
+
return with_selected_simd_levels<polysemous_training::SIMD_LEVELS>(
|
|
428
|
+
[&]<SIMDLevel SL>() {
|
|
429
|
+
return polysemous_training::distances_compute_cost<SL>(
|
|
430
|
+
*this, perm);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
double ReproduceDistancesObjective::cost_update(const int* perm, int iw, int jw)
|
|
435
|
+
const {
|
|
436
|
+
return with_selected_simd_levels<polysemous_training::SIMD_LEVELS>(
|
|
437
|
+
[&]<SIMDLevel SL>() {
|
|
438
|
+
return polysemous_training::distances_cost_update<SL>(
|
|
439
|
+
*this, perm, iw, jw);
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
375
443
|
ReproduceDistancesObjective::ReproduceDistancesObjective(
|
|
376
444
|
int n_in,
|
|
377
445
|
const double* source_dis_in,
|
|
@@ -862,32 +862,44 @@ void ProductQuantizer::search_sdc(
|
|
|
862
862
|
size_t k = res->k;
|
|
863
863
|
int64_t nq_signed = nq;
|
|
864
864
|
|
|
865
|
-
#pragma omp parallel
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
865
|
+
#pragma omp parallel
|
|
866
|
+
{
|
|
867
|
+
// One allocation per OMP thread instead of one per query.
|
|
868
|
+
std::vector<const float*> q_row(M);
|
|
869
|
+
#pragma omp for
|
|
870
|
+
for (int64_t i = 0; i < nq_signed; i++) {
|
|
871
|
+
idx_t* heap_ids = res->ids + i * k;
|
|
872
|
+
float* heap_dis = res->val + i * k;
|
|
873
|
+
const uint8_t* qcode = qcodes + i * code_size;
|
|
874
|
+
|
|
875
|
+
if (init_finalize_heap)
|
|
876
|
+
maxheap_heapify(k, heap_dis, heap_ids);
|
|
877
|
+
|
|
878
|
+
// Precompute per-subquantizer row pointers: q_row[m] points to
|
|
879
|
+
// sdc_table[m*ksub^2 + qcode[m]*ksub], eliminating M
|
|
880
|
+
// multiplications and M pointer advances per database vector in the
|
|
881
|
+
// j-loop.
|
|
882
|
+
const float* sdc = sdc_table.data();
|
|
879
883
|
for (size_t m = 0; m < M; m++) {
|
|
880
|
-
|
|
881
|
-
|
|
884
|
+
q_row[m] = sdc + m * (size_t)(ksub * ksub) +
|
|
885
|
+
(size_t)qcode[m] * ksub;
|
|
882
886
|
}
|
|
883
|
-
|
|
884
|
-
|
|
887
|
+
|
|
888
|
+
const uint8_t* bcode = bcodes;
|
|
889
|
+
for (size_t j = 0; j < nb; j++) {
|
|
890
|
+
float dis = 0;
|
|
891
|
+
for (size_t m = 0; m < M; m++) {
|
|
892
|
+
dis += q_row[m][bcode[m]];
|
|
893
|
+
}
|
|
894
|
+
if (dis < heap_dis[0]) {
|
|
895
|
+
maxheap_replace_top(k, heap_dis, heap_ids, dis, j);
|
|
896
|
+
}
|
|
897
|
+
bcode += code_size;
|
|
885
898
|
}
|
|
886
|
-
bcode += code_size;
|
|
887
|
-
}
|
|
888
899
|
|
|
889
|
-
|
|
890
|
-
|
|
900
|
+
if (init_finalize_heap)
|
|
901
|
+
maxheap_reorder(k, heap_dis, heap_ids);
|
|
902
|
+
}
|
|
891
903
|
}
|
|
892
904
|
}
|
|
893
905
|
|
|
@@ -188,46 +188,54 @@ QueryFactorsData compute_query_factors(
|
|
|
188
188
|
|
|
189
189
|
const float inv_d_sqrt = 1.0f / std::sqrt(static_cast<float>(d));
|
|
190
190
|
|
|
191
|
-
// Compute quantization range
|
|
192
|
-
float v_min = std::numeric_limits<float>::max();
|
|
193
|
-
float v_max = std::numeric_limits<float>::lowest();
|
|
194
|
-
|
|
195
191
|
const float* rq = rotated_q.data();
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
v_min = -v_radius;
|
|
200
|
-
v_max = v_radius;
|
|
201
|
-
} else {
|
|
202
|
-
for (size_t i = 0; i < d; i++) {
|
|
203
|
-
const float v_q = rq[i];
|
|
204
|
-
v_min = std::min(v_min, v_q);
|
|
205
|
-
v_max = std::max(v_max, v_q);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Quantize the query
|
|
210
|
-
const uint8_t max_code = (1 << qb) - 1;
|
|
211
|
-
const float delta = (v_max - v_min) / max_code;
|
|
212
|
-
const float inv_delta = 1.0f / delta;
|
|
213
|
-
|
|
214
|
-
rotated_qq.resize(d);
|
|
192
|
+
float v_min;
|
|
193
|
+
float v_max;
|
|
194
|
+
float delta;
|
|
215
195
|
size_t sum_qq = 0;
|
|
216
196
|
int64_t sum2_signed_odd_int = 0;
|
|
217
|
-
|
|
197
|
+
const uint8_t max_code = (1 << qb) - 1;
|
|
198
|
+
rotated_qq.resize(d);
|
|
218
199
|
uint8_t* rqq = rotated_qq.data();
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
200
|
+
|
|
201
|
+
// Select the SIMD implementation once for both range computation and
|
|
202
|
+
// quantization. This function runs once per query/probe pair.
|
|
203
|
+
with_selected_simd_levels<rabitq::RABITQ_QUANTIZATION_SIMD_LEVELS>(
|
|
204
|
+
[&]<SIMDLevel SL>() {
|
|
205
|
+
if (centered) {
|
|
206
|
+
const float z_max = Z_MAX_BY_QB[qb - 1];
|
|
207
|
+
const float v_radius =
|
|
208
|
+
z_max * std::sqrt(query_factors.qr_to_c_L2sqr / d);
|
|
209
|
+
v_min = -v_radius;
|
|
210
|
+
v_max = v_radius;
|
|
211
|
+
} else {
|
|
212
|
+
v_min = std::numeric_limits<float>::max();
|
|
213
|
+
v_max = std::numeric_limits<float>::lowest();
|
|
214
|
+
rabitq::minmax_values<SL>(rq, d, v_min, v_max);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
delta = (v_max - v_min) / max_code;
|
|
218
|
+
// A constant (or zero-norm) query has delta == 0. Preserve the
|
|
219
|
+
// scalar path's centered correction terms while avoiding
|
|
220
|
+
// 0 * inf during quantization.
|
|
221
|
+
if (delta <= 0.0f) {
|
|
222
|
+
memset(rqq, 0, d * sizeof(uint8_t));
|
|
223
|
+
if (centered) {
|
|
224
|
+
sum2_signed_odd_int = int64_t(d) * max_code * max_code;
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
rabitq::quantize_query_values<SL>(
|
|
228
|
+
rq,
|
|
229
|
+
d,
|
|
230
|
+
v_min,
|
|
231
|
+
1.0f / delta,
|
|
232
|
+
max_code,
|
|
233
|
+
centered,
|
|
234
|
+
rqq,
|
|
235
|
+
sum_qq,
|
|
236
|
+
sum2_signed_odd_int);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
231
239
|
|
|
232
240
|
// Compute query factors
|
|
233
241
|
query_factors.c1 = 2 * delta * inv_d_sqrt;
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
#include <faiss/utils/rabitq_simd.h>
|
|
14
14
|
#include <faiss/utils/simd_levels.h>
|
|
15
15
|
#include <algorithm>
|
|
16
|
+
#include <cassert>
|
|
16
17
|
#include <cstddef>
|
|
17
18
|
#include <cstdint>
|
|
18
19
|
#include <cstring>
|
|
@@ -86,6 +87,40 @@ struct QueryFactorsData {
|
|
|
86
87
|
*/
|
|
87
88
|
FAISS_API extern const float Z_MAX_BY_QB[8];
|
|
88
89
|
|
|
90
|
+
/** Fast half-away rounding for non-negative RaBitQ quantization values.
|
|
91
|
+
*
|
|
92
|
+
* Contract: x must be finite, non-NaN, and in [0, 255.5). This is not a
|
|
93
|
+
* bit-exact replacement for roundf within a few ulps below k + 0.5, where the
|
|
94
|
+
* addition may round across the tie. It is intended only for RaBitQ LUT
|
|
95
|
+
* quantization paths where a +/- 1 code difference at those boundaries is
|
|
96
|
+
* acceptable.
|
|
97
|
+
*/
|
|
98
|
+
inline uint8_t round_nonnegative_to_uint8(float x) {
|
|
99
|
+
assert(x == x);
|
|
100
|
+
assert(x >= 0.0f);
|
|
101
|
+
assert(x < 255.5f);
|
|
102
|
+
return rabitq::round_nonnegative_byte_scalar(x);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Same as round_nonnegative_to_uint8 for uint16 RaBitQ bias values. */
|
|
106
|
+
inline uint16_t round_nonnegative_to_uint16(float x) {
|
|
107
|
+
assert(x == x);
|
|
108
|
+
assert(x >= 0.0f);
|
|
109
|
+
assert(x < 65535.5f);
|
|
110
|
+
return static_cast<uint16_t>(static_cast<int>(x + 0.5f));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Fast clamped rounding for query-byte quantization.
|
|
114
|
+
*
|
|
115
|
+
* Contract: x must be non-NaN. Values outside [0, max_code] are clamped before
|
|
116
|
+
* applying the same non-bit-exact rounding used by
|
|
117
|
+
* round_nonnegative_to_uint8().
|
|
118
|
+
*/
|
|
119
|
+
inline uint8_t round_clamped_to_uint8(float x, uint8_t max_code) {
|
|
120
|
+
assert(x == x);
|
|
121
|
+
return rabitq::round_clamped_byte_scalar(x, max_code);
|
|
122
|
+
}
|
|
123
|
+
|
|
89
124
|
/** Compute factors for a single database vector using RaBitQ algorithm.
|
|
90
125
|
* This function consolidates the mathematical logic that was duplicated
|
|
91
126
|
* between IndexRaBitQ and IndexRaBitQFastScan.
|