faiss 0.6.3 → 0.6.4
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/Clustering.h +8 -0
- data/vendor/faiss/faiss/Index.h +1 -1
- data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +23 -2
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +12 -140
- data/vendor/faiss/faiss/IndexBinaryHNSW.h +0 -6
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +6 -14
- data/vendor/faiss/faiss/IndexBinaryIVF.h +8 -1
- data/vendor/faiss/faiss/IndexFlat.cpp +8 -4
- data/vendor/faiss/faiss/IndexFlat.h +2 -2
- data/vendor/faiss/faiss/IndexHNSW.cpp +301 -91
- data/vendor/faiss/faiss/IndexHNSW.h +31 -7
- data/vendor/faiss/faiss/IndexIDMap.cpp +11 -6
- data/vendor/faiss/faiss/IndexIVF.cpp +19 -4
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +9 -1
- data/vendor/faiss/faiss/IndexIVFPQ.h +11 -0
- data/vendor/faiss/faiss/IndexShards.cpp +3 -3
- data/vendor/faiss/faiss/IndexShardsIVF.cpp +4 -3
- data/vendor/faiss/faiss/SuperKMeans.cpp +31 -8
- data/vendor/faiss/faiss/SuperKMeans.h +3 -2
- data/vendor/faiss/faiss/VectorTransform.cpp +38 -0
- data/vendor/faiss/faiss/VectorTransform.h +3 -0
- data/vendor/faiss/faiss/clone_index.cpp +3 -0
- data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +1 -5
- data/vendor/faiss/faiss/gpu/GpuDistance.h +2 -5
- data/vendor/faiss/faiss/gpu/GpuIndex.h +4 -5
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +65 -42
- data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +5 -2
- data/vendor/faiss/faiss/impl/HNSW.cpp +419 -115
- data/vendor/faiss/faiss/impl/HNSW.h +46 -7
- data/vendor/faiss/faiss/impl/IDSelector.h +44 -0
- data/vendor/faiss/faiss/impl/NNDescent.cpp +10 -3
- data/vendor/faiss/faiss/impl/Panorama.h +11 -2
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +4 -4
- data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +1 -1
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +74 -8
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +47 -4
- data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -2
- data/vendor/faiss/faiss/impl/ResultHandler.h +9 -3
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +32 -30
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +6 -10
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +89 -13
- data/vendor/faiss/faiss/impl/expanded_scanners.h +5 -1
- data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +1 -0
- data/vendor/faiss/faiss/impl/index_read.cpp +130 -29
- data/vendor/faiss/faiss/impl/index_write.cpp +31 -26
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +73 -39
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +4 -4
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +6 -1
- data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +2 -2
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +6 -2
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +37 -17
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +109 -10
- data/vendor/faiss/faiss/impl/simd_dispatch.h +40 -22
- data/vendor/faiss/faiss/index_factory.cpp +8 -1
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +7 -4
- data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +10 -4
- data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +16 -4
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +8 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +4 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +9 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +2 -1
- data/vendor/faiss/faiss/utils/distances.cpp +20 -13
- data/vendor/faiss/faiss/utils/distances_dispatch.h +30 -24
- data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +1 -1
- data/vendor/faiss/faiss/utils/hamming.cpp +15 -9
- data/vendor/faiss/faiss/utils/hamming.h +10 -1
- data/vendor/faiss/faiss/utils/hamming_distance/common.h +14 -3
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +24 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +1 -1
- data/vendor/faiss/faiss/utils/hamming_distance/{hamming_computer-avx512_spr.h → hamming_computer-avx512_vpopcnt.h} +85 -24
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +141 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +194 -30
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +93 -55
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +57 -40
- data/vendor/faiss/faiss/utils/simd_impl/{rabitq_avx512_spr.cpp → rabitq_avx512_vpopcnt.cpp} +21 -24
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_dispatch.h +2 -7
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +6 -1
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +34 -0
- data/vendor/faiss/faiss/utils/simd_levels.cpp +172 -67
- data/vendor/faiss/faiss/utils/simd_levels.h +26 -15
- metadata +6 -5
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +0 -15
|
@@ -39,17 +39,50 @@ namespace faiss {
|
|
|
39
39
|
|
|
40
40
|
namespace {
|
|
41
41
|
|
|
42
|
+
// Whether the computer can measure a whole batch of codes in one call.
|
|
42
43
|
template <class HammingComputer>
|
|
43
|
-
|
|
44
|
+
constexpr bool has_hamming_batch =
|
|
45
|
+
requires(const uint8_t* tile, const uint8_t* codes, int32_t* dis) {
|
|
46
|
+
HammingComputer::batch_size;
|
|
47
|
+
HammingComputer::get_code_size();
|
|
48
|
+
HammingComputer::build_batch_query(tile, nullptr);
|
|
49
|
+
HammingComputer::hamming_batch(tile, codes, dis);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// The query repeated batch_size times, which is what hamming_batch() XORs
|
|
53
|
+
// against. Empty for a computer that does not batch, so those scanners carry
|
|
54
|
+
// neither the buffer nor its alignment.
|
|
55
|
+
template <class HammingComputer>
|
|
56
|
+
struct BatchQueryTile {};
|
|
57
|
+
|
|
58
|
+
template <class HammingComputer>
|
|
59
|
+
requires has_hamming_batch<HammingComputer>
|
|
60
|
+
struct BatchQueryTile<HammingComputer> {
|
|
61
|
+
alignas(64) uint8_t batch_query
|
|
62
|
+
[HammingComputer::batch_size * HammingComputer::get_code_size()];
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
template <class HammingComputer>
|
|
66
|
+
struct IVFBinaryScannerL2 : BinaryInvertedListScanner,
|
|
67
|
+
BatchQueryTile<HammingComputer> {
|
|
44
68
|
HammingComputer hc;
|
|
45
69
|
size_t code_size;
|
|
46
70
|
bool store_pairs;
|
|
47
71
|
|
|
48
72
|
IVFBinaryScannerL2(size_t code_size_, bool store_pairs_)
|
|
49
|
-
: code_size(code_size_), store_pairs(store_pairs_) {
|
|
73
|
+
: code_size(code_size_), store_pairs(store_pairs_) {
|
|
74
|
+
if constexpr (has_hamming_batch<HammingComputer>) {
|
|
75
|
+
// The batch kernel reads a fixed stride, so a caller that pairs
|
|
76
|
+
// this computer with another code size would read past the codes.
|
|
77
|
+
FAISS_THROW_IF_NOT(code_size == HammingComputer::get_code_size());
|
|
78
|
+
}
|
|
79
|
+
}
|
|
50
80
|
|
|
51
81
|
void set_query(const uint8_t* query_vector) override {
|
|
52
82
|
hc.set(query_vector, code_size);
|
|
83
|
+
if constexpr (has_hamming_batch<HammingComputer>) {
|
|
84
|
+
HammingComputer::build_batch_query(query_vector, this->batch_query);
|
|
85
|
+
}
|
|
53
86
|
}
|
|
54
87
|
|
|
55
88
|
idx_t list_no = 0;
|
|
@@ -61,6 +94,31 @@ struct IVFBinaryScannerL2 : BinaryInvertedListScanner {
|
|
|
61
94
|
return hc.hamming(code);
|
|
62
95
|
}
|
|
63
96
|
|
|
97
|
+
// Measures whole batches while at least batch_size codes remain, then
|
|
98
|
+
// leaves codes and j on the first code the caller must measure singly.
|
|
99
|
+
// bound is read per lane, so a caller that raises its heap top inside
|
|
100
|
+
// accept prunes the rest of the batch.
|
|
101
|
+
template <class Accept>
|
|
102
|
+
void scan_batch_prefix(
|
|
103
|
+
size_t n,
|
|
104
|
+
const uint8_t* __restrict& codes,
|
|
105
|
+
size_t& j,
|
|
106
|
+
const uint32_t& bound,
|
|
107
|
+
Accept&& accept) const {
|
|
108
|
+
if constexpr (has_hamming_batch<HammingComputer>) {
|
|
109
|
+
constexpr size_t B = HammingComputer::batch_size;
|
|
110
|
+
int32_t batch[B];
|
|
111
|
+
for (; j + B <= n; j += B, codes += B * code_size) {
|
|
112
|
+
HammingComputer::hamming_batch(this->batch_query, codes, batch);
|
|
113
|
+
for (size_t t = 0; t < B; t++) {
|
|
114
|
+
if (static_cast<uint32_t>(batch[t]) < bound) {
|
|
115
|
+
accept(batch[t], j + t);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
64
122
|
size_t scan_codes(
|
|
65
123
|
size_t n,
|
|
66
124
|
const uint8_t* __restrict codes,
|
|
@@ -70,15 +128,25 @@ struct IVFBinaryScannerL2 : BinaryInvertedListScanner {
|
|
|
70
128
|
size_t k) const override {
|
|
71
129
|
using C = CMax<int32_t, idx_t>;
|
|
72
130
|
|
|
131
|
+
uint32_t bound = static_cast<uint32_t>(simi[0]);
|
|
132
|
+
|
|
73
133
|
size_t nup = 0;
|
|
74
|
-
|
|
134
|
+
size_t j = 0;
|
|
135
|
+
|
|
136
|
+
auto accept = [&](int32_t dis, size_t at) {
|
|
137
|
+
idx_t id = store_pairs ? lo_build(list_no, at) : ids[at];
|
|
138
|
+
heap_replace_top<C>(k, simi, idxi, dis, id);
|
|
139
|
+
bound = static_cast<uint32_t>(simi[0]);
|
|
140
|
+
nup++;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
scan_batch_prefix(n, codes, j, bound, accept);
|
|
144
|
+
|
|
145
|
+
for (; j < n; j++, codes += code_size) {
|
|
75
146
|
uint32_t dis = hc.hamming(codes);
|
|
76
|
-
if (dis <
|
|
77
|
-
|
|
78
|
-
heap_replace_top<C>(k, simi, idxi, dis, id);
|
|
79
|
-
nup++;
|
|
147
|
+
if (dis < bound) {
|
|
148
|
+
accept(static_cast<int32_t>(dis), j);
|
|
80
149
|
}
|
|
81
|
-
codes += code_size;
|
|
82
150
|
}
|
|
83
151
|
return nup;
|
|
84
152
|
}
|
|
@@ -89,13 +157,21 @@ struct IVFBinaryScannerL2 : BinaryInvertedListScanner {
|
|
|
89
157
|
const idx_t* __restrict ids,
|
|
90
158
|
int radius,
|
|
91
159
|
RangeQueryResult& result) const override {
|
|
92
|
-
|
|
160
|
+
const uint32_t bound = static_cast<uint32_t>(radius);
|
|
161
|
+
size_t j = 0;
|
|
162
|
+
|
|
163
|
+
auto accept = [&](int32_t dis, size_t at) {
|
|
164
|
+
int64_t id = store_pairs ? lo_build(list_no, at) : ids[at];
|
|
165
|
+
result.add(static_cast<uint32_t>(dis), id);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
scan_batch_prefix(n, codes, j, bound, accept);
|
|
169
|
+
|
|
170
|
+
for (; j < n; j++, codes += code_size) {
|
|
93
171
|
uint32_t dis = hc.hamming(codes);
|
|
94
|
-
if (dis <
|
|
95
|
-
|
|
96
|
-
result.add(dis, id);
|
|
172
|
+
if (dis < bound) {
|
|
173
|
+
accept(static_cast<int32_t>(dis), j);
|
|
97
174
|
}
|
|
98
|
-
codes += code_size;
|
|
99
175
|
}
|
|
100
176
|
}
|
|
101
177
|
};
|
|
@@ -35,12 +35,16 @@ size_t run_scan_codes1(
|
|
|
35
35
|
size_t list_no = scanner.list_no;
|
|
36
36
|
size_t code_size = scanner.code_size;
|
|
37
37
|
const IDSelector* sel = scanner.sel;
|
|
38
|
+
// If the selector implements IDSelectorWithContext, hand it the scan
|
|
39
|
+
// context (ids, list_size, j) so it can exploit scan-order locality; the
|
|
40
|
+
// dispatch caches the once-per-list RTTI detection.
|
|
41
|
+
const IDSelectorContextDispatch sel_dispatch(sel, store_pairs);
|
|
38
42
|
float threshold = handler.threshold;
|
|
39
43
|
for (size_t j = 0; j < list_size; j++) {
|
|
40
44
|
if (use_sel) {
|
|
41
45
|
int64_t id = store_pairs ? lo_build(list_no, j) : ids[j];
|
|
42
46
|
// skip code without computing distance
|
|
43
|
-
if (!
|
|
47
|
+
if (!sel_dispatch.is_member(id, IDScanContext{ids, list_size, j})) {
|
|
44
48
|
codes += code_size;
|
|
45
49
|
continue;
|
|
46
50
|
}
|
|
@@ -545,7 +545,7 @@ std::unique_ptr<InvertedLists> read_InvertedLists_up(
|
|
|
545
545
|
READ1(n_levels);
|
|
546
546
|
FAISS_THROW_IF_NOT_FMT(
|
|
547
547
|
n_levels > 0, "invalid ilpn n_levels %zd", n_levels);
|
|
548
|
-
constexpr size_t bs = Panorama::
|
|
548
|
+
constexpr size_t bs = Panorama::kLegacyBatchSize;
|
|
549
549
|
auto ailp = std::make_unique<ArrayInvertedListsPanorama>(
|
|
550
550
|
nlist, code_size, n_levels, bs);
|
|
551
551
|
std::vector<size_t> sizes(nlist);
|
|
@@ -821,6 +821,10 @@ static void read_AdditiveQuantizer(AdditiveQuantizer& aq, IOReader* f) {
|
|
|
821
821
|
}
|
|
822
822
|
|
|
823
823
|
aq.set_derived_values();
|
|
824
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
825
|
+
aq.code_size > 0,
|
|
826
|
+
"invalid AdditiveQuantizer: nbits sum to 0 bits, code_size %zd",
|
|
827
|
+
aq.code_size);
|
|
824
828
|
|
|
825
829
|
// Sanity-check codebooks size without knowing the effective dimension.
|
|
826
830
|
// codebooks stores effective_d * total_codebook_size floats, so its
|
|
@@ -1160,8 +1164,7 @@ void read_ScalarQuantizer(
|
|
|
1160
1164
|
}
|
|
1161
1165
|
}
|
|
1162
1166
|
|
|
1163
|
-
// TurboQ full types: extract seed and qjl_type from trained
|
|
1164
|
-
// regenerate projection matrix.
|
|
1167
|
+
// TurboQ full types: extract seed and qjl_type from trained.
|
|
1165
1168
|
if (ScalarQuantizer::TurboQuantRefine::is_turboq_full(ivsc->qtype) &&
|
|
1166
1169
|
ivsc->trained.size() >= 3) {
|
|
1167
1170
|
size_t n = ivsc->trained.size();
|
|
@@ -1170,7 +1173,6 @@ void read_ScalarQuantizer(
|
|
|
1170
1173
|
ivsc->turboq_refine.seed =
|
|
1171
1174
|
ScalarQuantizer::TurboQuantRefine::unpack_seed(
|
|
1172
1175
|
ivsc->trained[n - 3], ivsc->trained[n - 2]);
|
|
1173
|
-
ivsc->turboq_refine.init_projection(ivsc->d);
|
|
1174
1176
|
}
|
|
1175
1177
|
}
|
|
1176
1178
|
|
|
@@ -1688,6 +1690,10 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1688
1690
|
int d;
|
|
1689
1691
|
size_t n_levels, batch_size;
|
|
1690
1692
|
READ1(d);
|
|
1693
|
+
// This branch does not go through read_index_header, so the range
|
|
1694
|
+
// check that guards `d` for every other index type has to be
|
|
1695
|
+
// repeated here.
|
|
1696
|
+
FAISS_CHECK_RANGE(d, 0, (1 << 20) + 1);
|
|
1691
1697
|
READ1(n_levels);
|
|
1692
1698
|
FAISS_THROW_IF_NOT_FMT(n_levels > 0, "invalid n_levels %zd", n_levels);
|
|
1693
1699
|
READ1(batch_size);
|
|
@@ -1702,24 +1708,36 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1702
1708
|
d, n_levels, batch_size);
|
|
1703
1709
|
}
|
|
1704
1710
|
READ1(idxp->ntotal);
|
|
1711
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1712
|
+
idxp->ntotal >= 0,
|
|
1713
|
+
"invalid ntotal %" PRId64 " read from IxFP index",
|
|
1714
|
+
(int64_t)idxp->ntotal);
|
|
1705
1715
|
READ1_BOOL(idxp->is_trained);
|
|
1706
1716
|
READVECTOR(idxp->codes);
|
|
1707
1717
|
READVECTOR(idxp->cum_sums);
|
|
1708
|
-
size_t
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1718
|
+
const size_t ntotal = (size_t)idxp->ntotal;
|
|
1719
|
+
const size_t num_batches =
|
|
1720
|
+
ntotal / batch_size + (ntotal % batch_size != 0);
|
|
1721
|
+
const size_t num_slots = mul_no_overflow(
|
|
1722
|
+
num_batches,
|
|
1723
|
+
batch_size,
|
|
1712
1724
|
"IndexFlatPanorama num_batches*batch_size");
|
|
1713
|
-
|
|
1714
|
-
idxp->codes
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
idxp->
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1725
|
+
const size_t expected_codes_size = mul_no_overflow(
|
|
1726
|
+
num_slots, idxp->code_size, "IndexFlatPanorama codes");
|
|
1727
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1728
|
+
idxp->codes.size() == expected_codes_size,
|
|
1729
|
+
"IndexFlatPanorama codes size mismatch: got %zu, expected %zu",
|
|
1730
|
+
idxp->codes.size(),
|
|
1731
|
+
expected_codes_size);
|
|
1732
|
+
const size_t expected_cum_sums_size = mul_no_overflow(
|
|
1733
|
+
num_slots,
|
|
1734
|
+
idxp->pano.n_levels + 1,
|
|
1735
|
+
"IndexFlatPanorama cum_sums");
|
|
1736
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1737
|
+
idxp->cum_sums.size() == expected_cum_sums_size,
|
|
1738
|
+
"IndexFlatPanorama cum_sums size mismatch: got %zu, expected %zu",
|
|
1739
|
+
idxp->cum_sums.size(),
|
|
1740
|
+
expected_cum_sums_size);
|
|
1723
1741
|
idxp->verbose = false;
|
|
1724
1742
|
idx = std::move(idxp);
|
|
1725
1743
|
} else if (
|
|
@@ -2136,7 +2154,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2136
2154
|
read_ivf_header(ivfp.get(), f);
|
|
2137
2155
|
ivfp->code_size = ivfp->d * sizeof(float);
|
|
2138
2156
|
READ1(ivfp->n_levels);
|
|
2139
|
-
ivfp->batch_size = Panorama::
|
|
2157
|
+
ivfp->batch_size = Panorama::kLegacyBatchSize;
|
|
2140
2158
|
read_InvertedLists(*ivfp, f, io_flags);
|
|
2141
2159
|
idx = std::move(ivfp);
|
|
2142
2160
|
} else if (h == fourcc("IwP2")) {
|
|
@@ -2163,6 +2181,12 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2163
2181
|
read_ScalarQuantizer(&idxs->sq, f, *idxs);
|
|
2164
2182
|
read_vector(idxs->codes, f);
|
|
2165
2183
|
idxs->code_size = idxs->sq.code_size;
|
|
2184
|
+
FAISS_THROW_IF_NOT(
|
|
2185
|
+
idxs->codes.size() ==
|
|
2186
|
+
mul_no_overflow(
|
|
2187
|
+
(size_t)idxs->ntotal,
|
|
2188
|
+
idxs->code_size,
|
|
2189
|
+
"IndexScalarQuantizer codes"));
|
|
2166
2190
|
idx = std::move(idxs);
|
|
2167
2191
|
} else if (h == fourcc("IxLa")) {
|
|
2168
2192
|
int d, nsq, scale_nbit, r2;
|
|
@@ -2374,8 +2398,18 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2374
2398
|
// Validate transform chain dimension consistency:
|
|
2375
2399
|
// chain[0].d_in must equal the outer index d, consecutive
|
|
2376
2400
|
// transforms must have matching d_out/d_in, and the last
|
|
2377
|
-
// transform's d_out must equal the sub-index d.
|
|
2378
|
-
|
|
2401
|
+
// transform's d_out must equal the sub-index d. With an empty
|
|
2402
|
+
// chain the sub-index d must equal the outer d directly, because
|
|
2403
|
+
// reconstruct() then hands the caller's buffer -- sized from the
|
|
2404
|
+
// outer d -- straight to the sub-index.
|
|
2405
|
+
if (nt == 0 && ixpt->index) {
|
|
2406
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2407
|
+
ixpt->index->d == ixpt->d,
|
|
2408
|
+
"IndexPreTransform empty chain: sub-index d=%d "
|
|
2409
|
+
"!= index d=%d",
|
|
2410
|
+
ixpt->index->d,
|
|
2411
|
+
ixpt->d);
|
|
2412
|
+
} else if (nt > 0) {
|
|
2379
2413
|
FAISS_THROW_IF_NOT_FMT(
|
|
2380
2414
|
ixpt->chain[0]->d_in == ixpt->d,
|
|
2381
2415
|
"IndexPreTransform chain[0] d_in=%d != index d=%d",
|
|
@@ -2497,7 +2531,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2497
2531
|
} else if (
|
|
2498
2532
|
h == fourcc("IHNf") || h == fourcc("IHNp") || h == fourcc("IHNs") ||
|
|
2499
2533
|
h == fourcc("IHN2") || h == fourcc("IHNc") || h == fourcc("IHc2") ||
|
|
2500
|
-
h == fourcc("IHfP") || h == fourcc("IH00")) {
|
|
2534
|
+
h == fourcc("IHfP") || h == fourcc("IHNr") || h == fourcc("IH00")) {
|
|
2501
2535
|
std::unique_ptr<IndexHNSW> idxhnsw;
|
|
2502
2536
|
if (h == fourcc("IH00")) {
|
|
2503
2537
|
idxhnsw = std::make_unique<IndexHNSW>();
|
|
@@ -2515,6 +2549,8 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2515
2549
|
idxhnsw = std::make_unique<IndexHNSWCagra>();
|
|
2516
2550
|
} else if (h == fourcc("IHc2")) {
|
|
2517
2551
|
idxhnsw = std::make_unique<IndexHNSWCagra>();
|
|
2552
|
+
} else if (h == fourcc("IHNr")) {
|
|
2553
|
+
idxhnsw = std::make_unique<IndexHNSWRaBitQ>();
|
|
2518
2554
|
}
|
|
2519
2555
|
read_index_header(*idxhnsw, f);
|
|
2520
2556
|
if (h == fourcc("IHfP")) {
|
|
@@ -2525,10 +2561,21 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2525
2561
|
"dynamic_cast to IndexHNSWFlatPanorama failed");
|
|
2526
2562
|
size_t nlevels;
|
|
2527
2563
|
READ1(nlevels);
|
|
2564
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2565
|
+
nlevels > 0, "invalid IHfP n_levels %zd", nlevels);
|
|
2528
2566
|
const_cast<size_t&>(idx_panorama->num_panorama_levels) = nlevels;
|
|
2529
2567
|
const_cast<Panorama&>(idx_panorama->pano) =
|
|
2530
2568
|
Panorama(idx_panorama->d * sizeof(float), nlevels, 1);
|
|
2531
2569
|
READVECTOR(idx_panorama->cum_sums);
|
|
2570
|
+
// Both the search path and get_cum_sum() index cum_sums at
|
|
2571
|
+
// stride pano.n_levels + 1, which may be lower than the
|
|
2572
|
+
// serialized nlevels if set_derived_values() truncated it.
|
|
2573
|
+
FAISS_THROW_IF_NOT(
|
|
2574
|
+
idx_panorama->cum_sums.size() ==
|
|
2575
|
+
mul_no_overflow(
|
|
2576
|
+
(size_t)idx_panorama->ntotal,
|
|
2577
|
+
idx_panorama->pano.n_levels + 1,
|
|
2578
|
+
"IndexHNSWFlatPanorama cum_sums"));
|
|
2532
2579
|
} else if (h == fourcc("IHNc") || h == fourcc("IHc2")) {
|
|
2533
2580
|
READ1_BOOL(idxhnsw->keep_max_size_level0);
|
|
2534
2581
|
auto idx_hnsw_cagra = dynamic_cast<IndexHNSWCagra*>(idxhnsw.get());
|
|
@@ -2549,7 +2596,8 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2549
2596
|
"HNSW levels size %zu != index ntotal %" PRId64,
|
|
2550
2597
|
idxhnsw->hnsw.levels.size(),
|
|
2551
2598
|
idxhnsw->ntotal);
|
|
2552
|
-
idxhnsw->hnsw.
|
|
2599
|
+
idxhnsw->hnsw.search_method =
|
|
2600
|
+
h == fourcc("IHfP") ? HNSW::SM_PANORAMA : HNSW::SM_DEFAULT;
|
|
2553
2601
|
// `HNSW::is_similarity` is intentionally not serialized, so we
|
|
2554
2602
|
// re-derive it here from the persisted metric type. Without this,
|
|
2555
2603
|
// a saved IP/similarity index would come back configured as a
|
|
@@ -2571,6 +2619,57 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2571
2619
|
idxhnsw->storage->d,
|
|
2572
2620
|
idxhnsw->d);
|
|
2573
2621
|
}
|
|
2622
|
+
if (h == fourcc("IHNr")) {
|
|
2623
|
+
auto* idx_rabitq = dynamic_cast<IndexHNSWRaBitQ*>(idxhnsw.get());
|
|
2624
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2625
|
+
idx_rabitq, "IHNr must deserialize to an IndexHNSWRaBitQ");
|
|
2626
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2627
|
+
idxhnsw->metric_type == METRIC_L2,
|
|
2628
|
+
"IndexHNSWRaBitQ supports only the L2 metric");
|
|
2629
|
+
bool staged;
|
|
2630
|
+
READ1_BOOL(staged);
|
|
2631
|
+
idxhnsw->hnsw.search_method =
|
|
2632
|
+
staged ? HNSW::SM_RABITQ : HNSW::SM_DEFAULT;
|
|
2633
|
+
if (idxhnsw->storage) {
|
|
2634
|
+
auto* rq = dynamic_cast<IndexRaBitQ*>(idxhnsw->storage);
|
|
2635
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2636
|
+
rq, "IndexHNSWRaBitQ storage must be an IndexRaBitQ");
|
|
2637
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2638
|
+
rq->metric_type == idxhnsw->metric_type &&
|
|
2639
|
+
rq->rabitq.metric_type == idxhnsw->metric_type,
|
|
2640
|
+
"IndexHNSWRaBitQ storage metric mismatch");
|
|
2641
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2642
|
+
rq->is_trained == idxhnsw->is_trained,
|
|
2643
|
+
"IndexHNSWRaBitQ storage training state mismatch");
|
|
2644
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2645
|
+
rq->rabitq.nb_bits >= 1 && rq->rabitq.nb_bits <= 9,
|
|
2646
|
+
"invalid RaBitQ nb_bits=%zu",
|
|
2647
|
+
rq->rabitq.nb_bits);
|
|
2648
|
+
const size_t expected_code_size =
|
|
2649
|
+
rq->rabitq.compute_code_size(rq->d, rq->rabitq.nb_bits);
|
|
2650
|
+
validate_code_size_match(
|
|
2651
|
+
rq->rabitq.code_size,
|
|
2652
|
+
expected_code_size,
|
|
2653
|
+
"IndexHNSWRaBitQ quantizer");
|
|
2654
|
+
validate_code_size_match(
|
|
2655
|
+
rq->code_size,
|
|
2656
|
+
expected_code_size,
|
|
2657
|
+
"IndexHNSWRaBitQ storage");
|
|
2658
|
+
FAISS_THROW_IF_NOT(
|
|
2659
|
+
rq->codes.size() ==
|
|
2660
|
+
mul_no_overflow(
|
|
2661
|
+
static_cast<size_t>(rq->ntotal),
|
|
2662
|
+
rq->code_size,
|
|
2663
|
+
"IndexHNSWRaBitQ codes"));
|
|
2664
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2665
|
+
!rq->is_trained ||
|
|
2666
|
+
rq->center.size() == static_cast<size_t>(rq->d),
|
|
2667
|
+
"IndexHNSWRaBitQ center size mismatch");
|
|
2668
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2669
|
+
staged == (rq->rabitq.nb_bits >= 2),
|
|
2670
|
+
"IndexHNSWRaBitQ staged-search metadata mismatch");
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2574
2673
|
if (h == fourcc("IHN2")) {
|
|
2575
2674
|
FAISS_THROW_IF_NOT_MSG(
|
|
2576
2675
|
idxhnsw->storage,
|
|
@@ -2838,7 +2937,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2838
2937
|
auto idxq = std::make_unique<IndexRaBitQ>();
|
|
2839
2938
|
read_index_header(*idxq, f);
|
|
2840
2939
|
read_RaBitQuantizer(idxq->rabitq, f, idxq->d, false);
|
|
2841
|
-
|
|
2940
|
+
read_vector(idxq->codes, f);
|
|
2842
2941
|
READVECTOR(idxq->center);
|
|
2843
2942
|
READ1(idxq->qb);
|
|
2844
2943
|
// qb=0: Not quantized - direct distance computation on given float32s.
|
|
@@ -2857,7 +2956,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
2857
2956
|
read_index_header(*idxq, f);
|
|
2858
2957
|
read_RaBitQuantizer(
|
|
2859
2958
|
idxq->rabitq, f, idxq->d, true); // Reads nb_bits from file
|
|
2860
|
-
|
|
2959
|
+
read_vector(idxq->codes, f);
|
|
2861
2960
|
READVECTOR(idxq->center);
|
|
2862
2961
|
READ1(idxq->qb);
|
|
2863
2962
|
// qb=0: Not quantized - direct distance computation on given float32s.
|
|
@@ -3052,7 +3151,8 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
3052
3151
|
bool initialized;
|
|
3053
3152
|
READ1_BOOL(initialized);
|
|
3054
3153
|
if (initialized) {
|
|
3055
|
-
faiss::svs_io::ReaderStreambuf rbuf(
|
|
3154
|
+
faiss::svs_io::ReaderStreambuf rbuf(
|
|
3155
|
+
f, get_deserialization_vector_byte_limit());
|
|
3056
3156
|
std::istream is(&rbuf);
|
|
3057
3157
|
svs_ivf->deserialize_impl(is);
|
|
3058
3158
|
}
|
|
@@ -3060,7 +3160,8 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
3060
3160
|
bool trained;
|
|
3061
3161
|
READ1_BOOL(trained);
|
|
3062
3162
|
if (trained) {
|
|
3063
|
-
faiss::svs_io::ReaderStreambuf rbuf(
|
|
3163
|
+
faiss::svs_io::ReaderStreambuf rbuf(
|
|
3164
|
+
f, get_deserialization_vector_byte_limit());
|
|
3064
3165
|
std::istream is(&rbuf);
|
|
3065
3166
|
auto* leanvec =
|
|
3066
3167
|
dynamic_cast<IndexSVSIVFLeanVec*>(svs_ivf.get());
|
|
@@ -3390,7 +3491,7 @@ std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
|
|
|
3390
3491
|
auto idxhnsw = std::make_unique<IndexBinaryHNSW>();
|
|
3391
3492
|
read_index_binary_header(*idxhnsw, f);
|
|
3392
3493
|
read_HNSW(idxhnsw->hnsw, f);
|
|
3393
|
-
idxhnsw->hnsw.
|
|
3494
|
+
idxhnsw->hnsw.search_method = HNSW::SM_DEFAULT;
|
|
3394
3495
|
FAISS_THROW_IF_NOT_FMT(
|
|
3395
3496
|
idxhnsw->hnsw.levels.size() == (size_t)idxhnsw->ntotal,
|
|
3396
3497
|
"IndexBinaryHNSW HNSW levels size %zu != ntotal %" PRId64,
|
|
@@ -3414,7 +3515,7 @@ std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
|
|
|
3414
3515
|
READ1_BOOL(idxhnsw->base_level_only);
|
|
3415
3516
|
READ1(idxhnsw->num_base_level_search_entrypoints);
|
|
3416
3517
|
read_HNSW(idxhnsw->hnsw, f);
|
|
3417
|
-
idxhnsw->hnsw.
|
|
3518
|
+
idxhnsw->hnsw.search_method = HNSW::SM_DEFAULT;
|
|
3418
3519
|
FAISS_THROW_IF_NOT_FMT(
|
|
3419
3520
|
idxhnsw->hnsw.levels.size() == (size_t)idxhnsw->ntotal,
|
|
3420
3521
|
"IndexBinaryHNSWCagra HNSW levels size %zu != ntotal %" PRId64,
|
|
@@ -275,20 +275,12 @@ void write_InvertedLists(const InvertedLists* ils, IOWriter* f) {
|
|
|
275
275
|
} else if (
|
|
276
276
|
const auto& ailp =
|
|
277
277
|
dynamic_cast<const ArrayInvertedListsPanorama*>(ils)) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
} else {
|
|
285
|
-
uint32_t h = fourcc("ilp2");
|
|
286
|
-
WRITE1(h);
|
|
287
|
-
WRITE1(ailp->nlist);
|
|
288
|
-
WRITE1(ailp->code_size);
|
|
289
|
-
WRITE1(ailp->n_levels);
|
|
290
|
-
WRITE1(ailp->pano.batch_size);
|
|
291
|
-
}
|
|
278
|
+
uint32_t h = fourcc("ilp2");
|
|
279
|
+
WRITE1(h);
|
|
280
|
+
WRITE1(ailp->nlist);
|
|
281
|
+
WRITE1(ailp->code_size);
|
|
282
|
+
WRITE1(ailp->n_levels);
|
|
283
|
+
WRITE1(ailp->pano.batch_size);
|
|
292
284
|
uint32_t list_type = fourcc("full");
|
|
293
285
|
WRITE1(list_type);
|
|
294
286
|
std::vector<size_t> sizes;
|
|
@@ -736,18 +728,11 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
|
|
|
736
728
|
} else if (
|
|
737
729
|
const IndexIVFFlatPanorama* ivfp =
|
|
738
730
|
dynamic_cast<const IndexIVFFlatPanorama*>(idx)) {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
} else {
|
|
745
|
-
uint32_t h = fourcc("IwP2");
|
|
746
|
-
WRITE1(h);
|
|
747
|
-
write_ivf_header(ivfp, f);
|
|
748
|
-
WRITE1(ivfp->n_levels);
|
|
749
|
-
WRITE1(ivfp->batch_size);
|
|
750
|
-
}
|
|
731
|
+
uint32_t h = fourcc("IwP2");
|
|
732
|
+
WRITE1(h);
|
|
733
|
+
write_ivf_header(ivfp, f);
|
|
734
|
+
WRITE1(ivfp->n_levels);
|
|
735
|
+
WRITE1(ivfp->batch_size);
|
|
751
736
|
write_InvertedLists(ivfp->invlists, f);
|
|
752
737
|
} else if (
|
|
753
738
|
const IndexIVFFlat* ivfl_2 =
|
|
@@ -888,12 +873,22 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
|
|
|
888
873
|
: dynamic_cast<const IndexHNSWSQ*>(idx) ? fourcc("IHNs")
|
|
889
874
|
: dynamic_cast<const IndexHNSW2Level*>(idx) ? fourcc("IHN2")
|
|
890
875
|
: dynamic_cast<const IndexHNSWCagra*>(idx) ? fourcc("IHc2")
|
|
876
|
+
: dynamic_cast<const IndexHNSWRaBitQ*>(idx) ? fourcc("IHNr")
|
|
891
877
|
: typeid(*idx) == typeid(IndexHNSW) ? fourcc("IH00")
|
|
892
878
|
: 0;
|
|
893
879
|
FAISS_THROW_IF_NOT_FMT(
|
|
894
880
|
h != 0,
|
|
895
881
|
"don't know how to serialize this IndexHNSW subtype: %s",
|
|
896
882
|
typeid(*idx).name());
|
|
883
|
+
const IndexRaBitQ* storage_rabitq = nullptr;
|
|
884
|
+
if (h == fourcc("IHNr")) {
|
|
885
|
+
storage_rabitq = dynamic_cast<const IndexRaBitQ*>(idxhnsw->storage);
|
|
886
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
887
|
+
storage_rabitq ||
|
|
888
|
+
((io_flags & IO_FLAG_SKIP_STORAGE) &&
|
|
889
|
+
idxhnsw->storage == nullptr),
|
|
890
|
+
"IndexHNSWRaBitQ requires IndexRaBitQ storage");
|
|
891
|
+
}
|
|
897
892
|
WRITE1(h);
|
|
898
893
|
write_index_header(idxhnsw, f);
|
|
899
894
|
if (h == fourcc("IHfP")) {
|
|
@@ -916,6 +911,16 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
|
|
|
916
911
|
} else {
|
|
917
912
|
write_index(idxhnsw->storage, f);
|
|
918
913
|
}
|
|
914
|
+
if (h == fourcc("IHNr")) {
|
|
915
|
+
// The staged flag is graph-traversal state, so it has to live here:
|
|
916
|
+
// with IO_FLAG_SKIP_STORAGE there is no storage to derive it from.
|
|
917
|
+
// Storage-owned settings are not duplicated in this payload;
|
|
918
|
+
// IndexRaBitQ serializes whatever it owns.
|
|
919
|
+
const bool staged = storage_rabitq
|
|
920
|
+
? storage_rabitq->rabitq.nb_bits >= 2
|
|
921
|
+
: idxhnsw->hnsw.search_method == HNSW::SM_RABITQ;
|
|
922
|
+
WRITE1(staged);
|
|
923
|
+
}
|
|
919
924
|
} else if (const IndexNSG* idxnsg = dynamic_cast<const IndexNSG*>(idx)) {
|
|
920
925
|
uint32_t h = dynamic_cast<const IndexNSGFlat*>(idx) ? fourcc("INSf")
|
|
921
926
|
: dynamic_cast<const IndexNSGPQ*>(idx) ? fourcc("INSp")
|