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
|
@@ -65,23 +65,14 @@ struct TrainState {
|
|
|
65
65
|
int n = 0;
|
|
66
66
|
std::vector<float> Y_tilde; // (k, d) row-major
|
|
67
67
|
|
|
68
|
-
std::vector<
|
|
69
|
-
std::vector<float> best_dists;
|
|
70
|
-
|
|
71
|
-
/// ||X_tilde[i, 0:d_prime]||^2; recomputed when d_prime changes.
|
|
72
|
-
std::vector<float> x_norms_partial;
|
|
68
|
+
std::vector<int32_t> assignments; // size n
|
|
69
|
+
std::vector<float> best_dists; // size n; tau per vector
|
|
73
70
|
|
|
74
71
|
int d_prime = 0;
|
|
75
72
|
|
|
76
73
|
/// ADSampling threshold table; size d+1.
|
|
77
74
|
std::vector<float> ad_coeff;
|
|
78
75
|
|
|
79
|
-
/// PDX block layout for the trailing pruning sweep: block b covers
|
|
80
|
-
/// original dims [true_block_end[b] - block_dim[b], true_block_end[b]).
|
|
81
|
-
/// Recomputed when d_prime changes.
|
|
82
|
-
std::vector<int> block_dim;
|
|
83
|
-
std::vector<int> true_block_end;
|
|
84
|
-
|
|
85
76
|
/// Counter for the verbose-mode "low pruning" warning.
|
|
86
77
|
int low_pruning_streak = 0;
|
|
87
78
|
bool low_pruning_warning_printed = false;
|
|
@@ -89,38 +80,34 @@ struct TrainState {
|
|
|
89
80
|
explicit TrainState(int d) : R(d, d) {}
|
|
90
81
|
};
|
|
91
82
|
|
|
92
|
-
///
|
|
93
|
-
///
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
83
|
+
/// PDX block layout for the trailing pruning sweep: block b covers original
|
|
84
|
+
/// dims [true_block_end[b] - block_dim[b], true_block_end[b]).
|
|
85
|
+
struct LocalBlockLayout {
|
|
86
|
+
std::vector<int> block_dim;
|
|
87
|
+
std::vector<int> true_block_end;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
LocalBlockLayout build_pdx_block_layout(
|
|
91
|
+
int d,
|
|
92
|
+
int d_prime,
|
|
93
|
+
int pdx_block_size) {
|
|
94
|
+
LocalBlockLayout layout;
|
|
95
|
+
const int d_trail = d - d_prime;
|
|
97
96
|
const int n_full_blocks = d_trail / pdx_block_size;
|
|
98
97
|
const int tail = d_trail % pdx_block_size;
|
|
99
98
|
const int n_blocks = n_full_blocks + (tail > 0 ? 1 : 0);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (tail > 0) {
|
|
109
|
-
state.block_dim[n_full_blocks] = tail;
|
|
110
|
-
state.true_block_end[n_full_blocks] = d;
|
|
111
|
-
}
|
|
99
|
+
layout.block_dim.assign(n_blocks, pdx_block_size);
|
|
100
|
+
layout.true_block_end.resize(n_blocks);
|
|
101
|
+
for (int b = 0; b < n_full_blocks; ++b) {
|
|
102
|
+
layout.true_block_end[b] = d_prime + (b + 1) * pdx_block_size;
|
|
103
|
+
}
|
|
104
|
+
if (tail > 0) {
|
|
105
|
+
layout.block_dim[n_full_blocks] = tail;
|
|
106
|
+
layout.true_block_end[n_full_blocks] = d;
|
|
112
107
|
}
|
|
108
|
+
return layout;
|
|
113
109
|
}
|
|
114
110
|
|
|
115
|
-
struct IterScratch {
|
|
116
|
-
std::vector<float> partial_ip; // (bx_max, by_max) for the GEMM tile
|
|
117
|
-
std::vector<float> Y_pdx; // PDX-laid-out trailing block
|
|
118
|
-
std::vector<float> Y_trail; // row-major (k, d_trail) input to pdxify
|
|
119
|
-
std::vector<float> y_norms_partial; // ||Y_tilde[j, 0:dp]||^2
|
|
120
|
-
std::vector<int64_t> labels64; // size n; widened state.assignments
|
|
121
|
-
int prev_d_trail = -1;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
111
|
/// Iter 0: full GEMM via knn_L2sqr (vanilla Lloyd's). Fills
|
|
125
112
|
/// state.assignments and state.best_dists. Returns objective.
|
|
126
113
|
double run_iter0_full_gemm(int d, int k, TrainState& state) {
|
|
@@ -148,183 +135,56 @@ double run_iter0_full_gemm(int d, int k, TrainState& state) {
|
|
|
148
135
|
return objective;
|
|
149
136
|
}
|
|
150
137
|
|
|
151
|
-
/// Iter 1+:
|
|
152
|
-
///
|
|
153
|
-
/// state.assignments and state.best_dists
|
|
154
|
-
/// pruned_at_gemm
|
|
138
|
+
/// Iter 1+: refresh tau, then run one shared super_kmeans_assign_iteration
|
|
139
|
+
/// pass (partial GEMM + ADSampling progressive pruning). Updates
|
|
140
|
+
/// state.assignments and state.best_dists, writes total_pairs and
|
|
141
|
+
/// pruned_at_gemm, and returns the objective.
|
|
155
142
|
double run_iter_pruned(
|
|
156
143
|
int d,
|
|
157
144
|
int k,
|
|
158
145
|
const SuperKMeansParameters& cp,
|
|
159
146
|
TrainState& state,
|
|
160
|
-
|
|
147
|
+
SuperKMeansAssignScratch& assign_scratch,
|
|
161
148
|
int64_t& total_pairs,
|
|
162
149
|
int64_t& pruned_at_gemm) {
|
|
163
|
-
const int dp = state.d_prime;
|
|
164
|
-
assert(dp >= 1);
|
|
165
|
-
assert(!state.ad_coeff.empty());
|
|
166
|
-
assert(!scratch.partial_ip.empty());
|
|
167
|
-
assert(!scratch.y_norms_partial.empty());
|
|
168
|
-
const int d_trail = d - dp;
|
|
169
150
|
const int n_train = state.n;
|
|
151
|
+
assert(state.d_prime >= 1);
|
|
152
|
+
assert(!state.ad_coeff.empty());
|
|
170
153
|
assert(static_cast<int>(state.best_dists.size()) >= n_train);
|
|
171
|
-
assert(static_cast<int>(state.x_norms_partial.size()) >= n_train);
|
|
172
154
|
assert(static_cast<int>(state.assignments.size()) >= n_train);
|
|
173
155
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
scratch.prev_d_trail = d_trail;
|
|
178
|
-
}
|
|
179
|
-
for (int j = 0; j < k; ++j) {
|
|
180
|
-
std::memcpy(
|
|
181
|
-
scratch.Y_trail.data() + static_cast<size_t>(j) * d_trail,
|
|
182
|
-
state.Y_tilde.data() + static_cast<size_t>(j) * d + dp,
|
|
183
|
-
d_trail * sizeof(float));
|
|
184
|
-
}
|
|
185
|
-
detail::pdxify(
|
|
186
|
-
scratch.Y_trail.data(),
|
|
187
|
-
k,
|
|
188
|
-
d_trail,
|
|
189
|
-
cp.pdx_block_size,
|
|
190
|
-
scratch.Y_pdx.data());
|
|
191
|
-
|
|
192
|
-
detail::compute_partial_norms(
|
|
193
|
-
state.Y_tilde.data(), k, d, dp, scratch.y_norms_partial.data());
|
|
194
|
-
|
|
195
|
-
const int n_blocks = static_cast<int>(state.block_dim.size());
|
|
196
|
-
|
|
197
|
-
for (int xi = 0; xi < n_train; xi += cp.x_batch) {
|
|
198
|
-
const int bx = std::min(cp.x_batch, n_train - xi);
|
|
199
|
-
|
|
200
|
-
// Refresh tau: recompute full-d L2 distance to the previously
|
|
201
|
-
// assigned centroid. This is intentionally over all d dims (not
|
|
202
|
-
// just d_prime) because tau must be an exact distance for the
|
|
203
|
-
// chi-squared pruning bound to be valid. Cost is O(bx * d) per
|
|
204
|
-
// x-batch, amortized across the y-batch tiles that follow.
|
|
156
|
+
// Refresh tau: exact full-d L2 distance to the currently assigned centroid.
|
|
157
|
+
// super_kmeans_assign_iteration requires an exact tau (the chi-squared
|
|
158
|
+
// bound assumes it), and centroids moved since the previous assignment.
|
|
205
159
|
#pragma omp parallel for
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
tau += diff * diff;
|
|
216
|
-
}
|
|
217
|
-
state.best_dists[xi + i] = tau;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
for (int yj = 0; yj < k; yj += cp.y_batch) {
|
|
221
|
-
const int by = std::min(cp.y_batch, k - yj);
|
|
222
|
-
|
|
223
|
-
// GEMM phase: column-major sgemm computes
|
|
224
|
-
// partial_ip[i*by + j] = <X[xi+i, 0:dp], Y[yj+j, 0:dp]>.
|
|
225
|
-
{
|
|
226
|
-
FINTEGER M = by;
|
|
227
|
-
FINTEGER N_ = bx;
|
|
228
|
-
FINTEGER K_ = dp;
|
|
229
|
-
float alpha = 1.0f;
|
|
230
|
-
float beta = 0.0f;
|
|
231
|
-
FINTEGER lda_y = d;
|
|
232
|
-
FINTEGER lda_x = d;
|
|
233
|
-
FINTEGER ldc = by;
|
|
234
|
-
sgemm_("Transpose",
|
|
235
|
-
"Not transpose",
|
|
236
|
-
&M,
|
|
237
|
-
&N_,
|
|
238
|
-
&K_,
|
|
239
|
-
&alpha,
|
|
240
|
-
state.Y_tilde.data() + static_cast<size_t>(yj) * d,
|
|
241
|
-
&lda_y,
|
|
242
|
-
state.X_tilde.data() + static_cast<size_t>(xi) * d,
|
|
243
|
-
&lda_x,
|
|
244
|
-
&beta,
|
|
245
|
-
scratch.partial_ip.data(),
|
|
246
|
-
&ldc);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// One SIMD dispatch per (xi, yj) tile — block_l2<SL> below is
|
|
250
|
-
// a direct call (no per-call switch on SIMDConfig::level).
|
|
251
|
-
with_simd_level([&]<SIMDLevel SL>() {
|
|
252
|
-
[[maybe_unused]] const int omp_chunk_local = cp.omp_chunk;
|
|
253
|
-
int64_t total_pairs_local = 0;
|
|
254
|
-
int64_t pruned_at_gemm_local = 0;
|
|
255
|
-
#pragma omp parallel for schedule(dynamic, omp_chunk_local) \
|
|
256
|
-
reduction(+ : total_pairs_local) reduction(+ : pruned_at_gemm_local)
|
|
257
|
-
for (int i = 0; i < bx; ++i) {
|
|
258
|
-
// tau is the best full-d distance found so far for this
|
|
259
|
-
// point; tightened as closer centroids are found.
|
|
260
|
-
float tau = state.best_dists[xi + i];
|
|
261
|
-
int best_j = state.assignments[xi + i];
|
|
262
|
-
const float xnp_i = state.x_norms_partial[xi + i];
|
|
263
|
-
const float* xrow = state.X_tilde.data() +
|
|
264
|
-
static_cast<size_t>(xi + i) * d;
|
|
265
|
-
|
|
266
|
-
for (int j = 0; j < by; ++j) {
|
|
267
|
-
++total_pairs_local;
|
|
268
|
-
|
|
269
|
-
// L2-from-IP; clamp to handle catastrophic
|
|
270
|
-
// cancellation when the true distance is ~0.
|
|
271
|
-
float pd = xnp_i + scratch.y_norms_partial[yj + j] -
|
|
272
|
-
2.0f *
|
|
273
|
-
scratch.partial_ip
|
|
274
|
-
[static_cast<size_t>(i) * by +
|
|
275
|
-
j];
|
|
276
|
-
if (pd < 0.0f) {
|
|
277
|
-
pd = 0.0f;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (pd > state.ad_coeff[dp] * tau) {
|
|
281
|
-
++pruned_at_gemm_local;
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// double accumulator mitigates float drift over many
|
|
286
|
-
// block additions.
|
|
287
|
-
double dist = pd;
|
|
288
|
-
bool keep = true;
|
|
289
|
-
|
|
290
|
-
// Progressive pruning across PDX blocks. Per block:
|
|
291
|
-
// stride = k * block_dim[b] floats, column-major
|
|
292
|
-
// across centroids.
|
|
293
|
-
size_t pdx_offset = 0;
|
|
294
|
-
for (int b = 0; b < n_blocks; ++b) {
|
|
295
|
-
const int n_in_block = state.block_dim.at(b);
|
|
296
|
-
const int true_end = state.true_block_end.at(b);
|
|
297
|
-
const float* xblk = xrow + (true_end - n_in_block);
|
|
298
|
-
const float* yblk = scratch.Y_pdx.data() +
|
|
299
|
-
pdx_offset +
|
|
300
|
-
static_cast<size_t>(yj + j) * n_in_block;
|
|
301
|
-
dist += faiss::detail::block_l2<SL>(
|
|
302
|
-
xblk, yblk, n_in_block);
|
|
303
|
-
pdx_offset += static_cast<size_t>(k) * n_in_block;
|
|
304
|
-
|
|
305
|
-
if (dist >
|
|
306
|
-
static_cast<double>(state.ad_coeff[true_end]) *
|
|
307
|
-
tau) {
|
|
308
|
-
keep = false;
|
|
309
|
-
break;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (keep && dist < tau) {
|
|
314
|
-
tau = static_cast<float>(dist);
|
|
315
|
-
best_j = yj + j;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
state.best_dists[xi + i] = tau;
|
|
320
|
-
state.assignments[xi + i] = best_j;
|
|
321
|
-
}
|
|
322
|
-
total_pairs += total_pairs_local;
|
|
323
|
-
pruned_at_gemm += pruned_at_gemm_local;
|
|
324
|
-
});
|
|
160
|
+
for (int i = 0; i < n_train; ++i) {
|
|
161
|
+
const int j_prev = state.assignments[i];
|
|
162
|
+
const float* xrow = state.X_tilde.data() + static_cast<size_t>(i) * d;
|
|
163
|
+
const float* yrow =
|
|
164
|
+
state.Y_tilde.data() + static_cast<size_t>(j_prev) * d;
|
|
165
|
+
float tau = 0.0f;
|
|
166
|
+
for (int m = 0; m < d; ++m) {
|
|
167
|
+
const float diff = xrow[m] - yrow[m];
|
|
168
|
+
tau += diff * diff;
|
|
325
169
|
}
|
|
170
|
+
state.best_dists[i] = tau;
|
|
326
171
|
}
|
|
327
172
|
|
|
173
|
+
super_kmeans_assign_iteration(
|
|
174
|
+
state.X_tilde.data(),
|
|
175
|
+
n_train,
|
|
176
|
+
d,
|
|
177
|
+
state.Y_tilde.data(),
|
|
178
|
+
k,
|
|
179
|
+
state.best_dists.data(),
|
|
180
|
+
state.assignments.data(),
|
|
181
|
+
state.d_prime,
|
|
182
|
+
state.ad_coeff.data(),
|
|
183
|
+
cp,
|
|
184
|
+
&total_pairs,
|
|
185
|
+
&pruned_at_gemm,
|
|
186
|
+
&assign_scratch);
|
|
187
|
+
|
|
328
188
|
double objective = 0.0;
|
|
329
189
|
for (int i = 0; i < n_train; ++i) {
|
|
330
190
|
objective += state.best_dists[i];
|
|
@@ -337,13 +197,13 @@ int update_centroids_and_split(
|
|
|
337
197
|
int d,
|
|
338
198
|
int k,
|
|
339
199
|
TrainState& state,
|
|
340
|
-
|
|
200
|
+
std::vector<int64_t>& labels64,
|
|
341
201
|
std::vector<float>& hassign) {
|
|
342
202
|
std::fill(hassign.begin(), hassign.end(), 0.0f);
|
|
343
|
-
assert(!
|
|
203
|
+
assert(!labels64.empty());
|
|
344
204
|
assert(!state.assignments.empty());
|
|
345
205
|
for (int i = 0; i < state.n; ++i) {
|
|
346
|
-
|
|
206
|
+
labels64[i] = static_cast<int64_t>(state.assignments[i]);
|
|
347
207
|
}
|
|
348
208
|
detail::compute_centroids(
|
|
349
209
|
d,
|
|
@@ -352,7 +212,7 @@ int update_centroids_and_split(
|
|
|
352
212
|
/*k_frozen=*/0,
|
|
353
213
|
reinterpret_cast<const uint8_t*>(state.X_tilde.data()),
|
|
354
214
|
/*codec=*/nullptr,
|
|
355
|
-
|
|
215
|
+
labels64.data(),
|
|
356
216
|
/*weights=*/nullptr,
|
|
357
217
|
hassign.data(),
|
|
358
218
|
state.Y_tilde.data());
|
|
@@ -368,9 +228,8 @@ int update_centroids_and_split(
|
|
|
368
228
|
state.Y_tilde.data());
|
|
369
229
|
}
|
|
370
230
|
|
|
371
|
-
/// Stay-in-band controller: nudge state.d_prime based on observed
|
|
372
|
-
/// pruning rate
|
|
373
|
-
/// the observed pruning rate (0 when there were no pairs).
|
|
231
|
+
/// Stay-in-band controller: nudge state.d_prime based on observed pruning
|
|
232
|
+
/// rate. Returns the observed pruning rate (0 when there were no pairs).
|
|
374
233
|
float adapt_d_prime(
|
|
375
234
|
int d,
|
|
376
235
|
const SuperKMeansParameters& cp,
|
|
@@ -392,25 +251,16 @@ float adapt_d_prime(
|
|
|
392
251
|
}
|
|
393
252
|
new_dp = std::max(cp.d_prime_min, new_dp);
|
|
394
253
|
new_dp = std::min(d / 2, new_dp);
|
|
395
|
-
|
|
396
|
-
state.d_prime = new_dp;
|
|
397
|
-
detail::compute_partial_norms(
|
|
398
|
-
state.X_tilde.data(),
|
|
399
|
-
state.n,
|
|
400
|
-
d,
|
|
401
|
-
state.d_prime,
|
|
402
|
-
state.x_norms_partial.data());
|
|
403
|
-
rebuild_pdx_block_layout(d, cp.pdx_block_size, state);
|
|
404
|
-
}
|
|
254
|
+
state.d_prime = new_dp;
|
|
405
255
|
return pruning_rate;
|
|
406
256
|
}
|
|
407
257
|
|
|
408
258
|
/// Pre-loop setup: subsample, rotate, Forgy init, build ADSampling table,
|
|
409
|
-
///
|
|
410
|
-
/// alive when subsampling occurred (otherwise empty).
|
|
259
|
+
/// size the label scratch. Returned `sampled_x_owner` keeps the subsampled
|
|
260
|
+
/// buffer alive when subsampling occurred (otherwise empty).
|
|
411
261
|
std::unique_ptr<uint8_t[]> setup_train_state(
|
|
412
262
|
TrainState& state,
|
|
413
|
-
|
|
263
|
+
std::vector<int64_t>& labels64,
|
|
414
264
|
std::vector<float>& hassign,
|
|
415
265
|
const SuperKMeansParameters& cp,
|
|
416
266
|
int d,
|
|
@@ -468,17 +318,6 @@ std::unique_ptr<uint8_t[]> setup_train_state(
|
|
|
468
318
|
state.d_prime =
|
|
469
319
|
std::max(cp.d_prime_min, static_cast<int>(d * cp.d_prime_fraction));
|
|
470
320
|
state.d_prime = std::min(state.d_prime, d / 2);
|
|
471
|
-
rebuild_pdx_block_layout(d, cp.pdx_block_size, state);
|
|
472
|
-
|
|
473
|
-
// Iter 1+ uses L2-from-IP only over [0, d_prime), so full ||X[i]||^2 is
|
|
474
|
-
// never read; iter 0 routes through knn_L2sqr which carries its own.
|
|
475
|
-
state.x_norms_partial.resize(state.n);
|
|
476
|
-
detail::compute_partial_norms(
|
|
477
|
-
state.X_tilde.data(),
|
|
478
|
-
state.n,
|
|
479
|
-
d,
|
|
480
|
-
state.d_prime,
|
|
481
|
-
state.x_norms_partial.data());
|
|
482
321
|
|
|
483
322
|
const double epsilon = static_cast<double>(cp.ad_epsilon_factor) / d;
|
|
484
323
|
state.ad_coeff = detail::precompute_ad_thresholds(d, epsilon);
|
|
@@ -491,11 +330,7 @@ std::unique_ptr<uint8_t[]> setup_train_state(
|
|
|
491
330
|
|
|
492
331
|
hassign.assign(k, 0.0f);
|
|
493
332
|
|
|
494
|
-
|
|
495
|
-
const int bx_max = std::min(cp.x_batch, state.n);
|
|
496
|
-
scratch.partial_ip.resize(static_cast<size_t>(bx_max) * by_max);
|
|
497
|
-
scratch.y_norms_partial.resize(k);
|
|
498
|
-
scratch.labels64.resize(state.n);
|
|
333
|
+
labels64.resize(state.n);
|
|
499
334
|
|
|
500
335
|
return sampled_x_owner;
|
|
501
336
|
}
|
|
@@ -557,7 +392,7 @@ SuperKMeans::SuperKMeans(int d, int k, const SuperKMeansParameters& cp_in)
|
|
|
557
392
|
|
|
558
393
|
void SuperKMeans::train(idx_t n, const float* x) {
|
|
559
394
|
FAISS_THROW_IF_NOT_MSG(n > 0, "SuperKMeans: n must be positive");
|
|
560
|
-
|
|
395
|
+
FAISS_THROW_IF_MSG(x == nullptr, "SuperKMeans: x must not be null");
|
|
561
396
|
FAISS_THROW_IF_NOT_MSG(
|
|
562
397
|
n >= static_cast<idx_t>(k), "SuperKMeans: n must be >= k");
|
|
563
398
|
if (cp.check_input_data_for_NaNs) {
|
|
@@ -577,10 +412,11 @@ void SuperKMeans::train(idx_t n, const float* x) {
|
|
|
577
412
|
}
|
|
578
413
|
|
|
579
414
|
TrainState state(d);
|
|
580
|
-
|
|
415
|
+
std::vector<int64_t> labels64;
|
|
416
|
+
SuperKMeansAssignScratch assign_scratch;
|
|
581
417
|
std::vector<float> hassign;
|
|
582
418
|
[[maybe_unused]] auto sampled_x_owner =
|
|
583
|
-
setup_train_state(state,
|
|
419
|
+
setup_train_state(state, labels64, hassign, cp, d, k, n, x);
|
|
584
420
|
|
|
585
421
|
iteration_stats.clear();
|
|
586
422
|
iteration_stats.reserve(cp.niter);
|
|
@@ -599,11 +435,17 @@ void SuperKMeans::train(idx_t n, const float* x) {
|
|
|
599
435
|
objective = run_iter0_full_gemm(d, k, state);
|
|
600
436
|
} else {
|
|
601
437
|
objective = run_iter_pruned(
|
|
602
|
-
d,
|
|
438
|
+
d,
|
|
439
|
+
k,
|
|
440
|
+
cp,
|
|
441
|
+
state,
|
|
442
|
+
assign_scratch,
|
|
443
|
+
total_pairs,
|
|
444
|
+
pruned_at_gemm);
|
|
603
445
|
}
|
|
604
446
|
|
|
605
447
|
const int nsplit =
|
|
606
|
-
update_centroids_and_split(d, k, state,
|
|
448
|
+
update_centroids_and_split(d, k, state, labels64, hassign);
|
|
607
449
|
const float pruning_rate = (iter == 0)
|
|
608
450
|
? 0.0f
|
|
609
451
|
: adapt_d_prime(d, cp, state, total_pairs, pruned_at_gemm);
|
|
@@ -653,4 +495,178 @@ void SuperKMeans::train(idx_t n, const float* x) {
|
|
|
653
495
|
untransform_centroids(centroids, state.R, d, k, state.Y_tilde.data());
|
|
654
496
|
}
|
|
655
497
|
|
|
498
|
+
void super_kmeans_assign_iteration(
|
|
499
|
+
const float* X_tilde,
|
|
500
|
+
int n,
|
|
501
|
+
int d,
|
|
502
|
+
const float* Y_tilde,
|
|
503
|
+
int k,
|
|
504
|
+
float* tau,
|
|
505
|
+
int32_t* assignments,
|
|
506
|
+
int d_prime,
|
|
507
|
+
const float* ad_coeff,
|
|
508
|
+
const SuperKMeansParameters& cp,
|
|
509
|
+
int64_t* total_pairs_out,
|
|
510
|
+
int64_t* pruned_at_gemm_out,
|
|
511
|
+
SuperKMeansAssignScratch* scratch_in) {
|
|
512
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
513
|
+
d_prime >= 1,
|
|
514
|
+
"super_kmeans_assign_iteration: d_prime must be >= 1");
|
|
515
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
516
|
+
d_prime < d, "super_kmeans_assign_iteration: d_prime must be < d");
|
|
517
|
+
FAISS_THROW_IF_MSG(
|
|
518
|
+
ad_coeff == nullptr,
|
|
519
|
+
"super_kmeans_assign_iteration: ad_coeff must not be null");
|
|
520
|
+
FAISS_THROW_IF_MSG(
|
|
521
|
+
tau == nullptr,
|
|
522
|
+
"super_kmeans_assign_iteration: tau must not be null");
|
|
523
|
+
FAISS_THROW_IF_MSG(
|
|
524
|
+
assignments == nullptr,
|
|
525
|
+
"super_kmeans_assign_iteration: assignments must not be null");
|
|
526
|
+
|
|
527
|
+
const int d_trail = d - d_prime;
|
|
528
|
+
const auto layout = build_pdx_block_layout(d, d_prime, cp.pdx_block_size);
|
|
529
|
+
const int n_blocks = static_cast<int>(layout.block_dim.size());
|
|
530
|
+
|
|
531
|
+
// Reuse the caller's scratch when provided (grow-only buffers, so a caller
|
|
532
|
+
// looping over iterations allocates nothing in steady state); otherwise use
|
|
533
|
+
// a local instance for one-shot callers.
|
|
534
|
+
SuperKMeansAssignScratch local_scratch;
|
|
535
|
+
SuperKMeansAssignScratch& s = scratch_in ? *scratch_in : local_scratch;
|
|
536
|
+
|
|
537
|
+
// PDX-lay out the centroid trailing block once per call.
|
|
538
|
+
auto& Y_trail = s.Y_trail;
|
|
539
|
+
auto& Y_pdx = s.Y_pdx;
|
|
540
|
+
Y_trail.resize(static_cast<size_t>(k) * d_trail);
|
|
541
|
+
Y_pdx.resize(static_cast<size_t>(k) * d_trail);
|
|
542
|
+
for (int j = 0; j < k; ++j) {
|
|
543
|
+
std::memcpy(
|
|
544
|
+
Y_trail.data() + static_cast<size_t>(j) * d_trail,
|
|
545
|
+
Y_tilde + static_cast<size_t>(j) * d + d_prime,
|
|
546
|
+
d_trail * sizeof(float));
|
|
547
|
+
}
|
|
548
|
+
detail::pdxify(Y_trail.data(), k, d_trail, cp.pdx_block_size, Y_pdx.data());
|
|
549
|
+
|
|
550
|
+
auto& x_norms_partial = s.x_norms_partial;
|
|
551
|
+
auto& y_norms_partial = s.y_norms_partial;
|
|
552
|
+
x_norms_partial.resize(n);
|
|
553
|
+
detail::compute_partial_norms(
|
|
554
|
+
X_tilde, n, d, d_prime, x_norms_partial.data());
|
|
555
|
+
y_norms_partial.resize(k);
|
|
556
|
+
detail::compute_partial_norms(
|
|
557
|
+
Y_tilde, k, d, d_prime, y_norms_partial.data());
|
|
558
|
+
|
|
559
|
+
int64_t total_pairs = 0;
|
|
560
|
+
int64_t pruned_at_gemm = 0;
|
|
561
|
+
|
|
562
|
+
// Size the GEMM tile buffer to the actual max tile, not cp.x_batch *
|
|
563
|
+
// cp.y_batch, which over-allocates when n < x_batch or k < y_batch.
|
|
564
|
+
const int bx_max = std::min(cp.x_batch, n);
|
|
565
|
+
const int by_max = std::min(cp.y_batch, k);
|
|
566
|
+
auto& partial_ip = s.partial_ip;
|
|
567
|
+
partial_ip.resize(static_cast<size_t>(bx_max) * by_max);
|
|
568
|
+
|
|
569
|
+
for (int xi = 0; xi < n; xi += cp.x_batch) {
|
|
570
|
+
const int bx = std::min(cp.x_batch, n - xi);
|
|
571
|
+
for (int yj = 0; yj < k; yj += cp.y_batch) {
|
|
572
|
+
const int by = std::min(cp.y_batch, k - yj);
|
|
573
|
+
|
|
574
|
+
// Partial GEMM over [0, d_prime): partial_ip[i*by + j] =
|
|
575
|
+
// <X[xi+i, 0:d_prime], Y[yj+j, 0:d_prime]>.
|
|
576
|
+
{
|
|
577
|
+
FINTEGER M = by;
|
|
578
|
+
FINTEGER N_ = bx;
|
|
579
|
+
FINTEGER K_ = d_prime;
|
|
580
|
+
float alpha = 1.0f;
|
|
581
|
+
float beta = 0.0f;
|
|
582
|
+
FINTEGER lda_y = d;
|
|
583
|
+
FINTEGER lda_x = d;
|
|
584
|
+
FINTEGER ldc = by;
|
|
585
|
+
sgemm_("Transpose",
|
|
586
|
+
"Not transpose",
|
|
587
|
+
&M,
|
|
588
|
+
&N_,
|
|
589
|
+
&K_,
|
|
590
|
+
&alpha,
|
|
591
|
+
Y_tilde + static_cast<size_t>(yj) * d,
|
|
592
|
+
&lda_y,
|
|
593
|
+
X_tilde + static_cast<size_t>(xi) * d,
|
|
594
|
+
&lda_x,
|
|
595
|
+
&beta,
|
|
596
|
+
partial_ip.data(),
|
|
597
|
+
&ldc);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// One SIMD dispatch per (xi, yj) tile.
|
|
601
|
+
with_simd_level([&]<SIMDLevel SL>() {
|
|
602
|
+
[[maybe_unused]] const int omp_chunk_local = cp.omp_chunk;
|
|
603
|
+
int64_t tile_total = 0;
|
|
604
|
+
int64_t tile_pruned = 0;
|
|
605
|
+
#pragma omp parallel for schedule(dynamic, omp_chunk_local) \
|
|
606
|
+
reduction(+ : tile_total) reduction(+ : tile_pruned)
|
|
607
|
+
for (int i = 0; i < bx; ++i) {
|
|
608
|
+
const float xnp_i = x_norms_partial[xi + i];
|
|
609
|
+
float tau_i = tau[xi + i];
|
|
610
|
+
int32_t best_j = assignments[xi + i];
|
|
611
|
+
const float* xrow =
|
|
612
|
+
X_tilde + static_cast<size_t>(xi + i) * d;
|
|
613
|
+
for (int j = 0; j < by; ++j) {
|
|
614
|
+
++tile_total;
|
|
615
|
+
// L2-from-IP; clamp against catastrophic cancellation
|
|
616
|
+
// when the true distance is ~0.
|
|
617
|
+
float pd = xnp_i + y_norms_partial[yj + j] -
|
|
618
|
+
2.0f *
|
|
619
|
+
partial_ip
|
|
620
|
+
[static_cast<size_t>(i) * by +
|
|
621
|
+
j];
|
|
622
|
+
if (pd < 0.0f) {
|
|
623
|
+
pd = 0.0f;
|
|
624
|
+
}
|
|
625
|
+
// ADSampling chi-squared bound at the d_prime boundary.
|
|
626
|
+
if (pd > ad_coeff[d_prime] * tau_i) {
|
|
627
|
+
++tile_pruned;
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
// Progressive pruning across PDX blocks; double
|
|
631
|
+
// accumulator mitigates float drift over many block
|
|
632
|
+
// additions.
|
|
633
|
+
double dist = pd;
|
|
634
|
+
bool keep = true;
|
|
635
|
+
size_t pdx_offset = 0;
|
|
636
|
+
for (int b = 0; b < n_blocks; ++b) {
|
|
637
|
+
const int n_in_block = layout.block_dim[b];
|
|
638
|
+
const int true_end = layout.true_block_end[b];
|
|
639
|
+
const float* xblk = xrow + (true_end - n_in_block);
|
|
640
|
+
const float* yblk = Y_pdx.data() + pdx_offset +
|
|
641
|
+
static_cast<size_t>(yj + j) * n_in_block;
|
|
642
|
+
dist += faiss::detail::block_l2<SL>(
|
|
643
|
+
xblk, yblk, n_in_block);
|
|
644
|
+
pdx_offset += static_cast<size_t>(k) * n_in_block;
|
|
645
|
+
if (dist > static_cast<double>(ad_coeff[true_end]) *
|
|
646
|
+
tau_i) {
|
|
647
|
+
keep = false;
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (keep && dist < tau_i) {
|
|
652
|
+
tau_i = static_cast<float>(dist);
|
|
653
|
+
best_j = static_cast<int32_t>(yj + j);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
tau[xi + i] = tau_i;
|
|
657
|
+
assignments[xi + i] = best_j;
|
|
658
|
+
}
|
|
659
|
+
total_pairs += tile_total;
|
|
660
|
+
pruned_at_gemm += tile_pruned;
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (total_pairs_out) {
|
|
665
|
+
*total_pairs_out = total_pairs;
|
|
666
|
+
}
|
|
667
|
+
if (pruned_at_gemm_out) {
|
|
668
|
+
*pruned_at_gemm_out = pruned_at_gemm;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
656
672
|
} // namespace faiss
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
#pragma once
|
|
25
25
|
|
|
26
|
+
#include <cstdint>
|
|
26
27
|
#include <vector>
|
|
27
28
|
|
|
28
29
|
#include <faiss/Clustering.h>
|
|
@@ -94,4 +95,33 @@ struct SuperKMeans {
|
|
|
94
95
|
void train(idx_t n, const float* x);
|
|
95
96
|
};
|
|
96
97
|
|
|
98
|
+
/// Reusable scratch for super_kmeans_assign_iteration; pass one instance across
|
|
99
|
+
/// a loop of calls to avoid reallocating (buffers grow only as needed).
|
|
100
|
+
struct SuperKMeansAssignScratch {
|
|
101
|
+
std::vector<float> Y_trail; // (k, d_trail) row-major, input to pdxify
|
|
102
|
+
std::vector<float> Y_pdx; // PDX-laid-out trailing block
|
|
103
|
+
std::vector<float> x_norms_partial; // ||X[i, 0:d_prime]||^2
|
|
104
|
+
std::vector<float> y_norms_partial; // ||Y[j, 0:d_prime]||^2
|
|
105
|
+
std::vector<float> partial_ip; // partial-GEMM tile buffer
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/// One SuperKMeans iter-1+ assignment pass: partial GEMM over the front
|
|
109
|
+
/// `d_prime` dims + ADSampling progressive pruning over the PDX-laid-out
|
|
110
|
+
/// trailing block. Updates `tau` and `assignments` in place.
|
|
111
|
+
void super_kmeans_assign_iteration(
|
|
112
|
+
const float* X_tilde, // (n, d) points, row-major, rotated space
|
|
113
|
+
int n,
|
|
114
|
+
int d,
|
|
115
|
+
const float* Y_tilde, // (k, d) centroids, row-major, rotated space
|
|
116
|
+
int k,
|
|
117
|
+
float* tau, // in/out, len n; entry = exact full-d L2 to assignment
|
|
118
|
+
int32_t* assignments, // in/out, len n
|
|
119
|
+
int d_prime, // GEMM/pruning split, 1 <= d_prime < d
|
|
120
|
+
const float* ad_coeff, // ADSampling threshold table, len d+1
|
|
121
|
+
const SuperKMeansParameters& cp,
|
|
122
|
+
int64_t* total_pairs = nullptr, // optional out-counter
|
|
123
|
+
int64_t* pruned_at_gemm = nullptr, // optional out-counter
|
|
124
|
+
SuperKMeansAssignScratch* scratch =
|
|
125
|
+
nullptr); // optional, reuse across calls
|
|
126
|
+
|
|
97
127
|
} // namespace faiss
|