faiss 0.6.0 → 0.6.1
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/ext/faiss/extconf.rb +2 -1
- data/ext/faiss/{index_rb.cpp → index.cpp} +1 -1
- data/ext/faiss/index_binary.cpp +1 -1
- data/ext/faiss/kmeans.cpp +1 -1
- data/ext/faiss/pca_matrix.cpp +1 -1
- data/ext/faiss/product_quantizer.cpp +1 -1
- data/ext/faiss/{utils_rb.cpp → utils.cpp} +1 -1
- data/lib/faiss/version.rb +1 -1
- data/vendor/faiss/faiss/AutoTune.cpp +93 -80
- data/vendor/faiss/faiss/Clustering.cpp +39 -240
- data/vendor/faiss/faiss/Clustering.h +6 -0
- data/vendor/faiss/faiss/IVFlib.cpp +41 -21
- data/vendor/faiss/faiss/Index.cpp +6 -5
- data/vendor/faiss/faiss/Index.h +5 -5
- data/vendor/faiss/faiss/Index2Layer.cpp +37 -53
- data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +49 -37
- data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +36 -34
- data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.h +4 -1
- data/vendor/faiss/faiss/IndexBinary.cpp +5 -3
- data/vendor/faiss/faiss/IndexBinary.h +4 -4
- data/vendor/faiss/faiss/IndexBinaryFlat.cpp +1 -1
- data/vendor/faiss/faiss/IndexBinaryFlat.h +1 -1
- data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +4 -4
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +84 -92
- data/vendor/faiss/faiss/IndexBinaryHNSW.h +9 -3
- data/vendor/faiss/faiss/IndexBinaryHash.cpp +45 -236
- data/vendor/faiss/faiss/IndexBinaryHash.h +6 -6
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +87 -415
- data/vendor/faiss/faiss/IndexFastScan.cpp +72 -109
- data/vendor/faiss/faiss/IndexFastScan.h +25 -23
- data/vendor/faiss/faiss/IndexFlat.cpp +27 -20
- data/vendor/faiss/faiss/IndexFlat.h +21 -18
- data/vendor/faiss/faiss/IndexFlatCodes.cpp +42 -19
- data/vendor/faiss/faiss/IndexHNSW.cpp +283 -145
- data/vendor/faiss/faiss/IndexHNSW.h +16 -2
- data/vendor/faiss/faiss/IndexIDMap.cpp +25 -21
- data/vendor/faiss/faiss/IndexIDMap.h +9 -7
- data/vendor/faiss/faiss/IndexIVF.cpp +465 -362
- data/vendor/faiss/faiss/IndexIVF.h +33 -12
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +77 -74
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +96 -93
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.h +4 -1
- data/vendor/faiss/faiss/IndexIVFFastScan.cpp +357 -238
- data/vendor/faiss/faiss/IndexIVFFastScan.h +42 -41
- data/vendor/faiss/faiss/IndexIVFFlat.cpp +36 -68
- data/vendor/faiss/faiss/IndexIVFFlat.h +32 -0
- data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +53 -30
- data/vendor/faiss/faiss/IndexIVFFlatPanorama.h +3 -1
- data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +18 -15
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +71 -843
- data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +151 -121
- data/vendor/faiss/faiss/IndexIVFPQFastScan.h +3 -0
- data/vendor/faiss/faiss/IndexIVFPQR.cpp +21 -17
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +26 -39
- data/vendor/faiss/faiss/IndexIVFRaBitQ.h +2 -1
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +475 -476
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +248 -93
- data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +41 -127
- data/vendor/faiss/faiss/IndexIVFSpectralHash.h +1 -1
- data/vendor/faiss/faiss/IndexLSH.cpp +36 -19
- data/vendor/faiss/faiss/IndexLattice.cpp +13 -13
- data/vendor/faiss/faiss/IndexNNDescent.cpp +36 -21
- data/vendor/faiss/faiss/IndexNNDescent.h +2 -2
- data/vendor/faiss/faiss/IndexNSG.cpp +39 -23
- data/vendor/faiss/faiss/IndexNeuralNetCodec.cpp +31 -11
- data/vendor/faiss/faiss/IndexPQ.cpp +128 -221
- data/vendor/faiss/faiss/IndexPQ.h +3 -2
- data/vendor/faiss/faiss/IndexPQFastScan.cpp +20 -14
- data/vendor/faiss/faiss/IndexPQFastScan.h +3 -0
- data/vendor/faiss/faiss/IndexPreTransform.cpp +25 -18
- data/vendor/faiss/faiss/IndexPreTransform.h +1 -1
- data/vendor/faiss/faiss/IndexRaBitQ.cpp +11 -36
- data/vendor/faiss/faiss/IndexRaBitQ.h +2 -1
- data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +41 -277
- data/vendor/faiss/faiss/IndexRaBitQFastScan.h +183 -27
- data/vendor/faiss/faiss/IndexRefine.cpp +30 -25
- data/vendor/faiss/faiss/IndexRefine.h +4 -4
- data/vendor/faiss/faiss/IndexReplicas.cpp +6 -6
- data/vendor/faiss/faiss/IndexRowwiseMinMax.cpp +15 -14
- data/vendor/faiss/faiss/IndexRowwiseMinMax.h +1 -1
- data/vendor/faiss/faiss/IndexScalarQuantizer.cpp +82 -14
- data/vendor/faiss/faiss/IndexShards.cpp +10 -9
- data/vendor/faiss/faiss/IndexShardsIVF.cpp +21 -15
- data/vendor/faiss/faiss/MatrixStats.cpp +5 -4
- data/vendor/faiss/faiss/MetaIndexes.cpp +19 -17
- data/vendor/faiss/faiss/MetaIndexes.h +1 -1
- data/vendor/faiss/faiss/MetricType.h +14 -7
- data/vendor/faiss/faiss/SuperKMeans.cpp +656 -0
- data/vendor/faiss/faiss/SuperKMeans.h +97 -0
- data/vendor/faiss/faiss/VectorTransform.cpp +237 -149
- data/vendor/faiss/faiss/VectorTransform.h +16 -16
- data/vendor/faiss/faiss/build.cpp +23 -0
- data/vendor/faiss/faiss/build.h +15 -0
- data/vendor/faiss/faiss/clone_index.cpp +48 -47
- data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-avx2-inl.h +47 -47
- data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-inl.h +11 -0
- data/vendor/faiss/faiss/cppcontrib/sa_decode/PQ-avx2-inl.h +38 -38
- data/vendor/faiss/faiss/cppcontrib/sa_decode/PQ-inl.h +11 -0
- data/vendor/faiss/faiss/factory_tools.cpp +5 -0
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +6 -5
- data/vendor/faiss/faiss/gpu/GpuResources.h +1 -1
- data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +9 -9
- data/vendor/faiss/faiss/gpu/StandardGpuResources.h +4 -3
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexFlat.cpp +46 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +56 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFPQ.cpp +78 -1
- data/vendor/faiss/faiss/gpu/test/TestUtils.cpp +72 -0
- data/vendor/faiss/faiss/gpu/test/TestUtils.h +23 -0
- data/vendor/faiss/faiss/gpu/utils/CuvsFilterConvert.h +1 -1
- data/vendor/faiss/faiss/gpu/utils/CuvsUtils.h +21 -10
- data/vendor/faiss/faiss/gpu_metal/GpuIndexFlat.h +22 -0
- data/vendor/faiss/faiss/gpu_metal/MetalCloner.h +35 -0
- data/vendor/faiss/faiss/gpu_metal/MetalFlatKernels.h +40 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +51 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexFlat.h +65 -0
- data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +66 -0
- data/vendor/faiss/faiss/gpu_metal/MetalResources.h +79 -0
- data/vendor/faiss/faiss/gpu_metal/StandardMetalResources.h +35 -0
- data/vendor/faiss/faiss/impl/AdSampling.cpp +103 -0
- data/vendor/faiss/faiss/impl/AdSampling.h +35 -0
- data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +29 -25
- data/vendor/faiss/faiss/impl/AdditiveQuantizer.h +1 -0
- data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +10 -9
- data/vendor/faiss/faiss/impl/AuxIndexStructures.h +3 -0
- data/vendor/faiss/faiss/impl/ClusteringHelpers.cpp +244 -0
- data/vendor/faiss/faiss/impl/ClusteringHelpers.h +94 -0
- data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +16 -16
- data/vendor/faiss/faiss/impl/CodePacker.cpp +3 -3
- data/vendor/faiss/faiss/impl/CodePackerRaBitQ.cpp +1 -1
- data/vendor/faiss/faiss/impl/DistanceComputer.h +8 -8
- data/vendor/faiss/faiss/impl/FaissAssert.h +6 -3
- data/vendor/faiss/faiss/impl/FaissException.h +50 -3
- data/vendor/faiss/faiss/impl/HNSW.cpp +92 -317
- data/vendor/faiss/faiss/impl/HNSW.h +13 -34
- data/vendor/faiss/faiss/impl/IDSelector.cpp +15 -11
- data/vendor/faiss/faiss/impl/IDSelector.h +8 -8
- data/vendor/faiss/faiss/impl/InvertedListScannerStats.h +26 -0
- data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +82 -77
- data/vendor/faiss/faiss/impl/NNDescent.cpp +62 -25
- data/vendor/faiss/faiss/impl/NNDescent.h +6 -2
- data/vendor/faiss/faiss/impl/NSG.cpp +38 -21
- data/vendor/faiss/faiss/impl/NSG.h +4 -4
- data/vendor/faiss/faiss/impl/Panorama.cpp +23 -6
- data/vendor/faiss/faiss/impl/Panorama.h +258 -87
- data/vendor/faiss/faiss/impl/PdxLayout.cpp +93 -0
- data/vendor/faiss/faiss/impl/PdxLayout.h +41 -0
- data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +46 -32
- data/vendor/faiss/faiss/impl/PolysemousTraining.h +3 -3
- data/vendor/faiss/faiss/impl/ProductAdditiveQuantizer.cpp +35 -35
- data/vendor/faiss/faiss/impl/ProductQuantizer-inl.h +21 -16
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +30 -23
- data/vendor/faiss/faiss/impl/Quantizer.h +2 -2
- data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +55 -49
- data/vendor/faiss/faiss/impl/RaBitQUtils.h +65 -0
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +296 -283
- data/vendor/faiss/faiss/impl/ResidualQuantizer.cpp +26 -23
- data/vendor/faiss/faiss/impl/ResidualQuantizer.h +1 -1
- data/vendor/faiss/faiss/impl/ResultHandler.h +99 -75
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +52 -4
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +27 -1
- data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +14 -11
- data/vendor/faiss/faiss/impl/VisitedTable.h +7 -0
- data/vendor/faiss/faiss/impl/approx_topk/approx_topk.h +276 -0
- data/vendor/faiss/faiss/impl/approx_topk/avx2.cpp +68 -0
- data/vendor/faiss/faiss/{utils → impl}/approx_topk/generic.h +15 -8
- data/vendor/faiss/faiss/impl/approx_topk/neon.cpp +68 -0
- data/vendor/faiss/faiss/impl/approx_topk/rq_beam_search_tab-inl.h +169 -0
- data/vendor/faiss/faiss/impl/approx_topk/rq_beam_search_tab.h +117 -0
- data/vendor/faiss/faiss/impl/approx_topk/simdlib256-inl.h +146 -0
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHNSW_impl.h +73 -0
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHash_impl.h +270 -0
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +460 -0
- data/vendor/faiss/faiss/impl/binary_hamming/IndexIVFSpectralHash_impl.h +159 -0
- data/vendor/faiss/faiss/impl/binary_hamming/IndexPQ_impl.h +92 -0
- data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +26 -0
- data/vendor/faiss/faiss/impl/binary_hamming/avx512.cpp +26 -0
- data/vendor/faiss/faiss/impl/binary_hamming/dispatch.h +143 -0
- data/vendor/faiss/faiss/impl/binary_hamming/neon.cpp +26 -0
- data/vendor/faiss/faiss/impl/binary_hamming/rvv.cpp +26 -0
- data/vendor/faiss/faiss/impl/expanded_scanners.h +8 -3
- data/vendor/faiss/faiss/impl/{FastScanDistancePostProcessing.h → fast_scan/FastScanDistancePostProcessing.h} +13 -6
- data/vendor/faiss/faiss/impl/{LookupTableScaler.h → fast_scan/LookupTableScaler.h} +16 -5
- data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops.h +237 -0
- data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops_512.h +185 -0
- data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +229 -0
- data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +268 -0
- data/vendor/faiss/faiss/impl/{pq4_fast_scan.cpp → fast_scan/fast_scan.cpp} +169 -2
- data/vendor/faiss/faiss/impl/fast_scan/fast_scan.h +341 -0
- data/vendor/faiss/faiss/impl/fast_scan/impl-avx2.cpp +36 -0
- data/vendor/faiss/faiss/impl/fast_scan/impl-avx512.cpp +40 -0
- data/vendor/faiss/faiss/impl/fast_scan/impl-neon.cpp +120 -0
- data/vendor/faiss/faiss/impl/fast_scan/impl-riscv.cpp +104 -0
- data/vendor/faiss/faiss/impl/fast_scan/kernels_simd256.h +213 -0
- data/vendor/faiss/faiss/impl/{pq4_fast_scan_search_qbs.cpp → fast_scan/kernels_simd512.h} +26 -356
- data/vendor/faiss/faiss/impl/fast_scan/rabitq_dispatching.h +90 -0
- data/vendor/faiss/faiss/impl/fast_scan/rabitq_result_handler.h +108 -0
- data/vendor/faiss/faiss/impl/{simd_result_handlers.h → fast_scan/simd_result_handlers.h} +282 -134
- data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +54 -0
- data/vendor/faiss/faiss/impl/hnsw/LockVector.h +64 -0
- data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.cpp +91 -0
- data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.h +64 -0
- data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +104 -0
- data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +111 -0
- data/vendor/faiss/faiss/impl/index_read.cpp +1132 -45
- data/vendor/faiss/faiss/impl/index_read_utils.h +1 -1
- data/vendor/faiss/faiss/impl/index_write.cpp +95 -13
- data/vendor/faiss/faiss/impl/io.cpp +6 -6
- data/vendor/faiss/faiss/impl/io_macros.h +33 -16
- data/vendor/faiss/faiss/impl/kmeans1d.cpp +10 -10
- data/vendor/faiss/faiss/impl/lattice_Zn.cpp +37 -23
- data/vendor/faiss/faiss/impl/lattice_Zn.h +6 -6
- data/vendor/faiss/faiss/impl/mapped_io.cpp +6 -6
- data/vendor/faiss/faiss/impl/platform_macros.h +11 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +549 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +245 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.h +105 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +106 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +21 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +21 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +21 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/{pq_code_distance-avx2.cpp → pq_code_distance-avx2.h} +9 -13
- data/vendor/faiss/faiss/impl/pq_code_distance/{pq_code_distance-avx512.cpp → pq_code_distance-avx512.h} +9 -57
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +29 -111
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.h +96 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +238 -5
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +5 -7
- data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +68 -0
- data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +311 -477
- data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.h +1 -1
- data/vendor/faiss/faiss/impl/scalar_quantizer/codecs.h +1 -1
- data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +3 -2
- data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +102 -11
- data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +27 -1
- data/vendor/faiss/faiss/impl/scalar_quantizer/similarities.h +3 -3
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +148 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +167 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +59 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +163 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +311 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +192 -8
- data/vendor/faiss/faiss/impl/scalar_quantizer/training.h +12 -0
- data/vendor/faiss/faiss/impl/simd_dispatch.h +100 -66
- data/vendor/faiss/faiss/impl/simdlib/simdlib.h +57 -0
- data/vendor/faiss/faiss/{utils → impl/simdlib}/simdlib_avx2.h +264 -172
- data/vendor/faiss/faiss/impl/simdlib/simdlib_avx512.h +414 -0
- data/vendor/faiss/faiss/impl/simdlib/simdlib_dispatch.h +44 -0
- data/vendor/faiss/faiss/{utils → impl/simdlib}/simdlib_emulated.h +231 -166
- data/vendor/faiss/faiss/{utils → impl/simdlib}/simdlib_neon.h +270 -218
- data/vendor/faiss/faiss/{utils → impl/simdlib}/simdlib_ppc64.h +201 -160
- data/vendor/faiss/faiss/impl/svs_io.cpp +12 -3
- data/vendor/faiss/faiss/impl/svs_io.h +8 -2
- data/vendor/faiss/faiss/index_factory.cpp +86 -18
- data/vendor/faiss/faiss/index_io.h +24 -0
- data/vendor/faiss/faiss/invlists/BlockInvertedLists.cpp +66 -16
- data/vendor/faiss/faiss/invlists/DirectMap.cpp +24 -14
- data/vendor/faiss/faiss/invlists/DirectMap.h +4 -3
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +157 -73
- data/vendor/faiss/faiss/invlists/InvertedLists.h +86 -23
- data/vendor/faiss/faiss/invlists/InvertedListsIOHook.cpp +4 -4
- data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +13 -13
- data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.h +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +2 -2
- data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +350 -0
- data/vendor/faiss/faiss/svs/IndexSVSIVF.h +128 -0
- data/vendor/faiss/faiss/svs/IndexSVSIVFLVQ.cpp +40 -0
- data/vendor/faiss/faiss/svs/IndexSVSIVFLVQ.h +43 -0
- data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +225 -0
- data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.h +71 -0
- data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +25 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +18 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +12 -3
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +7 -2
- data/vendor/faiss/faiss/utils/Heap.cpp +10 -10
- data/vendor/faiss/faiss/utils/NeuralNet.cpp +47 -36
- data/vendor/faiss/faiss/utils/NeuralNet.h +1 -1
- data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +10 -4
- data/vendor/faiss/faiss/utils/distances.cpp +390 -560
- data/vendor/faiss/faiss/utils/distances.h +20 -1
- data/vendor/faiss/faiss/utils/distances_dispatch.h +117 -37
- data/vendor/faiss/faiss/utils/distances_fused/avx512.cpp +8 -7
- data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +33 -14
- data/vendor/faiss/faiss/utils/distances_fused/distances_fused.h +12 -1
- data/vendor/faiss/faiss/utils/distances_fused/simdlib_based.cpp +16 -293
- data/vendor/faiss/faiss/utils/distances_fused/simdlib_based_neon.cpp +57 -0
- data/vendor/faiss/faiss/utils/distances_fused/simdlib_kernel-inl.h +290 -0
- data/vendor/faiss/faiss/utils/distances_simd.cpp +5 -177
- data/vendor/faiss/faiss/utils/extra_distances.cpp +9 -8
- data/vendor/faiss/faiss/utils/extra_distances.h +32 -6
- data/vendor/faiss/faiss/utils/hamming-inl.h +13 -11
- data/vendor/faiss/faiss/utils/hamming.cpp +66 -517
- data/vendor/faiss/faiss/utils/hamming.h +92 -2
- data/vendor/faiss/faiss/utils/hamming_distance/common.h +287 -10
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +15 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512.cpp +15 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx2.h +142 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +234 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-generic.h +368 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-neon.h +322 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-rvv.h +39 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer.h +146 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +481 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_neon.cpp +15 -0
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_rvv.cpp +15 -0
- data/vendor/faiss/faiss/utils/partitioning.cpp +66 -987
- data/vendor/faiss/faiss/utils/partitioning.h +31 -0
- data/vendor/faiss/faiss/utils/popcount.h +29 -0
- data/vendor/faiss/faiss/utils/pq_code_distance.h +2 -2
- data/vendor/faiss/faiss/utils/prefetch.h +2 -2
- data/vendor/faiss/faiss/utils/quantize_lut.cpp +30 -30
- data/vendor/faiss/faiss/utils/quantize_lut.h +1 -1
- data/vendor/faiss/faiss/utils/rabitq_simd.h +57 -536
- data/vendor/faiss/faiss/utils/random.cpp +6 -6
- data/vendor/faiss/faiss/utils/simd_impl/IVFFlatScanner-inl.h +51 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_aarch64.cpp +5 -1
- data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +213 -4
- data/vendor/faiss/faiss/utils/simd_impl/distances_autovec-inl.h +163 -10
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +250 -4
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +7 -4
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +189 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_simdlib256.h +195 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_sse-inl.h +2 -1
- data/vendor/faiss/faiss/utils/{distances_fused/simdlib_based.h → simd_impl/exhaustive_L2sqr_blas_cmax.h} +5 -10
- data/vendor/faiss/faiss/utils/simd_impl/hamming_impl.h +481 -0
- data/vendor/faiss/faiss/utils/simd_impl/partitioning_avx2.cpp +14 -0
- data/vendor/faiss/faiss/utils/simd_impl/partitioning_neon.cpp +14 -0
- data/vendor/faiss/faiss/utils/simd_impl/partitioning_simdlib256.h +1085 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +355 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +477 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +55 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +55 -0
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_dispatch.h +32 -0
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +43 -0
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx2.cpp +57 -0
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx512.cpp +45 -0
- data/vendor/faiss/faiss/utils/simd_levels.cpp +17 -5
- data/vendor/faiss/faiss/utils/simd_levels.h +93 -1
- data/vendor/faiss/faiss/utils/sorting.cpp +48 -36
- data/vendor/faiss/faiss/utils/utils.cpp +5 -5
- data/vendor/faiss/faiss/utils/utils.h +3 -3
- metadata +119 -34
- data/vendor/faiss/faiss/impl/RaBitQStats.cpp +0 -29
- data/vendor/faiss/faiss/impl/RaBitQStats.h +0 -56
- data/vendor/faiss/faiss/impl/pq4_fast_scan.h +0 -224
- data/vendor/faiss/faiss/impl/pq4_fast_scan_search_1.cpp +0 -230
- data/vendor/faiss/faiss/utils/approx_topk/approx_topk.h +0 -84
- data/vendor/faiss/faiss/utils/approx_topk/avx2-inl.h +0 -196
- data/vendor/faiss/faiss/utils/approx_topk/mode.h +0 -34
- data/vendor/faiss/faiss/utils/distances_fused/avx512.h +0 -36
- data/vendor/faiss/faiss/utils/extra_distances-inl.h +0 -235
- data/vendor/faiss/faiss/utils/hamming_distance/avx2-inl.h +0 -462
- data/vendor/faiss/faiss/utils/hamming_distance/avx512-inl.h +0 -490
- data/vendor/faiss/faiss/utils/hamming_distance/generic-inl.h +0 -449
- data/vendor/faiss/faiss/utils/hamming_distance/hamdis-inl.h +0 -87
- data/vendor/faiss/faiss/utils/hamming_distance/neon-inl.h +0 -524
- data/vendor/faiss/faiss/utils/simdlib.h +0 -42
- data/vendor/faiss/faiss/utils/simdlib_avx512.h +0 -365
- /data/ext/faiss/{utils_rb.h → utils.h} +0 -0
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
#include <faiss/impl/io_macros.h>
|
|
12
12
|
|
|
13
|
+
#include <cinttypes>
|
|
14
|
+
#include <cmath>
|
|
13
15
|
#include <cstdio>
|
|
14
16
|
#include <cstdlib>
|
|
15
17
|
#include <cstring>
|
|
@@ -56,6 +58,9 @@
|
|
|
56
58
|
#ifdef FAISS_ENABLE_SVS
|
|
57
59
|
#include <faiss/impl/svs_io.h>
|
|
58
60
|
#include <faiss/svs/IndexSVSFlat.h>
|
|
61
|
+
#include <faiss/svs/IndexSVSIVF.h>
|
|
62
|
+
#include <faiss/svs/IndexSVSIVFLVQ.h>
|
|
63
|
+
#include <faiss/svs/IndexSVSIVFLeanVec.h>
|
|
59
64
|
#include <faiss/svs/IndexSVSVamana.h>
|
|
60
65
|
#include <faiss/svs/IndexSVSVamanaLVQ.h>
|
|
61
66
|
#include <faiss/svs/IndexSVSVamanaLeanVec.h>
|
|
@@ -78,6 +83,59 @@
|
|
|
78
83
|
|
|
79
84
|
namespace faiss {
|
|
80
85
|
|
|
86
|
+
namespace {
|
|
87
|
+
size_t deserialization_loop_limit_ = 0;
|
|
88
|
+
size_t deserialization_vector_byte_limit_ = uint64_t{1} << 40; // 1 TB
|
|
89
|
+
|
|
90
|
+
#ifdef FAISS_ENABLE_SVS
|
|
91
|
+
// Read and validate an SVSStorageKind from the stream. Centralizes the
|
|
92
|
+
// [0, SVS_count) range check so every SVS read site rejects out-of-range
|
|
93
|
+
// values uniformly at the deserialization boundary, instead of letting
|
|
94
|
+
// to_svs_storage_kind() surface the failure later from inside an SVS
|
|
95
|
+
// runtime load.
|
|
96
|
+
SVSStorageKind read_svs_storage_kind(IOReader* f) {
|
|
97
|
+
int sk;
|
|
98
|
+
READ1(sk);
|
|
99
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
100
|
+
sk >= 0 && sk < static_cast<int>(SVS_count),
|
|
101
|
+
"invalid SVS storage_kind=%d (must be in [0, %d))",
|
|
102
|
+
sk,
|
|
103
|
+
static_cast<int>(SVS_count));
|
|
104
|
+
return static_cast<SVSStorageKind>(sk);
|
|
105
|
+
}
|
|
106
|
+
#endif // FAISS_ENABLE_SVS
|
|
107
|
+
} // namespace
|
|
108
|
+
|
|
109
|
+
size_t get_deserialization_loop_limit() {
|
|
110
|
+
return deserialization_loop_limit_;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
void set_deserialization_loop_limit(size_t value) {
|
|
114
|
+
deserialization_loop_limit_ = value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
size_t get_deserialization_vector_byte_limit() {
|
|
118
|
+
return deserialization_vector_byte_limit_;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
void set_deserialization_vector_byte_limit(size_t value) {
|
|
122
|
+
deserialization_vector_byte_limit_ = value;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#define FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(val, field_name) \
|
|
126
|
+
do { \
|
|
127
|
+
auto limit_ = get_deserialization_loop_limit(); \
|
|
128
|
+
if (limit_ > 0) { \
|
|
129
|
+
FAISS_THROW_IF_NOT_FMT( \
|
|
130
|
+
static_cast<size_t>(val) <= limit_, \
|
|
131
|
+
"%s=%zd exceeds deserialization_loop_limit" \
|
|
132
|
+
" of %zd", \
|
|
133
|
+
field_name, \
|
|
134
|
+
static_cast<size_t>(val), \
|
|
135
|
+
limit_); \
|
|
136
|
+
} \
|
|
137
|
+
} while (0)
|
|
138
|
+
|
|
81
139
|
/*************************************************************
|
|
82
140
|
* Mmap-ing and viewing facilities
|
|
83
141
|
**************************************************************/
|
|
@@ -268,8 +326,9 @@ std::unique_ptr<VectorTransform> read_VectorTransform_up(IOReader* f) {
|
|
|
268
326
|
READ1(lt->have_bias);
|
|
269
327
|
READVECTOR(lt->A);
|
|
270
328
|
READVECTOR(lt->b);
|
|
271
|
-
FAISS_THROW_IF_NOT(
|
|
272
|
-
|
|
329
|
+
FAISS_THROW_IF_NOT(
|
|
330
|
+
lt->A.size() >= size_t(lt->d_in) * size_t(lt->d_out));
|
|
331
|
+
FAISS_THROW_IF_NOT(!lt->have_bias || lt->b.size() >= size_t(lt->d_out));
|
|
273
332
|
lt->set_is_orthonormal();
|
|
274
333
|
vt = std::move(lt);
|
|
275
334
|
} else if (h == fourcc("RmDT")) {
|
|
@@ -318,10 +377,103 @@ std::unique_ptr<VectorTransform> read_VectorTransform_up(IOReader* f) {
|
|
|
318
377
|
READ1(vt->d_in);
|
|
319
378
|
READ1(vt->d_out);
|
|
320
379
|
READ1(vt->is_trained);
|
|
380
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
381
|
+
vt->d_in >= 0,
|
|
382
|
+
"invalid VectorTransform d_in=%d (must be >= 0)",
|
|
383
|
+
vt->d_in);
|
|
384
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
385
|
+
vt->d_out >= 0,
|
|
386
|
+
"invalid VectorTransform d_out=%d (must be >= 0)",
|
|
387
|
+
vt->d_out);
|
|
388
|
+
{
|
|
389
|
+
size_t dim_product = mul_no_overflow(
|
|
390
|
+
vt->d_in, vt->d_out, "VectorTransform d_in * d_out");
|
|
391
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
392
|
+
dim_product <=
|
|
393
|
+
get_deserialization_vector_byte_limit() / sizeof(float),
|
|
394
|
+
"VectorTransform d_in * d_out would exceed "
|
|
395
|
+
"deserialization vector byte limit");
|
|
396
|
+
}
|
|
321
397
|
if (h == fourcc("HRot")) {
|
|
398
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
399
|
+
vt->d_out > 0 && (vt->d_out & (vt->d_out - 1)) == 0,
|
|
400
|
+
"invalid HadamardRotation d_out=%d (must be a power of 2 > 0)",
|
|
401
|
+
vt->d_out);
|
|
402
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
403
|
+
vt->d_out >= vt->d_in,
|
|
404
|
+
"invalid HadamardRotation d_out=%d < d_in=%d",
|
|
405
|
+
vt->d_out,
|
|
406
|
+
vt->d_in);
|
|
407
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
408
|
+
static_cast<size_t>(vt->d_out) <=
|
|
409
|
+
get_deserialization_vector_byte_limit() /
|
|
410
|
+
(3 * sizeof(float)),
|
|
411
|
+
"HadamardRotation d_out=%d would exceed deserialization byte limit",
|
|
412
|
+
vt->d_out);
|
|
322
413
|
auto* hr = dynamic_cast<HadamardRotation*>(vt.get());
|
|
414
|
+
FAISS_THROW_IF_NOT_MSG(hr, "dynamic_cast to HadamardRotation failed");
|
|
415
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
416
|
+
vt->d_in > 0,
|
|
417
|
+
"invalid HadamardRotation d_in=%d (must be > 0)",
|
|
418
|
+
vt->d_in);
|
|
419
|
+
size_t p = 1;
|
|
420
|
+
while (p < static_cast<size_t>(vt->d_in)) {
|
|
421
|
+
p <<= 1;
|
|
422
|
+
}
|
|
423
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
424
|
+
static_cast<size_t>(vt->d_out) == p,
|
|
425
|
+
"invalid HadamardRotation d_out %d for d_in %d"
|
|
426
|
+
" (d_out must be the smallest power of 2 >= d_in)",
|
|
427
|
+
vt->d_out,
|
|
428
|
+
vt->d_in);
|
|
429
|
+
size_t byte_limit = get_deserialization_vector_byte_limit();
|
|
430
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
431
|
+
p <= byte_limit / (3 * sizeof(float)),
|
|
432
|
+
"HadamardRotation d_out exceeds deserialization byte limit");
|
|
323
433
|
hr->init(hr->seed);
|
|
324
434
|
}
|
|
435
|
+
if (h == fourcc("RmDT")) {
|
|
436
|
+
auto* rdt = dynamic_cast<RemapDimensionsTransform*>(vt.get());
|
|
437
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
438
|
+
rdt, "dynamic_cast to RemapDimensionsTransform failed");
|
|
439
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
440
|
+
static_cast<int>(rdt->map.size()) >= rdt->d_out,
|
|
441
|
+
"RemapDimensionsTransform map size %d < d_out %d",
|
|
442
|
+
(int)rdt->map.size(),
|
|
443
|
+
rdt->d_out);
|
|
444
|
+
}
|
|
445
|
+
if (h == fourcc("VNrm")) {
|
|
446
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
447
|
+
vt->d_in == vt->d_out,
|
|
448
|
+
"NormalizationTransform requires d_in == d_out, "
|
|
449
|
+
"got d_in=%d d_out=%d",
|
|
450
|
+
vt->d_in,
|
|
451
|
+
vt->d_out);
|
|
452
|
+
}
|
|
453
|
+
if (h == fourcc("VCnt")) {
|
|
454
|
+
auto* ct = dynamic_cast<CenteringTransform*>(vt.get());
|
|
455
|
+
FAISS_THROW_IF_NOT_MSG(ct, "dynamic_cast to CenteringTransform failed");
|
|
456
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
457
|
+
static_cast<int>(ct->mean.size()) >= ct->d_in,
|
|
458
|
+
"CenteringTransform mean size %d < d_in %d",
|
|
459
|
+
(int)ct->mean.size(),
|
|
460
|
+
ct->d_in);
|
|
461
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
462
|
+
vt->d_in == vt->d_out,
|
|
463
|
+
"CenteringTransform requires d_in == d_out, "
|
|
464
|
+
"got d_in=%d d_out=%d",
|
|
465
|
+
vt->d_in,
|
|
466
|
+
vt->d_out);
|
|
467
|
+
}
|
|
468
|
+
if (h == fourcc("Viqt")) {
|
|
469
|
+
auto* itqt = dynamic_cast<ITQTransform*>(vt.get());
|
|
470
|
+
FAISS_THROW_IF_NOT_MSG(itqt, "dynamic_cast to ITQTransform failed");
|
|
471
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
472
|
+
static_cast<int>(itqt->mean.size()) >= itqt->d_in,
|
|
473
|
+
"ITQTransform mean size %d < d_in %d",
|
|
474
|
+
(int)itqt->mean.size(),
|
|
475
|
+
itqt->d_in);
|
|
476
|
+
}
|
|
325
477
|
return vt;
|
|
326
478
|
}
|
|
327
479
|
|
|
@@ -357,33 +509,121 @@ static void read_ArrayInvertedLists_sizes(
|
|
|
357
509
|
}
|
|
358
510
|
}
|
|
359
511
|
|
|
512
|
+
bool index_read_warn_on_null_invlists = true;
|
|
513
|
+
|
|
360
514
|
std::unique_ptr<InvertedLists> read_InvertedLists_up(
|
|
361
515
|
IOReader* f,
|
|
362
516
|
int io_flags) {
|
|
363
517
|
uint32_t h;
|
|
364
518
|
READ1(h);
|
|
365
519
|
if (h == fourcc("il00")) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
520
|
+
if (index_read_warn_on_null_invlists) {
|
|
521
|
+
fprintf(stderr,
|
|
522
|
+
"read_InvertedLists:"
|
|
523
|
+
" WARN! inverted lists not stored with IVF object\n");
|
|
524
|
+
}
|
|
369
525
|
return nullptr;
|
|
370
526
|
} else if (h == fourcc("ilpn") && !(io_flags & IO_FLAG_SKIP_IVF_DATA)) {
|
|
371
527
|
size_t nlist, code_size, n_levels;
|
|
372
528
|
READ1(nlist);
|
|
529
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(nlist, "ilpn nlist");
|
|
530
|
+
READ1(code_size);
|
|
531
|
+
READ1(n_levels);
|
|
532
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
533
|
+
n_levels > 0, "invalid ilpn n_levels %zd", n_levels);
|
|
534
|
+
constexpr size_t bs = Panorama::kDefaultBatchSize;
|
|
535
|
+
auto ailp = std::make_unique<ArrayInvertedListsPanorama>(
|
|
536
|
+
nlist, code_size, n_levels, bs);
|
|
537
|
+
std::vector<size_t> sizes(nlist);
|
|
538
|
+
read_ArrayInvertedLists_sizes(f, sizes);
|
|
539
|
+
// Do resize + read in a single pass per list. See the matching
|
|
540
|
+
// comment in the `ilar` branch below for rationale.
|
|
541
|
+
size_t byte_limit = get_deserialization_vector_byte_limit();
|
|
542
|
+
for (size_t i = 0; i < nlist; i++) {
|
|
543
|
+
size_t n = sizes[i];
|
|
544
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
545
|
+
n <= byte_limit / sizeof(idx_t),
|
|
546
|
+
"inverted list %zu ids size %zu exceeds "
|
|
547
|
+
"deserialization byte limit",
|
|
548
|
+
i,
|
|
549
|
+
n);
|
|
550
|
+
ailp->ids[i].resize(n);
|
|
551
|
+
size_t num_elems = ((n + bs - 1) / bs) * bs;
|
|
552
|
+
size_t codes_bytes = mul_no_overflow(
|
|
553
|
+
num_elems, code_size, "inverted list codes");
|
|
554
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
555
|
+
codes_bytes <= byte_limit,
|
|
556
|
+
"inverted list %zu codes size %zu exceeds "
|
|
557
|
+
"deserialization byte limit",
|
|
558
|
+
i,
|
|
559
|
+
codes_bytes);
|
|
560
|
+
ailp->codes[i].resize(codes_bytes);
|
|
561
|
+
size_t cum_sums_count = mul_no_overflow(
|
|
562
|
+
num_elems,
|
|
563
|
+
add_no_overflow(
|
|
564
|
+
n_levels, 1, "inverted list cum_sums n_levels"),
|
|
565
|
+
"inverted list cum_sums");
|
|
566
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
567
|
+
cum_sums_count <= byte_limit / sizeof(ailp->cum_sums[0][0]),
|
|
568
|
+
"inverted list %zu cum_sums size %zu exceeds "
|
|
569
|
+
"deserialization byte limit",
|
|
570
|
+
i,
|
|
571
|
+
cum_sums_count);
|
|
572
|
+
ailp->cum_sums[i].resize(cum_sums_count);
|
|
573
|
+
if (n > 0) {
|
|
574
|
+
read_vector_with_known_size(
|
|
575
|
+
ailp->codes[i], f, ailp->codes[i].size());
|
|
576
|
+
read_vector_with_known_size(ailp->ids[i], f, n);
|
|
577
|
+
read_vector_with_known_size(
|
|
578
|
+
ailp->cum_sums[i], f, ailp->cum_sums[i].size());
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return ailp;
|
|
582
|
+
} else if (h == fourcc("ilp2") && !(io_flags & IO_FLAG_SKIP_IVF_DATA)) {
|
|
583
|
+
size_t nlist, code_size, n_levels, bs;
|
|
584
|
+
READ1(nlist);
|
|
585
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(nlist, "ilp2 nlist");
|
|
373
586
|
READ1(code_size);
|
|
374
587
|
READ1(n_levels);
|
|
588
|
+
READ1(bs);
|
|
589
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
590
|
+
n_levels > 0, "invalid ilp2 n_levels %zd", n_levels);
|
|
591
|
+
FAISS_THROW_IF_NOT_FMT(bs > 0, "invalid ilp2 batch_size %zd", bs);
|
|
375
592
|
auto ailp = std::make_unique<ArrayInvertedListsPanorama>(
|
|
376
|
-
nlist, code_size, n_levels);
|
|
593
|
+
nlist, code_size, n_levels, bs);
|
|
377
594
|
std::vector<size_t> sizes(nlist);
|
|
378
595
|
read_ArrayInvertedLists_sizes(f, sizes);
|
|
596
|
+
size_t byte_limit = get_deserialization_vector_byte_limit();
|
|
379
597
|
for (size_t i = 0; i < nlist; i++) {
|
|
598
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
599
|
+
sizes[i] <= byte_limit / sizeof(idx_t),
|
|
600
|
+
"inverted list %zu ids size %zu exceeds "
|
|
601
|
+
"deserialization byte limit",
|
|
602
|
+
i,
|
|
603
|
+
sizes[i]);
|
|
380
604
|
ailp->ids[i].resize(sizes[i]);
|
|
381
|
-
size_t num_elems =
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
605
|
+
size_t num_elems = ((sizes[i] + bs - 1) / bs) * bs;
|
|
606
|
+
size_t codes_bytes = mul_no_overflow(
|
|
607
|
+
num_elems, code_size, "inverted list codes");
|
|
608
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
609
|
+
codes_bytes <= byte_limit,
|
|
610
|
+
"inverted list %zu codes size %zu exceeds "
|
|
611
|
+
"deserialization byte limit",
|
|
612
|
+
i,
|
|
613
|
+
codes_bytes);
|
|
614
|
+
ailp->codes[i].resize(codes_bytes);
|
|
615
|
+
size_t cum_sums_count = mul_no_overflow(
|
|
616
|
+
num_elems,
|
|
617
|
+
add_no_overflow(
|
|
618
|
+
n_levels, 1, "inverted list cum_sums n_levels"),
|
|
619
|
+
"inverted list cum_sums");
|
|
620
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
621
|
+
cum_sums_count <= byte_limit / sizeof(ailp->cum_sums[0][0]),
|
|
622
|
+
"inverted list %zu cum_sums size %zu exceeds "
|
|
623
|
+
"deserialization byte limit",
|
|
624
|
+
i,
|
|
625
|
+
cum_sums_count);
|
|
626
|
+
ailp->cum_sums[i].resize(cum_sums_count);
|
|
387
627
|
}
|
|
388
628
|
for (size_t i = 0; i < nlist; i++) {
|
|
389
629
|
size_t n = sizes[i];
|
|
@@ -399,18 +639,36 @@ std::unique_ptr<InvertedLists> read_InvertedLists_up(
|
|
|
399
639
|
} else if (h == fourcc("ilar") && !(io_flags & IO_FLAG_SKIP_IVF_DATA)) {
|
|
400
640
|
auto ails = std::make_unique<ArrayInvertedLists>(0, 0);
|
|
401
641
|
READ1(ails->nlist);
|
|
642
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(ails->nlist, "ilar nlist");
|
|
402
643
|
READ1(ails->code_size);
|
|
403
644
|
ails->ids.resize(ails->nlist);
|
|
404
645
|
ails->codes.resize(ails->nlist);
|
|
405
646
|
std::vector<size_t> sizes(ails->nlist);
|
|
406
647
|
read_ArrayInvertedLists_sizes(f, sizes);
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
648
|
+
// Resize + read in a single pass per list so that each list's
|
|
649
|
+
// heap allocation is released by the mmap view-substitution
|
|
650
|
+
// before the next list is allocated. This bounds peak heap to
|
|
651
|
+
// one list's worth of memory, which matters for large IVF
|
|
652
|
+
// indexes (hundreds of GB) under IO_FLAG_MMAP_IFC.
|
|
653
|
+
size_t ilar_byte_limit = get_deserialization_vector_byte_limit();
|
|
654
|
+
for (size_t i = 0; i < sizes.size(); i++) {
|
|
655
|
+
size_t n = sizes[i];
|
|
656
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
657
|
+
n <= ilar_byte_limit / sizeof(idx_t),
|
|
658
|
+
"inverted list %zu ids size %zu exceeds "
|
|
659
|
+
"deserialization byte limit",
|
|
660
|
+
i,
|
|
661
|
+
n);
|
|
662
|
+
ails->ids[i].resize(n);
|
|
663
|
+
size_t codes_bytes =
|
|
664
|
+
mul_no_overflow(n, ails->code_size, "inverted list codes");
|
|
665
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
666
|
+
codes_bytes <= ilar_byte_limit,
|
|
667
|
+
"inverted list %zu codes size %zu exceeds "
|
|
668
|
+
"deserialization byte limit",
|
|
669
|
+
i,
|
|
670
|
+
codes_bytes);
|
|
671
|
+
ails->codes[i].resize(codes_bytes);
|
|
414
672
|
if (n > 0) {
|
|
415
673
|
read_vector_with_known_size(
|
|
416
674
|
ails->codes[i],
|
|
@@ -429,6 +687,7 @@ std::unique_ptr<InvertedLists> read_InvertedLists_up(
|
|
|
429
687
|
int h2 = (io_flags & 0xffff0000) | (fourcc("il__") & 0x0000ffff);
|
|
430
688
|
size_t nlist, code_size;
|
|
431
689
|
READ1(nlist);
|
|
690
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(nlist, "ilar skip nlist");
|
|
432
691
|
READ1(code_size);
|
|
433
692
|
std::vector<size_t> sizes(nlist);
|
|
434
693
|
read_ArrayInvertedLists_sizes(f, sizes);
|
|
@@ -463,13 +722,40 @@ void read_ProductQuantizer(ProductQuantizer* pq, IOReader* f) {
|
|
|
463
722
|
READ1(pq->nbits);
|
|
464
723
|
FAISS_THROW_IF_NOT_FMT(
|
|
465
724
|
pq->M > 0, "invalid ProductQuantizer M=%zd (must be > 0)", pq->M);
|
|
725
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
726
|
+
pq->nbits <= 24, "invalid ProductQuantizer nbits=%zd", pq->nbits);
|
|
727
|
+
{
|
|
728
|
+
size_t ksub = size_t{1} << pq->nbits;
|
|
729
|
+
size_t n = mul_no_overflow(pq->d, ksub, "PQ centroids");
|
|
730
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
731
|
+
n < get_deserialization_vector_byte_limit() / sizeof(float),
|
|
732
|
+
"PQ centroids allocation would exceed deserialization byte limit");
|
|
733
|
+
// Per-subquantizer tables (e.g. IVFPQ residual norms, search-time
|
|
734
|
+
// distance tables) are sized M * ksub.
|
|
735
|
+
size_t m_ksub = mul_no_overflow(pq->M, ksub, "PQ M*ksub");
|
|
736
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
737
|
+
m_ksub <
|
|
738
|
+
get_deserialization_vector_byte_limit() / sizeof(float),
|
|
739
|
+
"PQ M*ksub allocation would exceed deserialization byte limit");
|
|
740
|
+
}
|
|
466
741
|
pq->set_derived_values();
|
|
467
742
|
READVECTOR(pq->centroids);
|
|
743
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
744
|
+
pq->centroids.size() == pq->d * pq->ksub,
|
|
745
|
+
"ProductQuantizer centroids size %zu != d * ksub (%zu * %zu = %zu)",
|
|
746
|
+
pq->centroids.size(),
|
|
747
|
+
pq->d,
|
|
748
|
+
pq->ksub,
|
|
749
|
+
pq->d * pq->ksub);
|
|
468
750
|
}
|
|
469
751
|
|
|
470
752
|
static void read_ResidualQuantizer_old(ResidualQuantizer& rq, IOReader* f) {
|
|
471
753
|
READ1(rq.d);
|
|
754
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
755
|
+
rq.d > 0, "invalid AdditiveQuantizer d %zd, must be > 0", rq.d);
|
|
472
756
|
READ1(rq.M);
|
|
757
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
758
|
+
rq.M > 0, "invalid AdditiveQuantizer M %zd, must be > 0", rq.M);
|
|
473
759
|
READVECTOR(rq.nbits);
|
|
474
760
|
FAISS_THROW_IF_NOT_FMT(
|
|
475
761
|
rq.nbits.size() == rq.M,
|
|
@@ -488,7 +774,11 @@ static void read_ResidualQuantizer_old(ResidualQuantizer& rq, IOReader* f) {
|
|
|
488
774
|
|
|
489
775
|
static void read_AdditiveQuantizer(AdditiveQuantizer& aq, IOReader* f) {
|
|
490
776
|
READ1(aq.d);
|
|
777
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
778
|
+
aq.d > 0, "invalid AdditiveQuantizer d %zd, must be > 0", aq.d);
|
|
491
779
|
READ1(aq.M);
|
|
780
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
781
|
+
aq.M > 0, "invalid AdditiveQuantizer M %zd, must be > 0", aq.M);
|
|
492
782
|
READVECTOR(aq.nbits);
|
|
493
783
|
READ1(aq.is_trained);
|
|
494
784
|
READVECTOR(aq.codebooks);
|
|
@@ -515,6 +805,97 @@ static void read_AdditiveQuantizer(AdditiveQuantizer& aq, IOReader* f) {
|
|
|
515
805
|
}
|
|
516
806
|
|
|
517
807
|
aq.set_derived_values();
|
|
808
|
+
|
|
809
|
+
// Sanity-check codebooks size without knowing the effective dimension.
|
|
810
|
+
// codebooks stores effective_d * total_codebook_size floats, so its
|
|
811
|
+
// size must be a positive multiple of total_codebook_size.
|
|
812
|
+
if (aq.total_codebook_size > 0) {
|
|
813
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
814
|
+
aq.codebooks.size() >= aq.total_codebook_size &&
|
|
815
|
+
aq.codebooks.size() % aq.total_codebook_size == 0,
|
|
816
|
+
"AdditiveQuantizer codebooks size %zd is not a positive "
|
|
817
|
+
"multiple of total_codebook_size %zd",
|
|
818
|
+
aq.codebooks.size(),
|
|
819
|
+
aq.total_codebook_size);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Validate that the codebooks vector is large enough for the given
|
|
824
|
+
// effective dimension. For a standalone AdditiveQuantizer the effective
|
|
825
|
+
// dimension equals aq.d. For a ProductAdditiveQuantizer the codebooks
|
|
826
|
+
// are sized for d_sub = d / nsplits, so callers pass that instead.
|
|
827
|
+
static void validate_codebooks_size(
|
|
828
|
+
const AdditiveQuantizer& aq,
|
|
829
|
+
size_t effective_d) {
|
|
830
|
+
size_t required = mul_no_overflow(
|
|
831
|
+
effective_d, aq.total_codebook_size, "codebooks validation");
|
|
832
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
833
|
+
aq.codebooks.size() >= required,
|
|
834
|
+
"AdditiveQuantizer codebooks size %zd too small for "
|
|
835
|
+
"d=%zd * total_codebook_size=%zd",
|
|
836
|
+
aq.codebooks.size(),
|
|
837
|
+
effective_d,
|
|
838
|
+
aq.total_codebook_size);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Validate FastScan fields shared by all FastScan index types.
|
|
842
|
+
// M, ksub, bbs must be positive; bbs must be 32-aligned; M2 must be
|
|
843
|
+
// roundup(M, 2); and ksub * M / ksub * M2 must not overflow.
|
|
844
|
+
static void validate_fastscan_fields(
|
|
845
|
+
size_t M,
|
|
846
|
+
size_t M2,
|
|
847
|
+
size_t ksub,
|
|
848
|
+
int bbs,
|
|
849
|
+
const char* index_type) {
|
|
850
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
851
|
+
M > 0 && ksub > 0,
|
|
852
|
+
"%s: invalid quantizer state (M=%zd, ksub=%zd, must be > 0)",
|
|
853
|
+
index_type,
|
|
854
|
+
M,
|
|
855
|
+
ksub);
|
|
856
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
857
|
+
bbs > 0 && bbs % 32 == 0,
|
|
858
|
+
"%s: invalid bbs=%d (must be > 0 and a multiple of 32)",
|
|
859
|
+
index_type,
|
|
860
|
+
bbs);
|
|
861
|
+
size_t expected_M2 = (M + 1) & ~static_cast<size_t>(1); // roundup(M, 2)
|
|
862
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
863
|
+
M2 == expected_M2,
|
|
864
|
+
"%s: invalid M2=%zd (expected roundup(M=%zd, 2) = %zd)",
|
|
865
|
+
index_type,
|
|
866
|
+
M2,
|
|
867
|
+
M,
|
|
868
|
+
expected_M2);
|
|
869
|
+
mul_no_overflow(ksub, M, index_type);
|
|
870
|
+
mul_no_overflow(ksub, M2, index_type);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Validate that the AdditiveQuantizer dimension matches the index header
|
|
874
|
+
// dimension. compute_LUT() treats codebooks as a (d, total_codebook_size)
|
|
875
|
+
// matrix and query vectors are sized for idx_d, so a mismatch leads to
|
|
876
|
+
// out-of-bounds reads.
|
|
877
|
+
static void validate_aq_dimension_match(
|
|
878
|
+
const AdditiveQuantizer& aq,
|
|
879
|
+
int idx_d,
|
|
880
|
+
const char* index_type) {
|
|
881
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
882
|
+
aq.d == static_cast<size_t>(idx_d),
|
|
883
|
+
"%s: AdditiveQuantizer d=%zd does not match index d=%d",
|
|
884
|
+
index_type,
|
|
885
|
+
aq.d,
|
|
886
|
+
idx_d);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
static void validate_code_size_match(
|
|
890
|
+
size_t stored,
|
|
891
|
+
size_t expected,
|
|
892
|
+
const char* index_type) {
|
|
893
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
894
|
+
stored == expected,
|
|
895
|
+
"%s code_size mismatch: stored %zd vs derived %zd",
|
|
896
|
+
index_type,
|
|
897
|
+
stored,
|
|
898
|
+
expected);
|
|
518
899
|
}
|
|
519
900
|
|
|
520
901
|
static void read_ResidualQuantizer(
|
|
@@ -522,8 +903,29 @@ static void read_ResidualQuantizer(
|
|
|
522
903
|
IOReader* f,
|
|
523
904
|
int io_flags) {
|
|
524
905
|
read_AdditiveQuantizer(rq, f);
|
|
906
|
+
validate_codebooks_size(rq, rq.d);
|
|
525
907
|
READ1(rq.train_type);
|
|
526
908
|
READ1(rq.max_beam_size);
|
|
909
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
910
|
+
rq.max_beam_size > 0,
|
|
911
|
+
"invalid max_beam_size %d, must be > 0",
|
|
912
|
+
rq.max_beam_size);
|
|
913
|
+
{
|
|
914
|
+
// Validate that the key allocation driven by max_beam_size
|
|
915
|
+
// (beam_size * M * sizeof(int32_t)) fits within the byte limit.
|
|
916
|
+
size_t beam_alloc = mul_no_overflow(
|
|
917
|
+
static_cast<size_t>(rq.max_beam_size),
|
|
918
|
+
rq.M,
|
|
919
|
+
"max_beam_size * M");
|
|
920
|
+
beam_alloc = mul_no_overflow(
|
|
921
|
+
beam_alloc, sizeof(int32_t), "max_beam_size * M * elem");
|
|
922
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
923
|
+
beam_alloc < get_deserialization_vector_byte_limit(),
|
|
924
|
+
"max_beam_size %d * M %zd would exceed "
|
|
925
|
+
"deserialization vector byte limit",
|
|
926
|
+
rq.max_beam_size,
|
|
927
|
+
rq.M);
|
|
928
|
+
}
|
|
527
929
|
if ((rq.train_type & ResidualQuantizer::Skip_codebook_tables) ||
|
|
528
930
|
(io_flags & IO_FLAG_SKIP_PRECOMPUTE_TABLE)) {
|
|
529
931
|
// don't precompute the tables
|
|
@@ -534,6 +936,7 @@ static void read_ResidualQuantizer(
|
|
|
534
936
|
|
|
535
937
|
static void read_LocalSearchQuantizer(LocalSearchQuantizer& lsq, IOReader* f) {
|
|
536
938
|
read_AdditiveQuantizer(lsq, f);
|
|
939
|
+
validate_codebooks_size(lsq, lsq.d);
|
|
537
940
|
READ1(lsq.K);
|
|
538
941
|
READ1(lsq.train_iters);
|
|
539
942
|
READ1(lsq.encode_ils_iters);
|
|
@@ -552,6 +955,17 @@ static void read_ProductAdditiveQuantizer(
|
|
|
552
955
|
IOReader* f) {
|
|
553
956
|
read_AdditiveQuantizer(paq, f);
|
|
554
957
|
READ1(paq.nsplits);
|
|
958
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
959
|
+
paq.nsplits > 0,
|
|
960
|
+
"invalid ProductAdditiveQuantizer nsplits %zd (must be > 0)",
|
|
961
|
+
paq.nsplits);
|
|
962
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(paq.nsplits, "nsplits");
|
|
963
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
964
|
+
paq.d % paq.nsplits == 0,
|
|
965
|
+
"ProductAdditiveQuantizer d=%zd not divisible by nsplits=%zd",
|
|
966
|
+
paq.d,
|
|
967
|
+
paq.nsplits);
|
|
968
|
+
validate_codebooks_size(paq, paq.d / paq.nsplits);
|
|
555
969
|
}
|
|
556
970
|
|
|
557
971
|
static void read_ProductResidualQuantizer(
|
|
@@ -560,9 +974,19 @@ static void read_ProductResidualQuantizer(
|
|
|
560
974
|
int io_flags) {
|
|
561
975
|
read_ProductAdditiveQuantizer(prq, f);
|
|
562
976
|
|
|
977
|
+
size_t d_sub = prq.d / prq.nsplits;
|
|
563
978
|
for (size_t i = 0; i < prq.nsplits; i++) {
|
|
564
979
|
auto rq = std::make_unique<ResidualQuantizer>();
|
|
565
980
|
read_ResidualQuantizer(*rq, f, io_flags);
|
|
981
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
982
|
+
rq->d == d_sub,
|
|
983
|
+
"ProductResidualQuantizer sub-quantizer %zd has d=%zd, "
|
|
984
|
+
"expected d_sub=%zd (d=%zd / nsplits=%zd)",
|
|
985
|
+
i,
|
|
986
|
+
rq->d,
|
|
987
|
+
d_sub,
|
|
988
|
+
prq.d,
|
|
989
|
+
prq.nsplits);
|
|
566
990
|
prq.quantizers.push_back(rq.release());
|
|
567
991
|
}
|
|
568
992
|
}
|
|
@@ -572,20 +996,112 @@ static void read_ProductLocalSearchQuantizer(
|
|
|
572
996
|
IOReader* f) {
|
|
573
997
|
read_ProductAdditiveQuantizer(plsq, f);
|
|
574
998
|
|
|
999
|
+
size_t d_sub = plsq.d / plsq.nsplits;
|
|
575
1000
|
for (size_t i = 0; i < plsq.nsplits; i++) {
|
|
576
1001
|
auto lsq = std::make_unique<LocalSearchQuantizer>();
|
|
577
1002
|
read_LocalSearchQuantizer(*lsq, f);
|
|
1003
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1004
|
+
lsq->d == d_sub,
|
|
1005
|
+
"ProductLocalSearchQuantizer sub-quantizer %zd has d=%zd, "
|
|
1006
|
+
"expected d_sub=%zd (d=%zd / nsplits=%zd)",
|
|
1007
|
+
i,
|
|
1008
|
+
lsq->d,
|
|
1009
|
+
d_sub,
|
|
1010
|
+
plsq.d,
|
|
1011
|
+
plsq.nsplits);
|
|
578
1012
|
plsq.quantizers.push_back(lsq.release());
|
|
579
1013
|
}
|
|
580
1014
|
}
|
|
581
1015
|
|
|
582
|
-
void read_ScalarQuantizer(
|
|
583
|
-
|
|
1016
|
+
void read_ScalarQuantizer(
|
|
1017
|
+
ScalarQuantizer* ivsc,
|
|
1018
|
+
IOReader* f,
|
|
1019
|
+
const Index& idx) {
|
|
1020
|
+
int qtype_int;
|
|
1021
|
+
READ1(qtype_int);
|
|
1022
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1023
|
+
qtype_int >= ScalarQuantizer::QT_8bit &&
|
|
1024
|
+
qtype_int < ScalarQuantizer::QT_count,
|
|
1025
|
+
"invalid ScalarQuantizer qtype %d",
|
|
1026
|
+
qtype_int);
|
|
1027
|
+
ivsc->qtype = static_cast<ScalarQuantizer::QuantizerType>(qtype_int);
|
|
584
1028
|
READ1(ivsc->rangestat);
|
|
585
1029
|
READ1(ivsc->rangestat_arg);
|
|
586
1030
|
READ1(ivsc->d);
|
|
587
1031
|
READ1(ivsc->code_size);
|
|
1032
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1033
|
+
static_cast<size_t>(idx.d) == ivsc->d,
|
|
1034
|
+
"ScalarQuantizer d %zu != index header d %d",
|
|
1035
|
+
ivsc->d,
|
|
1036
|
+
idx.d);
|
|
588
1037
|
READVECTOR(ivsc->trained);
|
|
1038
|
+
// Validate trained vector size matches the quantizer type and dimension.
|
|
1039
|
+
// UNIFORM/NON_UNIFORM qtypes require training data; other qtypes
|
|
1040
|
+
// (fp16, bf16, 8bit_direct*) need none.
|
|
1041
|
+
// An untrained index (is_trained == false) legitimately has
|
|
1042
|
+
// trained.size() == 0, so we allow that case.
|
|
1043
|
+
{
|
|
1044
|
+
size_t expected = 0;
|
|
1045
|
+
switch (ivsc->qtype) {
|
|
1046
|
+
case ScalarQuantizer::QT_4bit_uniform:
|
|
1047
|
+
case ScalarQuantizer::QT_8bit_uniform:
|
|
1048
|
+
expected = 2;
|
|
1049
|
+
break;
|
|
1050
|
+
case ScalarQuantizer::QT_4bit:
|
|
1051
|
+
case ScalarQuantizer::QT_8bit:
|
|
1052
|
+
case ScalarQuantizer::QT_6bit:
|
|
1053
|
+
expected = 2 * ivsc->d;
|
|
1054
|
+
break;
|
|
1055
|
+
case ScalarQuantizer::QT_fp16:
|
|
1056
|
+
case ScalarQuantizer::QT_bf16:
|
|
1057
|
+
case ScalarQuantizer::QT_8bit_direct:
|
|
1058
|
+
case ScalarQuantizer::QT_8bit_direct_signed:
|
|
1059
|
+
case ScalarQuantizer::QT_0bit:
|
|
1060
|
+
case ScalarQuantizer::QT_count:
|
|
1061
|
+
expected = 0;
|
|
1062
|
+
break;
|
|
1063
|
+
case ScalarQuantizer::QT_1bit_tqmse:
|
|
1064
|
+
expected = 2 + 1; // 2^bits centroids + (2^bits - 1) boundaries
|
|
1065
|
+
break;
|
|
1066
|
+
case ScalarQuantizer::QT_2bit_tqmse:
|
|
1067
|
+
expected = 4 + 3;
|
|
1068
|
+
break;
|
|
1069
|
+
case ScalarQuantizer::QT_3bit_tqmse:
|
|
1070
|
+
expected = 8 + 7;
|
|
1071
|
+
break;
|
|
1072
|
+
case ScalarQuantizer::QT_4bit_tqmse:
|
|
1073
|
+
expected = 16 + 15;
|
|
1074
|
+
break;
|
|
1075
|
+
case ScalarQuantizer::QT_8bit_tqmse:
|
|
1076
|
+
expected = 256 + 255;
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1079
|
+
if (ivsc->trained.empty() && expected > 0) {
|
|
1080
|
+
// Empty trained is only valid for untrained indices.
|
|
1081
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1082
|
+
!idx.is_trained,
|
|
1083
|
+
"ScalarQuantizer trained size 0 != expected %zu "
|
|
1084
|
+
"for qtype %d, d %zu (index is marked as trained)",
|
|
1085
|
+
expected,
|
|
1086
|
+
(int)ivsc->qtype,
|
|
1087
|
+
ivsc->d);
|
|
1088
|
+
} else {
|
|
1089
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1090
|
+
ivsc->trained.size() == expected,
|
|
1091
|
+
"ScalarQuantizer trained size %zu != expected %zu "
|
|
1092
|
+
"for qtype %d, d %zu",
|
|
1093
|
+
ivsc->trained.size(),
|
|
1094
|
+
expected,
|
|
1095
|
+
(int)ivsc->qtype,
|
|
1096
|
+
ivsc->d);
|
|
1097
|
+
if (expected > 0) {
|
|
1098
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
1099
|
+
idx.is_trained,
|
|
1100
|
+
"ScalarQuantizer has training data but "
|
|
1101
|
+
"index header is_trained is false");
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
589
1105
|
ivsc->set_derived_sizes();
|
|
590
1106
|
}
|
|
591
1107
|
|
|
@@ -723,6 +1239,9 @@ static void read_HNSW(HNSW& hnsw, IOReader* f) {
|
|
|
723
1239
|
static void read_NSG(NSG& nsg, IOReader* f) {
|
|
724
1240
|
READ1(nsg.ntotal);
|
|
725
1241
|
READ1(nsg.R);
|
|
1242
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(nsg.ntotal, "nsg.ntotal");
|
|
1243
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(nsg.R, "nsg.R");
|
|
1244
|
+
FAISS_THROW_IF_NOT_FMT(nsg.R > 0, "invalid NSG R %d (must be > 0)", nsg.R);
|
|
726
1245
|
READ1(nsg.L);
|
|
727
1246
|
READ1(nsg.C);
|
|
728
1247
|
READ1(nsg.search_L);
|
|
@@ -785,6 +1304,26 @@ static void read_NNDescent(NNDescent& nnd, IOReader* f) {
|
|
|
785
1304
|
nnd.ntotal >= 0, "invalid NNDescent ntotal %d", nnd.ntotal);
|
|
786
1305
|
|
|
787
1306
|
READVECTOR(nnd.final_graph);
|
|
1307
|
+
// Validate neighbor IDs in the graph
|
|
1308
|
+
if (nnd.has_built && nnd.K > 0 && nnd.ntotal > 0) {
|
|
1309
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1310
|
+
nnd.final_graph.size() == (size_t)nnd.ntotal * (size_t)nnd.K,
|
|
1311
|
+
"NNDescent final_graph size %zu != ntotal * K (%d * %d = %zu)",
|
|
1312
|
+
nnd.final_graph.size(),
|
|
1313
|
+
nnd.ntotal,
|
|
1314
|
+
nnd.K,
|
|
1315
|
+
(size_t)nnd.ntotal * (size_t)nnd.K);
|
|
1316
|
+
for (size_t i = 0; i < nnd.final_graph.size(); i++) {
|
|
1317
|
+
int id = nnd.final_graph[i];
|
|
1318
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1319
|
+
id >= -1 && id < nnd.ntotal,
|
|
1320
|
+
"NNDescent final_graph[%zu] = %d out of range "
|
|
1321
|
+
"[-1, %d)",
|
|
1322
|
+
i,
|
|
1323
|
+
id,
|
|
1324
|
+
nnd.ntotal);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
788
1327
|
}
|
|
789
1328
|
|
|
790
1329
|
std::unique_ptr<ProductQuantizer> read_ProductQuantizer_up(const char* fname) {
|
|
@@ -809,6 +1348,7 @@ ProductQuantizer* read_ProductQuantizer(IOReader* reader) {
|
|
|
809
1348
|
static void read_RaBitQuantizer(
|
|
810
1349
|
RaBitQuantizer& rabitq,
|
|
811
1350
|
IOReader* f,
|
|
1351
|
+
int expected_d,
|
|
812
1352
|
bool multi_bit = true) {
|
|
813
1353
|
READ1(rabitq.d);
|
|
814
1354
|
READ1(rabitq.code_size);
|
|
@@ -821,6 +1361,12 @@ static void read_RaBitQuantizer(
|
|
|
821
1361
|
} else {
|
|
822
1362
|
rabitq.nb_bits = 1;
|
|
823
1363
|
}
|
|
1364
|
+
|
|
1365
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1366
|
+
rabitq.d == static_cast<size_t>(expected_d),
|
|
1367
|
+
"RaBitQuantizer dimension mismatch: rabitq.d=%zu vs index d=%d",
|
|
1368
|
+
rabitq.d,
|
|
1369
|
+
expected_d);
|
|
824
1370
|
}
|
|
825
1371
|
|
|
826
1372
|
void read_direct_map(DirectMap* dm, IOReader* f) {
|
|
@@ -845,6 +1391,7 @@ void read_ivf_header(
|
|
|
845
1391
|
std::vector<std::vector<idx_t>>* ids) {
|
|
846
1392
|
read_index_header(*ivf, f);
|
|
847
1393
|
READ1(ivf->nlist);
|
|
1394
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(ivf->nlist, "nlist");
|
|
848
1395
|
READ1(ivf->nprobe);
|
|
849
1396
|
ivf->quantizer = read_index(f);
|
|
850
1397
|
ivf->own_fields = true;
|
|
@@ -915,6 +1462,20 @@ static std::unique_ptr<IndexIVFPQ> read_ivfpq(
|
|
|
915
1462
|
read_ProductQuantizer(&ivfpqr->refine_pq, f);
|
|
916
1463
|
READVECTOR(ivfpqr->refine_codes);
|
|
917
1464
|
READ1(ivfpqr->k_factor);
|
|
1465
|
+
// k_factor multiplies k to size search-time allocations
|
|
1466
|
+
// (n * k * k_factor labels + distances). Defaults are 1
|
|
1467
|
+
// (IndexRefine) and 4 (IndexIVFPQR); AutoTune explores
|
|
1468
|
+
// powers-of-two up to 64. Cap at 1000 to leave ample
|
|
1469
|
+
// headroom beyond any known usage while still blocking
|
|
1470
|
+
// OOM from crafted files (same cap as beam_factor in
|
|
1471
|
+
// ResidualCoarseQuantizer).
|
|
1472
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1473
|
+
std::isfinite(ivfpqr->k_factor) &&
|
|
1474
|
+
ivfpqr->k_factor >= 1.0f &&
|
|
1475
|
+
ivfpqr->k_factor <= 1000.0f,
|
|
1476
|
+
"k_factor %.6g out of valid range [1, 1000]"
|
|
1477
|
+
" for IndexIVFPQR",
|
|
1478
|
+
ivfpqr->k_factor);
|
|
918
1479
|
}
|
|
919
1480
|
}
|
|
920
1481
|
return ivpq;
|
|
@@ -975,6 +1536,10 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
975
1536
|
READVECTOR(idxl->thresholds);
|
|
976
1537
|
int code_size_i;
|
|
977
1538
|
READ1(code_size_i);
|
|
1539
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1540
|
+
code_size_i >= 0,
|
|
1541
|
+
"IndexLSH invalid code_size %d (must be >= 0)",
|
|
1542
|
+
code_size_i);
|
|
978
1543
|
idxl->code_size = code_size_i;
|
|
979
1544
|
if (h == fourcc("IxHE")) {
|
|
980
1545
|
FAISS_THROW_IF_NOT_FMT(
|
|
@@ -985,6 +1550,8 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
985
1550
|
// leak
|
|
986
1551
|
idxl->code_size *= 8;
|
|
987
1552
|
}
|
|
1553
|
+
validate_code_size_match(
|
|
1554
|
+
idxl->code_size, (idxl->nbits + 7) / 8, "IndexLSH");
|
|
988
1555
|
{
|
|
989
1556
|
// Read, dereference, discard.
|
|
990
1557
|
auto sub_vt = read_VectorTransform_up(f);
|
|
@@ -1007,6 +1574,8 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1007
1574
|
read_ProductQuantizer(&idxp->pq, f);
|
|
1008
1575
|
idxp->code_size = idxp->pq.code_size;
|
|
1009
1576
|
read_vector(idxp->codes, f);
|
|
1577
|
+
FAISS_THROW_IF_NOT(
|
|
1578
|
+
idxp->codes.size() == idxp->ntotal * idxp->code_size);
|
|
1010
1579
|
if (h == fourcc("IxPo") || h == fourcc("IxPq")) {
|
|
1011
1580
|
READ1(idxp->search_type);
|
|
1012
1581
|
READ1(idxp->encode_signs);
|
|
@@ -1027,40 +1596,102 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1027
1596
|
} else {
|
|
1028
1597
|
read_ResidualQuantizer(idxr->rq, f, io_flags);
|
|
1029
1598
|
}
|
|
1599
|
+
validate_aq_dimension_match(
|
|
1600
|
+
idxr->rq, idxr->d, "IndexResidualQuantizer");
|
|
1030
1601
|
READ1(idxr->code_size);
|
|
1602
|
+
validate_code_size_match(
|
|
1603
|
+
idxr->code_size, idxr->rq.code_size, "IndexResidualQuantizer");
|
|
1031
1604
|
read_vector(idxr->codes, f);
|
|
1605
|
+
FAISS_THROW_IF_NOT(
|
|
1606
|
+
idxr->codes.size() == idxr->ntotal * idxr->code_size);
|
|
1032
1607
|
idx = std::move(idxr);
|
|
1033
1608
|
} else if (h == fourcc("IxLS")) {
|
|
1034
1609
|
auto idxr = std::make_unique<IndexLocalSearchQuantizer>();
|
|
1035
1610
|
read_index_header(*idxr, f);
|
|
1036
1611
|
read_LocalSearchQuantizer(idxr->lsq, f);
|
|
1612
|
+
validate_aq_dimension_match(
|
|
1613
|
+
idxr->lsq, idxr->d, "IndexLocalSearchQuantizer");
|
|
1037
1614
|
READ1(idxr->code_size);
|
|
1615
|
+
validate_code_size_match(
|
|
1616
|
+
idxr->code_size,
|
|
1617
|
+
idxr->lsq.code_size,
|
|
1618
|
+
"IndexLocalSearchQuantizer");
|
|
1038
1619
|
read_vector(idxr->codes, f);
|
|
1620
|
+
FAISS_THROW_IF_NOT(
|
|
1621
|
+
idxr->codes.size() == idxr->ntotal * idxr->code_size);
|
|
1039
1622
|
idx = std::move(idxr);
|
|
1040
1623
|
} else if (h == fourcc("IxPR")) {
|
|
1041
1624
|
auto idxpr = std::make_unique<IndexProductResidualQuantizer>();
|
|
1042
1625
|
read_index_header(*idxpr, f);
|
|
1043
1626
|
read_ProductResidualQuantizer(idxpr->prq, f, io_flags);
|
|
1627
|
+
validate_aq_dimension_match(
|
|
1628
|
+
idxpr->prq, idxpr->d, "IndexProductResidualQuantizer");
|
|
1044
1629
|
READ1(idxpr->code_size);
|
|
1630
|
+
validate_code_size_match(
|
|
1631
|
+
idxpr->code_size,
|
|
1632
|
+
idxpr->prq.code_size,
|
|
1633
|
+
"IndexProductResidualQuantizer");
|
|
1045
1634
|
read_vector(idxpr->codes, f);
|
|
1635
|
+
FAISS_THROW_IF_NOT(
|
|
1636
|
+
idxpr->codes.size() == idxpr->ntotal * idxpr->code_size);
|
|
1046
1637
|
idx = std::move(idxpr);
|
|
1047
1638
|
} else if (h == fourcc("IxPL")) {
|
|
1048
1639
|
auto idxpl = std::make_unique<IndexProductLocalSearchQuantizer>();
|
|
1049
1640
|
read_index_header(*idxpl, f);
|
|
1050
1641
|
read_ProductLocalSearchQuantizer(idxpl->plsq, f);
|
|
1642
|
+
validate_aq_dimension_match(
|
|
1643
|
+
idxpl->plsq, idxpl->d, "IndexProductLocalSearchQuantizer");
|
|
1051
1644
|
READ1(idxpl->code_size);
|
|
1645
|
+
validate_code_size_match(
|
|
1646
|
+
idxpl->code_size,
|
|
1647
|
+
idxpl->plsq.code_size,
|
|
1648
|
+
"IndexProductLocalSearchQuantizer");
|
|
1052
1649
|
read_vector(idxpl->codes, f);
|
|
1650
|
+
FAISS_THROW_IF_NOT(
|
|
1651
|
+
idxpl->codes.size() == idxpl->ntotal * idxpl->code_size);
|
|
1053
1652
|
idx = std::move(idxpl);
|
|
1054
1653
|
} else if (h == fourcc("ImRQ")) {
|
|
1055
1654
|
auto idxr = std::make_unique<ResidualCoarseQuantizer>();
|
|
1056
1655
|
read_index_header(*idxr, f);
|
|
1057
1656
|
read_ResidualQuantizer(idxr->rq, f, io_flags);
|
|
1657
|
+
validate_aq_dimension_match(
|
|
1658
|
+
idxr->rq, idxr->d, "ResidualCoarseQuantizer");
|
|
1058
1659
|
READ1(idxr->beam_factor);
|
|
1059
1660
|
if (io_flags & IO_FLAG_SKIP_PRECOMPUTE_TABLE) {
|
|
1060
1661
|
// then we force the beam factor to -1
|
|
1061
1662
|
// which skips the table precomputation.
|
|
1062
1663
|
idxr->beam_factor = -1;
|
|
1063
1664
|
}
|
|
1665
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
1666
|
+
static_cast<size_t>(idxr->ntotal) <
|
|
1667
|
+
get_deserialization_vector_byte_limit() / sizeof(float),
|
|
1668
|
+
"ResidualCoarseQuantizer centroid norms allocation would "
|
|
1669
|
+
"exceed deserialization byte limit");
|
|
1670
|
+
// Validate beam_factor to prevent overflow in search() where
|
|
1671
|
+
// beam_size = int(k * beam_factor) and allocations scale with it.
|
|
1672
|
+
if (idxr->beam_factor > 0) {
|
|
1673
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1674
|
+
idxr->beam_factor <= 1000.0f,
|
|
1675
|
+
"beam_factor %.6g is too large (max 1000)",
|
|
1676
|
+
idxr->beam_factor);
|
|
1677
|
+
}
|
|
1678
|
+
// Validate ntotal against byte limit: search() allocates
|
|
1679
|
+
// O(ntotal * M) when beam_size is capped to ntotal.
|
|
1680
|
+
{
|
|
1681
|
+
size_t ntotal_alloc = mul_no_overflow(
|
|
1682
|
+
static_cast<size_t>(idxr->ntotal),
|
|
1683
|
+
idxr->rq.M,
|
|
1684
|
+
"ntotal * M");
|
|
1685
|
+
ntotal_alloc = mul_no_overflow(
|
|
1686
|
+
ntotal_alloc, sizeof(int32_t), "ntotal * M * elem");
|
|
1687
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1688
|
+
ntotal_alloc < get_deserialization_vector_byte_limit(),
|
|
1689
|
+
"ResidualCoarseQuantizer ntotal %" PRId64
|
|
1690
|
+
" * M %zd would exceed "
|
|
1691
|
+
"deserialization vector byte limit",
|
|
1692
|
+
idxr->ntotal,
|
|
1693
|
+
idxr->rq.M);
|
|
1694
|
+
}
|
|
1064
1695
|
idxr->set_beam_factor(idxr->beam_factor);
|
|
1065
1696
|
idx = std::move(idxr);
|
|
1066
1697
|
} else if (
|
|
@@ -1095,10 +1726,13 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1095
1726
|
read_ProductResidualQuantizer(
|
|
1096
1727
|
*(ProductResidualQuantizer*)idxaqfs->aq, f, io_flags);
|
|
1097
1728
|
}
|
|
1729
|
+
validate_aq_dimension_match(
|
|
1730
|
+
*idxaqfs->aq, idxaqfs->d, "IndexAdditiveQuantizerFastScan");
|
|
1098
1731
|
|
|
1099
1732
|
READ1(idxaqfs->implem);
|
|
1100
1733
|
READ1(idxaqfs->bbs);
|
|
1101
1734
|
READ1(idxaqfs->qbs);
|
|
1735
|
+
FAISS_THROW_IF_NOT_MSG(idxaqfs->qbs >= 0, "qbs must be non-negative");
|
|
1102
1736
|
|
|
1103
1737
|
READ1(idxaqfs->M);
|
|
1104
1738
|
READ1(idxaqfs->nbits);
|
|
@@ -1112,6 +1746,14 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1112
1746
|
READ1(idxaqfs->max_train_points);
|
|
1113
1747
|
|
|
1114
1748
|
READVECTOR(idxaqfs->codes);
|
|
1749
|
+
|
|
1750
|
+
validate_fastscan_fields(
|
|
1751
|
+
idxaqfs->M,
|
|
1752
|
+
idxaqfs->M2,
|
|
1753
|
+
idxaqfs->ksub,
|
|
1754
|
+
idxaqfs->bbs,
|
|
1755
|
+
"IndexAdditiveQuantizerFastScan");
|
|
1756
|
+
|
|
1115
1757
|
idx = std::move(idxaqfs);
|
|
1116
1758
|
} else if (
|
|
1117
1759
|
h == fourcc("IVLf") || h == fourcc("IVRf") || h == fourcc("NPLf") ||
|
|
@@ -1146,11 +1788,14 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1146
1788
|
read_ProductResidualQuantizer(
|
|
1147
1789
|
*(ProductResidualQuantizer*)ivaqfs->aq, f, io_flags);
|
|
1148
1790
|
}
|
|
1791
|
+
validate_aq_dimension_match(
|
|
1792
|
+
*ivaqfs->aq, ivaqfs->d, "IndexIVFAdditiveQuantizerFastScan");
|
|
1149
1793
|
|
|
1150
1794
|
READ1(ivaqfs->by_residual);
|
|
1151
1795
|
READ1(ivaqfs->implem);
|
|
1152
1796
|
READ1(ivaqfs->bbs);
|
|
1153
1797
|
READ1(ivaqfs->qbs);
|
|
1798
|
+
FAISS_THROW_IF_NOT_MSG(ivaqfs->qbs >= 0, "qbs must be non-negative");
|
|
1154
1799
|
|
|
1155
1800
|
READ1(ivaqfs->M);
|
|
1156
1801
|
READ1(ivaqfs->nbits);
|
|
@@ -1165,6 +1810,14 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1165
1810
|
|
|
1166
1811
|
read_InvertedLists(*ivaqfs, f, io_flags);
|
|
1167
1812
|
ivaqfs->init_code_packer();
|
|
1813
|
+
|
|
1814
|
+
validate_fastscan_fields(
|
|
1815
|
+
ivaqfs->M,
|
|
1816
|
+
ivaqfs->M2,
|
|
1817
|
+
ivaqfs->ksub,
|
|
1818
|
+
ivaqfs->bbs,
|
|
1819
|
+
"IndexIVFAdditiveQuantizerFastScan");
|
|
1820
|
+
|
|
1168
1821
|
idx = std::move(ivaqfs);
|
|
1169
1822
|
} else if (h == fourcc("IvFl") || h == fourcc("IvFL")) { // legacy
|
|
1170
1823
|
auto ivfl = std::make_unique<IndexIVFFlat>();
|
|
@@ -1198,7 +1851,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1198
1851
|
"invalid IVFFlatDedup instances table size: %zd "
|
|
1199
1852
|
"(must be even)",
|
|
1200
1853
|
tab.size());
|
|
1201
|
-
for (
|
|
1854
|
+
for (size_t i = 0; i < tab.size(); i += 2) {
|
|
1202
1855
|
std::pair<idx_t, idx_t> pair(tab[i], tab[i + 1]);
|
|
1203
1856
|
ivfl->instances.insert(pair);
|
|
1204
1857
|
}
|
|
@@ -1210,6 +1863,15 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1210
1863
|
read_ivf_header(ivfp.get(), f);
|
|
1211
1864
|
ivfp->code_size = ivfp->d * sizeof(float);
|
|
1212
1865
|
READ1(ivfp->n_levels);
|
|
1866
|
+
ivfp->batch_size = Panorama::kDefaultBatchSize;
|
|
1867
|
+
read_InvertedLists(*ivfp, f, io_flags);
|
|
1868
|
+
idx = std::move(ivfp);
|
|
1869
|
+
} else if (h == fourcc("IwP2")) {
|
|
1870
|
+
auto ivfp = std::make_unique<IndexIVFFlatPanorama>();
|
|
1871
|
+
read_ivf_header(ivfp.get(), f);
|
|
1872
|
+
ivfp->code_size = ivfp->d * sizeof(float);
|
|
1873
|
+
READ1(ivfp->n_levels);
|
|
1874
|
+
READ1(ivfp->batch_size);
|
|
1213
1875
|
read_InvertedLists(*ivfp, f, io_flags);
|
|
1214
1876
|
idx = std::move(ivfp);
|
|
1215
1877
|
} else if (h == fourcc("IwFl")) {
|
|
@@ -1221,7 +1883,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1221
1883
|
} else if (h == fourcc("IxSQ")) {
|
|
1222
1884
|
auto idxs = std::make_unique<IndexScalarQuantizer>();
|
|
1223
1885
|
read_index_header(*idxs, f);
|
|
1224
|
-
read_ScalarQuantizer(&idxs->sq, f);
|
|
1886
|
+
read_ScalarQuantizer(&idxs->sq, f, *idxs);
|
|
1225
1887
|
read_vector(idxs->codes, f);
|
|
1226
1888
|
idxs->code_size = idxs->sq.code_size;
|
|
1227
1889
|
idx = std::move(idxs);
|
|
@@ -1248,6 +1910,27 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1248
1910
|
nsq,
|
|
1249
1911
|
dsq);
|
|
1250
1912
|
auto idxl = std::make_unique<IndexLattice>(d, nsq, scale_nbit, r2);
|
|
1913
|
+
// IndexLattice is a lossy compressor: code_size should be
|
|
1914
|
+
// smaller than the uncompressed vector (d floats). A corrupt
|
|
1915
|
+
// scale_nbit can overflow the total_nbit computation, producing
|
|
1916
|
+
// a code_size that wraps to a huge value.
|
|
1917
|
+
{
|
|
1918
|
+
size_t max_code_size = mul_no_overflow(
|
|
1919
|
+
static_cast<size_t>(d),
|
|
1920
|
+
sizeof(float),
|
|
1921
|
+
"IndexLattice uncompressed vector size");
|
|
1922
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1923
|
+
idxl->code_size <= max_code_size,
|
|
1924
|
+
"IndexLattice code_size %zd exceeds uncompressed "
|
|
1925
|
+
"vector size %zd (likely corrupt scale_nbit=%d, "
|
|
1926
|
+
"d=%d, nsq=%d, r2=%d)",
|
|
1927
|
+
idxl->code_size,
|
|
1928
|
+
max_code_size,
|
|
1929
|
+
scale_nbit,
|
|
1930
|
+
d,
|
|
1931
|
+
nsq,
|
|
1932
|
+
r2);
|
|
1933
|
+
}
|
|
1251
1934
|
read_index_header(*idxl, f);
|
|
1252
1935
|
READVECTOR(idxl->trained);
|
|
1253
1936
|
idx = std::move(idxl);
|
|
@@ -1255,17 +1938,21 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1255
1938
|
auto ivsc = std::make_unique<IndexIVFScalarQuantizer>();
|
|
1256
1939
|
std::vector<std::vector<idx_t>> ids;
|
|
1257
1940
|
read_ivf_header(ivsc.get(), f, &ids);
|
|
1258
|
-
read_ScalarQuantizer(&ivsc->sq, f);
|
|
1941
|
+
read_ScalarQuantizer(&ivsc->sq, f, *ivsc);
|
|
1259
1942
|
READ1(ivsc->code_size);
|
|
1943
|
+
validate_code_size_match(
|
|
1944
|
+
ivsc->code_size, ivsc->sq.code_size, "IndexIVFScalarQuantizer");
|
|
1260
1945
|
ArrayInvertedLists* ail = set_array_invlist(ivsc.get(), ids);
|
|
1261
|
-
for (
|
|
1946
|
+
for (size_t i = 0; i < ivsc->nlist; i++)
|
|
1262
1947
|
READVECTOR(ail->codes[i]);
|
|
1263
1948
|
idx = std::move(ivsc);
|
|
1264
1949
|
} else if (h == fourcc("IwSQ") || h == fourcc("IwSq")) {
|
|
1265
1950
|
auto ivsc = std::make_unique<IndexIVFScalarQuantizer>();
|
|
1266
1951
|
read_ivf_header(ivsc.get(), f);
|
|
1267
|
-
read_ScalarQuantizer(&ivsc->sq, f);
|
|
1952
|
+
read_ScalarQuantizer(&ivsc->sq, f, *ivsc);
|
|
1268
1953
|
READ1(ivsc->code_size);
|
|
1954
|
+
validate_code_size_match(
|
|
1955
|
+
ivsc->code_size, ivsc->sq.code_size, "IndexIVFScalarQuantizer");
|
|
1269
1956
|
if (h == fourcc("IwSQ")) {
|
|
1270
1957
|
ivsc->by_residual = true;
|
|
1271
1958
|
} else {
|
|
@@ -1302,6 +1989,12 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1302
1989
|
read_ProductResidualQuantizer(
|
|
1303
1990
|
*(ProductResidualQuantizer*)iva->aq, f, io_flags);
|
|
1304
1991
|
}
|
|
1992
|
+
validate_aq_dimension_match(
|
|
1993
|
+
*iva->aq, iva->d, "IndexIVFAdditiveQuantizer");
|
|
1994
|
+
validate_code_size_match(
|
|
1995
|
+
iva->code_size,
|
|
1996
|
+
iva->aq->code_size,
|
|
1997
|
+
"IndexIVFAdditiveQuantizer");
|
|
1305
1998
|
READ1(iva->by_residual);
|
|
1306
1999
|
READ1(iva->use_precomputed_table);
|
|
1307
2000
|
read_InvertedLists(*iva, f, io_flags);
|
|
@@ -1337,6 +2030,18 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1337
2030
|
indep->index_ivf = dynamic_cast<IndexIVF*>(ivf_idx.get());
|
|
1338
2031
|
FAISS_THROW_IF_NOT(indep->index_ivf);
|
|
1339
2032
|
ivf_idx.release();
|
|
2033
|
+
if (indep->vt) {
|
|
2034
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2035
|
+
indep->vt->d_in == indep->d,
|
|
2036
|
+
"IndexIVFIndependentQuantizer: vt->d_in (%d) != index d (%d)",
|
|
2037
|
+
indep->vt->d_in,
|
|
2038
|
+
indep->d);
|
|
2039
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2040
|
+
indep->vt->d_out == indep->index_ivf->d,
|
|
2041
|
+
"IndexIVFIndependentQuantizer: vt->d_out (%d) != index_ivf->d (%d)",
|
|
2042
|
+
indep->vt->d_out,
|
|
2043
|
+
indep->index_ivf->d);
|
|
2044
|
+
}
|
|
1340
2045
|
if (auto index_ivfpq = dynamic_cast<IndexIVFPQ*>(indep->index_ivf)) {
|
|
1341
2046
|
READ1(index_ivfpq->use_precomputed_table);
|
|
1342
2047
|
}
|
|
@@ -1351,10 +2056,46 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1351
2056
|
} else {
|
|
1352
2057
|
READ1(nt);
|
|
1353
2058
|
}
|
|
2059
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2060
|
+
nt >= 0,
|
|
2061
|
+
"invalid VectorTransform chain length %d (must be >= 0)",
|
|
2062
|
+
nt);
|
|
2063
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(
|
|
2064
|
+
nt, "VectorTransform chain length");
|
|
1354
2065
|
for (int i = 0; i < nt; i++) {
|
|
1355
2066
|
ixpt->chain.push_back(read_VectorTransform(f));
|
|
1356
2067
|
}
|
|
1357
2068
|
ixpt->index = read_index(f, io_flags);
|
|
2069
|
+
// Validate transform chain dimension consistency:
|
|
2070
|
+
// chain[0].d_in must equal the outer index d, consecutive
|
|
2071
|
+
// transforms must have matching d_out/d_in, and the last
|
|
2072
|
+
// transform's d_out must equal the sub-index d.
|
|
2073
|
+
if (nt > 0) {
|
|
2074
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2075
|
+
ixpt->chain[0]->d_in == ixpt->d,
|
|
2076
|
+
"IndexPreTransform chain[0] d_in=%d != index d=%d",
|
|
2077
|
+
ixpt->chain[0]->d_in,
|
|
2078
|
+
ixpt->d);
|
|
2079
|
+
for (int i = 1; i < nt; i++) {
|
|
2080
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2081
|
+
ixpt->chain[i]->d_in == ixpt->chain[i - 1]->d_out,
|
|
2082
|
+
"IndexPreTransform chain[%d] d_in=%d != "
|
|
2083
|
+
"chain[%d] d_out=%d",
|
|
2084
|
+
i,
|
|
2085
|
+
ixpt->chain[i]->d_in,
|
|
2086
|
+
i - 1,
|
|
2087
|
+
ixpt->chain[i - 1]->d_out);
|
|
2088
|
+
}
|
|
2089
|
+
if (ixpt->index) {
|
|
2090
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2091
|
+
ixpt->chain[nt - 1]->d_out == ixpt->index->d,
|
|
2092
|
+
"IndexPreTransform chain[%d] d_out=%d "
|
|
2093
|
+
"!= sub-index d=%d",
|
|
2094
|
+
nt - 1,
|
|
2095
|
+
ixpt->chain[nt - 1]->d_out,
|
|
2096
|
+
ixpt->index->d);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
1358
2099
|
idx = std::move(ixpt);
|
|
1359
2100
|
} else if (h == fourcc("Imiq")) {
|
|
1360
2101
|
auto imiq = std::make_unique<MultiIndexQuantizer>();
|
|
@@ -1367,6 +2108,12 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1367
2108
|
auto base = read_index_up(f, io_flags);
|
|
1368
2109
|
auto refine = read_index_up(f, io_flags);
|
|
1369
2110
|
READ1(idxrf->k_factor);
|
|
2111
|
+
// Same rationale as IndexIVFPQR k_factor above.
|
|
2112
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2113
|
+
std::isfinite(idxrf->k_factor) && idxrf->k_factor >= 1.0f &&
|
|
2114
|
+
idxrf->k_factor <= 1000.0f,
|
|
2115
|
+
"k_factor %.6g out of valid range [1, 1000] for IndexRefine",
|
|
2116
|
+
idxrf->k_factor);
|
|
1370
2117
|
if (h == fourcc("IxRP")) {
|
|
1371
2118
|
// then make a RefineFlatPanorama with it
|
|
1372
2119
|
auto idxrf_new = std::make_unique<IndexRefinePanorama>();
|
|
@@ -1392,6 +2139,18 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1392
2139
|
idxmap->index = read_index(f, io_flags);
|
|
1393
2140
|
idxmap->own_fields = true;
|
|
1394
2141
|
READVECTOR(idxmap->id_map);
|
|
2142
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2143
|
+
idxmap->id_map.size() == idxmap->ntotal,
|
|
2144
|
+
"IndexIDMap id_map size (%" PRId64
|
|
2145
|
+
") does not match ntotal (%" PRId64 ")",
|
|
2146
|
+
int64_t(idxmap->id_map.size()),
|
|
2147
|
+
idxmap->ntotal);
|
|
2148
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2149
|
+
idxmap->index->ntotal == idxmap->ntotal,
|
|
2150
|
+
"IndexIDMap inner index ntotal (%" PRId64
|
|
2151
|
+
") does not match IndexIDMap ntotal (%" PRId64 ")",
|
|
2152
|
+
idxmap->index->ntotal,
|
|
2153
|
+
idxmap->ntotal);
|
|
1395
2154
|
if (is_map2) {
|
|
1396
2155
|
static_cast<IndexIDMap2*>(idxmap.get())->construct_rev_map();
|
|
1397
2156
|
}
|
|
@@ -1400,12 +2159,21 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1400
2159
|
auto idxp = std::make_unique<Index2Layer>();
|
|
1401
2160
|
read_index_header(*idxp, f);
|
|
1402
2161
|
idxp->q1.quantizer = read_index(f, io_flags);
|
|
2162
|
+
idxp->q1.own_fields = true;
|
|
1403
2163
|
READ1(idxp->q1.nlist);
|
|
1404
2164
|
READ1(idxp->q1.quantizer_trains_alone);
|
|
1405
2165
|
read_ProductQuantizer(&idxp->pq, f);
|
|
1406
2166
|
READ1(idxp->code_size_1);
|
|
1407
2167
|
READ1(idxp->code_size_2);
|
|
1408
2168
|
READ1(idxp->code_size);
|
|
2169
|
+
validate_code_size_match(
|
|
2170
|
+
idxp->code_size_2,
|
|
2171
|
+
idxp->pq.code_size,
|
|
2172
|
+
"Index2Layer code_size_2");
|
|
2173
|
+
validate_code_size_match(
|
|
2174
|
+
idxp->code_size,
|
|
2175
|
+
idxp->code_size_1 + idxp->code_size_2,
|
|
2176
|
+
"Index2Layer");
|
|
1409
2177
|
read_vector(idxp->codes, f);
|
|
1410
2178
|
idx = std::move(idxp);
|
|
1411
2179
|
} else if (
|
|
@@ -1438,6 +2206,9 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1438
2206
|
if (h == fourcc("IHfP")) {
|
|
1439
2207
|
auto idx_panorama =
|
|
1440
2208
|
dynamic_cast<IndexHNSWFlatPanorama*>(idxhnsw.get());
|
|
2209
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2210
|
+
idx_panorama,
|
|
2211
|
+
"dynamic_cast to IndexHNSWFlatPanorama failed");
|
|
1441
2212
|
size_t nlevels;
|
|
1442
2213
|
READ1(nlevels);
|
|
1443
2214
|
const_cast<size_t&>(idx_panorama->num_panorama_levels) = nlevels;
|
|
@@ -1448,6 +2219,8 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1448
2219
|
if (h == fourcc("IHNc") || h == fourcc("IHc2")) {
|
|
1449
2220
|
READ1(idxhnsw->keep_max_size_level0);
|
|
1450
2221
|
auto idx_hnsw_cagra = dynamic_cast<IndexHNSWCagra*>(idxhnsw.get());
|
|
2222
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2223
|
+
idx_hnsw_cagra, "dynamic_cast to IndexHNSWCagra failed");
|
|
1451
2224
|
READ1(idx_hnsw_cagra->base_level_only);
|
|
1452
2225
|
READ1(idx_hnsw_cagra->num_base_level_search_entrypoints);
|
|
1453
2226
|
if (h == fourcc("IHc2")) {
|
|
@@ -1457,11 +2230,43 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1457
2230
|
}
|
|
1458
2231
|
}
|
|
1459
2232
|
read_HNSW(idxhnsw->hnsw, f);
|
|
2233
|
+
// Cross-check HNSW graph size against index header ntotal
|
|
2234
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2235
|
+
idxhnsw->hnsw.levels.size() == (size_t)idxhnsw->ntotal,
|
|
2236
|
+
"HNSW levels size %zu != index ntotal %" PRId64,
|
|
2237
|
+
idxhnsw->hnsw.levels.size(),
|
|
2238
|
+
idxhnsw->ntotal);
|
|
1460
2239
|
idxhnsw->hnsw.is_panorama = (h == fourcc("IHfP"));
|
|
1461
2240
|
idxhnsw->storage = read_index(f, io_flags);
|
|
1462
2241
|
idxhnsw->own_fields = idxhnsw->storage != nullptr;
|
|
2242
|
+
// Cross-check storage ntotal and d against index
|
|
2243
|
+
if (idxhnsw->storage) {
|
|
2244
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2245
|
+
idxhnsw->storage->ntotal == idxhnsw->ntotal,
|
|
2246
|
+
"HNSW storage ntotal %" PRId64 " != index ntotal %" PRId64,
|
|
2247
|
+
idxhnsw->storage->ntotal,
|
|
2248
|
+
idxhnsw->ntotal);
|
|
2249
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2250
|
+
idxhnsw->storage->d == idxhnsw->d,
|
|
2251
|
+
"HNSW storage d %d != index d %d",
|
|
2252
|
+
idxhnsw->storage->d,
|
|
2253
|
+
idxhnsw->d);
|
|
2254
|
+
}
|
|
2255
|
+
if (h == fourcc("IHN2")) {
|
|
2256
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2257
|
+
idxhnsw->storage,
|
|
2258
|
+
"IndexHNSW2Level requires non-null storage");
|
|
2259
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2260
|
+
dynamic_cast<Index2Layer*>(idxhnsw->storage) ||
|
|
2261
|
+
dynamic_cast<IndexIVFPQ*>(idxhnsw->storage),
|
|
2262
|
+
"IndexHNSW2Level storage must be Index2Layer or IndexIVFPQ");
|
|
2263
|
+
}
|
|
1463
2264
|
if (h == fourcc("IHNp") && !(io_flags & IO_FLAG_PQ_SKIP_SDC_TABLE)) {
|
|
1464
|
-
dynamic_cast<IndexPQ*>(idxhnsw->storage)
|
|
2265
|
+
auto* storage_pq = dynamic_cast<IndexPQ*>(idxhnsw->storage);
|
|
2266
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2267
|
+
storage_pq,
|
|
2268
|
+
"dynamic_cast to IndexPQ failed for HNSW storage");
|
|
2269
|
+
storage_pq->pq.compute_sdc_table();
|
|
1465
2270
|
}
|
|
1466
2271
|
idx = std::move(idxhnsw);
|
|
1467
2272
|
} else if (
|
|
@@ -1484,15 +2289,58 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1484
2289
|
READ1(idxnsg->nndescent_L);
|
|
1485
2290
|
READ1(idxnsg->nndescent_iter);
|
|
1486
2291
|
read_NSG(idxnsg->nsg, f);
|
|
2292
|
+
// Cross-check NSG graph ntotal against index header ntotal
|
|
2293
|
+
if (idxnsg->nsg.is_built) {
|
|
2294
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2295
|
+
idxnsg->nsg.ntotal == idxnsg->ntotal,
|
|
2296
|
+
"NSG ntotal %d != index ntotal %" PRId64,
|
|
2297
|
+
idxnsg->nsg.ntotal,
|
|
2298
|
+
idxnsg->ntotal);
|
|
2299
|
+
}
|
|
1487
2300
|
idxnsg->storage = read_index(f, io_flags);
|
|
1488
2301
|
idxnsg->own_fields = true;
|
|
2302
|
+
// Cross-check storage ntotal and d against index
|
|
2303
|
+
if (idxnsg->storage) {
|
|
2304
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2305
|
+
idxnsg->storage->ntotal == idxnsg->ntotal,
|
|
2306
|
+
"NSG storage ntotal %" PRId64 " != index ntotal %" PRId64,
|
|
2307
|
+
idxnsg->storage->ntotal,
|
|
2308
|
+
idxnsg->ntotal);
|
|
2309
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2310
|
+
idxnsg->storage->d == idxnsg->d,
|
|
2311
|
+
"NSG storage d %d != index d %d",
|
|
2312
|
+
idxnsg->storage->d,
|
|
2313
|
+
idxnsg->d);
|
|
2314
|
+
}
|
|
1489
2315
|
idx = std::move(idxnsg);
|
|
1490
2316
|
} else if (h == fourcc("INNf")) {
|
|
1491
2317
|
auto idxnnd = std::make_unique<IndexNNDescentFlat>();
|
|
1492
2318
|
read_index_header(*idxnnd, f);
|
|
1493
2319
|
read_NNDescent(idxnnd->nndescent, f);
|
|
2320
|
+
// Cross-check NNDescent ntotal against index header ntotal
|
|
2321
|
+
if (idxnnd->nndescent.has_built) {
|
|
2322
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2323
|
+
idxnnd->nndescent.ntotal == idxnnd->ntotal,
|
|
2324
|
+
"NNDescent ntotal %d != index ntotal %" PRId64,
|
|
2325
|
+
idxnnd->nndescent.ntotal,
|
|
2326
|
+
idxnnd->ntotal);
|
|
2327
|
+
}
|
|
1494
2328
|
idxnnd->storage = read_index(f, io_flags);
|
|
1495
2329
|
idxnnd->own_fields = true;
|
|
2330
|
+
// Cross-check storage ntotal and d against index
|
|
2331
|
+
if (idxnnd->storage) {
|
|
2332
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2333
|
+
idxnnd->storage->ntotal == idxnnd->ntotal,
|
|
2334
|
+
"NNDescent storage ntotal %" PRId64
|
|
2335
|
+
" != index ntotal %" PRId64,
|
|
2336
|
+
idxnnd->storage->ntotal,
|
|
2337
|
+
idxnnd->ntotal);
|
|
2338
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2339
|
+
idxnnd->storage->d == idxnnd->d,
|
|
2340
|
+
"NNDescent storage d %d != index d %d",
|
|
2341
|
+
idxnnd->storage->d,
|
|
2342
|
+
idxnnd->d);
|
|
2343
|
+
}
|
|
1496
2344
|
idx = std::move(idxnnd);
|
|
1497
2345
|
} else if (h == fourcc("IPfs")) {
|
|
1498
2346
|
auto idxpqfs = std::make_unique<IndexPQFastScan>();
|
|
@@ -1501,6 +2349,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1501
2349
|
READ1(idxpqfs->implem);
|
|
1502
2350
|
READ1(idxpqfs->bbs);
|
|
1503
2351
|
READ1(idxpqfs->qbs);
|
|
2352
|
+
FAISS_THROW_IF_NOT_MSG(idxpqfs->qbs >= 0, "qbs must be non-negative");
|
|
1504
2353
|
READ1(idxpqfs->ntotal2);
|
|
1505
2354
|
READ1(idxpqfs->M2);
|
|
1506
2355
|
READVECTOR(idxpqfs->codes);
|
|
@@ -1511,6 +2360,13 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1511
2360
|
idxpqfs->ksub = (1 << pq.nbits);
|
|
1512
2361
|
idxpqfs->code_size = pq.code_size;
|
|
1513
2362
|
|
|
2363
|
+
validate_fastscan_fields(
|
|
2364
|
+
idxpqfs->M,
|
|
2365
|
+
idxpqfs->M2,
|
|
2366
|
+
idxpqfs->ksub,
|
|
2367
|
+
idxpqfs->bbs,
|
|
2368
|
+
"IndexPQFastScan");
|
|
2369
|
+
|
|
1514
2370
|
idx = std::move(idxpqfs);
|
|
1515
2371
|
|
|
1516
2372
|
} else if (h == fourcc("IwPf")) {
|
|
@@ -1533,6 +2389,9 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1533
2389
|
ivpq->code_size = pq.code_size;
|
|
1534
2390
|
ivpq->init_code_packer();
|
|
1535
2391
|
|
|
2392
|
+
validate_fastscan_fields(
|
|
2393
|
+
ivpq->M, ivpq->M2, ivpq->ksub, ivpq->bbs, "IndexIVFPQFastScan");
|
|
2394
|
+
|
|
1536
2395
|
idx = std::move(ivpq);
|
|
1537
2396
|
} else if (h == fourcc("IRMf")) {
|
|
1538
2397
|
auto imm = std::make_unique<IndexRowwiseMinMax>();
|
|
@@ -1557,9 +2416,13 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1557
2416
|
|
|
1558
2417
|
auto idxqfs = std::make_unique<IndexRaBitQFastScan>();
|
|
1559
2418
|
read_index_header(*idxqfs, f);
|
|
1560
|
-
read_RaBitQuantizer(idxqfs->rabitq, f, true);
|
|
2419
|
+
read_RaBitQuantizer(idxqfs->rabitq, f, idxqfs->d, true);
|
|
1561
2420
|
READVECTOR(idxqfs->center);
|
|
1562
2421
|
READ1(idxqfs->qb);
|
|
2422
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2423
|
+
idxqfs->qb > 0 && idxqfs->qb <= 8,
|
|
2424
|
+
"invalid RaBitQ qb=%d (must be in [1, 8])",
|
|
2425
|
+
idxqfs->qb);
|
|
1563
2426
|
|
|
1564
2427
|
std::vector<uint8_t> legacy_flat_storage;
|
|
1565
2428
|
if (is_legacy) {
|
|
@@ -1577,6 +2440,13 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1577
2440
|
idxqfs->nbits = nbits_fastscan;
|
|
1578
2441
|
idxqfs->ksub = (1 << nbits_fastscan);
|
|
1579
2442
|
|
|
2443
|
+
validate_fastscan_fields(
|
|
2444
|
+
idxqfs->M,
|
|
2445
|
+
idxqfs->M2,
|
|
2446
|
+
idxqfs->ksub,
|
|
2447
|
+
idxqfs->bbs,
|
|
2448
|
+
"IndexRaBitQFastScan");
|
|
2449
|
+
|
|
1580
2450
|
READVECTOR(idxqfs->codes);
|
|
1581
2451
|
|
|
1582
2452
|
if (is_legacy) {
|
|
@@ -1602,12 +2472,19 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1602
2472
|
|
|
1603
2473
|
idx = std::move(idxqfs);
|
|
1604
2474
|
} else if (h == fourcc("Ixrq")) {
|
|
2475
|
+
// Ixrq = original single-bit format
|
|
1605
2476
|
auto idxq = std::make_unique<IndexRaBitQ>();
|
|
1606
2477
|
read_index_header(*idxq, f);
|
|
1607
|
-
read_RaBitQuantizer(idxq->rabitq, f, false);
|
|
2478
|
+
read_RaBitQuantizer(idxq->rabitq, f, idxq->d, false);
|
|
1608
2479
|
READVECTOR(idxq->codes);
|
|
1609
2480
|
READVECTOR(idxq->center);
|
|
1610
2481
|
READ1(idxq->qb);
|
|
2482
|
+
// qb=0: Not quantized - direct distance computation on given float32s.
|
|
2483
|
+
// qb>0 && qb<=8: Scalar-quantized with qb bits of precision.
|
|
2484
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2485
|
+
idxq->qb <= 8,
|
|
2486
|
+
"invalid RaBitQ qb=%d (must be in [0, 8])",
|
|
2487
|
+
idxq->qb);
|
|
1611
2488
|
|
|
1612
2489
|
// rabitq.nb_bits is already set to 1 by read_RaBitQuantizer
|
|
1613
2490
|
idxq->code_size = idxq->rabitq.code_size;
|
|
@@ -1616,20 +2493,33 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1616
2493
|
// Ixrr = multi-bit format (new)
|
|
1617
2494
|
auto idxq = std::make_unique<IndexRaBitQ>();
|
|
1618
2495
|
read_index_header(*idxq, f);
|
|
1619
|
-
read_RaBitQuantizer(
|
|
2496
|
+
read_RaBitQuantizer(
|
|
2497
|
+
idxq->rabitq, f, idxq->d, true); // Reads nb_bits from file
|
|
1620
2498
|
READVECTOR(idxq->codes);
|
|
1621
2499
|
READVECTOR(idxq->center);
|
|
1622
2500
|
READ1(idxq->qb);
|
|
2501
|
+
// qb=0: Not quantized - direct distance computation on given float32s.
|
|
2502
|
+
// qb>0 && qb<=8: Scalar-quantized with qb bits of precision.
|
|
2503
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2504
|
+
idxq->qb <= 8,
|
|
2505
|
+
"invalid RaBitQ qb=%d (must be in [0, 8])",
|
|
2506
|
+
idxq->qb);
|
|
1623
2507
|
|
|
1624
2508
|
idxq->code_size = idxq->rabitq.code_size;
|
|
1625
2509
|
idx = std::move(idxq);
|
|
1626
2510
|
} else if (h == fourcc("Iwrq")) {
|
|
1627
2511
|
auto ivrq = std::make_unique<IndexIVFRaBitQ>();
|
|
1628
2512
|
read_ivf_header(ivrq.get(), f);
|
|
1629
|
-
read_RaBitQuantizer(ivrq->rabitq, f, false);
|
|
2513
|
+
read_RaBitQuantizer(ivrq->rabitq, f, ivrq->d, false);
|
|
1630
2514
|
READ1(ivrq->code_size);
|
|
1631
2515
|
READ1(ivrq->by_residual);
|
|
1632
2516
|
READ1(ivrq->qb);
|
|
2517
|
+
// qb=0: Not quantized - direct distance computation on given float32s.
|
|
2518
|
+
// qb>0 && qb<=8: Scalar-quantized with qb bits of precision.
|
|
2519
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2520
|
+
ivrq->qb <= 8,
|
|
2521
|
+
"invalid RaBitQ qb=%d (must be in [0, 8])",
|
|
2522
|
+
ivrq->qb);
|
|
1633
2523
|
|
|
1634
2524
|
// rabitq.nb_bits is already set to 1 by read_RaBitQuantizer
|
|
1635
2525
|
// Update rabitq to match nb_bits
|
|
@@ -1642,10 +2532,17 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1642
2532
|
// Iwrr = multi-bit format (new)
|
|
1643
2533
|
auto ivrq = std::make_unique<IndexIVFRaBitQ>();
|
|
1644
2534
|
read_ivf_header(ivrq.get(), f);
|
|
1645
|
-
read_RaBitQuantizer(
|
|
2535
|
+
read_RaBitQuantizer(
|
|
2536
|
+
ivrq->rabitq, f, ivrq->d, true); // Reads nb_bits from file
|
|
1646
2537
|
READ1(ivrq->code_size);
|
|
1647
2538
|
READ1(ivrq->by_residual);
|
|
1648
2539
|
READ1(ivrq->qb);
|
|
2540
|
+
// qb=0: Not quantized - direct distance computation on given float32s.
|
|
2541
|
+
// qb>0 && qb<=8: Scalar-quantized with qb bits of precision.
|
|
2542
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2543
|
+
ivrq->qb <= 8,
|
|
2544
|
+
"invalid RaBitQ qb=%d (must be in [0, 8])",
|
|
2545
|
+
ivrq->qb);
|
|
1649
2546
|
|
|
1650
2547
|
// Update rabitq to match nb_bits
|
|
1651
2548
|
ivrq->rabitq.code_size =
|
|
@@ -1656,13 +2553,14 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1656
2553
|
}
|
|
1657
2554
|
#ifdef FAISS_ENABLE_SVS
|
|
1658
2555
|
else if (
|
|
1659
|
-
h == fourcc("ILVQ") || h == fourcc("ISVL") || h == fourcc("ISVD")
|
|
2556
|
+
h == fourcc("ILVQ") || h == fourcc("ISVL") || h == fourcc("ISVD") ||
|
|
2557
|
+
h == fourcc("ISV2")) {
|
|
1660
2558
|
std::unique_ptr<IndexSVSVamana> svs;
|
|
1661
2559
|
if (h == fourcc("ILVQ")) {
|
|
1662
2560
|
svs = std::make_unique<IndexSVSVamanaLVQ>();
|
|
1663
2561
|
} else if (h == fourcc("ISVL")) {
|
|
1664
2562
|
svs = std::make_unique<IndexSVSVamanaLeanVec>();
|
|
1665
|
-
} else if (h == fourcc("ISVD")) {
|
|
2563
|
+
} else if (h == fourcc("ISVD") || h == fourcc("ISV2")) {
|
|
1666
2564
|
svs = std::make_unique<IndexSVSVamana>();
|
|
1667
2565
|
}
|
|
1668
2566
|
|
|
@@ -1675,15 +2573,21 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1675
2573
|
READ1(svs->max_candidate_pool_size);
|
|
1676
2574
|
READ1(svs->prune_to);
|
|
1677
2575
|
READ1(svs->use_full_search_history);
|
|
1678
|
-
|
|
2576
|
+
|
|
2577
|
+
svs->storage_kind = read_svs_storage_kind(f);
|
|
2578
|
+
|
|
1679
2579
|
if (h == fourcc("ISVL")) {
|
|
1680
|
-
|
|
2580
|
+
auto* leanvec = dynamic_cast<IndexSVSVamanaLeanVec*>(svs.get());
|
|
2581
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2582
|
+
leanvec, "dynamic_cast to IndexSVSVamanaLeanVec failed");
|
|
2583
|
+
READ1(leanvec->leanvec_d);
|
|
1681
2584
|
}
|
|
1682
2585
|
|
|
1683
2586
|
bool initialized;
|
|
1684
2587
|
READ1(initialized);
|
|
1685
2588
|
if (initialized) {
|
|
1686
|
-
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2589
|
+
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2590
|
+
f, get_deserialization_vector_byte_limit());
|
|
1687
2591
|
std::istream is(&rbuf);
|
|
1688
2592
|
svs->deserialize_impl(is);
|
|
1689
2593
|
}
|
|
@@ -1691,12 +2595,21 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1691
2595
|
bool trained;
|
|
1692
2596
|
READ1(trained);
|
|
1693
2597
|
if (trained) {
|
|
1694
|
-
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2598
|
+
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2599
|
+
f, get_deserialization_vector_byte_limit());
|
|
1695
2600
|
std::istream is(&rbuf);
|
|
1696
|
-
dynamic_cast<IndexSVSVamanaLeanVec*>(svs.get())
|
|
1697
|
-
|
|
2601
|
+
auto* leanvec = dynamic_cast<IndexSVSVamanaLeanVec*>(svs.get());
|
|
2602
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2603
|
+
leanvec,
|
|
2604
|
+
"dynamic_cast to IndexSVSVamanaLeanVec failed");
|
|
2605
|
+
leanvec->deserialize_training_data(is);
|
|
1698
2606
|
}
|
|
1699
2607
|
}
|
|
2608
|
+
if (h == fourcc("ISV2")) {
|
|
2609
|
+
READVECTOR(svs->stored_vectors);
|
|
2610
|
+
} else {
|
|
2611
|
+
svs->stored_vectors_valid = false;
|
|
2612
|
+
}
|
|
1700
2613
|
idx = std::move(svs);
|
|
1701
2614
|
} else if (h == fourcc("ISVF")) {
|
|
1702
2615
|
auto svs = std::make_unique<IndexSVSFlat>();
|
|
@@ -1705,11 +2618,65 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1705
2618
|
bool initialized;
|
|
1706
2619
|
READ1(initialized);
|
|
1707
2620
|
if (initialized) {
|
|
1708
|
-
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2621
|
+
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2622
|
+
f, get_deserialization_vector_byte_limit());
|
|
1709
2623
|
std::istream is(&rbuf);
|
|
1710
2624
|
svs->deserialize_impl(is);
|
|
1711
2625
|
}
|
|
1712
2626
|
idx = std::move(svs);
|
|
2627
|
+
} else if (
|
|
2628
|
+
h == fourcc("ISIQ") || h == fourcc("ISIL") || h == fourcc("ISID")) {
|
|
2629
|
+
std::unique_ptr<IndexSVSIVF> svs_ivf;
|
|
2630
|
+
if (h == fourcc("ISIQ")) {
|
|
2631
|
+
svs_ivf = std::make_unique<IndexSVSIVFLVQ>();
|
|
2632
|
+
} else if (h == fourcc("ISIL")) {
|
|
2633
|
+
svs_ivf = std::make_unique<IndexSVSIVFLeanVec>();
|
|
2634
|
+
} else if (h == fourcc("ISID")) {
|
|
2635
|
+
svs_ivf = std::make_unique<IndexSVSIVF>();
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
read_index_header(*svs_ivf, f);
|
|
2639
|
+
READ1(svs_ivf->num_centroids);
|
|
2640
|
+
READ1(svs_ivf->minibatch_size);
|
|
2641
|
+
READ1(svs_ivf->num_iterations);
|
|
2642
|
+
READ1(svs_ivf->is_hierarchical);
|
|
2643
|
+
READ1(svs_ivf->training_fraction);
|
|
2644
|
+
READ1(svs_ivf->hierarchical_level1_clusters);
|
|
2645
|
+
READ1(svs_ivf->seed);
|
|
2646
|
+
READ1(svs_ivf->n_probes);
|
|
2647
|
+
READ1(svs_ivf->k_reorder);
|
|
2648
|
+
READ1(svs_ivf->num_threads);
|
|
2649
|
+
READ1(svs_ivf->intra_query_threads);
|
|
2650
|
+
svs_ivf->storage_kind = read_svs_storage_kind(f);
|
|
2651
|
+
READ1(svs_ivf->is_static);
|
|
2652
|
+
if (h == fourcc("ISIL")) {
|
|
2653
|
+
auto* leanvec = dynamic_cast<IndexSVSIVFLeanVec*>(svs_ivf.get());
|
|
2654
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2655
|
+
leanvec, "dynamic_cast to IndexSVSIVFLeanVec failed");
|
|
2656
|
+
READ1(leanvec->leanvec_d);
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
bool initialized;
|
|
2660
|
+
READ1(initialized);
|
|
2661
|
+
if (initialized) {
|
|
2662
|
+
faiss::svs_io::ReaderStreambuf rbuf(f);
|
|
2663
|
+
std::istream is(&rbuf);
|
|
2664
|
+
svs_ivf->deserialize_impl(is);
|
|
2665
|
+
}
|
|
2666
|
+
if (h == fourcc("ISIL")) {
|
|
2667
|
+
bool trained;
|
|
2668
|
+
READ1(trained);
|
|
2669
|
+
if (trained) {
|
|
2670
|
+
faiss::svs_io::ReaderStreambuf rbuf(f);
|
|
2671
|
+
std::istream is(&rbuf);
|
|
2672
|
+
auto* leanvec =
|
|
2673
|
+
dynamic_cast<IndexSVSIVFLeanVec*>(svs_ivf.get());
|
|
2674
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2675
|
+
leanvec, "dynamic_cast to IndexSVSIVFLeanVec failed");
|
|
2676
|
+
leanvec->deserialize_training_data(is);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
idx = std::move(svs_ivf);
|
|
1713
2680
|
}
|
|
1714
2681
|
#endif // FAISS_ENABLE_SVS
|
|
1715
2682
|
else if (h == fourcc("Iwrn") || h == fourcc("Iwrf")) {
|
|
@@ -1719,7 +2686,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1719
2686
|
|
|
1720
2687
|
auto ivrqfs = std::make_unique<IndexIVFRaBitQFastScan>();
|
|
1721
2688
|
read_ivf_header(ivrqfs.get(), f);
|
|
1722
|
-
read_RaBitQuantizer(ivrqfs->rabitq, f);
|
|
2689
|
+
read_RaBitQuantizer(ivrqfs->rabitq, f, ivrqfs->d);
|
|
1723
2690
|
READ1(ivrqfs->by_residual);
|
|
1724
2691
|
READ1(ivrqfs->code_size);
|
|
1725
2692
|
READ1(ivrqfs->bbs);
|
|
@@ -1727,6 +2694,10 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1727
2694
|
READ1(ivrqfs->M2);
|
|
1728
2695
|
READ1(ivrqfs->implem);
|
|
1729
2696
|
READ1(ivrqfs->qb);
|
|
2697
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2698
|
+
ivrqfs->qb > 0 && ivrqfs->qb <= 8,
|
|
2699
|
+
"invalid RaBitQ qb=%d (must be in [1, 8])",
|
|
2700
|
+
ivrqfs->qb);
|
|
1730
2701
|
READ1(ivrqfs->centered);
|
|
1731
2702
|
|
|
1732
2703
|
std::vector<uint8_t> legacy_flat_storage;
|
|
@@ -1741,6 +2712,13 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
|
1741
2712
|
ivrqfs->nbits = nbits_fastscan;
|
|
1742
2713
|
ivrqfs->ksub = (1 << nbits_fastscan);
|
|
1743
2714
|
|
|
2715
|
+
validate_fastscan_fields(
|
|
2716
|
+
ivrqfs->M,
|
|
2717
|
+
ivrqfs->M2,
|
|
2718
|
+
ivrqfs->ksub,
|
|
2719
|
+
ivrqfs->bbs,
|
|
2720
|
+
"IndexIVFRaBitQFastScan");
|
|
2721
|
+
|
|
1744
2722
|
read_InvertedLists(*ivrqfs, f, io_flags);
|
|
1745
2723
|
ivrqfs->init_code_packer();
|
|
1746
2724
|
|
|
@@ -1852,7 +2830,14 @@ static void read_index_binary_header(IndexBinary& idx, IOReader* f) {
|
|
|
1852
2830
|
READ1(metric_type_int);
|
|
1853
2831
|
idx.metric_type = metric_type_from_int(metric_type_int);
|
|
1854
2832
|
FAISS_THROW_IF_NOT_FMT(
|
|
1855
|
-
idx.d
|
|
2833
|
+
idx.d > 0 && idx.d % 8 == 0,
|
|
2834
|
+
"invalid binary index dimension %d (must be > 0 and a multiple of 8)",
|
|
2835
|
+
idx.d);
|
|
2836
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2837
|
+
idx.code_size == idx.d / 8,
|
|
2838
|
+
"binary index code_size=%d does not match d/8=%d",
|
|
2839
|
+
(int)idx.code_size,
|
|
2840
|
+
idx.d / 8);
|
|
1856
2841
|
FAISS_THROW_IF_NOT_FMT(
|
|
1857
2842
|
idx.ntotal >= 0,
|
|
1858
2843
|
"invalid binary index ntotal %" PRId64,
|
|
@@ -1866,6 +2851,7 @@ static void read_binary_ivf_header(
|
|
|
1866
2851
|
std::vector<std::vector<idx_t>>* ids = nullptr) {
|
|
1867
2852
|
read_index_binary_header(ivf, f);
|
|
1868
2853
|
READ1(ivf.nlist);
|
|
2854
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(ivf.nlist, "nlist");
|
|
1869
2855
|
READ1(ivf.nprobe);
|
|
1870
2856
|
ivf.quantizer = read_index_binary(f);
|
|
1871
2857
|
ivf.own_fields = true;
|
|
@@ -1880,11 +2866,25 @@ static void read_binary_ivf_header(
|
|
|
1880
2866
|
static void read_binary_hash_invlists(
|
|
1881
2867
|
IndexBinaryHash::InvertedListMap& invlists,
|
|
1882
2868
|
int b,
|
|
2869
|
+
size_t code_size,
|
|
1883
2870
|
IOReader* f) {
|
|
1884
2871
|
size_t sz;
|
|
1885
2872
|
READ1(sz);
|
|
2873
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(sz, "binary hash invlists sz");
|
|
1886
2874
|
int il_nbit = 0;
|
|
1887
2875
|
READ1(il_nbit);
|
|
2876
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2877
|
+
il_nbit >= 0,
|
|
2878
|
+
"invalid binary hash invlists il_nbit=%d (must be >= 0)",
|
|
2879
|
+
il_nbit);
|
|
2880
|
+
if (sz > 0) {
|
|
2881
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2882
|
+
il_nbit > 0,
|
|
2883
|
+
"invalid binary hash invlists il_nbit=%d for sz=%zd "
|
|
2884
|
+
"(must be > 0 when entries exist)",
|
|
2885
|
+
il_nbit,
|
|
2886
|
+
sz);
|
|
2887
|
+
}
|
|
1888
2888
|
// buffer for bitstrings
|
|
1889
2889
|
size_t bits_per_entry = (size_t)b + (size_t)il_nbit;
|
|
1890
2890
|
size_t total_bits =
|
|
@@ -1909,6 +2909,13 @@ static void read_binary_hash_invlists(
|
|
|
1909
2909
|
READVECTOR(il.ids);
|
|
1910
2910
|
FAISS_THROW_IF_NOT(il.ids.size() == ilsz);
|
|
1911
2911
|
READVECTOR(il.vecs);
|
|
2912
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2913
|
+
il.vecs.size() == il.ids.size() * code_size,
|
|
2914
|
+
"binary hash invlists: vecs size %zu != ids size %zu * "
|
|
2915
|
+
"code_size %zu",
|
|
2916
|
+
il.vecs.size(),
|
|
2917
|
+
il.ids.size(),
|
|
2918
|
+
code_size);
|
|
1912
2919
|
}
|
|
1913
2920
|
}
|
|
1914
2921
|
|
|
@@ -1921,6 +2928,7 @@ static void read_binary_multi_hash_map(
|
|
|
1921
2928
|
size_t sz;
|
|
1922
2929
|
READ1(id_bits);
|
|
1923
2930
|
READ1(sz);
|
|
2931
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(sz, "multi hash map sz");
|
|
1924
2932
|
std::vector<uint8_t> buf;
|
|
1925
2933
|
READVECTOR(buf);
|
|
1926
2934
|
size_t nbit = add_no_overflow(
|
|
@@ -1930,12 +2938,28 @@ static void read_binary_multi_hash_map(
|
|
|
1930
2938
|
FAISS_THROW_IF_NOT(buf.size() == (nbit + 7) / 8);
|
|
1931
2939
|
BitstringReader rd(buf.data(), buf.size());
|
|
1932
2940
|
map.reserve(sz);
|
|
2941
|
+
size_t total_ids = 0;
|
|
1933
2942
|
for (size_t i = 0; i < sz; i++) {
|
|
1934
2943
|
uint64_t hash = rd.read(b);
|
|
1935
2944
|
uint64_t ilsz = rd.read(id_bits);
|
|
2945
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2946
|
+
ilsz <= ntotal - total_ids,
|
|
2947
|
+
"multi hash map: ilsz=%zu at entry %zu would exceed "
|
|
2948
|
+
"ntotal=%zu (already read %zu ids)",
|
|
2949
|
+
(size_t)ilsz,
|
|
2950
|
+
i,
|
|
2951
|
+
ntotal,
|
|
2952
|
+
total_ids);
|
|
2953
|
+
total_ids += ilsz;
|
|
1936
2954
|
auto& il = map[hash];
|
|
1937
2955
|
for (size_t j = 0; j < ilsz; j++) {
|
|
1938
|
-
|
|
2956
|
+
uint64_t id = rd.read(id_bits);
|
|
2957
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2958
|
+
id < ntotal,
|
|
2959
|
+
"multi hash map: id=%zu >= ntotal=%zu",
|
|
2960
|
+
(size_t)id,
|
|
2961
|
+
ntotal);
|
|
2962
|
+
il.push_back(id);
|
|
1939
2963
|
}
|
|
1940
2964
|
}
|
|
1941
2965
|
}
|
|
@@ -1966,8 +2990,21 @@ std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
|
|
|
1966
2990
|
read_index_binary_header(*idxhnsw, f);
|
|
1967
2991
|
read_HNSW(idxhnsw->hnsw, f);
|
|
1968
2992
|
idxhnsw->hnsw.is_panorama = false;
|
|
2993
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2994
|
+
idxhnsw->hnsw.levels.size() == (size_t)idxhnsw->ntotal,
|
|
2995
|
+
"IndexBinaryHNSW HNSW levels size %zu != ntotal %" PRId64,
|
|
2996
|
+
idxhnsw->hnsw.levels.size(),
|
|
2997
|
+
idxhnsw->ntotal);
|
|
1969
2998
|
idxhnsw->storage = read_index_binary(f, io_flags);
|
|
1970
2999
|
idxhnsw->own_fields = true;
|
|
3000
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
3001
|
+
idxhnsw->storage &&
|
|
3002
|
+
dynamic_cast<IndexBinaryFlat*>(idxhnsw->storage) !=
|
|
3003
|
+
nullptr,
|
|
3004
|
+
"IndexBinaryHNSW requires IndexBinaryFlat storage");
|
|
3005
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
3006
|
+
idxhnsw->storage->ntotal == idxhnsw->ntotal,
|
|
3007
|
+
"IndexBinaryHNSW storage ntotal mismatch");
|
|
1971
3008
|
idx = std::move(idxhnsw);
|
|
1972
3009
|
} else if (h == fourcc("IBHc")) {
|
|
1973
3010
|
auto idxhnsw = std::make_unique<IndexBinaryHNSWCagra>();
|
|
@@ -1977,8 +3014,21 @@ std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
|
|
|
1977
3014
|
READ1(idxhnsw->num_base_level_search_entrypoints);
|
|
1978
3015
|
read_HNSW(idxhnsw->hnsw, f);
|
|
1979
3016
|
idxhnsw->hnsw.is_panorama = false;
|
|
3017
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
3018
|
+
idxhnsw->hnsw.levels.size() == (size_t)idxhnsw->ntotal,
|
|
3019
|
+
"IndexBinaryHNSWCagra HNSW levels size %zu != ntotal %" PRId64,
|
|
3020
|
+
idxhnsw->hnsw.levels.size(),
|
|
3021
|
+
idxhnsw->ntotal);
|
|
1980
3022
|
idxhnsw->storage = read_index_binary(f, io_flags);
|
|
1981
3023
|
idxhnsw->own_fields = true;
|
|
3024
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
3025
|
+
idxhnsw->storage &&
|
|
3026
|
+
dynamic_cast<IndexBinaryFlat*>(idxhnsw->storage) !=
|
|
3027
|
+
nullptr,
|
|
3028
|
+
"IndexBinaryHNSWCagra requires IndexBinaryFlat storage");
|
|
3029
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
3030
|
+
idxhnsw->storage->ntotal == idxhnsw->ntotal,
|
|
3031
|
+
"IndexBinaryHNSWCagra storage ntotal mismatch");
|
|
1982
3032
|
idx = std::move(idxhnsw);
|
|
1983
3033
|
} else if (h == fourcc("IBMp") || h == fourcc("IBM2")) {
|
|
1984
3034
|
bool is_map2 = h == fourcc("IBM2");
|
|
@@ -1989,6 +3039,18 @@ std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
|
|
|
1989
3039
|
idxmap->index = read_index_binary(f, io_flags);
|
|
1990
3040
|
idxmap->own_fields = true;
|
|
1991
3041
|
READVECTOR(idxmap->id_map);
|
|
3042
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
3043
|
+
idxmap->id_map.size() == idxmap->ntotal,
|
|
3044
|
+
"IndexBinaryIDMap id_map size (%" PRId64
|
|
3045
|
+
") does not match ntotal (%" PRId64 ")",
|
|
3046
|
+
int64_t(idxmap->id_map.size()),
|
|
3047
|
+
idxmap->ntotal);
|
|
3048
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
3049
|
+
idxmap->index->ntotal == idxmap->ntotal,
|
|
3050
|
+
"IndexBinaryIDMap inner index ntotal (%" PRId64
|
|
3051
|
+
") does not match IndexBinaryIDMap ntotal (%" PRId64 ")",
|
|
3052
|
+
idxmap->index->ntotal,
|
|
3053
|
+
idxmap->ntotal);
|
|
1992
3054
|
if (is_map2) {
|
|
1993
3055
|
static_cast<IndexBinaryIDMap2*>(idxmap.get())->construct_rev_map();
|
|
1994
3056
|
}
|
|
@@ -1997,8 +3059,17 @@ std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
|
|
|
1997
3059
|
auto idxh = std::make_unique<IndexBinaryHash>();
|
|
1998
3060
|
read_index_binary_header(*idxh, f);
|
|
1999
3061
|
READ1(idxh->b);
|
|
3062
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
3063
|
+
idxh->b > 0,
|
|
3064
|
+
"invalid IndexBinaryHash b=%d (must be > 0)",
|
|
3065
|
+
idxh->b);
|
|
3066
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
3067
|
+
static_cast<size_t>(idxh->b) <= idxh->code_size * 8,
|
|
3068
|
+
"IndexBinaryHash b=%d exceeds code_size=%d bits",
|
|
3069
|
+
idxh->b,
|
|
3070
|
+
idxh->code_size);
|
|
2000
3071
|
READ1(idxh->nflip);
|
|
2001
|
-
read_binary_hash_invlists(idxh->invlists, idxh->b, f);
|
|
3072
|
+
read_binary_hash_invlists(idxh->invlists, idxh->b, idxh->code_size, f);
|
|
2002
3073
|
idx = std::move(idxh);
|
|
2003
3074
|
} else if (h == fourcc("IBHm")) {
|
|
2004
3075
|
auto idxmh = std::make_unique<IndexBinaryMultiHash>();
|
|
@@ -2010,7 +3081,23 @@ std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
|
|
|
2010
3081
|
storage_idx.release();
|
|
2011
3082
|
idxmh->own_fields = true;
|
|
2012
3083
|
READ1(idxmh->b);
|
|
3084
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
3085
|
+
idxmh->b > 0,
|
|
3086
|
+
"invalid IndexBinaryMultiHash b=%d (must be > 0)",
|
|
3087
|
+
idxmh->b);
|
|
2013
3088
|
READ1(idxmh->nhash);
|
|
3089
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
3090
|
+
idxmh->nhash > 0,
|
|
3091
|
+
"invalid IndexBinaryMultiHash nhash %d (must be > 0)",
|
|
3092
|
+
idxmh->nhash);
|
|
3093
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(idxmh->nhash, "nhash");
|
|
3094
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
3095
|
+
mul_no_overflow(idxmh->nhash, idxmh->b, "nhash * b") <=
|
|
3096
|
+
mul_no_overflow(idxmh->code_size, 8, "code_size * 8"),
|
|
3097
|
+
"IndexBinaryMultiHash nhash=%d * b=%d exceeds code_size=%d bits",
|
|
3098
|
+
idxmh->nhash,
|
|
3099
|
+
idxmh->b,
|
|
3100
|
+
idxmh->code_size);
|
|
2014
3101
|
READ1(idxmh->nflip);
|
|
2015
3102
|
idxmh->maps.resize(idxmh->nhash);
|
|
2016
3103
|
for (int i = 0; i < idxmh->nhash; i++) {
|