faiss 0.6.1 → 0.6.2
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/Index.h +1 -1
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +6 -7
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +3 -3
- data/vendor/faiss/faiss/IndexHNSW.cpp +173 -143
- data/vendor/faiss/faiss/IndexIVF.cpp +2 -2
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +2 -2
- data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -1
- data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +3 -3
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +2 -3
- data/vendor/faiss/faiss/IndexIVFPQR.cpp +2 -3
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +4 -13
- data/vendor/faiss/faiss/IndexNNDescent.cpp +1 -1
- data/vendor/faiss/faiss/IndexNSG.cpp +1 -2
- data/vendor/faiss/faiss/IndexScalarQuantizer.cpp +68 -6
- data/vendor/faiss/faiss/IndexScalarQuantizer.h +10 -0
- data/vendor/faiss/faiss/cppcontrib/SaDecodeKernels.h +1 -1
- data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-neon-inl.h +902 -12
- data/vendor/faiss/faiss/cppcontrib/sa_decode/PQ-neon-inl.h +702 -10
- data/vendor/faiss/faiss/factory_tools.cpp +4 -0
- data/vendor/faiss/faiss/gpu/GpuResources.h +3 -2
- data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +11 -12
- data/vendor/faiss/faiss/gpu/StandardGpuResources.h +3 -3
- data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +87 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +7 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +181 -0
- data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +48 -3
- data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +45 -0
- data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFFlat.h +193 -0
- data/vendor/faiss/faiss/impl/HNSW.cpp +556 -199
- data/vendor/faiss/faiss/impl/HNSW.h +51 -13
- data/vendor/faiss/faiss/impl/NSG.cpp +15 -11
- data/vendor/faiss/faiss/impl/Panorama.h +11 -0
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +25 -2
- data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +1 -1
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +7 -1
- data/vendor/faiss/faiss/impl/ResultHandler.h +1 -0
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +271 -8
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +50 -0
- data/vendor/faiss/faiss/impl/VisitedTable.cpp +10 -10
- data/vendor/faiss/faiss/impl/VisitedTable.h +69 -34
- data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +3 -1
- data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.cpp +35 -43
- data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.h +64 -15
- data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +86 -40
- data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +81 -50
- data/vendor/faiss/faiss/impl/index_read.cpp +100 -39
- data/vendor/faiss/faiss/impl/index_write.cpp +1 -0
- data/vendor/faiss/faiss/impl/io_macros.h +25 -0
- data/vendor/faiss/faiss/impl/platform_macros.h +12 -8
- data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +2 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +2 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +2 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +20 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +36 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +5 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +105 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +2 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +6 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +327 -18
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +264 -27
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +553 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +559 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +199 -27
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +366 -3
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +144 -19
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +26 -0
- data/vendor/faiss/faiss/impl/simd_dispatch.h +65 -8
- data/vendor/faiss/faiss/index_factory.cpp +5 -1
- data/vendor/faiss/faiss/index_io.h +16 -0
- data/vendor/faiss/faiss/invlists/DirectMap.cpp +4 -1
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +13 -13
- data/vendor/faiss/faiss/invlists/InvertedLists.h +2 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +119 -22
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +15 -5
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +3 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +65 -24
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +3 -2
- data/vendor/faiss/faiss/utils/bf16.h +34 -0
- data/vendor/faiss/faiss/utils/distances_simd.cpp +0 -1
- data/vendor/faiss/faiss/utils/hamming.cpp +8 -8
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +2 -1
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +15 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +6 -30
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_spr.h +171 -0
- data/vendor/faiss/faiss/utils/partitioning.cpp +0 -2
- data/vendor/faiss/faiss/utils/simd_impl/partitioning_simdlib256.h +14 -68
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +343 -0
- data/vendor/faiss/faiss/utils/simd_levels.cpp +12 -2
- metadata +12 -2
|
@@ -302,6 +302,32 @@ struct DCTemplate<
|
|
|
302
302
|
}
|
|
303
303
|
};
|
|
304
304
|
|
|
305
|
+
/**********************************************************
|
|
306
|
+
* TurboQuant masked_sum RVV specialization (scalar fallback)
|
|
307
|
+
**********************************************************/
|
|
308
|
+
|
|
309
|
+
template <SIMDLevel SL0>
|
|
310
|
+
float turboq_masked_sum(const float* arr, const uint8_t* bits, size_t d);
|
|
311
|
+
|
|
312
|
+
template <>
|
|
313
|
+
float turboq_masked_sum<SIMDLevel::RISCV_RVV>(
|
|
314
|
+
const float* arr,
|
|
315
|
+
const uint8_t* bits,
|
|
316
|
+
size_t d) {
|
|
317
|
+
float result = 0;
|
|
318
|
+
for (size_t byte_idx = 0; byte_idx < (d + 7) / 8; byte_idx++) {
|
|
319
|
+
uint8_t b = bits[byte_idx];
|
|
320
|
+
size_t base = byte_idx * 8;
|
|
321
|
+
size_t end = std::min(base + 8, d);
|
|
322
|
+
for (size_t j = base; j < end; j++) {
|
|
323
|
+
if (b & (1 << (j - base))) {
|
|
324
|
+
result += arr[j];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
|
|
305
331
|
} // namespace scalar_quantizer
|
|
306
332
|
} // namespace faiss
|
|
307
333
|
|
|
@@ -36,6 +36,12 @@ constexpr int AVAILABLE_SIMD_LEVELS_AVX2_NEON = AVAILABLE_SIMD_LEVELS_NONE |
|
|
|
36
36
|
constexpr int AVAILABLE_SIMD_LEVELS_A0 = AVAILABLE_SIMD_LEVELS_AVX2_NEON |
|
|
37
37
|
(1 << int(SIMDLevel::AVX512)) | (1 << int(SIMDLevel::RISCV_RVV));
|
|
38
38
|
|
|
39
|
+
// A0_SPR: same as A0 + AVX512_SPR (for functions with a dedicated SPR
|
|
40
|
+
// specialization on top of an AVX512 fallback). Currently used by the
|
|
41
|
+
// RaBitQ popcount kernels, which use VPOPCNTDQ on SPR+.
|
|
42
|
+
constexpr int AVAILABLE_SIMD_LEVELS_A0_SPR =
|
|
43
|
+
AVAILABLE_SIMD_LEVELS_A0 | (1 << int(SIMDLevel::AVX512_SPR));
|
|
44
|
+
|
|
39
45
|
// A1: same + ARM_SVE (for functions with dedicated SVE implementations)
|
|
40
46
|
constexpr int AVAILABLE_SIMD_LEVELS_A1 =
|
|
41
47
|
AVAILABLE_SIMD_LEVELS_A0 | (1 << int(SIMDLevel::ARM_SVE));
|
|
@@ -47,6 +53,37 @@ constexpr int AVAILABLE_SIMD_LEVELS_A2 = AVAILABLE_SIMD_LEVELS_NONE |
|
|
|
47
53
|
|
|
48
54
|
constexpr int AVAILABLE_SIMD_LEVELS_ALL = -1;
|
|
49
55
|
|
|
56
|
+
constexpr SIMDLevel get_simd_fallback(SIMDLevel level) {
|
|
57
|
+
switch (level) {
|
|
58
|
+
case SIMDLevel::AVX512_SPR:
|
|
59
|
+
return SIMDLevel::AVX512;
|
|
60
|
+
case SIMDLevel::AVX512:
|
|
61
|
+
return SIMDLevel::AVX2;
|
|
62
|
+
case SIMDLevel::ARM_SVE:
|
|
63
|
+
return SIMDLevel::ARM_NEON;
|
|
64
|
+
case SIMDLevel::AVX2:
|
|
65
|
+
case SIMDLevel::ARM_NEON:
|
|
66
|
+
case SIMDLevel::RISCV_RVV:
|
|
67
|
+
return SIMDLevel::NONE;
|
|
68
|
+
default:
|
|
69
|
+
return SIMDLevel::NONE;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
template <int available_levels, SIMDLevel current_level, typename LambdaType>
|
|
74
|
+
inline auto dispatch_with_fallback(LambdaType&& action) {
|
|
75
|
+
if constexpr (available_levels & (1 << int(current_level))) {
|
|
76
|
+
return action.template operator()<current_level>();
|
|
77
|
+
} else if constexpr (current_level != SIMDLevel::NONE) {
|
|
78
|
+
return dispatch_with_fallback<
|
|
79
|
+
available_levels,
|
|
80
|
+
get_simd_fallback(current_level)>(
|
|
81
|
+
std::forward<LambdaType>(action));
|
|
82
|
+
} else {
|
|
83
|
+
return action.template operator()<SIMDLevel::NONE>();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
50
87
|
/** The complete dispatching function. It takes into account:
|
|
51
88
|
* - the currently selected SIMD level
|
|
52
89
|
* - the compiled in SIMD levels (given by COMPILE_SIMD_XXX)
|
|
@@ -114,14 +151,15 @@ inline auto with_selected_simd_levels(LambdaType&& action) {
|
|
|
114
151
|
}
|
|
115
152
|
#else // static dispatch
|
|
116
153
|
// In static mode, SINGLE_SIMD_LEVEL is a constexpr resolved at compile
|
|
117
|
-
// time.
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
154
|
+
// time. We mirror the DD fallthrough behavior at compile time via
|
|
155
|
+
// dispatch_with_fallback, which recursively walks get_simd_fallback:
|
|
156
|
+
// x86: AVX512_SPR -> AVX512 -> AVX2 -> NONE
|
|
157
|
+
// ARM: ARM_SVE -> ARM_NEON -> NONE
|
|
158
|
+
// RISCV: RISCV_RVV -> NONE
|
|
159
|
+
// The first level in the chain that appears in available_levels is
|
|
160
|
+
// selected; if none match, NONE is used unconditionally.
|
|
161
|
+
return dispatch_with_fallback<available_levels, SINGLE_SIMD_LEVEL>(
|
|
162
|
+
std::forward<LambdaType>(action));
|
|
125
163
|
#endif
|
|
126
164
|
}
|
|
127
165
|
|
|
@@ -160,6 +198,15 @@ inline auto with_simd_level(LambdaType&& action) {
|
|
|
160
198
|
std::forward<LambdaType>(action));
|
|
161
199
|
}
|
|
162
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Use for functions with AVX512_SPR-specific implementations.
|
|
203
|
+
*/
|
|
204
|
+
template <typename LambdaType>
|
|
205
|
+
inline auto with_simd_level_spr(LambdaType&& action) {
|
|
206
|
+
return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0_SPR>(
|
|
207
|
+
std::forward<LambdaType>(action));
|
|
208
|
+
}
|
|
209
|
+
|
|
163
210
|
/**
|
|
164
211
|
* Use for functions implemented with simdXintY (256-bit) operations
|
|
165
212
|
* that don't have dedicated AVX512 or SVE implementations.
|
|
@@ -170,4 +217,14 @@ inline auto with_simd_level_256bit(LambdaType&& action) {
|
|
|
170
217
|
std::forward<LambdaType>(action));
|
|
171
218
|
}
|
|
172
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Use for functions that have A0-level implementations plus an AVX512_SPR
|
|
222
|
+
* specialization (e.g. using VPOPCNTDQ).
|
|
223
|
+
*/
|
|
224
|
+
template <typename LambdaType>
|
|
225
|
+
inline auto with_simd_level_a0_spr(LambdaType&& action) {
|
|
226
|
+
return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0_SPR>(
|
|
227
|
+
std::forward<LambdaType>(action));
|
|
228
|
+
}
|
|
229
|
+
|
|
173
230
|
} // namespace faiss
|
|
@@ -168,9 +168,13 @@ std::map<std::string, ScalarQuantizer::QuantizerType> sq_types = {
|
|
|
168
168
|
{"SQtqmse3", ScalarQuantizer::QT_3bit_tqmse},
|
|
169
169
|
{"SQtqmse4", ScalarQuantizer::QT_4bit_tqmse},
|
|
170
170
|
{"SQtqmse8", ScalarQuantizer::QT_8bit_tqmse},
|
|
171
|
+
{"SQtq2", ScalarQuantizer::QT_2bit_tq},
|
|
172
|
+
{"SQtq3", ScalarQuantizer::QT_3bit_tq},
|
|
173
|
+
{"SQtq4", ScalarQuantizer::QT_4bit_tq},
|
|
174
|
+
{"SQtq5", ScalarQuantizer::QT_5bit_tq},
|
|
171
175
|
};
|
|
172
176
|
const std::string sq_pattern =
|
|
173
|
-
"(SQ0|SQ4|SQ8|SQ6|SQfp16|SQbf16|SQ8_direct_signed|SQ8_direct|SQtqmse1|SQtqmse2|SQtqmse3|SQtqmse4|SQtqmse8)";
|
|
177
|
+
"(SQ0|SQ4|SQ8|SQ6|SQfp16|SQbf16|SQ8_direct_signed|SQ8_direct|SQtqmse1|SQtqmse2|SQtqmse3|SQtqmse4|SQtqmse8|SQtq2|SQtq3|SQtq4|SQtq5)";
|
|
174
178
|
|
|
175
179
|
std::map<std::string, AdditiveQuantizer::Search_type_t> aq_search_type = {
|
|
176
180
|
{"_Nfloat", AdditiveQuantizer::ST_norm_float},
|
|
@@ -137,6 +137,22 @@ size_t get_deserialization_vector_byte_limit();
|
|
|
137
137
|
// and do not modify while deserialization is in progress on other threads.
|
|
138
138
|
void set_deserialization_vector_byte_limit(size_t value);
|
|
139
139
|
|
|
140
|
+
// Returns the current IndexLattice r2 limit for deserialization.
|
|
141
|
+
// When nonzero, deserialization rejects IndexLattice payloads whose
|
|
142
|
+
// r2 (squared lattice radius) exceeds this value. The
|
|
143
|
+
// ZnSphereCodecRec constructor that runs at IndexLattice deserialize
|
|
144
|
+
// time builds a decode cache whose population cost scales
|
|
145
|
+
// polynomially in r2 and dim, and can exceed real-world workload time
|
|
146
|
+
// budgets even for r2 values that do not trip the existing
|
|
147
|
+
// decode-cache memory cap.
|
|
148
|
+
// Default: 0 (no limit).
|
|
149
|
+
size_t get_deserialization_lattice_r2_limit();
|
|
150
|
+
|
|
151
|
+
// Sets the IndexLattice r2 deserialization limit.
|
|
152
|
+
// NOT thread-safe: set before any concurrent deserialization calls
|
|
153
|
+
// and do not modify while deserialization is in progress on other threads.
|
|
154
|
+
void set_deserialization_lattice_r2_limit(size_t value);
|
|
155
|
+
|
|
140
156
|
} // namespace faiss
|
|
141
157
|
|
|
142
158
|
#endif
|
|
@@ -254,7 +254,10 @@ void DirectMap::update_codes(
|
|
|
254
254
|
int64_t id2 = invlists->get_single_id(il, l - 1);
|
|
255
255
|
array[id2] = lo_build(il, ofs);
|
|
256
256
|
invlists->update_entry(
|
|
257
|
-
il,
|
|
257
|
+
il,
|
|
258
|
+
ofs,
|
|
259
|
+
id2,
|
|
260
|
+
InvertedLists::ScopedCodes(invlists, il, l - 1).get());
|
|
258
261
|
}
|
|
259
262
|
invlists->resize(il, l - 1);
|
|
260
263
|
}
|
|
@@ -498,12 +498,12 @@ void ReadOnlyInvertedLists::resize(size_t, size_t) {
|
|
|
498
498
|
* HStackInvertedLists implementation
|
|
499
499
|
******************************************/
|
|
500
500
|
|
|
501
|
-
HStackInvertedLists::HStackInvertedLists(int
|
|
501
|
+
HStackInvertedLists::HStackInvertedLists(int n_il, const InvertedLists** ils_in)
|
|
502
502
|
: ReadOnlyInvertedLists(
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
FAISS_THROW_IF_NOT(
|
|
506
|
-
for (int i = 0; i <
|
|
503
|
+
n_il > 0 ? ils_in[0]->nlist : 0,
|
|
504
|
+
n_il > 0 ? ils_in[0]->code_size : 0) {
|
|
505
|
+
FAISS_THROW_IF_NOT(n_il > 0);
|
|
506
|
+
for (int i = 0; i < n_il; i++) {
|
|
507
507
|
ils.push_back(ils_in[i]);
|
|
508
508
|
FAISS_THROW_IF_NOT(
|
|
509
509
|
ils_in[i]->code_size == code_size && ils_in[i]->nlist == nlist);
|
|
@@ -683,9 +683,9 @@ int translate_list_no(const VStackInvertedLists* vil, idx_t list_no) {
|
|
|
683
683
|
return i0;
|
|
684
684
|
}
|
|
685
685
|
|
|
686
|
-
idx_t sum_il_sizes(int
|
|
686
|
+
idx_t sum_il_sizes(int n_il, const InvertedLists** ils_in) {
|
|
687
687
|
idx_t tot = 0;
|
|
688
|
-
for (int i = 0; i <
|
|
688
|
+
for (int i = 0; i < n_il; i++) {
|
|
689
689
|
tot += ils_in[i]->nlist;
|
|
690
690
|
}
|
|
691
691
|
return tot;
|
|
@@ -693,13 +693,13 @@ idx_t sum_il_sizes(int nil, const InvertedLists** ils_in) {
|
|
|
693
693
|
|
|
694
694
|
} // namespace
|
|
695
695
|
|
|
696
|
-
VStackInvertedLists::VStackInvertedLists(int
|
|
696
|
+
VStackInvertedLists::VStackInvertedLists(int n_il, const InvertedLists** ils_in)
|
|
697
697
|
: ReadOnlyInvertedLists(
|
|
698
|
-
sum_il_sizes(
|
|
699
|
-
|
|
700
|
-
FAISS_THROW_IF_NOT(
|
|
701
|
-
cumsz.resize(
|
|
702
|
-
for (int i = 0; i <
|
|
698
|
+
sum_il_sizes(n_il, ils_in),
|
|
699
|
+
n_il > 0 ? ils_in[0]->code_size : 0) {
|
|
700
|
+
FAISS_THROW_IF_NOT(n_il > 0);
|
|
701
|
+
cumsz.resize(n_il + 1);
|
|
702
|
+
for (int i = 0; i < n_il; i++) {
|
|
703
703
|
ils.push_back(ils_in[i]);
|
|
704
704
|
FAISS_THROW_IF_NOT(ils_in[i]->code_size == code_size);
|
|
705
705
|
cumsz[i + 1] = cumsz[i] + ils_in[i]->nlist;
|
|
@@ -376,7 +376,7 @@ struct HStackInvertedLists : ReadOnlyInvertedLists {
|
|
|
376
376
|
std::vector<const InvertedLists*> ils;
|
|
377
377
|
|
|
378
378
|
/// build InvertedLists by concatenating nil of them
|
|
379
|
-
HStackInvertedLists(int
|
|
379
|
+
HStackInvertedLists(int n_il, const InvertedLists** ils);
|
|
380
380
|
|
|
381
381
|
size_t list_size(size_t list_no) const override;
|
|
382
382
|
const uint8_t* get_codes(size_t list_no) const override;
|
|
@@ -422,7 +422,7 @@ struct VStackInvertedLists : ReadOnlyInvertedLists {
|
|
|
422
422
|
std::vector<idx_t> cumsz;
|
|
423
423
|
|
|
424
424
|
/// build InvertedLists by concatenating nil of them
|
|
425
|
-
VStackInvertedLists(int
|
|
425
|
+
VStackInvertedLists(int n_il, const InvertedLists** ils);
|
|
426
426
|
|
|
427
427
|
size_t list_size(size_t list_no) const override;
|
|
428
428
|
const uint8_t* get_codes(size_t list_no) const override;
|
|
@@ -65,8 +65,12 @@ IndexSVSVamana::IndexSVSVamana(
|
|
|
65
65
|
idx_t d,
|
|
66
66
|
size_t degree,
|
|
67
67
|
MetricType metric,
|
|
68
|
-
SVSStorageKind storage
|
|
69
|
-
|
|
68
|
+
SVSStorageKind storage,
|
|
69
|
+
bool is_static)
|
|
70
|
+
: Index(d, metric),
|
|
71
|
+
graph_max_degree{degree},
|
|
72
|
+
is_static{is_static},
|
|
73
|
+
storage_kind{storage} {
|
|
70
74
|
prune_to = graph_max_degree < 4 ? graph_max_degree : graph_max_degree - 4;
|
|
71
75
|
alpha = metric == METRIC_L2 ? 1.2f : 0.95f;
|
|
72
76
|
|
|
@@ -74,8 +78,9 @@ IndexSVSVamana::IndexSVSVamana(
|
|
|
74
78
|
// NB: LVQ/LeanVec are only available on Intel(R) hardware AND when using
|
|
75
79
|
// a build based on LVQ/LeanVec-enabled SVS.
|
|
76
80
|
auto svs_storage = to_svs_storage_kind(storage_kind);
|
|
77
|
-
auto status =
|
|
78
|
-
svs_runtime::
|
|
81
|
+
auto status = is_static
|
|
82
|
+
? svs_runtime::VamanaIndex::check_storage_kind(svs_storage)
|
|
83
|
+
: svs_runtime::DynamicVamanaIndex::check_storage_kind(svs_storage);
|
|
79
84
|
if (!status.ok()) {
|
|
80
85
|
FAISS_THROW_MSG(status.message());
|
|
81
86
|
}
|
|
@@ -83,11 +88,19 @@ IndexSVSVamana::IndexSVSVamana(
|
|
|
83
88
|
|
|
84
89
|
bool IndexSVSVamana::is_lvq_leanvec_enabled() {
|
|
85
90
|
auto lvq = to_svs_storage_kind(SVS_LVQ4x0);
|
|
86
|
-
auto status = svs_runtime::
|
|
91
|
+
auto status = svs_runtime::VamanaIndex::check_storage_kind(lvq);
|
|
92
|
+
if (!status.ok()) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
status = svs_runtime::DynamicVamanaIndex::check_storage_kind(lvq);
|
|
87
96
|
if (!status.ok()) {
|
|
88
97
|
return false;
|
|
89
98
|
}
|
|
90
99
|
auto leanvec = to_svs_storage_kind(SVS_LeanVec4x4);
|
|
100
|
+
status = svs_runtime::VamanaIndex::check_storage_kind(leanvec);
|
|
101
|
+
if (!status.ok()) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
91
104
|
status = svs_runtime::DynamicVamanaIndex::check_storage_kind(leanvec);
|
|
92
105
|
if (!status.ok()) {
|
|
93
106
|
return false;
|
|
@@ -97,21 +110,43 @@ bool IndexSVSVamana::is_lvq_leanvec_enabled() {
|
|
|
97
110
|
|
|
98
111
|
IndexSVSVamana::~IndexSVSVamana() {
|
|
99
112
|
if (impl) {
|
|
100
|
-
|
|
113
|
+
svs_runtime::Status status;
|
|
114
|
+
if (is_static) {
|
|
115
|
+
status = svs_runtime::VamanaIndex::destroy(impl);
|
|
116
|
+
} else {
|
|
117
|
+
status = svs_runtime::DynamicVamanaIndex::destroy(
|
|
118
|
+
static_cast<svs_runtime::DynamicVamanaIndex*>(impl));
|
|
119
|
+
}
|
|
101
120
|
FAISS_ASSERT(status.ok());
|
|
102
121
|
impl = nullptr;
|
|
103
122
|
}
|
|
104
123
|
}
|
|
105
124
|
|
|
106
125
|
void IndexSVSVamana::add(idx_t n, const float* x) {
|
|
126
|
+
if (is_static) {
|
|
127
|
+
FAISS_THROW_IF_MSG(
|
|
128
|
+
impl,
|
|
129
|
+
"Static Vamana index does not support add() after initial "
|
|
130
|
+
"build. All data must be provided in a single add() call.");
|
|
131
|
+
create_impl(n, x);
|
|
132
|
+
if (stored_vectors_valid) {
|
|
133
|
+
stored_vectors.resize(static_cast<size_t>(n) * d);
|
|
134
|
+
std::memcpy(stored_vectors.data(), x, sizeof(float) * n * d);
|
|
135
|
+
}
|
|
136
|
+
ntotal = n;
|
|
137
|
+
is_trained = true;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
107
141
|
if (!impl) {
|
|
108
|
-
create_impl();
|
|
142
|
+
create_impl(0, nullptr);
|
|
109
143
|
}
|
|
110
144
|
|
|
111
145
|
std::vector<size_t> labels(n);
|
|
112
146
|
std::iota(labels.begin(), labels.end(), ntotal);
|
|
113
147
|
|
|
114
|
-
auto
|
|
148
|
+
auto* dyn = dynamic_impl();
|
|
149
|
+
auto status = dyn->add(n, labels.data(), x);
|
|
115
150
|
if (!status.ok()) {
|
|
116
151
|
FAISS_THROW_MSG(status.message());
|
|
117
152
|
}
|
|
@@ -137,7 +172,18 @@ void IndexSVSVamana::reconstruct(idx_t key, float* recons) const {
|
|
|
137
172
|
|
|
138
173
|
void IndexSVSVamana::reset() {
|
|
139
174
|
if (impl) {
|
|
140
|
-
|
|
175
|
+
if (is_static) {
|
|
176
|
+
// Static index: destroy the impl so the next add() rebuilds from
|
|
177
|
+
// scratch. The static SVS backend has no in-place reset that
|
|
178
|
+
// permits a follow-up add().
|
|
179
|
+
auto status = svs_runtime::VamanaIndex::destroy(impl);
|
|
180
|
+
FAISS_ASSERT(status.ok());
|
|
181
|
+
impl = nullptr;
|
|
182
|
+
} else {
|
|
183
|
+
// Dynamic index: in-place reset preserves the impl and avoids
|
|
184
|
+
// tearing down its allocated state.
|
|
185
|
+
impl->reset();
|
|
186
|
+
}
|
|
141
187
|
}
|
|
142
188
|
stored_vectors.clear();
|
|
143
189
|
stored_vectors_valid = true;
|
|
@@ -204,17 +250,22 @@ void IndexSVSVamana::range_search(
|
|
|
204
250
|
}
|
|
205
251
|
|
|
206
252
|
size_t IndexSVSVamana::remove_ids(const IDSelector& sel) {
|
|
253
|
+
FAISS_THROW_IF_MSG(
|
|
254
|
+
is_static,
|
|
255
|
+
"Static Vamana index does not support remove_ids(). "
|
|
256
|
+
"The index is immutable after creation.");
|
|
207
257
|
FAISS_THROW_IF_NOT(impl);
|
|
258
|
+
auto* dyn = dynamic_impl();
|
|
208
259
|
auto id_filter = FaissIDFilter{sel};
|
|
209
260
|
size_t removed = 0;
|
|
210
|
-
auto Status =
|
|
261
|
+
auto Status = dyn->remove_selected(&removed, id_filter);
|
|
211
262
|
ntotal -= removed;
|
|
212
263
|
stored_vectors.clear();
|
|
213
264
|
stored_vectors_valid = false;
|
|
214
265
|
return removed;
|
|
215
266
|
}
|
|
216
267
|
|
|
217
|
-
void IndexSVSVamana::create_impl() {
|
|
268
|
+
void IndexSVSVamana::create_impl(idx_t n, const float* x) {
|
|
218
269
|
FAISS_THROW_IF_NOT(!impl);
|
|
219
270
|
ntotal = 0;
|
|
220
271
|
auto svs_metric = to_svs_metric(metric_type);
|
|
@@ -231,15 +282,45 @@ void IndexSVSVamana::create_impl() {
|
|
|
231
282
|
.search_window_size = search_window_size,
|
|
232
283
|
.search_buffer_capacity = search_buffer_capacity,
|
|
233
284
|
};
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
285
|
+
|
|
286
|
+
svs_runtime::Status Status;
|
|
287
|
+
if (is_static) {
|
|
288
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
289
|
+
n > 0 && x != nullptr,
|
|
290
|
+
"Static Vamana index requires data at build time.");
|
|
291
|
+
Status = svs_runtime::VamanaIndex::build(
|
|
292
|
+
&impl,
|
|
293
|
+
d,
|
|
294
|
+
svs_metric,
|
|
295
|
+
svs_storage_kind,
|
|
296
|
+
build_params,
|
|
297
|
+
search_params);
|
|
298
|
+
if (!Status.ok()) {
|
|
299
|
+
FAISS_THROW_MSG(Status.message());
|
|
300
|
+
}
|
|
301
|
+
FAISS_THROW_IF_NOT(impl);
|
|
302
|
+
// Populate the static index with the full dataset (one-shot add).
|
|
303
|
+
Status = impl->add(static_cast<size_t>(n), x);
|
|
304
|
+
if (!Status.ok()) {
|
|
305
|
+
// Best-effort cleanup before propagating the error.
|
|
306
|
+
auto destroy_status = svs_runtime::VamanaIndex::destroy(impl);
|
|
307
|
+
FAISS_ASSERT(destroy_status.ok());
|
|
308
|
+
impl = nullptr;
|
|
309
|
+
FAISS_THROW_MSG(Status.message());
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
svs_runtime::DynamicVamanaIndex* dyn_impl = nullptr;
|
|
313
|
+
Status = svs_runtime::DynamicVamanaIndex::build(
|
|
314
|
+
&dyn_impl,
|
|
315
|
+
d,
|
|
316
|
+
svs_metric,
|
|
317
|
+
svs_storage_kind,
|
|
318
|
+
build_params,
|
|
319
|
+
search_params);
|
|
320
|
+
if (!Status.ok()) {
|
|
321
|
+
FAISS_THROW_MSG(Status.message());
|
|
322
|
+
}
|
|
323
|
+
impl = dyn_impl;
|
|
243
324
|
}
|
|
244
325
|
FAISS_THROW_IF_NOT(impl);
|
|
245
326
|
}
|
|
@@ -258,12 +339,28 @@ void IndexSVSVamana::deserialize_impl(std::istream& in) {
|
|
|
258
339
|
FAISS_THROW_IF_MSG(impl, "Cannot deserialize: SVS index already loaded.");
|
|
259
340
|
auto svs_metric = to_svs_metric(metric_type);
|
|
260
341
|
auto svs_storage_kind = to_svs_storage_kind(storage_kind);
|
|
261
|
-
|
|
262
|
-
|
|
342
|
+
|
|
343
|
+
svs_runtime::Status status;
|
|
344
|
+
if (is_static) {
|
|
345
|
+
status = svs_runtime::VamanaIndex::load(
|
|
346
|
+
&impl, in, svs_metric, svs_storage_kind);
|
|
347
|
+
} else {
|
|
348
|
+
svs_runtime::DynamicVamanaIndex* dyn_impl = nullptr;
|
|
349
|
+
status = svs_runtime::DynamicVamanaIndex::load(
|
|
350
|
+
&dyn_impl, in, svs_metric, svs_storage_kind);
|
|
351
|
+
impl = dyn_impl;
|
|
352
|
+
}
|
|
263
353
|
if (!status.ok()) {
|
|
264
354
|
FAISS_THROW_MSG(status.message());
|
|
265
355
|
}
|
|
266
356
|
FAISS_THROW_IF_NOT_MSG(impl, "Failed to load SVS Vamana index.");
|
|
267
357
|
}
|
|
268
358
|
|
|
359
|
+
svs_runtime::DynamicVamanaIndex* IndexSVSVamana::dynamic_impl() const {
|
|
360
|
+
FAISS_THROW_IF_MSG(
|
|
361
|
+
is_static, "Operation not supported on a static Vamana index.");
|
|
362
|
+
FAISS_THROW_IF_NOT(impl);
|
|
363
|
+
return static_cast<svs_runtime::DynamicVamanaIndex*>(impl);
|
|
364
|
+
}
|
|
365
|
+
|
|
269
366
|
} // namespace faiss
|
|
@@ -94,6 +94,9 @@ struct IndexSVSVamana : Index {
|
|
|
94
94
|
size_t max_candidate_pool_size = 200;
|
|
95
95
|
bool use_full_search_history = true;
|
|
96
96
|
|
|
97
|
+
/// Whether this is a static (immutable) Vamana index
|
|
98
|
+
bool is_static = false;
|
|
99
|
+
|
|
97
100
|
SVSStorageKind storage_kind = SVS_FP32;
|
|
98
101
|
|
|
99
102
|
IndexSVSVamana();
|
|
@@ -102,7 +105,8 @@ struct IndexSVSVamana : Index {
|
|
|
102
105
|
idx_t d,
|
|
103
106
|
size_t degree,
|
|
104
107
|
MetricType metric = METRIC_L2,
|
|
105
|
-
SVSStorageKind storage = SVSStorageKind::SVS_FP32
|
|
108
|
+
SVSStorageKind storage = SVSStorageKind::SVS_FP32,
|
|
109
|
+
bool is_static = false);
|
|
106
110
|
|
|
107
111
|
~IndexSVSVamana() override;
|
|
108
112
|
|
|
@@ -137,8 +141,9 @@ struct IndexSVSVamana : Index {
|
|
|
137
141
|
void serialize_impl(std::ostream& out) const;
|
|
138
142
|
virtual void deserialize_impl(std::istream& in);
|
|
139
143
|
|
|
140
|
-
/* The actual SVS implementation
|
|
141
|
-
|
|
144
|
+
/* The actual SVS implementation (VamanaIndex is the base for both
|
|
145
|
+
static and dynamic variants) */
|
|
146
|
+
svs_runtime::VamanaIndex* impl{nullptr};
|
|
142
147
|
|
|
143
148
|
// The SVS runtime API does not expose vector retrieval, so we keep a copy
|
|
144
149
|
// of added vectors to support reconstruct(). When used as a coarse
|
|
@@ -147,8 +152,13 @@ struct IndexSVSVamana : Index {
|
|
|
147
152
|
bool stored_vectors_valid{true};
|
|
148
153
|
|
|
149
154
|
protected:
|
|
150
|
-
/* Initializes the implementation
|
|
151
|
-
|
|
155
|
+
/* Initializes the implementation. For static indexes the data is consumed
|
|
156
|
+
at build time; for dynamic indexes n/x are ignored and add() populates
|
|
157
|
+
the index afterwards. */
|
|
158
|
+
virtual void create_impl(idx_t n, const float* x);
|
|
159
|
+
|
|
160
|
+
/* Returns the dynamic impl pointer, throwing if static */
|
|
161
|
+
svs_runtime::DynamicVamanaIndex* dynamic_impl() const;
|
|
152
162
|
};
|
|
153
163
|
|
|
154
164
|
} // namespace faiss
|
|
@@ -33,7 +33,8 @@ IndexSVSVamanaLVQ::IndexSVSVamanaLVQ(
|
|
|
33
33
|
idx_t d,
|
|
34
34
|
size_t degree,
|
|
35
35
|
MetricType metric,
|
|
36
|
-
SVSStorageKind storage
|
|
37
|
-
|
|
36
|
+
SVSStorageKind storage,
|
|
37
|
+
bool is_static)
|
|
38
|
+
: IndexSVSVamana(d, degree, metric, storage, is_static) {}
|
|
38
39
|
|
|
39
40
|
} // namespace faiss
|
|
@@ -34,7 +34,8 @@ struct IndexSVSVamanaLVQ : IndexSVSVamana {
|
|
|
34
34
|
idx_t d,
|
|
35
35
|
size_t degree,
|
|
36
36
|
MetricType metric = METRIC_L2,
|
|
37
|
-
SVSStorageKind storage = SVSStorageKind::SVS_LVQ4x0
|
|
37
|
+
SVSStorageKind storage = SVSStorageKind::SVS_LVQ4x0,
|
|
38
|
+
bool is_static = false);
|
|
38
39
|
|
|
39
40
|
~IndexSVSVamanaLVQ() override = default;
|
|
40
41
|
};
|
|
@@ -44,8 +44,9 @@ IndexSVSVamanaLeanVec::IndexSVSVamanaLeanVec(
|
|
|
44
44
|
size_t degree,
|
|
45
45
|
MetricType metric,
|
|
46
46
|
size_t leanvec_dims,
|
|
47
|
-
SVSStorageKind storage_kind
|
|
48
|
-
|
|
47
|
+
SVSStorageKind storage_kind,
|
|
48
|
+
bool is_static)
|
|
49
|
+
: IndexSVSVamana(d, degree, metric, storage_kind, is_static) {
|
|
49
50
|
is_trained = false;
|
|
50
51
|
leanvec_d = leanvec_dims == 0 ? d / 2 : leanvec_dims;
|
|
51
52
|
}
|
|
@@ -120,7 +121,8 @@ void IndexSVSVamanaLeanVec::deserialize_training_data(std::istream& in) {
|
|
|
120
121
|
training_data = tdata;
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
void IndexSVSVamanaLeanVec::create_impl() {
|
|
124
|
+
void IndexSVSVamanaLeanVec::create_impl(idx_t n, const float* x) {
|
|
125
|
+
FAISS_THROW_IF_NOT(!impl);
|
|
124
126
|
ntotal = 0;
|
|
125
127
|
auto svs_metric = to_svs_metric(metric_type);
|
|
126
128
|
auto svs_storage_kind = to_svs_storage_kind(storage_kind);
|
|
@@ -136,29 +138,68 @@ void IndexSVSVamanaLeanVec::create_impl() {
|
|
|
136
138
|
.search_window_size = search_window_size,
|
|
137
139
|
.search_buffer_capacity = search_buffer_capacity,
|
|
138
140
|
};
|
|
141
|
+
|
|
139
142
|
auto status = svs_runtime::Status_Ok;
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
if (is_static) {
|
|
144
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
145
|
+
n > 0 && x != nullptr,
|
|
146
|
+
"Static Vamana LeanVec index requires data at build time.");
|
|
147
|
+
if (training_data) {
|
|
148
|
+
status = svs_runtime::VamanaIndexLeanVec::build(
|
|
149
|
+
&impl,
|
|
150
|
+
d,
|
|
151
|
+
svs_metric,
|
|
152
|
+
svs_storage_kind,
|
|
153
|
+
training_data,
|
|
154
|
+
build_params,
|
|
155
|
+
search_params);
|
|
156
|
+
} else {
|
|
157
|
+
status = svs_runtime::VamanaIndexLeanVec::build(
|
|
158
|
+
&impl,
|
|
159
|
+
d,
|
|
160
|
+
svs_metric,
|
|
161
|
+
svs_storage_kind,
|
|
162
|
+
leanvec_d,
|
|
163
|
+
build_params,
|
|
164
|
+
search_params);
|
|
165
|
+
}
|
|
166
|
+
if (!status.ok()) {
|
|
167
|
+
FAISS_THROW_MSG(status.message());
|
|
168
|
+
}
|
|
169
|
+
FAISS_THROW_IF_NOT(impl);
|
|
170
|
+
// Populate the static index with the full dataset (one-shot add).
|
|
171
|
+
status = impl->add(static_cast<size_t>(n), x);
|
|
172
|
+
if (!status.ok()) {
|
|
173
|
+
auto destroy_status = svs_runtime::VamanaIndex::destroy(impl);
|
|
174
|
+
FAISS_ASSERT(destroy_status.ok());
|
|
175
|
+
impl = nullptr;
|
|
176
|
+
FAISS_THROW_MSG(status.message());
|
|
177
|
+
}
|
|
149
178
|
} else {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
179
|
+
svs_runtime::DynamicVamanaIndex* dyn_impl = nullptr;
|
|
180
|
+
if (training_data) {
|
|
181
|
+
status = svs_runtime::DynamicVamanaIndexLeanVec::build(
|
|
182
|
+
&dyn_impl,
|
|
183
|
+
d,
|
|
184
|
+
svs_metric,
|
|
185
|
+
svs_storage_kind,
|
|
186
|
+
training_data,
|
|
187
|
+
build_params,
|
|
188
|
+
search_params);
|
|
189
|
+
} else {
|
|
190
|
+
status = svs_runtime::DynamicVamanaIndexLeanVec::build(
|
|
191
|
+
&dyn_impl,
|
|
192
|
+
d,
|
|
193
|
+
svs_metric,
|
|
194
|
+
svs_storage_kind,
|
|
195
|
+
leanvec_d,
|
|
196
|
+
build_params,
|
|
197
|
+
search_params);
|
|
198
|
+
}
|
|
199
|
+
if (!status.ok()) {
|
|
200
|
+
FAISS_THROW_MSG(status.message());
|
|
201
|
+
}
|
|
202
|
+
impl = dyn_impl;
|
|
162
203
|
}
|
|
163
204
|
FAISS_THROW_IF_NOT(impl);
|
|
164
205
|
}
|