faiss 0.5.3 → 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 +12 -0
- data/ext/faiss/ext.cpp +1 -1
- data/ext/faiss/extconf.rb +4 -4
- data/ext/faiss/index.cpp +63 -45
- data/ext/faiss/index_binary.cpp +37 -27
- data/ext/faiss/kmeans.cpp +9 -8
- data/ext/faiss/pca_matrix.cpp +9 -7
- data/ext/faiss/product_quantizer.cpp +13 -11
- data/ext/faiss/utils.cpp +4 -2
- data/ext/faiss/utils.h +4 -0
- data/lib/faiss/version.rb +1 -1
- data/lib/faiss.rb +1 -1
- data/vendor/faiss/faiss/AutoTune.cpp +214 -82
- data/vendor/faiss/faiss/AutoTune.h +14 -1
- data/vendor/faiss/faiss/Clustering.cpp +97 -249
- data/vendor/faiss/faiss/Clustering.h +18 -0
- data/vendor/faiss/faiss/IVFlib.cpp +67 -44
- data/vendor/faiss/faiss/Index.cpp +25 -12
- data/vendor/faiss/faiss/Index.h +26 -4
- data/vendor/faiss/faiss/Index2Layer.cpp +37 -53
- data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +68 -61
- data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +36 -34
- data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.h +4 -1
- data/vendor/faiss/faiss/IndexBinary.cpp +6 -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 +92 -95
- 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 +120 -414
- data/vendor/faiss/faiss/IndexFastScan.cpp +105 -129
- data/vendor/faiss/faiss/IndexFastScan.h +35 -24
- data/vendor/faiss/faiss/IndexFlat.cpp +216 -152
- data/vendor/faiss/faiss/IndexFlat.h +32 -14
- data/vendor/faiss/faiss/IndexFlatCodes.cpp +88 -41
- data/vendor/faiss/faiss/IndexFlatCodes.h +7 -1
- data/vendor/faiss/faiss/IndexHNSW.cpp +299 -187
- data/vendor/faiss/faiss/IndexHNSW.h +30 -14
- data/vendor/faiss/faiss/IndexIDMap.cpp +26 -22
- data/vendor/faiss/faiss/IndexIDMap.h +9 -7
- data/vendor/faiss/faiss/IndexIVF.cpp +535 -405
- data/vendor/faiss/faiss/IndexIVF.h +47 -16
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +77 -74
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +105 -99
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.h +6 -3
- data/vendor/faiss/faiss/IndexIVFFastScan.cpp +379 -249
- data/vendor/faiss/faiss/IndexIVFFastScan.h +65 -60
- data/vendor/faiss/faiss/IndexIVFFlat.cpp +41 -124
- data/vendor/faiss/faiss/IndexIVFFlat.h +32 -0
- data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +89 -138
- data/vendor/faiss/faiss/IndexIVFFlatPanorama.h +3 -1
- data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +18 -15
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +77 -907
- data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +184 -122
- data/vendor/faiss/faiss/IndexIVFPQFastScan.h +3 -0
- data/vendor/faiss/faiss/IndexIVFPQR.cpp +23 -18
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +59 -60
- data/vendor/faiss/faiss/IndexIVFRaBitQ.h +4 -3
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +564 -416
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +269 -111
- data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +41 -127
- data/vendor/faiss/faiss/IndexIVFSpectralHash.h +1 -1
- data/vendor/faiss/faiss/IndexLSH.cpp +44 -25
- data/vendor/faiss/faiss/IndexLattice.cpp +41 -36
- data/vendor/faiss/faiss/IndexNNDescent.cpp +37 -21
- data/vendor/faiss/faiss/IndexNNDescent.h +2 -2
- data/vendor/faiss/faiss/IndexNSG.cpp +40 -23
- data/vendor/faiss/faiss/IndexNSG.h +0 -2
- data/vendor/faiss/faiss/IndexNeuralNetCodec.cpp +32 -12
- data/vendor/faiss/faiss/IndexPQ.cpp +129 -213
- 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 +31 -43
- data/vendor/faiss/faiss/IndexRaBitQ.h +4 -3
- data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +135 -317
- data/vendor/faiss/faiss/IndexRaBitQFastScan.h +192 -34
- data/vendor/faiss/faiss/IndexRefine.cpp +30 -55
- 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 +13 -13
- 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 +29 -6
- data/vendor/faiss/faiss/SuperKMeans.cpp +656 -0
- data/vendor/faiss/faiss/SuperKMeans.h +97 -0
- data/vendor/faiss/faiss/VectorTransform.cpp +349 -141
- data/vendor/faiss/faiss/VectorTransform.h +39 -16
- data/vendor/faiss/faiss/build.cpp +23 -0
- data/vendor/faiss/faiss/build.h +15 -0
- data/vendor/faiss/faiss/clone_index.cpp +55 -51
- 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/{cppcontrib/factory_tools.cpp → factory_tools.cpp} +6 -1
- data/vendor/faiss/faiss/gpu/GpuCloner.cpp +1 -1
- 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 +64 -34
- 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 -28
- 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 +367 -0
- data/vendor/faiss/faiss/impl/ClusteringInitialization.h +107 -0
- data/vendor/faiss/faiss/impl/CodePacker.cpp +7 -3
- data/vendor/faiss/faiss/impl/CodePacker.h +11 -3
- data/vendor/faiss/faiss/impl/CodePackerRaBitQ.cpp +83 -0
- data/vendor/faiss/faiss/impl/CodePackerRaBitQ.h +47 -0
- data/vendor/faiss/faiss/impl/DistanceComputer.h +8 -8
- data/vendor/faiss/faiss/impl/FaissAssert.h +64 -3
- data/vendor/faiss/faiss/impl/FaissException.h +50 -3
- data/vendor/faiss/faiss/impl/HNSW.cpp +117 -351
- data/vendor/faiss/faiss/impl/HNSW.h +21 -40
- 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 +114 -102
- data/vendor/faiss/faiss/impl/NNDescent.cpp +63 -26
- data/vendor/faiss/faiss/impl/NNDescent.h +6 -2
- data/vendor/faiss/faiss/impl/NSG.cpp +44 -26
- data/vendor/faiss/faiss/impl/NSG.h +20 -10
- data/vendor/faiss/faiss/impl/Panorama.cpp +76 -52
- data/vendor/faiss/faiss/impl/Panorama.h +265 -78
- 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 +62 -37
- 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 +99 -80
- data/vendor/faiss/faiss/impl/Quantizer.h +2 -2
- data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +135 -37
- data/vendor/faiss/faiss/impl/RaBitQUtils.h +148 -21
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +298 -301
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +3 -10
- data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +15 -41
- data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.h +0 -4
- data/vendor/faiss/faiss/impl/ResidualQuantizer.cpp +40 -32
- data/vendor/faiss/faiss/impl/ResidualQuantizer.h +1 -1
- data/vendor/faiss/faiss/impl/ResultHandler.h +218 -113
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +119 -2362
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +27 -3
- data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +14 -11
- data/vendor/faiss/faiss/impl/VisitedTable.cpp +42 -0
- data/vendor/faiss/faiss/impl/VisitedTable.h +76 -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 +163 -0
- 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} +176 -4
- 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 -348
- 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} +290 -142
- 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 +1950 -505
- data/vendor/faiss/faiss/impl/index_read_utils.h +1 -2
- data/vendor/faiss/faiss/impl/index_write.cpp +112 -21
- 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 +81 -40
- data/vendor/faiss/faiss/impl/lattice_Zn.h +6 -6
- data/vendor/faiss/faiss/impl/mapped_io.cpp +15 -8
- 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/{code_distance/code_distance-avx2.h → pq_code_distance/pq_code_distance-avx2.h} +43 -220
- data/vendor/faiss/faiss/impl/{code_distance/code_distance-avx512.h → pq_code_distance/pq_code_distance-avx512.h} +25 -112
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +59 -0
- 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 +256 -0
- data/vendor/faiss/faiss/impl/{code_distance/code_distance-sve.h → pq_code_distance/pq_code_distance-sve.cpp} +57 -146
- data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +68 -0
- data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +320 -483
- data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.h +1 -1
- data/vendor/faiss/faiss/impl/scalar_quantizer/codecs.h +121 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +137 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +371 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +190 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/similarities.h +94 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +603 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +597 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +388 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +630 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +311 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +387 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/training.h +54 -0
- data/vendor/faiss/faiss/impl/simd_dispatch.h +173 -0
- data/vendor/faiss/faiss/impl/simdlib/simdlib.h +57 -0
- data/vendor/faiss/faiss/{utils → impl/simdlib}/simdlib_avx2.h +274 -171
- 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 +275 -217
- 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 +115 -28
- data/vendor/faiss/faiss/index_io.h +53 -3
- data/vendor/faiss/faiss/invlists/BlockInvertedLists.cpp +73 -20
- 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 +14 -14
- data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.h +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +9 -19
- data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +2 -2
- data/vendor/faiss/faiss/svs/IndexSVSFlat.h +2 -0
- 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 +19 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +19 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +14 -0
- data/vendor/faiss/faiss/utils/Heap.cpp +56 -10
- data/vendor/faiss/faiss/utils/Heap.h +21 -0
- data/vendor/faiss/faiss/utils/NeuralNet.cpp +54 -40
- 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 +507 -559
- data/vendor/faiss/faiss/utils/distances.h +118 -1
- data/vendor/faiss/faiss/utils/distances_dispatch.h +250 -0
- 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 +72 -3681
- data/vendor/faiss/faiss/utils/extra_distances.cpp +60 -102
- data/vendor/faiss/faiss/utils/extra_distances.h +79 -7
- 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 +251 -0
- 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 +124 -343
- 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 +154 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +777 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_autovec-inl.h +306 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +1431 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +1095 -0
- 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 +392 -0
- 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 +334 -0
- data/vendor/faiss/faiss/utils/simd_levels.h +183 -0
- data/vendor/faiss/faiss/utils/sorting.cpp +48 -36
- data/vendor/faiss/faiss/utils/utils.cpp +21 -14
- data/vendor/faiss/faiss/utils/utils.h +3 -3
- metadata +156 -42
- data/vendor/faiss/faiss/impl/RaBitQStats.cpp +0 -29
- data/vendor/faiss/faiss/impl/RaBitQStats.h +0 -56
- data/vendor/faiss/faiss/impl/code_distance/code_distance-generic.h +0 -81
- data/vendor/faiss/faiss/impl/code_distance/code_distance.h +0 -186
- data/vendor/faiss/faiss/impl/pq4_fast_scan.h +0 -216
- data/vendor/faiss/faiss/impl/pq4_fast_scan_search_1.cpp +0 -224
- 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 -228
- 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 -450
- 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 -296
- /data/vendor/faiss/faiss/{cppcontrib/factory_tools.h → factory_tools.h} +0 -0
|
@@ -10,16 +10,23 @@
|
|
|
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>
|
|
17
|
+
#include <cstring>
|
|
18
|
+
#include <memory>
|
|
15
19
|
#include <optional>
|
|
16
20
|
|
|
17
21
|
#include <faiss/impl/FaissAssert.h>
|
|
22
|
+
#include <faiss/impl/RaBitQUtils.h>
|
|
18
23
|
#include <faiss/impl/io.h>
|
|
19
24
|
#include <faiss/utils/hamming.h>
|
|
20
25
|
|
|
21
26
|
#include <faiss/invlists/InvertedListsIOHook.h>
|
|
22
27
|
|
|
28
|
+
#include <faiss/invlists/BlockInvertedLists.h>
|
|
29
|
+
|
|
23
30
|
#include <faiss/Index2Layer.h>
|
|
24
31
|
#include <faiss/IndexAdditiveQuantizer.h>
|
|
25
32
|
#include <faiss/IndexAdditiveQuantizerFastScan.h>
|
|
@@ -51,6 +58,9 @@
|
|
|
51
58
|
#ifdef FAISS_ENABLE_SVS
|
|
52
59
|
#include <faiss/impl/svs_io.h>
|
|
53
60
|
#include <faiss/svs/IndexSVSFlat.h>
|
|
61
|
+
#include <faiss/svs/IndexSVSIVF.h>
|
|
62
|
+
#include <faiss/svs/IndexSVSIVFLVQ.h>
|
|
63
|
+
#include <faiss/svs/IndexSVSIVFLeanVec.h>
|
|
54
64
|
#include <faiss/svs/IndexSVSVamana.h>
|
|
55
65
|
#include <faiss/svs/IndexSVSVamanaLVQ.h>
|
|
56
66
|
#include <faiss/svs/IndexSVSVamanaLeanVec.h>
|
|
@@ -73,6 +83,59 @@
|
|
|
73
83
|
|
|
74
84
|
namespace faiss {
|
|
75
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
|
+
|
|
76
139
|
/*************************************************************
|
|
77
140
|
* Mmap-ing and viewing facilities
|
|
78
141
|
**************************************************************/
|
|
@@ -205,34 +268,41 @@ void read_xb_vector(VectorT& target, IOReader* f) {
|
|
|
205
268
|
* Read
|
|
206
269
|
**************************************************************/
|
|
207
270
|
|
|
208
|
-
void read_index_header(Index
|
|
209
|
-
READ1(idx
|
|
210
|
-
READ1(idx
|
|
271
|
+
static void read_index_header(Index& idx, IOReader* f) {
|
|
272
|
+
READ1(idx.d);
|
|
273
|
+
READ1(idx.ntotal);
|
|
274
|
+
FAISS_CHECK_RANGE(idx.d, 0, (1 << 20) + 1);
|
|
275
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
276
|
+
idx.ntotal >= 0,
|
|
277
|
+
"invalid ntotal %" PRId64 " read from index",
|
|
278
|
+
(int64_t)idx.ntotal);
|
|
211
279
|
idx_t dummy;
|
|
212
280
|
READ1(dummy);
|
|
213
281
|
READ1(dummy);
|
|
214
|
-
READ1(idx
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
282
|
+
READ1(idx.is_trained);
|
|
283
|
+
int metric_type_int;
|
|
284
|
+
READ1(metric_type_int);
|
|
285
|
+
idx.metric_type = metric_type_from_int(metric_type_int);
|
|
286
|
+
if (idx.metric_type > 1) {
|
|
287
|
+
READ1(idx.metric_arg);
|
|
218
288
|
}
|
|
219
|
-
idx
|
|
289
|
+
idx.verbose = false;
|
|
220
290
|
}
|
|
221
291
|
|
|
222
|
-
VectorTransform
|
|
292
|
+
std::unique_ptr<VectorTransform> read_VectorTransform_up(IOReader* f) {
|
|
223
293
|
uint32_t h;
|
|
224
294
|
READ1(h);
|
|
225
|
-
VectorTransform
|
|
295
|
+
std::unique_ptr<VectorTransform> vt;
|
|
226
296
|
|
|
227
297
|
if (h == fourcc("rrot") || h == fourcc("PCAm") || h == fourcc("LTra") ||
|
|
228
298
|
h == fourcc("PcAm") || h == fourcc("Viqm") || h == fourcc("Pcam")) {
|
|
229
|
-
LinearTransform
|
|
299
|
+
std::unique_ptr<LinearTransform> lt;
|
|
230
300
|
if (h == fourcc("rrot")) {
|
|
231
|
-
lt =
|
|
301
|
+
lt = std::make_unique<RandomRotationMatrix>();
|
|
232
302
|
} else if (
|
|
233
303
|
h == fourcc("PCAm") || h == fourcc("PcAm") ||
|
|
234
304
|
h == fourcc("Pcam")) {
|
|
235
|
-
|
|
305
|
+
auto pca = std::make_unique<PCAMatrix>();
|
|
236
306
|
READ1(pca->eigen_power);
|
|
237
307
|
if (h == fourcc("Pcam")) {
|
|
238
308
|
READ1(pca->epsilon);
|
|
@@ -244,53 +314,59 @@ VectorTransform* read_VectorTransform(IOReader* f) {
|
|
|
244
314
|
READVECTOR(pca->mean);
|
|
245
315
|
READVECTOR(pca->eigenvalues);
|
|
246
316
|
READVECTOR(pca->PCAMat);
|
|
247
|
-
lt = pca;
|
|
317
|
+
lt = std::move(pca);
|
|
248
318
|
} else if (h == fourcc("Viqm")) {
|
|
249
|
-
|
|
319
|
+
auto itqm = std::make_unique<ITQMatrix>();
|
|
250
320
|
READ1(itqm->max_iter);
|
|
251
321
|
READ1(itqm->seed);
|
|
252
|
-
lt = itqm;
|
|
322
|
+
lt = std::move(itqm);
|
|
253
323
|
} else if (h == fourcc("LTra")) {
|
|
254
|
-
lt =
|
|
324
|
+
lt = std::make_unique<LinearTransform>();
|
|
255
325
|
}
|
|
256
326
|
READ1(lt->have_bias);
|
|
257
327
|
READVECTOR(lt->A);
|
|
258
328
|
READVECTOR(lt->b);
|
|
259
|
-
FAISS_THROW_IF_NOT(
|
|
260
|
-
|
|
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));
|
|
261
332
|
lt->set_is_orthonormal();
|
|
262
|
-
vt = lt;
|
|
333
|
+
vt = std::move(lt);
|
|
263
334
|
} else if (h == fourcc("RmDT")) {
|
|
264
|
-
|
|
335
|
+
auto rdt = std::make_unique<RemapDimensionsTransform>();
|
|
265
336
|
READVECTOR(rdt->map);
|
|
266
|
-
vt = rdt;
|
|
337
|
+
vt = std::move(rdt);
|
|
267
338
|
} else if (h == fourcc("VNrm")) {
|
|
268
|
-
|
|
339
|
+
auto nt = std::make_unique<NormalizationTransform>();
|
|
269
340
|
READ1(nt->norm);
|
|
270
|
-
vt = nt;
|
|
341
|
+
vt = std::move(nt);
|
|
271
342
|
} else if (h == fourcc("VCnt")) {
|
|
272
|
-
|
|
343
|
+
auto ct = std::make_unique<CenteringTransform>();
|
|
273
344
|
READVECTOR(ct->mean);
|
|
274
|
-
vt = ct;
|
|
345
|
+
vt = std::move(ct);
|
|
275
346
|
} else if (h == fourcc("Viqt")) {
|
|
276
|
-
|
|
347
|
+
auto itqt = std::make_unique<ITQTransform>();
|
|
277
348
|
|
|
278
349
|
READVECTOR(itqt->mean);
|
|
279
350
|
READ1(itqt->do_pca);
|
|
280
351
|
{
|
|
281
|
-
|
|
352
|
+
// Read, dereference, discard.
|
|
353
|
+
auto sub_vt = read_VectorTransform_up(f);
|
|
354
|
+
ITQMatrix* itqm = dynamic_cast<ITQMatrix*>(sub_vt.get());
|
|
282
355
|
FAISS_THROW_IF_NOT(itqm);
|
|
283
356
|
itqt->itq = *itqm;
|
|
284
|
-
delete itqm;
|
|
285
357
|
}
|
|
286
358
|
{
|
|
287
|
-
|
|
288
|
-
|
|
359
|
+
// Read, dereference, discard.
|
|
360
|
+
auto sub_vt = read_VectorTransform_up(f);
|
|
361
|
+
LinearTransform* pi = dynamic_cast<LinearTransform*>(sub_vt.get());
|
|
289
362
|
FAISS_THROW_IF_NOT(pi);
|
|
290
363
|
itqt->pca_then_itq = *pi;
|
|
291
|
-
delete pi;
|
|
292
364
|
}
|
|
293
|
-
vt = itqt;
|
|
365
|
+
vt = std::move(itqt);
|
|
366
|
+
} else if (h == fourcc("HRot")) {
|
|
367
|
+
auto hr = std::make_unique<HadamardRotation>();
|
|
368
|
+
READ1(hr->seed);
|
|
369
|
+
vt = std::move(hr);
|
|
294
370
|
} else {
|
|
295
371
|
FAISS_THROW_FMT(
|
|
296
372
|
"fourcc %ud (\"%s\") not recognized in %s",
|
|
@@ -301,9 +377,110 @@ VectorTransform* read_VectorTransform(IOReader* f) {
|
|
|
301
377
|
READ1(vt->d_in);
|
|
302
378
|
READ1(vt->d_out);
|
|
303
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
|
+
}
|
|
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);
|
|
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");
|
|
433
|
+
hr->init(hr->seed);
|
|
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
|
+
}
|
|
304
477
|
return vt;
|
|
305
478
|
}
|
|
306
479
|
|
|
480
|
+
VectorTransform* read_VectorTransform(IOReader* f) {
|
|
481
|
+
return read_VectorTransform_up(f).release();
|
|
482
|
+
}
|
|
483
|
+
|
|
307
484
|
static void read_ArrayInvertedLists_sizes(
|
|
308
485
|
IOReader* f,
|
|
309
486
|
std::vector<size_t>& sizes) {
|
|
@@ -316,6 +493,10 @@ static void read_ArrayInvertedLists_sizes(
|
|
|
316
493
|
} else if (list_type == fourcc("sprs")) {
|
|
317
494
|
std::vector<size_t> idsizes;
|
|
318
495
|
READVECTOR(idsizes);
|
|
496
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
497
|
+
idsizes.size() % 2 == 0,
|
|
498
|
+
"invalid sparse inverted list size: %zd (must be even)",
|
|
499
|
+
idsizes.size());
|
|
319
500
|
for (size_t j = 0; j < idsizes.size(); j += 2) {
|
|
320
501
|
FAISS_THROW_IF_NOT(idsizes[j] < sizes.size());
|
|
321
502
|
sizes[idsizes[j]] = idsizes[j + 1];
|
|
@@ -328,30 +509,121 @@ static void read_ArrayInvertedLists_sizes(
|
|
|
328
509
|
}
|
|
329
510
|
}
|
|
330
511
|
|
|
331
|
-
|
|
512
|
+
bool index_read_warn_on_null_invlists = true;
|
|
513
|
+
|
|
514
|
+
std::unique_ptr<InvertedLists> read_InvertedLists_up(
|
|
515
|
+
IOReader* f,
|
|
516
|
+
int io_flags) {
|
|
332
517
|
uint32_t h;
|
|
333
518
|
READ1(h);
|
|
334
519
|
if (h == fourcc("il00")) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
+
}
|
|
338
525
|
return nullptr;
|
|
339
526
|
} else if (h == fourcc("ilpn") && !(io_flags & IO_FLAG_SKIP_IVF_DATA)) {
|
|
340
527
|
size_t nlist, code_size, n_levels;
|
|
341
528
|
READ1(nlist);
|
|
529
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(nlist, "ilpn nlist");
|
|
342
530
|
READ1(code_size);
|
|
343
531
|
READ1(n_levels);
|
|
344
|
-
|
|
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);
|
|
345
537
|
std::vector<size_t> sizes(nlist);
|
|
346
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();
|
|
347
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");
|
|
586
|
+
READ1(code_size);
|
|
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);
|
|
592
|
+
auto ailp = std::make_unique<ArrayInvertedListsPanorama>(
|
|
593
|
+
nlist, code_size, n_levels, bs);
|
|
594
|
+
std::vector<size_t> sizes(nlist);
|
|
595
|
+
read_ArrayInvertedLists_sizes(f, sizes);
|
|
596
|
+
size_t byte_limit = get_deserialization_vector_byte_limit();
|
|
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]);
|
|
348
604
|
ailp->ids[i].resize(sizes[i]);
|
|
349
|
-
size_t num_elems =
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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);
|
|
355
627
|
}
|
|
356
628
|
for (size_t i = 0; i < nlist; i++) {
|
|
357
629
|
size_t n = sizes[i];
|
|
@@ -365,22 +637,44 @@ InvertedLists* read_InvertedLists(IOReader* f, int io_flags) {
|
|
|
365
637
|
}
|
|
366
638
|
return ailp;
|
|
367
639
|
} else if (h == fourcc("ilar") && !(io_flags & IO_FLAG_SKIP_IVF_DATA)) {
|
|
368
|
-
auto ails =
|
|
640
|
+
auto ails = std::make_unique<ArrayInvertedLists>(0, 0);
|
|
369
641
|
READ1(ails->nlist);
|
|
642
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(ails->nlist, "ilar nlist");
|
|
370
643
|
READ1(ails->code_size);
|
|
371
644
|
ails->ids.resize(ails->nlist);
|
|
372
645
|
ails->codes.resize(ails->nlist);
|
|
373
646
|
std::vector<size_t> sizes(ails->nlist);
|
|
374
647
|
read_ArrayInvertedLists_sizes(f, sizes);
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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);
|
|
381
672
|
if (n > 0) {
|
|
382
673
|
read_vector_with_known_size(
|
|
383
|
-
ails->codes[i],
|
|
674
|
+
ails->codes[i],
|
|
675
|
+
f,
|
|
676
|
+
mul_no_overflow(
|
|
677
|
+
n, ails->code_size, "inverted list codes"));
|
|
384
678
|
read_vector_with_known_size(ails->ids[i], f, n);
|
|
385
679
|
}
|
|
386
680
|
}
|
|
@@ -393,240 +687,686 @@ InvertedLists* read_InvertedLists(IOReader* f, int io_flags) {
|
|
|
393
687
|
int h2 = (io_flags & 0xffff0000) | (fourcc("il__") & 0x0000ffff);
|
|
394
688
|
size_t nlist, code_size;
|
|
395
689
|
READ1(nlist);
|
|
690
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(nlist, "ilar skip nlist");
|
|
396
691
|
READ1(code_size);
|
|
397
692
|
std::vector<size_t> sizes(nlist);
|
|
398
693
|
read_ArrayInvertedLists_sizes(f, sizes);
|
|
399
|
-
return
|
|
400
|
-
|
|
694
|
+
return std::unique_ptr<InvertedLists>(
|
|
695
|
+
InvertedListsIOHook::lookup(h2)->read_ArrayInvertedLists(
|
|
696
|
+
f, io_flags, nlist, code_size, sizes));
|
|
401
697
|
} else {
|
|
402
|
-
return
|
|
698
|
+
return std::unique_ptr<InvertedLists>(
|
|
699
|
+
InvertedListsIOHook::lookup(h)->read(f, io_flags));
|
|
403
700
|
}
|
|
404
701
|
}
|
|
405
702
|
|
|
406
|
-
|
|
407
|
-
|
|
703
|
+
InvertedLists* read_InvertedLists(IOReader* f, int io_flags) {
|
|
704
|
+
return read_InvertedLists_up(f, io_flags).release();
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
void read_InvertedLists(IndexIVF& ivf, IOReader* f, int io_flags) {
|
|
708
|
+
auto ils = read_InvertedLists_up(f, io_flags);
|
|
408
709
|
if (ils) {
|
|
409
|
-
FAISS_THROW_IF_NOT(ils->nlist == ivf
|
|
710
|
+
FAISS_THROW_IF_NOT(ils->nlist == ivf.nlist);
|
|
410
711
|
FAISS_THROW_IF_NOT(
|
|
411
712
|
ils->code_size == InvertedLists::INVALID_CODE_SIZE ||
|
|
412
|
-
ils->code_size == ivf
|
|
713
|
+
ils->code_size == ivf.code_size);
|
|
413
714
|
}
|
|
414
|
-
ivf
|
|
415
|
-
ivf
|
|
715
|
+
ivf.invlists = ils.release();
|
|
716
|
+
ivf.own_invlists = true;
|
|
416
717
|
}
|
|
417
718
|
|
|
418
719
|
void read_ProductQuantizer(ProductQuantizer* pq, IOReader* f) {
|
|
419
720
|
READ1(pq->d);
|
|
420
721
|
READ1(pq->M);
|
|
421
722
|
READ1(pq->nbits);
|
|
723
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
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
|
+
}
|
|
422
741
|
pq->set_derived_values();
|
|
423
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);
|
|
424
750
|
}
|
|
425
751
|
|
|
426
|
-
static void read_ResidualQuantizer_old(ResidualQuantizer
|
|
427
|
-
READ1(rq
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
READ1(rq
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
READVECTOR(rq
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
752
|
+
static void read_ResidualQuantizer_old(ResidualQuantizer& rq, IOReader* f) {
|
|
753
|
+
READ1(rq.d);
|
|
754
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
755
|
+
rq.d > 0, "invalid AdditiveQuantizer d %zd, must be > 0", rq.d);
|
|
756
|
+
READ1(rq.M);
|
|
757
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
758
|
+
rq.M > 0, "invalid AdditiveQuantizer M %zd, must be > 0", rq.M);
|
|
759
|
+
READVECTOR(rq.nbits);
|
|
760
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
761
|
+
rq.nbits.size() == rq.M,
|
|
762
|
+
"ResidualQuantizer nbits size %zd != M %zd",
|
|
763
|
+
rq.nbits.size(),
|
|
764
|
+
rq.M);
|
|
765
|
+
READ1(rq.is_trained);
|
|
766
|
+
READ1(rq.train_type);
|
|
767
|
+
READ1(rq.max_beam_size);
|
|
768
|
+
READVECTOR(rq.codebooks);
|
|
769
|
+
READ1(rq.search_type);
|
|
770
|
+
READ1(rq.norm_min);
|
|
771
|
+
READ1(rq.norm_max);
|
|
772
|
+
rq.set_derived_values();
|
|
438
773
|
}
|
|
439
774
|
|
|
440
|
-
static void read_AdditiveQuantizer(AdditiveQuantizer
|
|
441
|
-
READ1(aq
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
READ1(aq
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
READ1(aq
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
775
|
+
static void read_AdditiveQuantizer(AdditiveQuantizer& aq, IOReader* f) {
|
|
776
|
+
READ1(aq.d);
|
|
777
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
778
|
+
aq.d > 0, "invalid AdditiveQuantizer d %zd, must be > 0", aq.d);
|
|
779
|
+
READ1(aq.M);
|
|
780
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
781
|
+
aq.M > 0, "invalid AdditiveQuantizer M %zd, must be > 0", aq.M);
|
|
782
|
+
READVECTOR(aq.nbits);
|
|
783
|
+
READ1(aq.is_trained);
|
|
784
|
+
READVECTOR(aq.codebooks);
|
|
785
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
786
|
+
aq.nbits.size() == aq.M,
|
|
787
|
+
"AdditiveQuantizer nbits size %zd != M %zd",
|
|
788
|
+
aq.nbits.size(),
|
|
789
|
+
aq.M);
|
|
790
|
+
READ1(aq.search_type);
|
|
791
|
+
READ1(aq.norm_min);
|
|
792
|
+
READ1(aq.norm_max);
|
|
793
|
+
if (aq.search_type == AdditiveQuantizer::ST_norm_cqint8 ||
|
|
794
|
+
aq.search_type == AdditiveQuantizer::ST_norm_cqint4 ||
|
|
795
|
+
aq.search_type == AdditiveQuantizer::ST_norm_lsq2x4 ||
|
|
796
|
+
aq.search_type == AdditiveQuantizer::ST_norm_rq2x4) {
|
|
797
|
+
read_xb_vector(aq.qnorm.codes, f);
|
|
798
|
+
aq.qnorm.ntotal = aq.qnorm.codes.size() / 4;
|
|
799
|
+
aq.qnorm.update_permutation();
|
|
456
800
|
}
|
|
457
801
|
|
|
458
|
-
if (aq
|
|
459
|
-
aq
|
|
460
|
-
READVECTOR(aq
|
|
802
|
+
if (aq.search_type == AdditiveQuantizer::ST_norm_lsq2x4 ||
|
|
803
|
+
aq.search_type == AdditiveQuantizer::ST_norm_rq2x4) {
|
|
804
|
+
READVECTOR(aq.norm_tabs);
|
|
461
805
|
}
|
|
462
806
|
|
|
463
|
-
aq
|
|
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);
|
|
464
899
|
}
|
|
465
900
|
|
|
466
901
|
static void read_ResidualQuantizer(
|
|
467
|
-
ResidualQuantizer
|
|
902
|
+
ResidualQuantizer& rq,
|
|
468
903
|
IOReader* f,
|
|
469
904
|
int io_flags) {
|
|
470
905
|
read_AdditiveQuantizer(rq, f);
|
|
471
|
-
|
|
472
|
-
READ1(rq
|
|
473
|
-
|
|
906
|
+
validate_codebooks_size(rq, rq.d);
|
|
907
|
+
READ1(rq.train_type);
|
|
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
|
+
}
|
|
929
|
+
if ((rq.train_type & ResidualQuantizer::Skip_codebook_tables) ||
|
|
474
930
|
(io_flags & IO_FLAG_SKIP_PRECOMPUTE_TABLE)) {
|
|
475
931
|
// don't precompute the tables
|
|
476
932
|
} else {
|
|
477
|
-
rq
|
|
933
|
+
rq.compute_codebook_tables();
|
|
478
934
|
}
|
|
479
935
|
}
|
|
480
936
|
|
|
481
|
-
static void read_LocalSearchQuantizer(LocalSearchQuantizer
|
|
937
|
+
static void read_LocalSearchQuantizer(LocalSearchQuantizer& lsq, IOReader* f) {
|
|
482
938
|
read_AdditiveQuantizer(lsq, f);
|
|
483
|
-
|
|
484
|
-
READ1(lsq
|
|
485
|
-
READ1(lsq
|
|
486
|
-
READ1(lsq
|
|
487
|
-
READ1(lsq
|
|
488
|
-
READ1(lsq
|
|
489
|
-
READ1(lsq
|
|
490
|
-
READ1(lsq
|
|
491
|
-
READ1(lsq
|
|
492
|
-
READ1(lsq
|
|
493
|
-
READ1(lsq
|
|
939
|
+
validate_codebooks_size(lsq, lsq.d);
|
|
940
|
+
READ1(lsq.K);
|
|
941
|
+
READ1(lsq.train_iters);
|
|
942
|
+
READ1(lsq.encode_ils_iters);
|
|
943
|
+
READ1(lsq.train_ils_iters);
|
|
944
|
+
READ1(lsq.icm_iters);
|
|
945
|
+
READ1(lsq.p);
|
|
946
|
+
READ1(lsq.lambd);
|
|
947
|
+
READ1(lsq.chunk_size);
|
|
948
|
+
READ1(lsq.random_seed);
|
|
949
|
+
READ1(lsq.nperts);
|
|
950
|
+
READ1(lsq.update_codebooks_with_double);
|
|
494
951
|
}
|
|
495
952
|
|
|
496
953
|
static void read_ProductAdditiveQuantizer(
|
|
497
|
-
ProductAdditiveQuantizer
|
|
954
|
+
ProductAdditiveQuantizer& paq,
|
|
498
955
|
IOReader* f) {
|
|
499
956
|
read_AdditiveQuantizer(paq, f);
|
|
500
|
-
READ1(paq
|
|
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);
|
|
501
969
|
}
|
|
502
970
|
|
|
503
971
|
static void read_ProductResidualQuantizer(
|
|
504
|
-
ProductResidualQuantizer
|
|
972
|
+
ProductResidualQuantizer& prq,
|
|
505
973
|
IOReader* f,
|
|
506
974
|
int io_flags) {
|
|
507
975
|
read_ProductAdditiveQuantizer(prq, f);
|
|
508
976
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
977
|
+
size_t d_sub = prq.d / prq.nsplits;
|
|
978
|
+
for (size_t i = 0; i < prq.nsplits; i++) {
|
|
979
|
+
auto rq = std::make_unique<ResidualQuantizer>();
|
|
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);
|
|
990
|
+
prq.quantizers.push_back(rq.release());
|
|
513
991
|
}
|
|
514
992
|
}
|
|
515
993
|
|
|
516
994
|
static void read_ProductLocalSearchQuantizer(
|
|
517
|
-
ProductLocalSearchQuantizer
|
|
995
|
+
ProductLocalSearchQuantizer& plsq,
|
|
518
996
|
IOReader* f) {
|
|
519
997
|
read_ProductAdditiveQuantizer(plsq, f);
|
|
520
998
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
999
|
+
size_t d_sub = plsq.d / plsq.nsplits;
|
|
1000
|
+
for (size_t i = 0; i < plsq.nsplits; i++) {
|
|
1001
|
+
auto lsq = std::make_unique<LocalSearchQuantizer>();
|
|
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);
|
|
1012
|
+
plsq.quantizers.push_back(lsq.release());
|
|
525
1013
|
}
|
|
526
1014
|
}
|
|
527
1015
|
|
|
528
|
-
void read_ScalarQuantizer(
|
|
529
|
-
|
|
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);
|
|
530
1028
|
READ1(ivsc->rangestat);
|
|
531
1029
|
READ1(ivsc->rangestat_arg);
|
|
532
1030
|
READ1(ivsc->d);
|
|
533
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);
|
|
534
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
|
+
}
|
|
535
1105
|
ivsc->set_derived_sizes();
|
|
536
1106
|
}
|
|
537
1107
|
|
|
538
|
-
static void
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
1108
|
+
static void validate_HNSW(const HNSW& hnsw) {
|
|
1109
|
+
size_t ntotal = hnsw.levels.size();
|
|
1110
|
+
size_t nb_neighbors_size = hnsw.neighbors.size();
|
|
1111
|
+
|
|
1112
|
+
// cum_nneighbor_per_level must be non-empty and monotonically
|
|
1113
|
+
// non-decreasing, starting at 0
|
|
1114
|
+
if (!hnsw.cum_nneighbor_per_level.empty()) {
|
|
1115
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1116
|
+
hnsw.cum_nneighbor_per_level[0] == 0,
|
|
1117
|
+
"HNSW cum_nneighbor_per_level[0] = %d, expected 0",
|
|
1118
|
+
hnsw.cum_nneighbor_per_level[0]);
|
|
1119
|
+
for (size_t i = 1; i < hnsw.cum_nneighbor_per_level.size(); i++) {
|
|
1120
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1121
|
+
hnsw.cum_nneighbor_per_level[i] >=
|
|
1122
|
+
hnsw.cum_nneighbor_per_level[i - 1],
|
|
1123
|
+
"HNSW cum_nneighbor_per_level not monotonic at %zd: "
|
|
1124
|
+
"%d < %d",
|
|
1125
|
+
i,
|
|
1126
|
+
hnsw.cum_nneighbor_per_level[i],
|
|
1127
|
+
hnsw.cum_nneighbor_per_level[i - 1]);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// every levels[i] must be a valid index into cum_nneighbor_per_level
|
|
1132
|
+
size_t cum_size = hnsw.cum_nneighbor_per_level.size();
|
|
1133
|
+
for (size_t i = 0; i < ntotal; i++) {
|
|
1134
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1135
|
+
hnsw.levels[i] >= 0 &&
|
|
1136
|
+
static_cast<size_t>(hnsw.levels[i]) < cum_size,
|
|
1137
|
+
"HNSW levels[%zd] = %d out of range [0, %zd)",
|
|
1138
|
+
i,
|
|
1139
|
+
hnsw.levels[i],
|
|
1140
|
+
cum_size);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// offsets must have size ntotal + 1, be monotonically non-decreasing,
|
|
1144
|
+
// and all values must be <= neighbors.size()
|
|
1145
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1146
|
+
hnsw.offsets.size() == ntotal + 1,
|
|
1147
|
+
"HNSW offsets size %zd != levels size %zd + 1",
|
|
1148
|
+
hnsw.offsets.size(),
|
|
1149
|
+
ntotal);
|
|
1150
|
+
for (size_t i = 0; i < hnsw.offsets.size(); i++) {
|
|
1151
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1152
|
+
hnsw.offsets[i] <= nb_neighbors_size,
|
|
1153
|
+
"HNSW offsets[%zd] = %zd > neighbors.size() = %zd",
|
|
1154
|
+
i,
|
|
1155
|
+
hnsw.offsets[i],
|
|
1156
|
+
nb_neighbors_size);
|
|
1157
|
+
if (i > 0) {
|
|
1158
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1159
|
+
hnsw.offsets[i] ==
|
|
1160
|
+
hnsw.offsets[i - 1] +
|
|
1161
|
+
hnsw.cum_nneighbor_per_level
|
|
1162
|
+
[hnsw.levels[i - 1]],
|
|
1163
|
+
"HNSW offsets not increasing by cum_neighbor_per_level at %zd: %zd + %d != %zd",
|
|
1164
|
+
i,
|
|
1165
|
+
hnsw.offsets[i - 1],
|
|
1166
|
+
hnsw.cum_nneighbor_per_level[hnsw.levels[i - 1]],
|
|
1167
|
+
hnsw.offsets[i]);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// max_level must be valid
|
|
1172
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1173
|
+
hnsw.max_level < (int)hnsw.cum_nneighbor_per_level.size(),
|
|
1174
|
+
"HNSW max_level %d >= cum_nneighbor_per_level size %zd",
|
|
1175
|
+
hnsw.max_level,
|
|
1176
|
+
hnsw.cum_nneighbor_per_level.size());
|
|
1177
|
+
|
|
1178
|
+
// entry_point must be -1 (empty) or a valid node id
|
|
1179
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1180
|
+
hnsw.entry_point >= -1 && hnsw.entry_point < (int)ntotal,
|
|
1181
|
+
"HNSW entry_point %d out of range [-1, %zd)",
|
|
1182
|
+
(int)hnsw.entry_point,
|
|
1183
|
+
ntotal);
|
|
1184
|
+
|
|
1185
|
+
// All neighbor ids must be -1 or in [0, ntotal)
|
|
1186
|
+
for (size_t i = 0; i < nb_neighbors_size; i++) {
|
|
1187
|
+
auto id = hnsw.neighbors[i];
|
|
1188
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1189
|
+
id >= -1 && id < (int)ntotal,
|
|
1190
|
+
"HNSW neighbors[%zd] = %d out of range [-1, %zd)",
|
|
1191
|
+
i,
|
|
1192
|
+
(int)id,
|
|
1193
|
+
ntotal);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// For each node, verify that its level is valid and that
|
|
1197
|
+
// offsets[i] + cum_nneighbor_per_level[levels[i]] <= neighbors.size().
|
|
1198
|
+
// This ensures neighbor_range() can never produce an out-of-bounds offset
|
|
1199
|
+
// into neighbors.
|
|
1200
|
+
int cum_levels = (int)hnsw.cum_nneighbor_per_level.size();
|
|
1201
|
+
for (size_t i = 0; i < ntotal; i++) {
|
|
1202
|
+
int level = hnsw.levels[i];
|
|
1203
|
+
FAISS_CHECK_RANGE(level, 1, cum_levels + 1);
|
|
1204
|
+
size_t end = hnsw.offsets[i] + hnsw.cum_nneighbor_per_level[level];
|
|
1205
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1206
|
+
end <= nb_neighbors_size,
|
|
1207
|
+
"HNSW neighbor range overflow for node %zd: "
|
|
1208
|
+
"offsets[%zd] (%zd) + cum_nneighbor_per_level[%d] (%d) "
|
|
1209
|
+
"= %zd > neighbors.size() (%zd)",
|
|
1210
|
+
i,
|
|
1211
|
+
i,
|
|
1212
|
+
hnsw.offsets[i],
|
|
1213
|
+
level,
|
|
1214
|
+
hnsw.cum_nneighbor_per_level[level],
|
|
1215
|
+
end,
|
|
1216
|
+
nb_neighbors_size);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
static void read_HNSW(HNSW& hnsw, IOReader* f) {
|
|
1221
|
+
READVECTOR(hnsw.assign_probas);
|
|
1222
|
+
READVECTOR(hnsw.cum_nneighbor_per_level);
|
|
1223
|
+
READVECTOR(hnsw.levels);
|
|
1224
|
+
READVECTOR(hnsw.offsets);
|
|
1225
|
+
read_vector(hnsw.neighbors, f);
|
|
544
1226
|
|
|
545
|
-
READ1(hnsw
|
|
546
|
-
READ1(hnsw
|
|
547
|
-
READ1(hnsw
|
|
548
|
-
READ1(hnsw
|
|
1227
|
+
READ1(hnsw.entry_point);
|
|
1228
|
+
READ1(hnsw.max_level);
|
|
1229
|
+
READ1(hnsw.efConstruction);
|
|
1230
|
+
READ1(hnsw.efSearch);
|
|
549
1231
|
|
|
550
1232
|
// // deprecated field
|
|
551
|
-
// READ1(hnsw
|
|
1233
|
+
// READ1(hnsw.upper_beam);
|
|
552
1234
|
READ1_DUMMY(int)
|
|
553
|
-
}
|
|
554
1235
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
READ1(nsg->R);
|
|
558
|
-
READ1(nsg->L);
|
|
559
|
-
READ1(nsg->C);
|
|
560
|
-
READ1(nsg->search_L);
|
|
561
|
-
READ1(nsg->enterpoint);
|
|
562
|
-
READ1(nsg->is_built);
|
|
1236
|
+
validate_HNSW(hnsw);
|
|
1237
|
+
}
|
|
563
1238
|
|
|
564
|
-
|
|
1239
|
+
static void read_NSG(NSG& nsg, IOReader* f) {
|
|
1240
|
+
READ1(nsg.ntotal);
|
|
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);
|
|
1245
|
+
READ1(nsg.L);
|
|
1246
|
+
READ1(nsg.C);
|
|
1247
|
+
READ1(nsg.search_L);
|
|
1248
|
+
READ1(nsg.enterpoint);
|
|
1249
|
+
READ1(nsg.is_built);
|
|
1250
|
+
|
|
1251
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1252
|
+
nsg.ntotal >= 0, "invalid NSG ntotal %d", nsg.ntotal);
|
|
1253
|
+
|
|
1254
|
+
if (!nsg.is_built) {
|
|
565
1255
|
return;
|
|
566
1256
|
}
|
|
567
1257
|
|
|
568
1258
|
constexpr int EMPTY_ID = -1;
|
|
569
|
-
int N = nsg
|
|
570
|
-
int R = nsg
|
|
571
|
-
|
|
1259
|
+
int N = nsg.ntotal;
|
|
1260
|
+
int R = nsg.R;
|
|
1261
|
+
|
|
1262
|
+
auto& graph = nsg.final_graph;
|
|
572
1263
|
graph = std::make_shared<nsg::Graph<int>>(N, R);
|
|
573
|
-
std::fill_n(graph->data, N * R, EMPTY_ID);
|
|
1264
|
+
std::fill_n(graph->data, (size_t)N * R, EMPTY_ID);
|
|
574
1265
|
|
|
575
1266
|
for (int i = 0; i < N; i++) {
|
|
576
|
-
|
|
1267
|
+
int j;
|
|
1268
|
+
for (j = 0; j < R; j++) {
|
|
577
1269
|
int id;
|
|
578
1270
|
READ1(id);
|
|
579
1271
|
if (id != EMPTY_ID) {
|
|
1272
|
+
FAISS_CHECK_RANGE(id, 0, N);
|
|
580
1273
|
graph->at(i, j) = id;
|
|
581
1274
|
} else {
|
|
582
1275
|
break;
|
|
583
1276
|
}
|
|
584
1277
|
}
|
|
1278
|
+
if (j == R) {
|
|
1279
|
+
// All R neighbor slots were filled; consume the trailing
|
|
1280
|
+
// EMPTY_ID sentinel that write_NSG always appends.
|
|
1281
|
+
int sentinel;
|
|
1282
|
+
READ1(sentinel);
|
|
1283
|
+
FAISS_THROW_IF_NOT(sentinel == EMPTY_ID);
|
|
1284
|
+
}
|
|
585
1285
|
}
|
|
1286
|
+
|
|
1287
|
+
// enterpoint must be a valid node id
|
|
1288
|
+
FAISS_CHECK_RANGE(nsg.enterpoint, 0, N);
|
|
586
1289
|
}
|
|
587
1290
|
|
|
588
|
-
static void read_NNDescent(NNDescent
|
|
589
|
-
READ1(nnd
|
|
590
|
-
READ1(nnd
|
|
591
|
-
READ1(nnd
|
|
592
|
-
READ1(nnd
|
|
593
|
-
READ1(nnd
|
|
594
|
-
READ1(nnd
|
|
595
|
-
READ1(nnd
|
|
596
|
-
READ1(nnd
|
|
597
|
-
READ1(nnd
|
|
598
|
-
READ1(nnd
|
|
599
|
-
|
|
600
|
-
|
|
1291
|
+
static void read_NNDescent(NNDescent& nnd, IOReader* f) {
|
|
1292
|
+
READ1(nnd.ntotal);
|
|
1293
|
+
READ1(nnd.d);
|
|
1294
|
+
READ1(nnd.K);
|
|
1295
|
+
READ1(nnd.S);
|
|
1296
|
+
READ1(nnd.R);
|
|
1297
|
+
READ1(nnd.L);
|
|
1298
|
+
READ1(nnd.iter);
|
|
1299
|
+
READ1(nnd.search_L);
|
|
1300
|
+
READ1(nnd.random_seed);
|
|
1301
|
+
READ1(nnd.has_built);
|
|
1302
|
+
|
|
1303
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1304
|
+
nnd.ntotal >= 0, "invalid NNDescent ntotal %d", nnd.ntotal);
|
|
1305
|
+
|
|
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
|
+
}
|
|
601
1327
|
}
|
|
602
1328
|
|
|
603
|
-
ProductQuantizer
|
|
1329
|
+
std::unique_ptr<ProductQuantizer> read_ProductQuantizer_up(const char* fname) {
|
|
604
1330
|
FileIOReader reader(fname);
|
|
605
|
-
return
|
|
1331
|
+
return read_ProductQuantizer_up(&reader);
|
|
606
1332
|
}
|
|
607
1333
|
|
|
608
|
-
ProductQuantizer* read_ProductQuantizer(
|
|
609
|
-
|
|
610
|
-
|
|
1334
|
+
ProductQuantizer* read_ProductQuantizer(const char* fname) {
|
|
1335
|
+
return read_ProductQuantizer_up(fname).release();
|
|
1336
|
+
}
|
|
611
1337
|
|
|
612
|
-
|
|
613
|
-
|
|
1338
|
+
std::unique_ptr<ProductQuantizer> read_ProductQuantizer_up(IOReader* reader) {
|
|
1339
|
+
auto pq = std::make_unique<ProductQuantizer>();
|
|
1340
|
+
read_ProductQuantizer(pq.get(), reader);
|
|
614
1341
|
return pq;
|
|
615
1342
|
}
|
|
616
1343
|
|
|
1344
|
+
ProductQuantizer* read_ProductQuantizer(IOReader* reader) {
|
|
1345
|
+
return read_ProductQuantizer_up(reader).release();
|
|
1346
|
+
}
|
|
1347
|
+
|
|
617
1348
|
static void read_RaBitQuantizer(
|
|
618
|
-
RaBitQuantizer
|
|
1349
|
+
RaBitQuantizer& rabitq,
|
|
619
1350
|
IOReader* f,
|
|
1351
|
+
int expected_d,
|
|
620
1352
|
bool multi_bit = true) {
|
|
621
|
-
READ1(rabitq
|
|
622
|
-
READ1(rabitq
|
|
623
|
-
|
|
1353
|
+
READ1(rabitq.d);
|
|
1354
|
+
READ1(rabitq.code_size);
|
|
1355
|
+
int metric_type_int;
|
|
1356
|
+
READ1(metric_type_int);
|
|
1357
|
+
rabitq.metric_type = metric_type_from_int(metric_type_int);
|
|
624
1358
|
|
|
625
1359
|
if (multi_bit) {
|
|
626
|
-
READ1(rabitq
|
|
1360
|
+
READ1(rabitq.nb_bits);
|
|
627
1361
|
} else {
|
|
628
|
-
rabitq
|
|
1362
|
+
rabitq.nb_bits = 1;
|
|
629
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);
|
|
630
1370
|
}
|
|
631
1371
|
|
|
632
1372
|
void read_direct_map(DirectMap* dm, IOReader* f) {
|
|
@@ -649,8 +1389,9 @@ void read_ivf_header(
|
|
|
649
1389
|
IndexIVF* ivf,
|
|
650
1390
|
IOReader* f,
|
|
651
1391
|
std::vector<std::vector<idx_t>>* ids) {
|
|
652
|
-
read_index_header(ivf, f);
|
|
1392
|
+
read_index_header(*ivf, f);
|
|
653
1393
|
READ1(ivf->nlist);
|
|
1394
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(ivf->nlist, "nlist");
|
|
654
1395
|
READ1(ivf->nprobe);
|
|
655
1396
|
ivf->quantizer = read_index(f);
|
|
656
1397
|
ivf->own_fields = true;
|
|
@@ -666,39 +1407,46 @@ void read_ivf_header(
|
|
|
666
1407
|
ArrayInvertedLists* set_array_invlist(
|
|
667
1408
|
IndexIVF* ivf,
|
|
668
1409
|
std::vector<std::vector<idx_t>>& ids) {
|
|
669
|
-
|
|
670
|
-
new ArrayInvertedLists(ivf->nlist, ivf->code_size);
|
|
1410
|
+
auto ail = std::make_unique<ArrayInvertedLists>(ivf->nlist, ivf->code_size);
|
|
671
1411
|
|
|
672
1412
|
ail->ids.resize(ids.size());
|
|
673
1413
|
for (size_t i = 0; i < ids.size(); i++) {
|
|
674
1414
|
ail->ids[i] = MaybeOwnedVector<idx_t>(std::move(ids[i]));
|
|
675
1415
|
}
|
|
676
1416
|
|
|
677
|
-
|
|
1417
|
+
ArrayInvertedLists* result = ail.get();
|
|
1418
|
+
ivf->invlists = ail.release();
|
|
678
1419
|
ivf->own_invlists = true;
|
|
679
|
-
return
|
|
1420
|
+
return result;
|
|
680
1421
|
}
|
|
681
1422
|
|
|
682
|
-
static IndexIVFPQ
|
|
1423
|
+
static std::unique_ptr<IndexIVFPQ> read_ivfpq(
|
|
1424
|
+
IOReader* f,
|
|
1425
|
+
uint32_t h,
|
|
1426
|
+
int io_flags) {
|
|
683
1427
|
bool legacy = h == fourcc("IvQR") || h == fourcc("IvPQ");
|
|
684
1428
|
|
|
685
|
-
IndexIVFPQR* ivfpqr =
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
1429
|
+
IndexIVFPQR* ivfpqr = nullptr;
|
|
1430
|
+
std::unique_ptr<IndexIVFPQ> ivpq;
|
|
1431
|
+
if (h == fourcc("IvQR") || h == fourcc("IwQR")) {
|
|
1432
|
+
ivpq = std::make_unique<IndexIVFPQR>();
|
|
1433
|
+
ivfpqr = static_cast<IndexIVFPQR*>(ivpq.get());
|
|
1434
|
+
} else {
|
|
1435
|
+
ivpq = std::make_unique<IndexIVFPQ>();
|
|
1436
|
+
}
|
|
689
1437
|
|
|
690
1438
|
std::vector<std::vector<idx_t>> ids;
|
|
691
|
-
read_ivf_header(ivpq, f, legacy ? &ids : nullptr);
|
|
1439
|
+
read_ivf_header(ivpq.get(), f, legacy ? &ids : nullptr);
|
|
692
1440
|
READ1(ivpq->by_residual);
|
|
693
1441
|
READ1(ivpq->code_size);
|
|
694
1442
|
read_ProductQuantizer(&ivpq->pq, f);
|
|
695
1443
|
|
|
696
1444
|
if (legacy) {
|
|
697
|
-
ArrayInvertedLists* ail = set_array_invlist(ivpq, ids);
|
|
1445
|
+
ArrayInvertedLists* ail = set_array_invlist(ivpq.get(), ids);
|
|
698
1446
|
for (size_t i = 0; i < ail->nlist; i++)
|
|
699
1447
|
READVECTOR(ail->codes[i]);
|
|
700
1448
|
} else {
|
|
701
|
-
read_InvertedLists(ivpq, f, io_flags);
|
|
1449
|
+
read_InvertedLists(*ivpq, f, io_flags);
|
|
702
1450
|
}
|
|
703
1451
|
|
|
704
1452
|
if (ivpq->is_trained) {
|
|
@@ -714,6 +1462,20 @@ static IndexIVFPQ* read_ivfpq(IOReader* f, uint32_t h, int io_flags) {
|
|
|
714
1462
|
read_ProductQuantizer(&ivfpqr->refine_pq, f);
|
|
715
1463
|
READVECTOR(ivfpqr->refine_codes);
|
|
716
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);
|
|
717
1479
|
}
|
|
718
1480
|
}
|
|
719
1481
|
return ivpq;
|
|
@@ -721,53 +1483,63 @@ static IndexIVFPQ* read_ivfpq(IOReader* f, uint32_t h, int io_flags) {
|
|
|
721
1483
|
|
|
722
1484
|
int read_old_fmt_hack = 0;
|
|
723
1485
|
|
|
724
|
-
Index
|
|
725
|
-
Index
|
|
1486
|
+
std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
|
1487
|
+
std::unique_ptr<Index> idx;
|
|
726
1488
|
uint32_t h;
|
|
727
1489
|
READ1(h);
|
|
728
1490
|
if (h == fourcc("null")) {
|
|
729
1491
|
// denotes a missing index, useful for some cases
|
|
730
|
-
return
|
|
731
|
-
} else if (h == fourcc("IxFP")) {
|
|
1492
|
+
return idx;
|
|
1493
|
+
} else if (h == fourcc("IxFP") || h == fourcc("IxFp")) {
|
|
732
1494
|
int d;
|
|
733
1495
|
size_t n_levels, batch_size;
|
|
734
1496
|
READ1(d);
|
|
735
1497
|
READ1(n_levels);
|
|
1498
|
+
FAISS_THROW_IF_NOT_FMT(n_levels > 0, "invalid n_levels %zd", n_levels);
|
|
736
1499
|
READ1(batch_size);
|
|
737
|
-
|
|
738
|
-
|
|
1500
|
+
std::unique_ptr<IndexFlatPanorama> idxp;
|
|
1501
|
+
if (h == fourcc("IxFP")) {
|
|
1502
|
+
idxp = std::make_unique<IndexFlatL2Panorama>(
|
|
1503
|
+
d, n_levels, batch_size);
|
|
1504
|
+
} else {
|
|
1505
|
+
idxp = std::make_unique<IndexFlatIPPanorama>(
|
|
1506
|
+
d, n_levels, batch_size);
|
|
1507
|
+
}
|
|
739
1508
|
READ1(idxp->ntotal);
|
|
740
1509
|
READ1(idxp->is_trained);
|
|
741
1510
|
READVECTOR(idxp->codes);
|
|
742
1511
|
READVECTOR(idxp->cum_sums);
|
|
743
1512
|
idxp->verbose = false;
|
|
744
|
-
idx = idxp;
|
|
1513
|
+
idx = std::move(idxp);
|
|
745
1514
|
} else if (
|
|
746
1515
|
h == fourcc("IxFI") || h == fourcc("IxF2") || h == fourcc("IxFl")) {
|
|
747
|
-
IndexFlat
|
|
1516
|
+
std::unique_ptr<IndexFlat> idxf;
|
|
748
1517
|
if (h == fourcc("IxFI")) {
|
|
749
|
-
idxf =
|
|
1518
|
+
idxf = std::make_unique<IndexFlatIP>();
|
|
750
1519
|
} else if (h == fourcc("IxF2")) {
|
|
751
|
-
idxf =
|
|
1520
|
+
idxf = std::make_unique<IndexFlatL2>();
|
|
752
1521
|
} else {
|
|
753
|
-
idxf =
|
|
1522
|
+
idxf = std::make_unique<IndexFlat>();
|
|
754
1523
|
}
|
|
755
|
-
read_index_header(idxf, f);
|
|
1524
|
+
read_index_header(*idxf, f);
|
|
756
1525
|
idxf->code_size = idxf->d * sizeof(float);
|
|
757
1526
|
read_xb_vector(idxf->codes, f);
|
|
758
1527
|
FAISS_THROW_IF_NOT(
|
|
759
1528
|
idxf->codes.size() == idxf->ntotal * idxf->code_size);
|
|
760
|
-
|
|
761
|
-
idx = idxf;
|
|
1529
|
+
idx = std::move(idxf);
|
|
762
1530
|
} else if (h == fourcc("IxHE") || h == fourcc("IxHe")) {
|
|
763
|
-
|
|
764
|
-
read_index_header(idxl, f);
|
|
1531
|
+
auto idxl = std::make_unique<IndexLSH>();
|
|
1532
|
+
read_index_header(*idxl, f);
|
|
765
1533
|
READ1(idxl->nbits);
|
|
766
1534
|
READ1(idxl->rotate_data);
|
|
767
1535
|
READ1(idxl->train_thresholds);
|
|
768
1536
|
READVECTOR(idxl->thresholds);
|
|
769
1537
|
int code_size_i;
|
|
770
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);
|
|
771
1543
|
idxl->code_size = code_size_i;
|
|
772
1544
|
if (h == fourcc("IxHE")) {
|
|
773
1545
|
FAISS_THROW_IF_NOT_FMT(
|
|
@@ -778,27 +1550,32 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
778
1550
|
// leak
|
|
779
1551
|
idxl->code_size *= 8;
|
|
780
1552
|
}
|
|
1553
|
+
validate_code_size_match(
|
|
1554
|
+
idxl->code_size, (idxl->nbits + 7) / 8, "IndexLSH");
|
|
781
1555
|
{
|
|
782
|
-
|
|
783
|
-
|
|
1556
|
+
// Read, dereference, discard.
|
|
1557
|
+
auto sub_vt = read_VectorTransform_up(f);
|
|
1558
|
+
RandomRotationMatrix* rrot =
|
|
1559
|
+
dynamic_cast<RandomRotationMatrix*>(sub_vt.get());
|
|
784
1560
|
FAISS_THROW_IF_NOT_MSG(rrot, "expected a random rotation");
|
|
785
1561
|
idxl->rrot = *rrot;
|
|
786
|
-
delete rrot;
|
|
787
1562
|
}
|
|
788
1563
|
read_vector(idxl->codes, f);
|
|
789
1564
|
FAISS_THROW_IF_NOT(
|
|
790
1565
|
idxl->rrot.d_in == idxl->d && idxl->rrot.d_out == idxl->nbits);
|
|
791
1566
|
FAISS_THROW_IF_NOT(
|
|
792
1567
|
idxl->codes.size() == idxl->ntotal * idxl->code_size);
|
|
793
|
-
idx = idxl;
|
|
1568
|
+
idx = std::move(idxl);
|
|
794
1569
|
} else if (
|
|
795
1570
|
h == fourcc("IxPQ") || h == fourcc("IxPo") || h == fourcc("IxPq")) {
|
|
796
1571
|
// IxPQ and IxPo were merged into the same IndexPQ object
|
|
797
|
-
|
|
798
|
-
read_index_header(idxp, f);
|
|
1572
|
+
auto idxp = std::make_unique<IndexPQ>();
|
|
1573
|
+
read_index_header(*idxp, f);
|
|
799
1574
|
read_ProductQuantizer(&idxp->pq, f);
|
|
800
1575
|
idxp->code_size = idxp->pq.code_size;
|
|
801
1576
|
read_vector(idxp->codes, f);
|
|
1577
|
+
FAISS_THROW_IF_NOT(
|
|
1578
|
+
idxp->codes.size() == idxp->ntotal * idxp->code_size);
|
|
802
1579
|
if (h == fourcc("IxPo") || h == fourcc("IxPq")) {
|
|
803
1580
|
READ1(idxp->search_type);
|
|
804
1581
|
READ1(idxp->encode_signs);
|
|
@@ -810,51 +1587,113 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
810
1587
|
if (h == fourcc("IxPQ") || h == fourcc("IxPo")) {
|
|
811
1588
|
idxp->metric_type = METRIC_L2;
|
|
812
1589
|
}
|
|
813
|
-
idx = idxp;
|
|
1590
|
+
idx = std::move(idxp);
|
|
814
1591
|
} else if (h == fourcc("IxRQ") || h == fourcc("IxRq")) {
|
|
815
|
-
|
|
816
|
-
read_index_header(idxr, f);
|
|
1592
|
+
auto idxr = std::make_unique<IndexResidualQuantizer>();
|
|
1593
|
+
read_index_header(*idxr, f);
|
|
817
1594
|
if (h == fourcc("IxRQ")) {
|
|
818
|
-
read_ResidualQuantizer_old(
|
|
1595
|
+
read_ResidualQuantizer_old(idxr->rq, f);
|
|
819
1596
|
} else {
|
|
820
|
-
read_ResidualQuantizer(
|
|
1597
|
+
read_ResidualQuantizer(idxr->rq, f, io_flags);
|
|
821
1598
|
}
|
|
1599
|
+
validate_aq_dimension_match(
|
|
1600
|
+
idxr->rq, idxr->d, "IndexResidualQuantizer");
|
|
822
1601
|
READ1(idxr->code_size);
|
|
1602
|
+
validate_code_size_match(
|
|
1603
|
+
idxr->code_size, idxr->rq.code_size, "IndexResidualQuantizer");
|
|
823
1604
|
read_vector(idxr->codes, f);
|
|
824
|
-
|
|
1605
|
+
FAISS_THROW_IF_NOT(
|
|
1606
|
+
idxr->codes.size() == idxr->ntotal * idxr->code_size);
|
|
1607
|
+
idx = std::move(idxr);
|
|
825
1608
|
} else if (h == fourcc("IxLS")) {
|
|
826
|
-
auto idxr =
|
|
827
|
-
read_index_header(idxr, f);
|
|
828
|
-
read_LocalSearchQuantizer(
|
|
1609
|
+
auto idxr = std::make_unique<IndexLocalSearchQuantizer>();
|
|
1610
|
+
read_index_header(*idxr, f);
|
|
1611
|
+
read_LocalSearchQuantizer(idxr->lsq, f);
|
|
1612
|
+
validate_aq_dimension_match(
|
|
1613
|
+
idxr->lsq, idxr->d, "IndexLocalSearchQuantizer");
|
|
829
1614
|
READ1(idxr->code_size);
|
|
1615
|
+
validate_code_size_match(
|
|
1616
|
+
idxr->code_size,
|
|
1617
|
+
idxr->lsq.code_size,
|
|
1618
|
+
"IndexLocalSearchQuantizer");
|
|
830
1619
|
read_vector(idxr->codes, f);
|
|
831
|
-
|
|
1620
|
+
FAISS_THROW_IF_NOT(
|
|
1621
|
+
idxr->codes.size() == idxr->ntotal * idxr->code_size);
|
|
1622
|
+
idx = std::move(idxr);
|
|
832
1623
|
} else if (h == fourcc("IxPR")) {
|
|
833
|
-
auto idxpr =
|
|
834
|
-
read_index_header(idxpr, f);
|
|
835
|
-
read_ProductResidualQuantizer(
|
|
1624
|
+
auto idxpr = std::make_unique<IndexProductResidualQuantizer>();
|
|
1625
|
+
read_index_header(*idxpr, f);
|
|
1626
|
+
read_ProductResidualQuantizer(idxpr->prq, f, io_flags);
|
|
1627
|
+
validate_aq_dimension_match(
|
|
1628
|
+
idxpr->prq, idxpr->d, "IndexProductResidualQuantizer");
|
|
836
1629
|
READ1(idxpr->code_size);
|
|
1630
|
+
validate_code_size_match(
|
|
1631
|
+
idxpr->code_size,
|
|
1632
|
+
idxpr->prq.code_size,
|
|
1633
|
+
"IndexProductResidualQuantizer");
|
|
837
1634
|
read_vector(idxpr->codes, f);
|
|
838
|
-
|
|
1635
|
+
FAISS_THROW_IF_NOT(
|
|
1636
|
+
idxpr->codes.size() == idxpr->ntotal * idxpr->code_size);
|
|
1637
|
+
idx = std::move(idxpr);
|
|
839
1638
|
} else if (h == fourcc("IxPL")) {
|
|
840
|
-
auto idxpl =
|
|
841
|
-
read_index_header(idxpl, f);
|
|
842
|
-
read_ProductLocalSearchQuantizer(
|
|
1639
|
+
auto idxpl = std::make_unique<IndexProductLocalSearchQuantizer>();
|
|
1640
|
+
read_index_header(*idxpl, f);
|
|
1641
|
+
read_ProductLocalSearchQuantizer(idxpl->plsq, f);
|
|
1642
|
+
validate_aq_dimension_match(
|
|
1643
|
+
idxpl->plsq, idxpl->d, "IndexProductLocalSearchQuantizer");
|
|
843
1644
|
READ1(idxpl->code_size);
|
|
1645
|
+
validate_code_size_match(
|
|
1646
|
+
idxpl->code_size,
|
|
1647
|
+
idxpl->plsq.code_size,
|
|
1648
|
+
"IndexProductLocalSearchQuantizer");
|
|
844
1649
|
read_vector(idxpl->codes, f);
|
|
845
|
-
|
|
1650
|
+
FAISS_THROW_IF_NOT(
|
|
1651
|
+
idxpl->codes.size() == idxpl->ntotal * idxpl->code_size);
|
|
1652
|
+
idx = std::move(idxpl);
|
|
846
1653
|
} else if (h == fourcc("ImRQ")) {
|
|
847
|
-
|
|
848
|
-
read_index_header(idxr, f);
|
|
849
|
-
read_ResidualQuantizer(
|
|
1654
|
+
auto idxr = std::make_unique<ResidualCoarseQuantizer>();
|
|
1655
|
+
read_index_header(*idxr, f);
|
|
1656
|
+
read_ResidualQuantizer(idxr->rq, f, io_flags);
|
|
1657
|
+
validate_aq_dimension_match(
|
|
1658
|
+
idxr->rq, idxr->d, "ResidualCoarseQuantizer");
|
|
850
1659
|
READ1(idxr->beam_factor);
|
|
851
1660
|
if (io_flags & IO_FLAG_SKIP_PRECOMPUTE_TABLE) {
|
|
852
1661
|
// then we force the beam factor to -1
|
|
853
1662
|
// which skips the table precomputation.
|
|
854
1663
|
idxr->beam_factor = -1;
|
|
855
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
|
+
}
|
|
856
1695
|
idxr->set_beam_factor(idxr->beam_factor);
|
|
857
|
-
idx = idxr;
|
|
1696
|
+
idx = std::move(idxr);
|
|
858
1697
|
} else if (
|
|
859
1698
|
h == fourcc("ILfs") || h == fourcc("IRfs") || h == fourcc("IPRf") ||
|
|
860
1699
|
h == fourcc("IPLf")) {
|
|
@@ -862,34 +1701,38 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
862
1701
|
bool is_RQ = h == fourcc("IRfs");
|
|
863
1702
|
bool is_PLSQ = h == fourcc("IPLf");
|
|
864
1703
|
|
|
865
|
-
IndexAdditiveQuantizerFastScan
|
|
1704
|
+
std::unique_ptr<IndexAdditiveQuantizerFastScan> idxaqfs;
|
|
866
1705
|
if (is_LSQ) {
|
|
867
|
-
idxaqfs =
|
|
1706
|
+
idxaqfs = std::make_unique<IndexLocalSearchQuantizerFastScan>();
|
|
868
1707
|
} else if (is_RQ) {
|
|
869
|
-
idxaqfs =
|
|
1708
|
+
idxaqfs = std::make_unique<IndexResidualQuantizerFastScan>();
|
|
870
1709
|
} else if (is_PLSQ) {
|
|
871
|
-
idxaqfs =
|
|
1710
|
+
idxaqfs = std::make_unique<
|
|
1711
|
+
IndexProductLocalSearchQuantizerFastScan>();
|
|
872
1712
|
} else {
|
|
873
|
-
idxaqfs =
|
|
1713
|
+
idxaqfs = std::make_unique<IndexProductResidualQuantizerFastScan>();
|
|
874
1714
|
}
|
|
875
|
-
read_index_header(idxaqfs, f);
|
|
1715
|
+
read_index_header(*idxaqfs, f);
|
|
876
1716
|
|
|
877
1717
|
if (is_LSQ) {
|
|
878
|
-
read_LocalSearchQuantizer((LocalSearchQuantizer*)idxaqfs->aq, f);
|
|
1718
|
+
read_LocalSearchQuantizer(*(LocalSearchQuantizer*)idxaqfs->aq, f);
|
|
879
1719
|
} else if (is_RQ) {
|
|
880
1720
|
read_ResidualQuantizer(
|
|
881
|
-
(ResidualQuantizer*)idxaqfs->aq, f, io_flags);
|
|
1721
|
+
*(ResidualQuantizer*)idxaqfs->aq, f, io_flags);
|
|
882
1722
|
} else if (is_PLSQ) {
|
|
883
1723
|
read_ProductLocalSearchQuantizer(
|
|
884
|
-
(ProductLocalSearchQuantizer*)idxaqfs->aq, f);
|
|
1724
|
+
*(ProductLocalSearchQuantizer*)idxaqfs->aq, f);
|
|
885
1725
|
} else {
|
|
886
1726
|
read_ProductResidualQuantizer(
|
|
887
|
-
(ProductResidualQuantizer*)idxaqfs->aq, f, io_flags);
|
|
1727
|
+
*(ProductResidualQuantizer*)idxaqfs->aq, f, io_flags);
|
|
888
1728
|
}
|
|
1729
|
+
validate_aq_dimension_match(
|
|
1730
|
+
*idxaqfs->aq, idxaqfs->d, "IndexAdditiveQuantizerFastScan");
|
|
889
1731
|
|
|
890
1732
|
READ1(idxaqfs->implem);
|
|
891
1733
|
READ1(idxaqfs->bbs);
|
|
892
1734
|
READ1(idxaqfs->qbs);
|
|
1735
|
+
FAISS_THROW_IF_NOT_MSG(idxaqfs->qbs >= 0, "qbs must be non-negative");
|
|
893
1736
|
|
|
894
1737
|
READ1(idxaqfs->M);
|
|
895
1738
|
READ1(idxaqfs->nbits);
|
|
@@ -903,7 +1746,15 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
903
1746
|
READ1(idxaqfs->max_train_points);
|
|
904
1747
|
|
|
905
1748
|
READVECTOR(idxaqfs->codes);
|
|
906
|
-
|
|
1749
|
+
|
|
1750
|
+
validate_fastscan_fields(
|
|
1751
|
+
idxaqfs->M,
|
|
1752
|
+
idxaqfs->M2,
|
|
1753
|
+
idxaqfs->ksub,
|
|
1754
|
+
idxaqfs->bbs,
|
|
1755
|
+
"IndexAdditiveQuantizerFastScan");
|
|
1756
|
+
|
|
1757
|
+
idx = std::move(idxaqfs);
|
|
907
1758
|
} else if (
|
|
908
1759
|
h == fourcc("IVLf") || h == fourcc("IVRf") || h == fourcc("NPLf") ||
|
|
909
1760
|
h == fourcc("NPRf")) {
|
|
@@ -911,34 +1762,40 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
911
1762
|
bool is_RQ = h == fourcc("IVRf");
|
|
912
1763
|
bool is_PLSQ = h == fourcc("NPLf");
|
|
913
1764
|
|
|
914
|
-
IndexIVFAdditiveQuantizerFastScan
|
|
1765
|
+
std::unique_ptr<IndexIVFAdditiveQuantizerFastScan> ivaqfs;
|
|
915
1766
|
if (is_LSQ) {
|
|
916
|
-
ivaqfs =
|
|
1767
|
+
ivaqfs = std::make_unique<IndexIVFLocalSearchQuantizerFastScan>();
|
|
917
1768
|
} else if (is_RQ) {
|
|
918
|
-
ivaqfs =
|
|
1769
|
+
ivaqfs = std::make_unique<IndexIVFResidualQuantizerFastScan>();
|
|
919
1770
|
} else if (is_PLSQ) {
|
|
920
|
-
ivaqfs =
|
|
1771
|
+
ivaqfs = std::make_unique<
|
|
1772
|
+
IndexIVFProductLocalSearchQuantizerFastScan>();
|
|
921
1773
|
} else {
|
|
922
|
-
ivaqfs =
|
|
1774
|
+
ivaqfs = std::make_unique<
|
|
1775
|
+
IndexIVFProductResidualQuantizerFastScan>();
|
|
923
1776
|
}
|
|
924
|
-
read_ivf_header(ivaqfs, f);
|
|
1777
|
+
read_ivf_header(ivaqfs.get(), f);
|
|
925
1778
|
|
|
926
1779
|
if (is_LSQ) {
|
|
927
|
-
read_LocalSearchQuantizer((LocalSearchQuantizer*)ivaqfs->aq, f);
|
|
1780
|
+
read_LocalSearchQuantizer(*(LocalSearchQuantizer*)ivaqfs->aq, f);
|
|
928
1781
|
} else if (is_RQ) {
|
|
929
|
-
read_ResidualQuantizer(
|
|
1782
|
+
read_ResidualQuantizer(
|
|
1783
|
+
*(ResidualQuantizer*)ivaqfs->aq, f, io_flags);
|
|
930
1784
|
} else if (is_PLSQ) {
|
|
931
1785
|
read_ProductLocalSearchQuantizer(
|
|
932
|
-
(ProductLocalSearchQuantizer*)ivaqfs->aq, f);
|
|
1786
|
+
*(ProductLocalSearchQuantizer*)ivaqfs->aq, f);
|
|
933
1787
|
} else {
|
|
934
1788
|
read_ProductResidualQuantizer(
|
|
935
|
-
(ProductResidualQuantizer*)ivaqfs->aq, f, io_flags);
|
|
1789
|
+
*(ProductResidualQuantizer*)ivaqfs->aq, f, io_flags);
|
|
936
1790
|
}
|
|
1791
|
+
validate_aq_dimension_match(
|
|
1792
|
+
*ivaqfs->aq, ivaqfs->d, "IndexIVFAdditiveQuantizerFastScan");
|
|
937
1793
|
|
|
938
1794
|
READ1(ivaqfs->by_residual);
|
|
939
1795
|
READ1(ivaqfs->implem);
|
|
940
1796
|
READ1(ivaqfs->bbs);
|
|
941
1797
|
READ1(ivaqfs->qbs);
|
|
1798
|
+
FAISS_THROW_IF_NOT_MSG(ivaqfs->qbs >= 0, "qbs must be non-negative");
|
|
942
1799
|
|
|
943
1800
|
READ1(ivaqfs->M);
|
|
944
1801
|
READ1(ivaqfs->nbits);
|
|
@@ -951,15 +1808,23 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
951
1808
|
READ1(ivaqfs->norm_scale);
|
|
952
1809
|
READ1(ivaqfs->max_train_points);
|
|
953
1810
|
|
|
954
|
-
read_InvertedLists(ivaqfs, f, io_flags);
|
|
1811
|
+
read_InvertedLists(*ivaqfs, f, io_flags);
|
|
955
1812
|
ivaqfs->init_code_packer();
|
|
956
|
-
|
|
1813
|
+
|
|
1814
|
+
validate_fastscan_fields(
|
|
1815
|
+
ivaqfs->M,
|
|
1816
|
+
ivaqfs->M2,
|
|
1817
|
+
ivaqfs->ksub,
|
|
1818
|
+
ivaqfs->bbs,
|
|
1819
|
+
"IndexIVFAdditiveQuantizerFastScan");
|
|
1820
|
+
|
|
1821
|
+
idx = std::move(ivaqfs);
|
|
957
1822
|
} else if (h == fourcc("IvFl") || h == fourcc("IvFL")) { // legacy
|
|
958
|
-
|
|
1823
|
+
auto ivfl = std::make_unique<IndexIVFFlat>();
|
|
959
1824
|
std::vector<std::vector<idx_t>> ids;
|
|
960
|
-
read_ivf_header(ivfl, f, &ids);
|
|
1825
|
+
read_ivf_header(ivfl.get(), f, &ids);
|
|
961
1826
|
ivfl->code_size = ivfl->d * sizeof(float);
|
|
962
|
-
ArrayInvertedLists* ail = set_array_invlist(ivfl, ids);
|
|
1827
|
+
ArrayInvertedLists* ail = set_array_invlist(ivfl.get(), ids);
|
|
963
1828
|
|
|
964
1829
|
if (h == fourcc("IvFL")) {
|
|
965
1830
|
for (size_t i = 0; i < ivfl->nlist; i++) {
|
|
@@ -973,109 +1838,170 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
973
1838
|
memcpy(ail->codes[i].data(), vec.data(), ail->codes[i].size());
|
|
974
1839
|
}
|
|
975
1840
|
}
|
|
976
|
-
idx = ivfl;
|
|
1841
|
+
idx = std::move(ivfl);
|
|
977
1842
|
} else if (h == fourcc("IwFd")) {
|
|
978
|
-
|
|
979
|
-
read_ivf_header(ivfl, f);
|
|
1843
|
+
auto ivfl = std::make_unique<IndexIVFFlatDedup>();
|
|
1844
|
+
read_ivf_header(ivfl.get(), f);
|
|
980
1845
|
ivfl->code_size = ivfl->d * sizeof(float);
|
|
981
1846
|
{
|
|
982
1847
|
std::vector<idx_t> tab;
|
|
983
1848
|
READVECTOR(tab);
|
|
984
|
-
|
|
1849
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1850
|
+
tab.size() % 2 == 0,
|
|
1851
|
+
"invalid IVFFlatDedup instances table size: %zd "
|
|
1852
|
+
"(must be even)",
|
|
1853
|
+
tab.size());
|
|
1854
|
+
for (size_t i = 0; i < tab.size(); i += 2) {
|
|
985
1855
|
std::pair<idx_t, idx_t> pair(tab[i], tab[i + 1]);
|
|
986
1856
|
ivfl->instances.insert(pair);
|
|
987
1857
|
}
|
|
988
1858
|
}
|
|
989
|
-
read_InvertedLists(ivfl, f, io_flags);
|
|
990
|
-
idx = ivfl;
|
|
1859
|
+
read_InvertedLists(*ivfl, f, io_flags);
|
|
1860
|
+
idx = std::move(ivfl);
|
|
991
1861
|
} else if (h == fourcc("IwPn")) {
|
|
992
|
-
|
|
993
|
-
read_ivf_header(ivfp, f);
|
|
1862
|
+
auto ivfp = std::make_unique<IndexIVFFlatPanorama>();
|
|
1863
|
+
read_ivf_header(ivfp.get(), f);
|
|
1864
|
+
ivfp->code_size = ivfp->d * sizeof(float);
|
|
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);
|
|
994
1872
|
ivfp->code_size = ivfp->d * sizeof(float);
|
|
995
1873
|
READ1(ivfp->n_levels);
|
|
996
|
-
|
|
997
|
-
|
|
1874
|
+
READ1(ivfp->batch_size);
|
|
1875
|
+
read_InvertedLists(*ivfp, f, io_flags);
|
|
1876
|
+
idx = std::move(ivfp);
|
|
998
1877
|
} else if (h == fourcc("IwFl")) {
|
|
999
|
-
|
|
1000
|
-
read_ivf_header(ivfl, f);
|
|
1878
|
+
auto ivfl = std::make_unique<IndexIVFFlat>();
|
|
1879
|
+
read_ivf_header(ivfl.get(), f);
|
|
1001
1880
|
ivfl->code_size = ivfl->d * sizeof(float);
|
|
1002
|
-
read_InvertedLists(ivfl, f, io_flags);
|
|
1003
|
-
idx = ivfl;
|
|
1881
|
+
read_InvertedLists(*ivfl, f, io_flags);
|
|
1882
|
+
idx = std::move(ivfl);
|
|
1004
1883
|
} else if (h == fourcc("IxSQ")) {
|
|
1005
|
-
|
|
1006
|
-
read_index_header(idxs, f);
|
|
1007
|
-
read_ScalarQuantizer(&idxs->sq, f);
|
|
1884
|
+
auto idxs = std::make_unique<IndexScalarQuantizer>();
|
|
1885
|
+
read_index_header(*idxs, f);
|
|
1886
|
+
read_ScalarQuantizer(&idxs->sq, f, *idxs);
|
|
1008
1887
|
read_vector(idxs->codes, f);
|
|
1009
1888
|
idxs->code_size = idxs->sq.code_size;
|
|
1010
|
-
idx = idxs;
|
|
1889
|
+
idx = std::move(idxs);
|
|
1011
1890
|
} else if (h == fourcc("IxLa")) {
|
|
1012
1891
|
int d, nsq, scale_nbit, r2;
|
|
1013
1892
|
READ1(d);
|
|
1014
1893
|
READ1(nsq);
|
|
1015
1894
|
READ1(scale_nbit);
|
|
1016
1895
|
READ1(r2);
|
|
1017
|
-
|
|
1018
|
-
|
|
1896
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1897
|
+
nsq > 0, "invalid IndexLattice nsq %d (must be > 0)", nsq);
|
|
1898
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1899
|
+
d > 0 && d % nsq == 0,
|
|
1900
|
+
"invalid IndexLattice d=%d, nsq=%d (d must be > 0 and divisible by nsq)",
|
|
1901
|
+
d,
|
|
1902
|
+
nsq);
|
|
1903
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1904
|
+
r2 > 0, "invalid IndexLattice r2 %d (must be > 0)", r2);
|
|
1905
|
+
int dsq = d / nsq;
|
|
1906
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
1907
|
+
dsq >= 2 && (dsq & (dsq - 1)) == 0,
|
|
1908
|
+
"invalid IndexLattice d=%d, nsq=%d: d/nsq=%d must be a power of 2 >= 2",
|
|
1909
|
+
d,
|
|
1910
|
+
nsq,
|
|
1911
|
+
dsq);
|
|
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
|
+
}
|
|
1934
|
+
read_index_header(*idxl, f);
|
|
1019
1935
|
READVECTOR(idxl->trained);
|
|
1020
|
-
idx = idxl;
|
|
1936
|
+
idx = std::move(idxl);
|
|
1021
1937
|
} else if (h == fourcc("IvSQ")) { // legacy
|
|
1022
|
-
|
|
1938
|
+
auto ivsc = std::make_unique<IndexIVFScalarQuantizer>();
|
|
1023
1939
|
std::vector<std::vector<idx_t>> ids;
|
|
1024
|
-
read_ivf_header(ivsc, f, &ids);
|
|
1025
|
-
read_ScalarQuantizer(&ivsc->sq, f);
|
|
1940
|
+
read_ivf_header(ivsc.get(), f, &ids);
|
|
1941
|
+
read_ScalarQuantizer(&ivsc->sq, f, *ivsc);
|
|
1026
1942
|
READ1(ivsc->code_size);
|
|
1027
|
-
|
|
1028
|
-
|
|
1943
|
+
validate_code_size_match(
|
|
1944
|
+
ivsc->code_size, ivsc->sq.code_size, "IndexIVFScalarQuantizer");
|
|
1945
|
+
ArrayInvertedLists* ail = set_array_invlist(ivsc.get(), ids);
|
|
1946
|
+
for (size_t i = 0; i < ivsc->nlist; i++)
|
|
1029
1947
|
READVECTOR(ail->codes[i]);
|
|
1030
|
-
idx = ivsc;
|
|
1948
|
+
idx = std::move(ivsc);
|
|
1031
1949
|
} else if (h == fourcc("IwSQ") || h == fourcc("IwSq")) {
|
|
1032
|
-
|
|
1033
|
-
read_ivf_header(ivsc, f);
|
|
1034
|
-
read_ScalarQuantizer(&ivsc->sq, f);
|
|
1950
|
+
auto ivsc = std::make_unique<IndexIVFScalarQuantizer>();
|
|
1951
|
+
read_ivf_header(ivsc.get(), f);
|
|
1952
|
+
read_ScalarQuantizer(&ivsc->sq, f, *ivsc);
|
|
1035
1953
|
READ1(ivsc->code_size);
|
|
1954
|
+
validate_code_size_match(
|
|
1955
|
+
ivsc->code_size, ivsc->sq.code_size, "IndexIVFScalarQuantizer");
|
|
1036
1956
|
if (h == fourcc("IwSQ")) {
|
|
1037
1957
|
ivsc->by_residual = true;
|
|
1038
1958
|
} else {
|
|
1039
1959
|
READ1(ivsc->by_residual);
|
|
1040
1960
|
}
|
|
1041
|
-
read_InvertedLists(ivsc, f, io_flags);
|
|
1042
|
-
idx = ivsc;
|
|
1961
|
+
read_InvertedLists(*ivsc, f, io_flags);
|
|
1962
|
+
idx = std::move(ivsc);
|
|
1043
1963
|
} else if (
|
|
1044
1964
|
h == fourcc("IwLS") || h == fourcc("IwRQ") || h == fourcc("IwPL") ||
|
|
1045
1965
|
h == fourcc("IwPR")) {
|
|
1046
1966
|
bool is_LSQ = h == fourcc("IwLS");
|
|
1047
1967
|
bool is_RQ = h == fourcc("IwRQ");
|
|
1048
1968
|
bool is_PLSQ = h == fourcc("IwPL");
|
|
1049
|
-
IndexIVFAdditiveQuantizer
|
|
1969
|
+
std::unique_ptr<IndexIVFAdditiveQuantizer> iva;
|
|
1050
1970
|
if (is_LSQ) {
|
|
1051
|
-
iva =
|
|
1971
|
+
iva = std::make_unique<IndexIVFLocalSearchQuantizer>();
|
|
1052
1972
|
} else if (is_RQ) {
|
|
1053
|
-
iva =
|
|
1973
|
+
iva = std::make_unique<IndexIVFResidualQuantizer>();
|
|
1054
1974
|
} else if (is_PLSQ) {
|
|
1055
|
-
iva =
|
|
1975
|
+
iva = std::make_unique<IndexIVFProductLocalSearchQuantizer>();
|
|
1056
1976
|
} else {
|
|
1057
|
-
iva =
|
|
1977
|
+
iva = std::make_unique<IndexIVFProductResidualQuantizer>();
|
|
1058
1978
|
}
|
|
1059
|
-
read_ivf_header(iva, f);
|
|
1979
|
+
read_ivf_header(iva.get(), f);
|
|
1060
1980
|
READ1(iva->code_size);
|
|
1061
1981
|
if (is_LSQ) {
|
|
1062
|
-
read_LocalSearchQuantizer((LocalSearchQuantizer*)iva->aq, f);
|
|
1982
|
+
read_LocalSearchQuantizer(*(LocalSearchQuantizer*)iva->aq, f);
|
|
1063
1983
|
} else if (is_RQ) {
|
|
1064
|
-
read_ResidualQuantizer((ResidualQuantizer*)iva->aq, f, io_flags);
|
|
1984
|
+
read_ResidualQuantizer(*(ResidualQuantizer*)iva->aq, f, io_flags);
|
|
1065
1985
|
} else if (is_PLSQ) {
|
|
1066
1986
|
read_ProductLocalSearchQuantizer(
|
|
1067
|
-
(ProductLocalSearchQuantizer*)iva->aq, f);
|
|
1987
|
+
*(ProductLocalSearchQuantizer*)iva->aq, f);
|
|
1068
1988
|
} else {
|
|
1069
1989
|
read_ProductResidualQuantizer(
|
|
1070
|
-
(ProductResidualQuantizer*)iva->aq, f, io_flags);
|
|
1990
|
+
*(ProductResidualQuantizer*)iva->aq, f, io_flags);
|
|
1071
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");
|
|
1072
1998
|
READ1(iva->by_residual);
|
|
1073
1999
|
READ1(iva->use_precomputed_table);
|
|
1074
|
-
read_InvertedLists(iva, f, io_flags);
|
|
1075
|
-
idx = iva;
|
|
2000
|
+
read_InvertedLists(*iva, f, io_flags);
|
|
2001
|
+
idx = std::move(iva);
|
|
1076
2002
|
} else if (h == fourcc("IwSh")) {
|
|
1077
|
-
|
|
1078
|
-
read_ivf_header(ivsp, f);
|
|
2003
|
+
auto ivsp = std::make_unique<IndexIVFSpectralHash>();
|
|
2004
|
+
read_ivf_header(ivsp.get(), f);
|
|
1079
2005
|
ivsp->vt = read_VectorTransform(f);
|
|
1080
2006
|
ivsp->own_fields = true;
|
|
1081
2007
|
READ1(ivsp->nbit);
|
|
@@ -1084,132 +2010,217 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1084
2010
|
READ1(ivsp->period);
|
|
1085
2011
|
READ1(ivsp->threshold_type);
|
|
1086
2012
|
READVECTOR(ivsp->trained);
|
|
1087
|
-
read_InvertedLists(ivsp, f, io_flags);
|
|
1088
|
-
idx = ivsp;
|
|
2013
|
+
read_InvertedLists(*ivsp, f, io_flags);
|
|
2014
|
+
idx = std::move(ivsp);
|
|
1089
2015
|
} else if (
|
|
1090
2016
|
h == fourcc("IvPQ") || h == fourcc("IvQR") || h == fourcc("IwPQ") ||
|
|
1091
2017
|
h == fourcc("IwQR")) {
|
|
1092
2018
|
idx = read_ivfpq(f, h, io_flags);
|
|
1093
2019
|
} else if (h == fourcc("IwIQ")) {
|
|
1094
|
-
auto
|
|
2020
|
+
auto indep = std::make_unique<IndexIVFIndependentQuantizer>();
|
|
1095
2021
|
indep->own_fields = true;
|
|
1096
|
-
read_index_header(indep, f);
|
|
2022
|
+
read_index_header(*indep, f);
|
|
1097
2023
|
indep->quantizer = read_index(f, io_flags);
|
|
1098
2024
|
bool has_vt;
|
|
1099
2025
|
READ1(has_vt);
|
|
1100
2026
|
if (has_vt) {
|
|
1101
2027
|
indep->vt = read_VectorTransform(f);
|
|
1102
2028
|
}
|
|
1103
|
-
|
|
2029
|
+
auto ivf_idx = read_index_up(f, io_flags);
|
|
2030
|
+
indep->index_ivf = dynamic_cast<IndexIVF*>(ivf_idx.get());
|
|
1104
2031
|
FAISS_THROW_IF_NOT(indep->index_ivf);
|
|
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
|
+
}
|
|
1105
2045
|
if (auto index_ivfpq = dynamic_cast<IndexIVFPQ*>(indep->index_ivf)) {
|
|
1106
2046
|
READ1(index_ivfpq->use_precomputed_table);
|
|
1107
2047
|
}
|
|
1108
|
-
idx = indep;
|
|
2048
|
+
idx = std::move(indep);
|
|
1109
2049
|
} else if (h == fourcc("IxPT")) {
|
|
1110
|
-
|
|
2050
|
+
auto ixpt = std::make_unique<IndexPreTransform>();
|
|
1111
2051
|
ixpt->own_fields = true;
|
|
1112
|
-
read_index_header(ixpt, f);
|
|
2052
|
+
read_index_header(*ixpt, f);
|
|
1113
2053
|
int nt;
|
|
1114
2054
|
if (read_old_fmt_hack == 2) {
|
|
1115
2055
|
nt = 1;
|
|
1116
2056
|
} else {
|
|
1117
2057
|
READ1(nt);
|
|
1118
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");
|
|
1119
2065
|
for (int i = 0; i < nt; i++) {
|
|
1120
2066
|
ixpt->chain.push_back(read_VectorTransform(f));
|
|
1121
2067
|
}
|
|
1122
2068
|
ixpt->index = read_index(f, io_flags);
|
|
1123
|
-
|
|
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
|
+
}
|
|
2099
|
+
idx = std::move(ixpt);
|
|
1124
2100
|
} else if (h == fourcc("Imiq")) {
|
|
1125
|
-
|
|
1126
|
-
read_index_header(imiq, f);
|
|
2101
|
+
auto imiq = std::make_unique<MultiIndexQuantizer>();
|
|
2102
|
+
read_index_header(*imiq, f);
|
|
1127
2103
|
read_ProductQuantizer(&imiq->pq, f);
|
|
1128
|
-
idx = imiq;
|
|
2104
|
+
idx = std::move(imiq);
|
|
1129
2105
|
} else if (h == fourcc("IxRF") || h == fourcc("IxRP")) {
|
|
1130
|
-
|
|
1131
|
-
read_index_header(idxrf, f);
|
|
1132
|
-
|
|
1133
|
-
|
|
2106
|
+
auto idxrf = std::make_unique<IndexRefine>();
|
|
2107
|
+
read_index_header(*idxrf, f);
|
|
2108
|
+
auto base = read_index_up(f, io_flags);
|
|
2109
|
+
auto refine = read_index_up(f, io_flags);
|
|
1134
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);
|
|
1135
2117
|
if (h == fourcc("IxRP")) {
|
|
1136
2118
|
// then make a RefineFlatPanorama with it
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
} else if (dynamic_cast<IndexFlat*>(idxrf->refine_index)) {
|
|
2119
|
+
auto idxrf_new = std::make_unique<IndexRefinePanorama>();
|
|
2120
|
+
static_cast<IndexRefine&>(*idxrf_new) = *idxrf;
|
|
2121
|
+
idxrf = std::move(idxrf_new);
|
|
2122
|
+
} else if (dynamic_cast<IndexFlat*>(refine.get())) {
|
|
1142
2123
|
// then make a RefineFlat with it
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
delete idxrf_old;
|
|
2124
|
+
auto idxrf_new = std::make_unique<IndexRefineFlat>();
|
|
2125
|
+
static_cast<IndexRefine&>(*idxrf_new) = *idxrf;
|
|
2126
|
+
idxrf = std::move(idxrf_new);
|
|
1147
2127
|
}
|
|
2128
|
+
idxrf->base_index = base.release();
|
|
2129
|
+
idxrf->refine_index = refine.release();
|
|
1148
2130
|
idxrf->own_fields = true;
|
|
1149
2131
|
idxrf->own_refine_index = true;
|
|
1150
|
-
idx = idxrf;
|
|
2132
|
+
idx = std::move(idxrf);
|
|
1151
2133
|
} else if (h == fourcc("IxMp") || h == fourcc("IxM2")) {
|
|
1152
2134
|
bool is_map2 = h == fourcc("IxM2");
|
|
1153
|
-
IndexIDMap
|
|
1154
|
-
|
|
2135
|
+
std::unique_ptr<IndexIDMap> idxmap = is_map2
|
|
2136
|
+
? std::make_unique<IndexIDMap2>()
|
|
2137
|
+
: std::make_unique<IndexIDMap>();
|
|
2138
|
+
read_index_header(*idxmap, f);
|
|
1155
2139
|
idxmap->index = read_index(f, io_flags);
|
|
1156
2140
|
idxmap->own_fields = true;
|
|
1157
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);
|
|
1158
2154
|
if (is_map2) {
|
|
1159
|
-
static_cast<IndexIDMap2*>(idxmap)->construct_rev_map();
|
|
2155
|
+
static_cast<IndexIDMap2*>(idxmap.get())->construct_rev_map();
|
|
1160
2156
|
}
|
|
1161
|
-
idx = idxmap;
|
|
2157
|
+
idx = std::move(idxmap);
|
|
1162
2158
|
} else if (h == fourcc("Ix2L")) {
|
|
1163
|
-
|
|
1164
|
-
read_index_header(idxp, f);
|
|
2159
|
+
auto idxp = std::make_unique<Index2Layer>();
|
|
2160
|
+
read_index_header(*idxp, f);
|
|
1165
2161
|
idxp->q1.quantizer = read_index(f, io_flags);
|
|
2162
|
+
idxp->q1.own_fields = true;
|
|
1166
2163
|
READ1(idxp->q1.nlist);
|
|
1167
2164
|
READ1(idxp->q1.quantizer_trains_alone);
|
|
1168
2165
|
read_ProductQuantizer(&idxp->pq, f);
|
|
1169
2166
|
READ1(idxp->code_size_1);
|
|
1170
2167
|
READ1(idxp->code_size_2);
|
|
1171
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");
|
|
1172
2177
|
read_vector(idxp->codes, f);
|
|
1173
|
-
idx = idxp;
|
|
2178
|
+
idx = std::move(idxp);
|
|
1174
2179
|
} else if (
|
|
1175
2180
|
h == fourcc("IHNf") || h == fourcc("IHNp") || h == fourcc("IHNs") ||
|
|
1176
2181
|
h == fourcc("IHN2") || h == fourcc("IHNc") || h == fourcc("IHc2") ||
|
|
1177
2182
|
h == fourcc("IHfP")) {
|
|
1178
|
-
IndexHNSW
|
|
2183
|
+
std::unique_ptr<IndexHNSW> idxhnsw;
|
|
1179
2184
|
if (h == fourcc("IHNf")) {
|
|
1180
|
-
idxhnsw =
|
|
2185
|
+
idxhnsw = std::make_unique<IndexHNSWFlat>();
|
|
1181
2186
|
}
|
|
1182
2187
|
if (h == fourcc("IHfP")) {
|
|
1183
|
-
idxhnsw =
|
|
2188
|
+
idxhnsw = std::make_unique<IndexHNSWFlatPanorama>();
|
|
1184
2189
|
}
|
|
1185
2190
|
if (h == fourcc("IHNp")) {
|
|
1186
|
-
idxhnsw =
|
|
2191
|
+
idxhnsw = std::make_unique<IndexHNSWPQ>();
|
|
1187
2192
|
}
|
|
1188
2193
|
if (h == fourcc("IHNs")) {
|
|
1189
|
-
idxhnsw =
|
|
2194
|
+
idxhnsw = std::make_unique<IndexHNSWSQ>();
|
|
1190
2195
|
}
|
|
1191
2196
|
if (h == fourcc("IHN2")) {
|
|
1192
|
-
idxhnsw =
|
|
2197
|
+
idxhnsw = std::make_unique<IndexHNSW2Level>();
|
|
1193
2198
|
}
|
|
1194
2199
|
if (h == fourcc("IHNc")) {
|
|
1195
|
-
idxhnsw =
|
|
2200
|
+
idxhnsw = std::make_unique<IndexHNSWCagra>();
|
|
1196
2201
|
}
|
|
1197
2202
|
if (h == fourcc("IHc2")) {
|
|
1198
|
-
idxhnsw =
|
|
2203
|
+
idxhnsw = std::make_unique<IndexHNSWCagra>();
|
|
1199
2204
|
}
|
|
1200
|
-
read_index_header(idxhnsw, f);
|
|
2205
|
+
read_index_header(*idxhnsw, f);
|
|
1201
2206
|
if (h == fourcc("IHfP")) {
|
|
1202
|
-
auto idx_panorama =
|
|
2207
|
+
auto idx_panorama =
|
|
2208
|
+
dynamic_cast<IndexHNSWFlatPanorama*>(idxhnsw.get());
|
|
2209
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2210
|
+
idx_panorama,
|
|
2211
|
+
"dynamic_cast to IndexHNSWFlatPanorama failed");
|
|
1203
2212
|
size_t nlevels;
|
|
1204
2213
|
READ1(nlevels);
|
|
1205
2214
|
const_cast<size_t&>(idx_panorama->num_panorama_levels) = nlevels;
|
|
1206
|
-
const_cast<
|
|
1207
|
-
(idx_panorama->d
|
|
2215
|
+
const_cast<Panorama&>(idx_panorama->pano) =
|
|
2216
|
+
Panorama(idx_panorama->d * sizeof(float), nlevels, 1);
|
|
1208
2217
|
READVECTOR(idx_panorama->cum_sums);
|
|
1209
2218
|
}
|
|
1210
2219
|
if (h == fourcc("IHNc") || h == fourcc("IHc2")) {
|
|
1211
2220
|
READ1(idxhnsw->keep_max_size_level0);
|
|
1212
|
-
auto idx_hnsw_cagra = dynamic_cast<IndexHNSWCagra*>(idxhnsw);
|
|
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");
|
|
1213
2224
|
READ1(idx_hnsw_cagra->base_level_only);
|
|
1214
2225
|
READ1(idx_hnsw_cagra->num_base_level_search_entrypoints);
|
|
1215
2226
|
if (h == fourcc("IHc2")) {
|
|
@@ -1218,51 +2229,127 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1218
2229
|
idx_hnsw_cagra->set_numeric_type(faiss::Float32);
|
|
1219
2230
|
}
|
|
1220
2231
|
}
|
|
1221
|
-
read_HNSW(
|
|
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);
|
|
1222
2239
|
idxhnsw->hnsw.is_panorama = (h == fourcc("IHfP"));
|
|
1223
2240
|
idxhnsw->storage = read_index(f, io_flags);
|
|
1224
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
|
+
}
|
|
1225
2264
|
if (h == fourcc("IHNp") && !(io_flags & IO_FLAG_PQ_SKIP_SDC_TABLE)) {
|
|
1226
|
-
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();
|
|
1227
2270
|
}
|
|
1228
|
-
idx = idxhnsw;
|
|
2271
|
+
idx = std::move(idxhnsw);
|
|
1229
2272
|
} else if (
|
|
1230
2273
|
h == fourcc("INSf") || h == fourcc("INSp") || h == fourcc("INSs")) {
|
|
1231
|
-
IndexNSG
|
|
2274
|
+
std::unique_ptr<IndexNSG> idxnsg;
|
|
1232
2275
|
if (h == fourcc("INSf")) {
|
|
1233
|
-
idxnsg =
|
|
2276
|
+
idxnsg = std::make_unique<IndexNSGFlat>();
|
|
1234
2277
|
}
|
|
1235
2278
|
if (h == fourcc("INSp")) {
|
|
1236
|
-
idxnsg =
|
|
2279
|
+
idxnsg = std::make_unique<IndexNSGPQ>();
|
|
1237
2280
|
}
|
|
1238
2281
|
if (h == fourcc("INSs")) {
|
|
1239
|
-
idxnsg =
|
|
2282
|
+
idxnsg = std::make_unique<IndexNSGSQ>();
|
|
1240
2283
|
}
|
|
1241
|
-
read_index_header(idxnsg, f);
|
|
2284
|
+
read_index_header(*idxnsg, f);
|
|
1242
2285
|
READ1(idxnsg->GK);
|
|
1243
2286
|
READ1(idxnsg->build_type);
|
|
1244
2287
|
READ1(idxnsg->nndescent_S);
|
|
1245
2288
|
READ1(idxnsg->nndescent_R);
|
|
1246
2289
|
READ1(idxnsg->nndescent_L);
|
|
1247
2290
|
READ1(idxnsg->nndescent_iter);
|
|
1248
|
-
read_NSG(
|
|
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
|
+
}
|
|
1249
2300
|
idxnsg->storage = read_index(f, io_flags);
|
|
1250
2301
|
idxnsg->own_fields = true;
|
|
1251
|
-
|
|
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
|
+
}
|
|
2315
|
+
idx = std::move(idxnsg);
|
|
1252
2316
|
} else if (h == fourcc("INNf")) {
|
|
1253
|
-
|
|
1254
|
-
read_index_header(idxnnd, f);
|
|
1255
|
-
read_NNDescent(
|
|
2317
|
+
auto idxnnd = std::make_unique<IndexNNDescentFlat>();
|
|
2318
|
+
read_index_header(*idxnnd, f);
|
|
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
|
+
}
|
|
1256
2328
|
idxnnd->storage = read_index(f, io_flags);
|
|
1257
2329
|
idxnnd->own_fields = true;
|
|
1258
|
-
|
|
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
|
+
}
|
|
2344
|
+
idx = std::move(idxnnd);
|
|
1259
2345
|
} else if (h == fourcc("IPfs")) {
|
|
1260
|
-
|
|
1261
|
-
read_index_header(idxpqfs, f);
|
|
2346
|
+
auto idxpqfs = std::make_unique<IndexPQFastScan>();
|
|
2347
|
+
read_index_header(*idxpqfs, f);
|
|
1262
2348
|
read_ProductQuantizer(&idxpqfs->pq, f);
|
|
1263
2349
|
READ1(idxpqfs->implem);
|
|
1264
2350
|
READ1(idxpqfs->bbs);
|
|
1265
2351
|
READ1(idxpqfs->qbs);
|
|
2352
|
+
FAISS_THROW_IF_NOT_MSG(idxpqfs->qbs >= 0, "qbs must be non-negative");
|
|
1266
2353
|
READ1(idxpqfs->ntotal2);
|
|
1267
2354
|
READ1(idxpqfs->M2);
|
|
1268
2355
|
READVECTOR(idxpqfs->codes);
|
|
@@ -1273,11 +2360,18 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1273
2360
|
idxpqfs->ksub = (1 << pq.nbits);
|
|
1274
2361
|
idxpqfs->code_size = pq.code_size;
|
|
1275
2362
|
|
|
1276
|
-
|
|
2363
|
+
validate_fastscan_fields(
|
|
2364
|
+
idxpqfs->M,
|
|
2365
|
+
idxpqfs->M2,
|
|
2366
|
+
idxpqfs->ksub,
|
|
2367
|
+
idxpqfs->bbs,
|
|
2368
|
+
"IndexPQFastScan");
|
|
2369
|
+
|
|
2370
|
+
idx = std::move(idxpqfs);
|
|
1277
2371
|
|
|
1278
2372
|
} else if (h == fourcc("IwPf")) {
|
|
1279
|
-
|
|
1280
|
-
read_ivf_header(ivpq, f);
|
|
2373
|
+
auto ivpq = std::make_unique<IndexIVFPQFastScan>();
|
|
2374
|
+
read_ivf_header(ivpq.get(), f);
|
|
1281
2375
|
READ1(ivpq->by_residual);
|
|
1282
2376
|
READ1(ivpq->code_size);
|
|
1283
2377
|
READ1(ivpq->bbs);
|
|
@@ -1285,7 +2379,7 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1285
2379
|
READ1(ivpq->implem);
|
|
1286
2380
|
READ1(ivpq->qbs2);
|
|
1287
2381
|
read_ProductQuantizer(&ivpq->pq, f);
|
|
1288
|
-
read_InvertedLists(ivpq, f, io_flags);
|
|
2382
|
+
read_InvertedLists(*ivpq, f, io_flags);
|
|
1289
2383
|
ivpq->precompute_table();
|
|
1290
2384
|
|
|
1291
2385
|
const auto& pq = ivpq->pq;
|
|
@@ -1295,30 +2389,45 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1295
2389
|
ivpq->code_size = pq.code_size;
|
|
1296
2390
|
ivpq->init_code_packer();
|
|
1297
2391
|
|
|
1298
|
-
|
|
2392
|
+
validate_fastscan_fields(
|
|
2393
|
+
ivpq->M, ivpq->M2, ivpq->ksub, ivpq->bbs, "IndexIVFPQFastScan");
|
|
2394
|
+
|
|
2395
|
+
idx = std::move(ivpq);
|
|
1299
2396
|
} else if (h == fourcc("IRMf")) {
|
|
1300
|
-
|
|
1301
|
-
read_index_header(imm, f);
|
|
2397
|
+
auto imm = std::make_unique<IndexRowwiseMinMax>();
|
|
2398
|
+
read_index_header(*imm, f);
|
|
1302
2399
|
|
|
1303
2400
|
imm->index = read_index(f, io_flags);
|
|
1304
2401
|
imm->own_fields = true;
|
|
1305
2402
|
|
|
1306
|
-
idx = imm;
|
|
2403
|
+
idx = std::move(imm);
|
|
1307
2404
|
} else if (h == fourcc("IRMh")) {
|
|
1308
|
-
|
|
1309
|
-
read_index_header(imm, f);
|
|
2405
|
+
auto imm = std::make_unique<IndexRowwiseMinMaxFP16>();
|
|
2406
|
+
read_index_header(*imm, f);
|
|
1310
2407
|
|
|
1311
2408
|
imm->index = read_index(f, io_flags);
|
|
1312
2409
|
imm->own_fields = true;
|
|
1313
2410
|
|
|
1314
|
-
idx = imm;
|
|
1315
|
-
} else if (h == fourcc("Irfs")) {
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
2411
|
+
idx = std::move(imm);
|
|
2412
|
+
} else if (h == fourcc("Irfn") || h == fourcc("Irfs")) {
|
|
2413
|
+
// Irfn = new format (aux data embedded in SIMD blocks)
|
|
2414
|
+
// Irfs = legacy format (flat_storage separate, needs migration)
|
|
2415
|
+
const bool is_legacy = (h == fourcc("Irfs"));
|
|
2416
|
+
|
|
2417
|
+
auto idxqfs = std::make_unique<IndexRaBitQFastScan>();
|
|
2418
|
+
read_index_header(*idxqfs, f);
|
|
2419
|
+
read_RaBitQuantizer(idxqfs->rabitq, f, idxqfs->d, true);
|
|
1319
2420
|
READVECTOR(idxqfs->center);
|
|
1320
2421
|
READ1(idxqfs->qb);
|
|
1321
|
-
|
|
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);
|
|
2426
|
+
|
|
2427
|
+
std::vector<uint8_t> legacy_flat_storage;
|
|
2428
|
+
if (is_legacy) {
|
|
2429
|
+
READVECTOR(legacy_flat_storage);
|
|
2430
|
+
}
|
|
1322
2431
|
|
|
1323
2432
|
READ1(idxqfs->bbs);
|
|
1324
2433
|
READ1(idxqfs->ntotal2);
|
|
@@ -1331,74 +2440,131 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1331
2440
|
idxqfs->nbits = nbits_fastscan;
|
|
1332
2441
|
idxqfs->ksub = (1 << nbits_fastscan);
|
|
1333
2442
|
|
|
2443
|
+
validate_fastscan_fields(
|
|
2444
|
+
idxqfs->M,
|
|
2445
|
+
idxqfs->M2,
|
|
2446
|
+
idxqfs->ksub,
|
|
2447
|
+
idxqfs->bbs,
|
|
2448
|
+
"IndexRaBitQFastScan");
|
|
2449
|
+
|
|
1334
2450
|
READVECTOR(idxqfs->codes);
|
|
1335
|
-
|
|
2451
|
+
|
|
2452
|
+
if (is_legacy) {
|
|
2453
|
+
const size_t storage_size =
|
|
2454
|
+
rabitq_utils::compute_per_vector_storage_size(
|
|
2455
|
+
idxqfs->rabitq.nb_bits, idxqfs->d);
|
|
2456
|
+
|
|
2457
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
2458
|
+
legacy_flat_storage.size() ==
|
|
2459
|
+
static_cast<size_t>(idxqfs->ntotal) * storage_size,
|
|
2460
|
+
"legacy flat_storage size mismatch during migration");
|
|
2461
|
+
|
|
2462
|
+
rabitq_utils::populate_block_aux_from_flat_storage(
|
|
2463
|
+
legacy_flat_storage,
|
|
2464
|
+
idxqfs->codes,
|
|
2465
|
+
static_cast<size_t>(idxqfs->ntotal),
|
|
2466
|
+
idxqfs->bbs,
|
|
2467
|
+
idxqfs->M2,
|
|
2468
|
+
((idxqfs->M2 + 1) / 2) * idxqfs->bbs,
|
|
2469
|
+
idxqfs->get_block_stride(),
|
|
2470
|
+
storage_size);
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
idx = std::move(idxqfs);
|
|
1336
2474
|
} else if (h == fourcc("Ixrq")) {
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
2475
|
+
// Ixrq = original single-bit format
|
|
2476
|
+
auto idxq = std::make_unique<IndexRaBitQ>();
|
|
2477
|
+
read_index_header(*idxq, f);
|
|
2478
|
+
read_RaBitQuantizer(idxq->rabitq, f, idxq->d, false);
|
|
1340
2479
|
READVECTOR(idxq->codes);
|
|
1341
2480
|
READVECTOR(idxq->center);
|
|
1342
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);
|
|
1343
2488
|
|
|
1344
2489
|
// rabitq.nb_bits is already set to 1 by read_RaBitQuantizer
|
|
1345
2490
|
idxq->code_size = idxq->rabitq.code_size;
|
|
1346
|
-
idx = idxq;
|
|
2491
|
+
idx = std::move(idxq);
|
|
1347
2492
|
} else if (h == fourcc("Ixrr")) {
|
|
1348
2493
|
// Ixrr = multi-bit format (new)
|
|
1349
|
-
|
|
1350
|
-
read_index_header(idxq, f);
|
|
1351
|
-
read_RaBitQuantizer(
|
|
2494
|
+
auto idxq = std::make_unique<IndexRaBitQ>();
|
|
2495
|
+
read_index_header(*idxq, f);
|
|
2496
|
+
read_RaBitQuantizer(
|
|
2497
|
+
idxq->rabitq, f, idxq->d, true); // Reads nb_bits from file
|
|
1352
2498
|
READVECTOR(idxq->codes);
|
|
1353
2499
|
READVECTOR(idxq->center);
|
|
1354
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);
|
|
1355
2507
|
|
|
1356
2508
|
idxq->code_size = idxq->rabitq.code_size;
|
|
1357
|
-
idx = idxq;
|
|
2509
|
+
idx = std::move(idxq);
|
|
1358
2510
|
} else if (h == fourcc("Iwrq")) {
|
|
1359
|
-
|
|
1360
|
-
read_ivf_header(ivrq, f);
|
|
1361
|
-
read_RaBitQuantizer(
|
|
2511
|
+
auto ivrq = std::make_unique<IndexIVFRaBitQ>();
|
|
2512
|
+
read_ivf_header(ivrq.get(), f);
|
|
2513
|
+
read_RaBitQuantizer(ivrq->rabitq, f, ivrq->d, false);
|
|
1362
2514
|
READ1(ivrq->code_size);
|
|
1363
2515
|
READ1(ivrq->by_residual);
|
|
1364
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);
|
|
1365
2523
|
|
|
1366
2524
|
// rabitq.nb_bits is already set to 1 by read_RaBitQuantizer
|
|
1367
2525
|
// Update rabitq to match nb_bits
|
|
1368
2526
|
ivrq->rabitq.code_size =
|
|
1369
2527
|
ivrq->rabitq.compute_code_size(ivrq->d, ivrq->rabitq.nb_bits);
|
|
1370
2528
|
ivrq->code_size = ivrq->rabitq.code_size;
|
|
1371
|
-
read_InvertedLists(ivrq, f, io_flags);
|
|
1372
|
-
idx = ivrq;
|
|
2529
|
+
read_InvertedLists(*ivrq, f, io_flags);
|
|
2530
|
+
idx = std::move(ivrq);
|
|
1373
2531
|
} else if (h == fourcc("Iwrr")) {
|
|
1374
2532
|
// Iwrr = multi-bit format (new)
|
|
1375
|
-
|
|
1376
|
-
read_ivf_header(ivrq, f);
|
|
1377
|
-
read_RaBitQuantizer(
|
|
2533
|
+
auto ivrq = std::make_unique<IndexIVFRaBitQ>();
|
|
2534
|
+
read_ivf_header(ivrq.get(), f);
|
|
2535
|
+
read_RaBitQuantizer(
|
|
2536
|
+
ivrq->rabitq, f, ivrq->d, true); // Reads nb_bits from file
|
|
1378
2537
|
READ1(ivrq->code_size);
|
|
1379
2538
|
READ1(ivrq->by_residual);
|
|
1380
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);
|
|
1381
2546
|
|
|
1382
2547
|
// Update rabitq to match nb_bits
|
|
1383
2548
|
ivrq->rabitq.code_size =
|
|
1384
2549
|
ivrq->rabitq.compute_code_size(ivrq->d, ivrq->rabitq.nb_bits);
|
|
1385
2550
|
ivrq->code_size = ivrq->rabitq.code_size;
|
|
1386
|
-
read_InvertedLists(ivrq, f, io_flags);
|
|
1387
|
-
idx = ivrq;
|
|
2551
|
+
read_InvertedLists(*ivrq, f, io_flags);
|
|
2552
|
+
idx = std::move(ivrq);
|
|
1388
2553
|
}
|
|
1389
2554
|
#ifdef FAISS_ENABLE_SVS
|
|
1390
2555
|
else if (
|
|
1391
|
-
h == fourcc("ILVQ") || h == fourcc("ISVL") || h == fourcc("ISVD")
|
|
1392
|
-
|
|
2556
|
+
h == fourcc("ILVQ") || h == fourcc("ISVL") || h == fourcc("ISVD") ||
|
|
2557
|
+
h == fourcc("ISV2")) {
|
|
2558
|
+
std::unique_ptr<IndexSVSVamana> svs;
|
|
1393
2559
|
if (h == fourcc("ILVQ")) {
|
|
1394
|
-
svs =
|
|
2560
|
+
svs = std::make_unique<IndexSVSVamanaLVQ>();
|
|
1395
2561
|
} else if (h == fourcc("ISVL")) {
|
|
1396
|
-
svs =
|
|
1397
|
-
} else if (h == fourcc("ISVD")) {
|
|
1398
|
-
svs =
|
|
2562
|
+
svs = std::make_unique<IndexSVSVamanaLeanVec>();
|
|
2563
|
+
} else if (h == fourcc("ISVD") || h == fourcc("ISV2")) {
|
|
2564
|
+
svs = std::make_unique<IndexSVSVamana>();
|
|
1399
2565
|
}
|
|
1400
2566
|
|
|
1401
|
-
read_index_header(svs, f);
|
|
2567
|
+
read_index_header(*svs, f);
|
|
1402
2568
|
READ1(svs->graph_max_degree);
|
|
1403
2569
|
READ1(svs->alpha);
|
|
1404
2570
|
READ1(svs->search_window_size);
|
|
@@ -1407,15 +2573,21 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1407
2573
|
READ1(svs->max_candidate_pool_size);
|
|
1408
2574
|
READ1(svs->prune_to);
|
|
1409
2575
|
READ1(svs->use_full_search_history);
|
|
1410
|
-
|
|
2576
|
+
|
|
2577
|
+
svs->storage_kind = read_svs_storage_kind(f);
|
|
2578
|
+
|
|
1411
2579
|
if (h == fourcc("ISVL")) {
|
|
1412
|
-
|
|
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);
|
|
1413
2584
|
}
|
|
1414
2585
|
|
|
1415
2586
|
bool initialized;
|
|
1416
2587
|
READ1(initialized);
|
|
1417
2588
|
if (initialized) {
|
|
1418
|
-
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2589
|
+
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2590
|
+
f, get_deserialization_vector_byte_limit());
|
|
1419
2591
|
std::istream is(&rbuf);
|
|
1420
2592
|
svs->deserialize_impl(is);
|
|
1421
2593
|
}
|
|
@@ -1423,31 +2595,98 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1423
2595
|
bool trained;
|
|
1424
2596
|
READ1(trained);
|
|
1425
2597
|
if (trained) {
|
|
1426
|
-
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2598
|
+
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2599
|
+
f, get_deserialization_vector_byte_limit());
|
|
1427
2600
|
std::istream is(&rbuf);
|
|
1428
|
-
dynamic_cast<IndexSVSVamanaLeanVec*>(svs)
|
|
1429
|
-
|
|
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);
|
|
1430
2606
|
}
|
|
1431
2607
|
}
|
|
1432
|
-
|
|
2608
|
+
if (h == fourcc("ISV2")) {
|
|
2609
|
+
READVECTOR(svs->stored_vectors);
|
|
2610
|
+
} else {
|
|
2611
|
+
svs->stored_vectors_valid = false;
|
|
2612
|
+
}
|
|
2613
|
+
idx = std::move(svs);
|
|
1433
2614
|
} else if (h == fourcc("ISVF")) {
|
|
1434
|
-
|
|
1435
|
-
read_index_header(svs, f);
|
|
2615
|
+
auto svs = std::make_unique<IndexSVSFlat>();
|
|
2616
|
+
read_index_header(*svs, f);
|
|
1436
2617
|
|
|
1437
2618
|
bool initialized;
|
|
1438
2619
|
READ1(initialized);
|
|
1439
2620
|
if (initialized) {
|
|
1440
|
-
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2621
|
+
faiss::svs_io::ReaderStreambuf rbuf(
|
|
2622
|
+
f, get_deserialization_vector_byte_limit());
|
|
1441
2623
|
std::istream is(&rbuf);
|
|
1442
2624
|
svs->deserialize_impl(is);
|
|
1443
|
-
idx = svs;
|
|
1444
2625
|
}
|
|
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);
|
|
1445
2680
|
}
|
|
1446
2681
|
#endif // FAISS_ENABLE_SVS
|
|
1447
|
-
else if (h == fourcc("Iwrf")) {
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
2682
|
+
else if (h == fourcc("Iwrn") || h == fourcc("Iwrf")) {
|
|
2683
|
+
// Iwrn = new format (aux data embedded in SIMD blocks)
|
|
2684
|
+
// Iwrf = legacy format (flat_storage separate, needs migration)
|
|
2685
|
+
const bool is_legacy = (h == fourcc("Iwrf"));
|
|
2686
|
+
|
|
2687
|
+
auto ivrqfs = std::make_unique<IndexIVFRaBitQFastScan>();
|
|
2688
|
+
read_ivf_header(ivrqfs.get(), f);
|
|
2689
|
+
read_RaBitQuantizer(ivrqfs->rabitq, f, ivrqfs->d);
|
|
1451
2690
|
READ1(ivrqfs->by_residual);
|
|
1452
2691
|
READ1(ivrqfs->code_size);
|
|
1453
2692
|
READ1(ivrqfs->bbs);
|
|
@@ -1455,8 +2694,16 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1455
2694
|
READ1(ivrqfs->M2);
|
|
1456
2695
|
READ1(ivrqfs->implem);
|
|
1457
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);
|
|
1458
2701
|
READ1(ivrqfs->centered);
|
|
1459
|
-
|
|
2702
|
+
|
|
2703
|
+
std::vector<uint8_t> legacy_flat_storage;
|
|
2704
|
+
if (is_legacy) {
|
|
2705
|
+
READVECTOR(legacy_flat_storage);
|
|
2706
|
+
}
|
|
1460
2707
|
|
|
1461
2708
|
// Initialize FastScan base class fields
|
|
1462
2709
|
const size_t M_fastscan = (ivrqfs->d + 3) / 4;
|
|
@@ -1465,100 +2712,194 @@ Index* read_index(IOReader* f, int io_flags) {
|
|
|
1465
2712
|
ivrqfs->nbits = nbits_fastscan;
|
|
1466
2713
|
ivrqfs->ksub = (1 << nbits_fastscan);
|
|
1467
2714
|
|
|
1468
|
-
|
|
2715
|
+
validate_fastscan_fields(
|
|
2716
|
+
ivrqfs->M,
|
|
2717
|
+
ivrqfs->M2,
|
|
2718
|
+
ivrqfs->ksub,
|
|
2719
|
+
ivrqfs->bbs,
|
|
2720
|
+
"IndexIVFRaBitQFastScan");
|
|
2721
|
+
|
|
2722
|
+
read_InvertedLists(*ivrqfs, f, io_flags);
|
|
1469
2723
|
ivrqfs->init_code_packer();
|
|
1470
|
-
|
|
2724
|
+
|
|
2725
|
+
if (is_legacy) {
|
|
2726
|
+
auto* bil = dynamic_cast<BlockInvertedLists*>(ivrqfs->invlists);
|
|
2727
|
+
FAISS_THROW_IF_NOT(bil);
|
|
2728
|
+
|
|
2729
|
+
const size_t storage_size =
|
|
2730
|
+
rabitq_utils::compute_per_vector_storage_size(
|
|
2731
|
+
ivrqfs->rabitq.nb_bits, ivrqfs->d);
|
|
2732
|
+
const size_t new_block_stride = ivrqfs->get_block_stride();
|
|
2733
|
+
|
|
2734
|
+
for (size_t list_no = 0; list_no < ivrqfs->nlist; list_no++) {
|
|
2735
|
+
if (bil->list_size(list_no) == 0) {
|
|
2736
|
+
continue;
|
|
2737
|
+
}
|
|
2738
|
+
rabitq_utils::populate_block_aux_from_flat_storage(
|
|
2739
|
+
legacy_flat_storage,
|
|
2740
|
+
bil->codes[list_no],
|
|
2741
|
+
bil->list_size(list_no),
|
|
2742
|
+
ivrqfs->bbs,
|
|
2743
|
+
ivrqfs->M2,
|
|
2744
|
+
bil->block_size,
|
|
2745
|
+
new_block_stride,
|
|
2746
|
+
storage_size,
|
|
2747
|
+
bil->ids[list_no].data());
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
if (bil->block_size < new_block_stride) {
|
|
2751
|
+
bil->block_size = new_block_stride;
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
idx = std::move(ivrqfs);
|
|
1471
2756
|
} else {
|
|
1472
2757
|
FAISS_THROW_FMT(
|
|
1473
2758
|
"Index type 0x%08x (\"%s\") not recognized",
|
|
1474
2759
|
h,
|
|
1475
2760
|
fourcc_inv_printable(h).c_str());
|
|
1476
|
-
idx
|
|
2761
|
+
idx.reset();
|
|
1477
2762
|
}
|
|
1478
2763
|
return idx;
|
|
1479
2764
|
}
|
|
1480
2765
|
|
|
1481
|
-
Index* read_index(
|
|
2766
|
+
Index* read_index(IOReader* f, int io_flags) {
|
|
2767
|
+
return read_index_up(f, io_flags).release();
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
std::unique_ptr<Index> read_index_up(FILE* f, int io_flags) {
|
|
1482
2771
|
if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC) {
|
|
1483
2772
|
// enable mmap-supporting IOReader
|
|
1484
2773
|
auto owner = std::make_shared<MmappedFileMappingOwner>(f);
|
|
1485
2774
|
MappedFileIOReader reader(owner);
|
|
1486
|
-
return
|
|
2775
|
+
return read_index_up(&reader, io_flags);
|
|
1487
2776
|
} else {
|
|
1488
2777
|
FileIOReader reader(f);
|
|
1489
|
-
return
|
|
2778
|
+
return read_index_up(&reader, io_flags);
|
|
1490
2779
|
}
|
|
1491
2780
|
}
|
|
1492
2781
|
|
|
1493
|
-
Index* read_index(
|
|
2782
|
+
Index* read_index(FILE* f, int io_flags) {
|
|
2783
|
+
return read_index_up(f, io_flags).release();
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
std::unique_ptr<Index> read_index_up(const char* fname, int io_flags) {
|
|
1494
2787
|
if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC) {
|
|
1495
2788
|
// enable mmap-supporting IOReader
|
|
1496
2789
|
auto owner = std::make_shared<MmappedFileMappingOwner>(fname);
|
|
1497
2790
|
MappedFileIOReader reader(owner);
|
|
1498
|
-
return
|
|
2791
|
+
return read_index_up(&reader, io_flags);
|
|
1499
2792
|
} else {
|
|
1500
2793
|
FileIOReader reader(fname);
|
|
1501
|
-
|
|
1502
|
-
return idx;
|
|
2794
|
+
return read_index_up(&reader, io_flags);
|
|
1503
2795
|
}
|
|
1504
2796
|
}
|
|
1505
2797
|
|
|
1506
|
-
|
|
2798
|
+
Index* read_index(const char* fname, int io_flags) {
|
|
2799
|
+
return read_index_up(fname, io_flags).release();
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
std::unique_ptr<VectorTransform> read_VectorTransform_up(const char* fname) {
|
|
1507
2803
|
FileIOReader reader(fname);
|
|
1508
|
-
|
|
1509
|
-
|
|
2804
|
+
return read_VectorTransform_up(&reader);
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
VectorTransform* read_VectorTransform(const char* fname) {
|
|
2808
|
+
return read_VectorTransform_up(fname).release();
|
|
1510
2809
|
}
|
|
1511
2810
|
|
|
1512
2811
|
/*************************************************************
|
|
1513
2812
|
* Read binary indexes
|
|
1514
2813
|
**************************************************************/
|
|
1515
2814
|
|
|
1516
|
-
static void read_InvertedLists(IndexBinaryIVF
|
|
1517
|
-
|
|
2815
|
+
static void read_InvertedLists(IndexBinaryIVF& ivf, IOReader* f, int io_flags) {
|
|
2816
|
+
auto ils = read_InvertedLists_up(f, io_flags);
|
|
1518
2817
|
FAISS_THROW_IF_NOT(
|
|
1519
2818
|
!ils ||
|
|
1520
|
-
(ils->nlist == ivf
|
|
1521
|
-
ivf
|
|
1522
|
-
ivf
|
|
2819
|
+
(ils->nlist == ivf.nlist && ils->code_size == ivf.code_size));
|
|
2820
|
+
ivf.invlists = ils.release();
|
|
2821
|
+
ivf.own_invlists = true;
|
|
1523
2822
|
}
|
|
1524
2823
|
|
|
1525
|
-
static void read_index_binary_header(IndexBinary
|
|
1526
|
-
READ1(idx
|
|
1527
|
-
READ1(idx
|
|
1528
|
-
READ1(idx
|
|
1529
|
-
READ1(idx
|
|
1530
|
-
|
|
1531
|
-
|
|
2824
|
+
static void read_index_binary_header(IndexBinary& idx, IOReader* f) {
|
|
2825
|
+
READ1(idx.d);
|
|
2826
|
+
READ1(idx.code_size);
|
|
2827
|
+
READ1(idx.ntotal);
|
|
2828
|
+
READ1(idx.is_trained);
|
|
2829
|
+
int metric_type_int;
|
|
2830
|
+
READ1(metric_type_int);
|
|
2831
|
+
idx.metric_type = metric_type_from_int(metric_type_int);
|
|
2832
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
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);
|
|
2841
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2842
|
+
idx.ntotal >= 0,
|
|
2843
|
+
"invalid binary index ntotal %" PRId64,
|
|
2844
|
+
(int64_t)idx.ntotal);
|
|
2845
|
+
idx.verbose = false;
|
|
1532
2846
|
}
|
|
1533
2847
|
|
|
1534
2848
|
static void read_binary_ivf_header(
|
|
1535
|
-
IndexBinaryIVF
|
|
2849
|
+
IndexBinaryIVF& ivf,
|
|
1536
2850
|
IOReader* f,
|
|
1537
2851
|
std::vector<std::vector<idx_t>>* ids = nullptr) {
|
|
1538
2852
|
read_index_binary_header(ivf, f);
|
|
1539
|
-
READ1(ivf
|
|
1540
|
-
|
|
1541
|
-
ivf
|
|
1542
|
-
ivf
|
|
2853
|
+
READ1(ivf.nlist);
|
|
2854
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(ivf.nlist, "nlist");
|
|
2855
|
+
READ1(ivf.nprobe);
|
|
2856
|
+
ivf.quantizer = read_index_binary(f);
|
|
2857
|
+
ivf.own_fields = true;
|
|
1543
2858
|
if (ids) { // used in legacy "Iv" formats
|
|
1544
|
-
ids->resize(ivf
|
|
1545
|
-
for (size_t i = 0; i < ivf
|
|
2859
|
+
ids->resize(ivf.nlist);
|
|
2860
|
+
for (size_t i = 0; i < ivf.nlist; i++)
|
|
1546
2861
|
READVECTOR((*ids)[i]);
|
|
1547
2862
|
}
|
|
1548
|
-
read_direct_map(&ivf
|
|
2863
|
+
read_direct_map(&ivf.direct_map, f);
|
|
1549
2864
|
}
|
|
1550
2865
|
|
|
1551
2866
|
static void read_binary_hash_invlists(
|
|
1552
2867
|
IndexBinaryHash::InvertedListMap& invlists,
|
|
1553
2868
|
int b,
|
|
2869
|
+
size_t code_size,
|
|
1554
2870
|
IOReader* f) {
|
|
1555
2871
|
size_t sz;
|
|
1556
2872
|
READ1(sz);
|
|
2873
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(sz, "binary hash invlists sz");
|
|
1557
2874
|
int il_nbit = 0;
|
|
1558
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
|
+
}
|
|
1559
2888
|
// buffer for bitstrings
|
|
1560
|
-
|
|
2889
|
+
size_t bits_per_entry = (size_t)b + (size_t)il_nbit;
|
|
2890
|
+
size_t total_bits =
|
|
2891
|
+
mul_no_overflow(bits_per_entry, sz, "binary hash invlists");
|
|
2892
|
+
size_t needed_bytes = (total_bits + 7) / 8;
|
|
2893
|
+
std::vector<uint8_t> buf;
|
|
1561
2894
|
READVECTOR(buf);
|
|
2895
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
2896
|
+
buf.size() >= needed_bytes,
|
|
2897
|
+
"binary hash invlists: buffer size %zd < needed %zd bytes "
|
|
2898
|
+
"for %zd entries of %zd bits each",
|
|
2899
|
+
buf.size(),
|
|
2900
|
+
needed_bytes,
|
|
2901
|
+
sz,
|
|
2902
|
+
bits_per_entry);
|
|
1562
2903
|
BitstringReader rd(buf.data(), buf.size());
|
|
1563
2904
|
invlists.reserve(sz);
|
|
1564
2905
|
for (size_t i = 0; i < sz; i++) {
|
|
@@ -1568,6 +2909,13 @@ static void read_binary_hash_invlists(
|
|
|
1568
2909
|
READVECTOR(il.ids);
|
|
1569
2910
|
FAISS_THROW_IF_NOT(il.ids.size() == ilsz);
|
|
1570
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);
|
|
1571
2919
|
}
|
|
1572
2920
|
}
|
|
1573
2921
|
|
|
@@ -1580,131 +2928,228 @@ static void read_binary_multi_hash_map(
|
|
|
1580
2928
|
size_t sz;
|
|
1581
2929
|
READ1(id_bits);
|
|
1582
2930
|
READ1(sz);
|
|
2931
|
+
FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT(sz, "multi hash map sz");
|
|
1583
2932
|
std::vector<uint8_t> buf;
|
|
1584
2933
|
READVECTOR(buf);
|
|
1585
|
-
size_t nbit = (
|
|
2934
|
+
size_t nbit = add_no_overflow(
|
|
2935
|
+
mul_no_overflow((size_t)(b + id_bits), sz, "multi hash map"),
|
|
2936
|
+
mul_no_overflow(ntotal, (size_t)id_bits, "multi hash map"),
|
|
2937
|
+
"multi hash map total bits");
|
|
1586
2938
|
FAISS_THROW_IF_NOT(buf.size() == (nbit + 7) / 8);
|
|
1587
2939
|
BitstringReader rd(buf.data(), buf.size());
|
|
1588
2940
|
map.reserve(sz);
|
|
2941
|
+
size_t total_ids = 0;
|
|
1589
2942
|
for (size_t i = 0; i < sz; i++) {
|
|
1590
2943
|
uint64_t hash = rd.read(b);
|
|
1591
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;
|
|
1592
2954
|
auto& il = map[hash];
|
|
1593
2955
|
for (size_t j = 0; j < ilsz; j++) {
|
|
1594
|
-
|
|
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);
|
|
1595
2963
|
}
|
|
1596
2964
|
}
|
|
1597
2965
|
}
|
|
1598
2966
|
|
|
1599
|
-
IndexBinary
|
|
1600
|
-
IndexBinary
|
|
2967
|
+
std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
|
|
2968
|
+
std::unique_ptr<IndexBinary> idx;
|
|
1601
2969
|
uint32_t h;
|
|
1602
2970
|
READ1(h);
|
|
1603
2971
|
if (h == fourcc("IBxF")) {
|
|
1604
|
-
|
|
1605
|
-
read_index_binary_header(idxf, f);
|
|
2972
|
+
auto idxf = std::make_unique<IndexBinaryFlat>();
|
|
2973
|
+
read_index_binary_header(*idxf, f);
|
|
1606
2974
|
read_vector(idxf->xb, f);
|
|
1607
2975
|
FAISS_THROW_IF_NOT(idxf->xb.size() == idxf->ntotal * idxf->code_size);
|
|
1608
|
-
|
|
1609
|
-
idx = idxf;
|
|
2976
|
+
idx = std::move(idxf);
|
|
1610
2977
|
} else if (h == fourcc("IBwF")) {
|
|
1611
|
-
|
|
1612
|
-
read_binary_ivf_header(ivf, f);
|
|
1613
|
-
read_InvertedLists(ivf, f, io_flags);
|
|
1614
|
-
idx = ivf;
|
|
2978
|
+
auto ivf = std::make_unique<IndexBinaryIVF>();
|
|
2979
|
+
read_binary_ivf_header(*ivf, f);
|
|
2980
|
+
read_InvertedLists(*ivf, f, io_flags);
|
|
2981
|
+
idx = std::move(ivf);
|
|
1615
2982
|
} else if (h == fourcc("IBFf")) {
|
|
1616
|
-
|
|
1617
|
-
read_index_binary_header(idxff, f);
|
|
2983
|
+
auto idxff = std::make_unique<IndexBinaryFromFloat>();
|
|
2984
|
+
read_index_binary_header(*idxff, f);
|
|
1618
2985
|
idxff->own_fields = true;
|
|
1619
2986
|
idxff->index = read_index(f, io_flags);
|
|
1620
|
-
idx = idxff;
|
|
2987
|
+
idx = std::move(idxff);
|
|
1621
2988
|
} else if (h == fourcc("IBHf")) {
|
|
1622
|
-
|
|
1623
|
-
read_index_binary_header(idxhnsw, f);
|
|
1624
|
-
read_HNSW(
|
|
2989
|
+
auto idxhnsw = std::make_unique<IndexBinaryHNSW>();
|
|
2990
|
+
read_index_binary_header(*idxhnsw, f);
|
|
2991
|
+
read_HNSW(idxhnsw->hnsw, f);
|
|
1625
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);
|
|
1626
2998
|
idxhnsw->storage = read_index_binary(f, io_flags);
|
|
1627
2999
|
idxhnsw->own_fields = true;
|
|
1628
|
-
|
|
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");
|
|
3008
|
+
idx = std::move(idxhnsw);
|
|
1629
3009
|
} else if (h == fourcc("IBHc")) {
|
|
1630
|
-
|
|
1631
|
-
read_index_binary_header(idxhnsw, f);
|
|
3010
|
+
auto idxhnsw = std::make_unique<IndexBinaryHNSWCagra>();
|
|
3011
|
+
read_index_binary_header(*idxhnsw, f);
|
|
1632
3012
|
READ1(idxhnsw->keep_max_size_level0);
|
|
1633
3013
|
READ1(idxhnsw->base_level_only);
|
|
1634
3014
|
READ1(idxhnsw->num_base_level_search_entrypoints);
|
|
1635
|
-
read_HNSW(
|
|
3015
|
+
read_HNSW(idxhnsw->hnsw, f);
|
|
1636
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);
|
|
1637
3022
|
idxhnsw->storage = read_index_binary(f, io_flags);
|
|
1638
3023
|
idxhnsw->own_fields = true;
|
|
1639
|
-
|
|
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");
|
|
3032
|
+
idx = std::move(idxhnsw);
|
|
1640
3033
|
} else if (h == fourcc("IBMp") || h == fourcc("IBM2")) {
|
|
1641
3034
|
bool is_map2 = h == fourcc("IBM2");
|
|
1642
|
-
IndexBinaryIDMap
|
|
1643
|
-
|
|
1644
|
-
|
|
3035
|
+
std::unique_ptr<IndexBinaryIDMap> idxmap = is_map2
|
|
3036
|
+
? std::make_unique<IndexBinaryIDMap2>()
|
|
3037
|
+
: std::make_unique<IndexBinaryIDMap>();
|
|
3038
|
+
read_index_binary_header(*idxmap, f);
|
|
1645
3039
|
idxmap->index = read_index_binary(f, io_flags);
|
|
1646
3040
|
idxmap->own_fields = true;
|
|
1647
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);
|
|
1648
3054
|
if (is_map2) {
|
|
1649
|
-
static_cast<IndexBinaryIDMap2*>(idxmap)->construct_rev_map();
|
|
3055
|
+
static_cast<IndexBinaryIDMap2*>(idxmap.get())->construct_rev_map();
|
|
1650
3056
|
}
|
|
1651
|
-
idx = idxmap;
|
|
3057
|
+
idx = std::move(idxmap);
|
|
1652
3058
|
} else if (h == fourcc("IBHh")) {
|
|
1653
|
-
|
|
1654
|
-
read_index_binary_header(idxh, f);
|
|
3059
|
+
auto idxh = std::make_unique<IndexBinaryHash>();
|
|
3060
|
+
read_index_binary_header(*idxh, f);
|
|
1655
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);
|
|
1656
3071
|
READ1(idxh->nflip);
|
|
1657
|
-
read_binary_hash_invlists(idxh->invlists, idxh->b, f);
|
|
1658
|
-
idx = idxh;
|
|
3072
|
+
read_binary_hash_invlists(idxh->invlists, idxh->b, idxh->code_size, f);
|
|
3073
|
+
idx = std::move(idxh);
|
|
1659
3074
|
} else if (h == fourcc("IBHm")) {
|
|
1660
|
-
|
|
1661
|
-
read_index_binary_header(idxmh, f);
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
3075
|
+
auto idxmh = std::make_unique<IndexBinaryMultiHash>();
|
|
3076
|
+
read_index_binary_header(*idxmh, f);
|
|
3077
|
+
auto storage_idx = read_index_binary_up(f);
|
|
3078
|
+
auto* flat_ptr = dynamic_cast<IndexBinaryFlat*>(storage_idx.get());
|
|
3079
|
+
FAISS_THROW_IF_NOT(flat_ptr && flat_ptr->ntotal == idxmh->ntotal);
|
|
3080
|
+
idxmh->storage = flat_ptr;
|
|
3081
|
+
storage_idx.release();
|
|
1665
3082
|
idxmh->own_fields = true;
|
|
1666
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);
|
|
1667
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);
|
|
1668
3101
|
READ1(idxmh->nflip);
|
|
1669
3102
|
idxmh->maps.resize(idxmh->nhash);
|
|
1670
3103
|
for (int i = 0; i < idxmh->nhash; i++) {
|
|
1671
3104
|
read_binary_multi_hash_map(
|
|
1672
3105
|
idxmh->maps[i], idxmh->b, idxmh->ntotal, f);
|
|
1673
3106
|
}
|
|
1674
|
-
idx = idxmh;
|
|
3107
|
+
idx = std::move(idxmh);
|
|
1675
3108
|
} else {
|
|
1676
3109
|
FAISS_THROW_FMT(
|
|
1677
3110
|
"Index type %08x (\"%s\") not recognized",
|
|
1678
3111
|
h,
|
|
1679
3112
|
fourcc_inv_printable(h).c_str());
|
|
1680
|
-
idx = nullptr;
|
|
1681
3113
|
}
|
|
1682
3114
|
return idx;
|
|
1683
3115
|
}
|
|
1684
3116
|
|
|
1685
|
-
IndexBinary* read_index_binary(
|
|
3117
|
+
IndexBinary* read_index_binary(IOReader* f, int io_flags) {
|
|
3118
|
+
return read_index_binary_up(f, io_flags).release();
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
std::unique_ptr<IndexBinary> read_index_binary_up(FILE* f, int io_flags) {
|
|
1686
3122
|
if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC) {
|
|
1687
3123
|
// enable mmap-supporting IOReader
|
|
1688
3124
|
auto owner = std::make_shared<MmappedFileMappingOwner>(f);
|
|
1689
3125
|
MappedFileIOReader reader(owner);
|
|
1690
|
-
return
|
|
3126
|
+
return read_index_binary_up(&reader, io_flags);
|
|
1691
3127
|
} else {
|
|
1692
3128
|
FileIOReader reader(f);
|
|
1693
|
-
return
|
|
3129
|
+
return read_index_binary_up(&reader, io_flags);
|
|
1694
3130
|
}
|
|
1695
3131
|
}
|
|
1696
3132
|
|
|
1697
|
-
IndexBinary* read_index_binary(
|
|
3133
|
+
IndexBinary* read_index_binary(FILE* f, int io_flags) {
|
|
3134
|
+
return read_index_binary_up(f, io_flags).release();
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
std::unique_ptr<IndexBinary> read_index_binary_up(
|
|
3138
|
+
const char* fname,
|
|
3139
|
+
int io_flags) {
|
|
1698
3140
|
if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC) {
|
|
1699
3141
|
// enable mmap-supporting IOReader
|
|
1700
3142
|
auto owner = std::make_shared<MmappedFileMappingOwner>(fname);
|
|
1701
3143
|
MappedFileIOReader reader(owner);
|
|
1702
|
-
return
|
|
3144
|
+
return read_index_binary_up(&reader, io_flags);
|
|
1703
3145
|
} else {
|
|
1704
3146
|
FileIOReader reader(fname);
|
|
1705
|
-
|
|
1706
|
-
return idx;
|
|
3147
|
+
return read_index_binary_up(&reader, io_flags);
|
|
1707
3148
|
}
|
|
1708
3149
|
}
|
|
1709
3150
|
|
|
3151
|
+
IndexBinary* read_index_binary(const char* fname, int io_flags) {
|
|
3152
|
+
return read_index_binary_up(fname, io_flags).release();
|
|
3153
|
+
}
|
|
3154
|
+
|
|
1710
3155
|
} // namespace faiss
|