From df500c923e76cb62f5c383c62e86af113b2353de Mon Sep 17 00:00:00 2001 From: Xiaohai Xu Date: Fri, 15 Jan 2021 10:48:22 +0800 Subject: [PATCH] Add IVF_HNSW index (#4613) * Add IVF_HNSW index Signed-off-by: sahuang * Fix serialize-load bug Signed-off-by: sahuang --- core/src/index/knowhere/CMakeLists.txt | 1 + .../knowhere/knowhere/index/IndexType.cpp | 1 + .../index/knowhere/knowhere/index/IndexType.h | 1 + .../index/vector_index/ConfAdapter.cpp | 28 ++++ .../knowhere/index/vector_index/ConfAdapter.h | 9 ++ .../index/vector_index/ConfAdapterMgr.cpp | 1 + .../index/vector_index/IndexIVFHNSW.cpp | 134 ++++++++++++++++++ .../index/vector_index/IndexIVFHNSW.h | 56 ++++++++ .../index/vector_index/IndexIVFSQ.cpp | 1 - .../index/vector_index/gpu/IndexGPUIVFSQ.cpp | 1 - .../index/vector_offset_index/IndexIVF_NM.cpp | 1 - core/src/index/thirdparty/faiss/IndexHNSW.cpp | 6 +- .../src/index/thirdparty/faiss/IndexRHNSW.cpp | 6 +- core/src/index/unittest/CMakeLists.txt | 9 ++ core/src/index/unittest/Helper.h | 16 +++ core/src/index/unittest/test_ivf_hnsw.cpp | 96 +++++++++++++ core/src/server/ValidationUtil.cpp | 14 ++ core/src/server/web_impl/Constants.cpp | 1 + core/src/server/web_impl/Constants.h | 1 + sdk/include/MilvusApi.h | 1 + 20 files changed, 375 insertions(+), 9 deletions(-) create mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIVFHNSW.cpp create mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIVFHNSW.h create mode 100644 core/src/index/unittest/test_ivf_hnsw.cpp diff --git a/core/src/index/knowhere/CMakeLists.txt b/core/src/index/knowhere/CMakeLists.txt index 6a081d67f8..0469fc7300 100644 --- a/core/src/index/knowhere/CMakeLists.txt +++ b/core/src/index/knowhere/CMakeLists.txt @@ -71,6 +71,7 @@ set(vector_index_srcs knowhere/index/vector_index/IndexIVF.cpp knowhere/index/vector_index/IndexIVFPQ.cpp knowhere/index/vector_index/IndexIVFSQ.cpp + knowhere/index/vector_index/IndexIVFHNSW.cpp knowhere/index/vector_index/IndexAnnoy.cpp knowhere/index/vector_index/IndexRHNSW.cpp knowhere/index/vector_index/IndexHNSW.cpp diff --git a/core/src/index/knowhere/knowhere/index/IndexType.cpp b/core/src/index/knowhere/knowhere/index/IndexType.cpp index 51bd67b7b6..0f3b054601 100644 --- a/core/src/index/knowhere/knowhere/index/IndexType.cpp +++ b/core/src/index/knowhere/knowhere/index/IndexType.cpp @@ -25,6 +25,7 @@ const char* INDEX_FAISS_IVFFLAT = "IVF_FLAT"; const char* INDEX_FAISS_IVFPQ = "IVF_PQ"; const char* INDEX_FAISS_IVFSQ8 = "IVF_SQ8"; const char* INDEX_FAISS_IVFSQ8H = "IVF_SQ8_HYBRID"; +const char* INDEX_FAISS_IVFHNSW = "IVF_HNSW"; const char* INDEX_FAISS_BIN_IDMAP = "BIN_FLAT"; const char* INDEX_FAISS_BIN_IVFFLAT = "BIN_IVF_FLAT"; const char* INDEX_NSG = "NSG"; diff --git a/core/src/index/knowhere/knowhere/index/IndexType.h b/core/src/index/knowhere/knowhere/index/IndexType.h index 69b68697ca..40f9a34f1b 100644 --- a/core/src/index/knowhere/knowhere/index/IndexType.h +++ b/core/src/index/knowhere/knowhere/index/IndexType.h @@ -54,6 +54,7 @@ extern const char* INDEX_FAISS_IVFFLAT; extern const char* INDEX_FAISS_IVFPQ; extern const char* INDEX_FAISS_IVFSQ8; extern const char* INDEX_FAISS_IVFSQ8H; +extern const char* INDEX_FAISS_IVFHNSW; extern const char* INDEX_FAISS_BIN_IDMAP; extern const char* INDEX_FAISS_BIN_IVFFLAT; extern const char* INDEX_NSG; diff --git a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.cpp b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.cpp index bd9fb1e9a2..11b58d692b 100644 --- a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.cpp +++ b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.cpp @@ -207,6 +207,34 @@ IVFPQConfAdapter::CheckCPUPQParams(int64_t dimension, int64_t m) { return (dimension % m == 0); } +bool +IVFHNSWConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { + // HNSW param check + CheckIntByRange(knowhere::IndexParams::efConstruction, HNSW_MIN_EFCONSTRUCTION, HNSW_MAX_EFCONSTRUCTION); + CheckIntByRange(knowhere::IndexParams::M, HNSW_MIN_M, HNSW_MAX_M); + + // IVF param check + CheckIntByRange(knowhere::IndexParams::nlist, MIN_NLIST, MAX_NLIST); + + // auto tune params + auto rows = oricfg[knowhere::meta::ROWS].get(); + auto nlist = oricfg[knowhere::IndexParams::nlist].get(); + oricfg[knowhere::IndexParams::nlist] = MatchNlist(rows, nlist); + + return ConfAdapter::CheckTrain(oricfg, mode); +} + +bool +IVFHNSWConfAdapter::CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) { + // HNSW param check + CheckIntByRange(knowhere::IndexParams::ef, oricfg[knowhere::meta::TOPK], HNSW_MAX_EF); + + // IVF param check + CheckIntByRange(knowhere::IndexParams::nprobe, MIN_NPROBE, MAX_NPROBE); + + return ConfAdapter::CheckSearch(oricfg, type, mode); +} + bool NSGConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { const int64_t MIN_KNNG = 5; diff --git a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.h b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.h index 022018b242..296f8b31fc 100644 --- a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.h +++ b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.h @@ -61,6 +61,15 @@ class IVFPQConfAdapter : public IVFConfAdapter { CheckCPUPQParams(int64_t dimension, int64_t m); }; +class IVFHNSWConfAdapter : public ConfAdapter { + public: + bool + CheckTrain(Config& oricfg, const IndexMode mode) override; + + bool + CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) override; +}; + class NSGConfAdapter : public IVFConfAdapter { public: bool diff --git a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.cpp b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.cpp index 7b4ae64a4a..b83384942d 100644 --- a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.cpp +++ b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.cpp @@ -41,6 +41,7 @@ AdapterMgr::RegisterAdapter() { REGISTER_CONF_ADAPTER(IVFPQConfAdapter, IndexEnum::INDEX_FAISS_IVFPQ, ivfpq_adapter); REGISTER_CONF_ADAPTER(IVFSQConfAdapter, IndexEnum::INDEX_FAISS_IVFSQ8, ivfsq8_adapter); REGISTER_CONF_ADAPTER(IVFSQConfAdapter, IndexEnum::INDEX_FAISS_IVFSQ8H, ivfsq8h_adapter); + REGISTER_CONF_ADAPTER(IVFHNSWConfAdapter, IndexEnum::INDEX_FAISS_IVFHNSW, ivfhnsw_adapter); REGISTER_CONF_ADAPTER(BinIDMAPConfAdapter, IndexEnum::INDEX_FAISS_BIN_IDMAP, idmap_bin_adapter); REGISTER_CONF_ADAPTER(BinIVFConfAdapter, IndexEnum::INDEX_FAISS_BIN_IVFFLAT, ivf_bin_adapter); REGISTER_CONF_ADAPTER(NSGConfAdapter, IndexEnum::INDEX_NSG, nsg_adapter); diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFHNSW.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFHNSW.cpp new file mode 100644 index 0000000000..0d3d7a70bd --- /dev/null +++ b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFHNSW.cpp @@ -0,0 +1,134 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied. See the License for the specific language governing permissions and limitations under the License + +#include +#include +#include + +#include +#include +#include +#include + +#include "faiss/IndexRHNSW.h" + +#include "knowhere/common/Exception.h" +#include "knowhere/index/vector_index/IndexIVFHNSW.h" +#include "knowhere/index/vector_index/adapter/VectorAdapter.h" +#include "knowhere/index/vector_index/helpers/IndexParameter.h" + +namespace milvus { +namespace knowhere { + +BinarySet +IVFHNSW::Serialize(const Config& config) { + if (!index_ || !index_->is_trained) { + KNOWHERE_THROW_MSG("index not initialize or trained"); + } + + try { + // Serialize IVF index and HNSW data + auto res_set = SerializeImpl(index_type_); + auto index = dynamic_cast(index_.get()); + auto real_idx = dynamic_cast(index->quantizer); + if (real_idx == nullptr) { + KNOWHERE_THROW_MSG("Quantizer index is not a faiss::IndexRHNSWFlat"); + } + + MemoryIOWriter writer; + faiss::write_index(real_idx->storage, &writer); + std::shared_ptr data(writer.data_); + res_set.Append("HNSW_META", data, writer.rp); + + if (config.contains(INDEX_FILE_SLICE_SIZE_IN_MEGABYTE)) { + Disassemble(config[INDEX_FILE_SLICE_SIZE_IN_MEGABYTE].get() * 1024 * 1024, res_set); + } + return res_set; + } catch (std::exception& e) { + KNOWHERE_THROW_MSG(e.what()); + } +} + +void +IVFHNSW::Load(const BinarySet& binary_set) { + try { + // Load IVF index and HNSW data + Assemble(const_cast(binary_set)); + LoadImpl(binary_set, index_type_); + + auto index = dynamic_cast(index_.get()); + MemoryIOReader reader; + auto binary = binary_set.GetByName("HNSW_META"); + reader.total = static_cast(binary->size); + reader.data_ = binary->data.get(); + + auto idx = faiss::read_index(&reader); + auto real_idx = dynamic_cast(index->quantizer); + real_idx->storage = new faiss::IndexFlat(idx->d, idx->metric_type); + real_idx->storage->add(idx->ntotal, + reinterpret_cast(dynamic_cast(idx)->xb.data())); + real_idx->init_hnsw(); + } catch (std::exception& e) { + KNOWHERE_THROW_MSG(e.what()); + } +} + +void +IVFHNSW::Train(const DatasetPtr& dataset_ptr, const Config& config) { + GET_TENSOR_DATA_DIM(dataset_ptr) + + faiss::MetricType metric_type = GetMetricType(config[Metric::TYPE].get()); + auto coarse_quantizer = new faiss::IndexRHNSWFlat(dim, config[IndexParams::M], metric_type); + coarse_quantizer->hnsw.efConstruction = config[IndexParams::efConstruction]; + auto index = std::make_shared(coarse_quantizer, dim, config[IndexParams::nlist].get(), + metric_type); + index->own_fields = true; + index->train(rows, reinterpret_cast(p_data)); + index_ = index; +} + +VecIndexPtr +IVFHNSW::CopyCpuToGpu(const int64_t device_id, const Config& config) { + KNOWHERE_THROW_MSG("IVFHNSW::CopyCpuToGpu not supported."); +} + +void +IVFHNSW::UpdateIndexSize() { + if (!index_) { + KNOWHERE_THROW_MSG("index not initialize"); + } + auto ivf_index = static_cast(index_.get()); + auto nb = ivf_index->invlists->compute_ntotal(); + auto code_size = ivf_index->code_size; + auto hnsw_quantizer = dynamic_cast(ivf_index->quantizer); + // ivf codes, ivf ids and hnsw_flat quantizer + index_size_ = nb * code_size + nb * sizeof(int64_t) + hnsw_quantizer->cal_size(); +} + +void +IVFHNSW::QueryImpl(int64_t n, const float* data, int64_t k, float* distances, int64_t* labels, const Config& config, + const faiss::BitsetView& bitset) { + auto params = GenParams(config); + auto ivf_index = dynamic_cast(index_.get()); + ivf_index->nprobe = std::min(params->nprobe, ivf_index->invlists->nlist); + if (params->nprobe > 1 && n <= 4) { + ivf_index->parallel_mode = 1; + } else { + ivf_index->parallel_mode = 0; + } + // Update HNSW quantizer search param + auto hnsw_quantizer = dynamic_cast(ivf_index->quantizer); + hnsw_quantizer->hnsw.efSearch = config[IndexParams::ef].get(); + ivf_index->search(n, data, k, distances, labels, bitset); +} + +} // namespace knowhere +} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFHNSW.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFHNSW.h new file mode 100644 index 0000000000..ab0321bec5 --- /dev/null +++ b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFHNSW.h @@ -0,0 +1,56 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied. See the License for the specific language governing permissions and limitations under the License + +#pragma once + +#include +#include + +#include "knowhere/index/vector_index/IndexIVF.h" + +namespace milvus { +namespace knowhere { + +class IVFHNSW : public IVF { + public: + IVFHNSW() : IVF() { + index_type_ = IndexEnum::INDEX_FAISS_IVFHNSW; + } + + explicit IVFHNSW(std::shared_ptr index) : IVF(std::move(index)) { + index_type_ = IndexEnum::INDEX_FAISS_IVFHNSW; + } + + BinarySet + Serialize(const Config&) override; + + void + Load(const BinarySet&) override; + + void + Train(const DatasetPtr&, const Config&) override; + + VecIndexPtr + CopyCpuToGpu(const int64_t, const Config&) override; + + void + UpdateIndexSize() override; + + protected: + void + QueryImpl(int64_t n, const float* data, int64_t k, float* distances, int64_t* labels, const Config& config, + const faiss::BitsetView& bitset) override; +}; + +using IVFHNSWPtr = std::shared_ptr; + +} // namespace knowhere +} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp index 3f0a2650e1..fbce536dc7 100644 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp +++ b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include "knowhere/common/Exception.h" #include "knowhere/index/vector_index/IndexIVFSQ.h" diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp index edf586ee1a..a87cdfcbbc 100644 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp +++ b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include diff --git a/core/src/index/knowhere/knowhere/index/vector_offset_index/IndexIVF_NM.cpp b/core/src/index/knowhere/knowhere/index/vector_offset_index/IndexIVF_NM.cpp index d4e8983c3e..15eca61960 100644 --- a/core/src/index/knowhere/knowhere/index/vector_offset_index/IndexIVF_NM.cpp +++ b/core/src/index/knowhere/knowhere/index/vector_offset_index/IndexIVF_NM.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #ifdef MILVUS_GPU_VERSION #include diff --git a/core/src/index/thirdparty/faiss/IndexHNSW.cpp b/core/src/index/thirdparty/faiss/IndexHNSW.cpp index b0346deddf..820c6dff56 100644 --- a/core/src/index/thirdparty/faiss/IndexHNSW.cpp +++ b/core/src/index/thirdparty/faiss/IndexHNSW.cpp @@ -277,7 +277,7 @@ IndexHNSW::~IndexHNSW() { void IndexHNSW::train(idx_t n, const float* x) { FAISS_THROW_IF_NOT_MSG(storage, - "Please use IndexHSNWFlat (or variants) instead of IndexHNSW directly"); + "Please use IndexHNSWFlat (or variants) instead of IndexHNSW directly"); // hnsw structure does not require training storage->train (n, x); is_trained = true; @@ -288,7 +288,7 @@ void IndexHNSW::search (idx_t n, const float *x, idx_t k, { FAISS_THROW_IF_NOT_MSG(storage, - "Please use IndexHSNWFlat (or variants) instead of IndexHNSW directly"); + "Please use IndexHNSWFlat (or variants) instead of IndexHNSW directly"); size_t nreorder = 0; idx_t check_period = InterruptCallback::get_period_hint ( @@ -348,7 +348,7 @@ void IndexHNSW::search (idx_t n, const float *x, idx_t k, void IndexHNSW::add(idx_t n, const float *x) { FAISS_THROW_IF_NOT_MSG(storage, - "Please use IndexHSNWFlat (or variants) instead of IndexHNSW directly"); + "Please use IndexHNSWFlat (or variants) instead of IndexHNSW directly"); FAISS_THROW_IF_NOT(is_trained); int n0 = ntotal; storage->add(n, x); diff --git a/core/src/index/thirdparty/faiss/IndexRHNSW.cpp b/core/src/index/thirdparty/faiss/IndexRHNSW.cpp index c997b8b7c7..4c66994333 100644 --- a/core/src/index/thirdparty/faiss/IndexRHNSW.cpp +++ b/core/src/index/thirdparty/faiss/IndexRHNSW.cpp @@ -230,7 +230,7 @@ IndexRHNSW::clear_stats() { void IndexRHNSW::train(idx_t n, const float* x) { FAISS_THROW_IF_NOT_MSG(storage, - "Please use IndexHSNWFlat (or variants) instead of IndexRHNSW directly"); + "Please use IndexRHNSWFlat (or variants) instead of IndexRHNSW directly"); // hnsw structure does not require training storage->train (n, x); is_trained = true; @@ -241,7 +241,7 @@ void IndexRHNSW::search (idx_t n, const float *x, idx_t k, { FAISS_THROW_IF_NOT_MSG(storage, - "Please use IndexHSNWFlat (or variants) instead of IndexRHNSW directly"); + "Please use IndexRHNSWFlat (or variants) instead of IndexRHNSW directly"); size_t nreorder = 0; idx_t check_period = InterruptCallback::get_period_hint ( @@ -324,7 +324,7 @@ void IndexRHNSW::search (idx_t n, const float *x, idx_t k, void IndexRHNSW::add(idx_t n, const float *x) { FAISS_THROW_IF_NOT_MSG(storage, - "Please use IndexHSNWFlat (or variants) instead of IndexRHNSW directly"); + "Please use IndexRHNSWFlat (or variants) instead of IndexRHNSW directly"); FAISS_THROW_IF_NOT(is_trained); int n0 = ntotal; storage->add(n, x); diff --git a/core/src/index/unittest/CMakeLists.txt b/core/src/index/unittest/CMakeLists.txt index 82aac9e6ac..6bde08522d 100644 --- a/core/src/index/unittest/CMakeLists.txt +++ b/core/src/index/unittest/CMakeLists.txt @@ -65,6 +65,7 @@ set(faiss_srcs ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexIVF.cpp ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexIVFPQ.cpp + ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexIVFHNSW.cpp ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_offset_index/OffsetBaseIndex.cpp ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_offset_index/IndexIVF_NM.cpp ) @@ -127,6 +128,14 @@ endif () target_link_libraries(test_ivf ${depend_libs} ${unittest_libs} ${basic_libs}) install(TARGETS test_ivf DESTINATION unittest) +################################################################################ +# +if (NOT TARGET test_ivf_hnsw) + add_executable(test_ivf_hnsw test_ivf_hnsw.cpp ${faiss_srcs} ${util_srcs}) +endif () +target_link_libraries(test_ivf_hnsw ${depend_libs} ${unittest_libs} ${basic_libs}) +install(TARGETS test_ivf_hnsw DESTINATION unittest) + ################################################################################ # if (NOT TARGET test_ivf_cpu_nm) diff --git a/core/src/index/unittest/Helper.h b/core/src/index/unittest/Helper.h index b5db319605..d2a8e6fc4a 100644 --- a/core/src/index/unittest/Helper.h +++ b/core/src/index/unittest/Helper.h @@ -14,6 +14,7 @@ #include "knowhere/index/IndexType.h" #include "knowhere/index/vector_index/IndexIVF.h" +#include "knowhere/index/vector_index/IndexIVFHNSW.h" #include "knowhere/index/vector_index/IndexIVFPQ.h" #include "knowhere/index/vector_index/IndexIVFSQ.h" #include "knowhere/index/vector_index/helpers/IndexParameter.h" @@ -45,6 +46,8 @@ IndexFactory(const milvus::knowhere::IndexType& type, const milvus::knowhere::In return std::make_shared(); } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8) { return std::make_shared(); + } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFHNSW) { + return std::make_shared(); } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H) { std::cout << "IVFSQ8H does not support MODE_CPU" << std::endl; } else { @@ -124,6 +127,19 @@ class ParamGenerator { {milvus::knowhere::INDEX_FILE_SLICE_SIZE_IN_MEGABYTE, 4}, {milvus::knowhere::meta::DEVICEID, DEVICEID}, }; + } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFHNSW) { + return milvus::knowhere::Config{ + {milvus::knowhere::meta::DIM, DIM}, + {milvus::knowhere::meta::TOPK, K}, + {milvus::knowhere::IndexParams::nlist, 100}, + {milvus::knowhere::IndexParams::nprobe, 4}, + {milvus::knowhere::IndexParams::M, 16}, + {milvus::knowhere::IndexParams::efConstruction, 200}, + {milvus::knowhere::IndexParams::ef, 200}, + {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, + {milvus::knowhere::INDEX_FILE_SLICE_SIZE_IN_MEGABYTE, 4}, + {milvus::knowhere::meta::DEVICEID, DEVICEID}, + }; } else { std::cout << "Invalid index type " << type << std::endl; } diff --git a/core/src/index/unittest/test_ivf_hnsw.cpp b/core/src/index/unittest/test_ivf_hnsw.cpp new file mode 100644 index 0000000000..04a730fca8 --- /dev/null +++ b/core/src/index/unittest/test_ivf_hnsw.cpp @@ -0,0 +1,96 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied. See the License for the specific language governing permissions and limitations under the License. + +#include + +#include +#include +#include +#include + +#include "knowhere/common/Exception.h" +#include "knowhere/common/Timer.h" +#include "knowhere/index/IndexType.h" +#include "knowhere/index/vector_index/IndexIVF.h" +#include "knowhere/index/vector_index/IndexIVFHNSW.h" +#include "knowhere/index/vector_index/adapter/VectorAdapter.h" + +#include "unittest/Helper.h" +#include "unittest/utils.h" + +using ::testing::Combine; +using ::testing::TestWithParam; +using ::testing::Values; + +class IVFHNSWTest : public DataGen, + public TestWithParam<::std::tuple> { + protected: + void + SetUp() override { + std::tie(index_type_, index_mode_) = GetParam(); + Generate(dim, nb, nq); + index_ = IndexFactory(index_type_, index_mode_); + conf_ = ParamGenerator::GetInstance().Gen(index_type_); + } + + void + TearDown() override { +#ifdef MILVUS_GPU_VERSION + milvus::knowhere::FaissGpuResourceMgr::GetInstance().Free(); +#endif + } + + protected: + milvus::knowhere::IndexType index_type_; + milvus::knowhere::IndexMode index_mode_; + milvus::knowhere::Config conf_; + milvus::knowhere::IVFPtr index_ = nullptr; +}; + +INSTANTIATE_TEST_CASE_P( + IVFParameters, IVFHNSWTest, + Values( + std::make_tuple(milvus::knowhere::IndexEnum::INDEX_FAISS_IVFHNSW, milvus::knowhere::IndexMode::MODE_CPU))); + +TEST_P(IVFHNSWTest, ivfhnsw_basic_cpu) { + assert(!xb.empty()); + + if (index_mode_ != milvus::knowhere::IndexMode::MODE_CPU) { + return; + } + + // null faiss index + ASSERT_ANY_THROW(index_->AddWithoutIds(base_dataset, conf_)); + + index_->Train(base_dataset, conf_); + index_->AddWithoutIds(base_dataset, conf_); + EXPECT_EQ(index_->Count(), nb); + EXPECT_EQ(index_->Dim(), dim); + + auto result = index_->Query(query_dataset, conf_, nullptr); + AssertAnns(result, nq, k); +} + +TEST_P(IVFHNSWTest, ivfhnsw_slice) { + fiu_init(0); + { + // serialize index + index_->Train(base_dataset, conf_); + index_->AddWithoutIds(base_dataset, conf_); + auto binaryset = index_->Serialize(conf_); + // load index + index_->Load(binaryset); + EXPECT_EQ(index_->Count(), nb); + EXPECT_EQ(index_->Dim(), dim); + auto result = index_->Query(query_dataset, conf_, nullptr); + AssertAnns(result, nq, k); + } +} diff --git a/core/src/server/ValidationUtil.cpp b/core/src/server/ValidationUtil.cpp index bee49ecf9f..830e713c21 100644 --- a/core/src/server/ValidationUtil.cpp +++ b/core/src/server/ValidationUtil.cpp @@ -192,6 +192,7 @@ ValidateVectorIndexType(std::string& index_type, bool is_binary) { knowhere::IndexEnum::INDEX_FAISS_IVFFLAT, knowhere::IndexEnum::INDEX_FAISS_IVFPQ, knowhere::IndexEnum::INDEX_FAISS_IVFSQ8, + knowhere::IndexEnum::INDEX_FAISS_IVFHNSW, #ifdef MILVUS_GPU_VERSION knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H, #endif @@ -354,6 +355,19 @@ ValidateIndexParams(const milvus::json& index_params, int64_t dimension, const s if (!status.ok()) { return status; } + } else if (index_type == knowhere::IndexEnum::INDEX_FAISS_IVFHNSW) { + auto status = CheckParameterRange(index_params, knowhere::IndexParams::nlist, 1, 65536); + if (!status.ok()) { + return status; + } + status = CheckParameterRange(index_params, knowhere::IndexParams::M, 4, 64); + if (!status.ok()) { + return status; + } + status = CheckParameterRange(index_params, knowhere::IndexParams::efConstruction, 8, 512); + if (!status.ok()) { + return status; + } } return Status::OK(); diff --git a/core/src/server/web_impl/Constants.cpp b/core/src/server/web_impl/Constants.cpp index 25ab6902d7..71795decc0 100644 --- a/core/src/server/web_impl/Constants.cpp +++ b/core/src/server/web_impl/Constants.cpp @@ -21,6 +21,7 @@ const char* NAME_ENGINE_TYPE_IVFSQ8 = "IVF_SQ8"; const char* NAME_ENGINE_TYPE_IVFSQ8H = "IVF_SQ8H"; const char* NAME_ENGINE_TYPE_RNSG = "RNSG"; const char* NAME_ENGINE_TYPE_IVFPQ = "IVF_PQ"; +const char* NAME_ENGINE_TYPE_IVFHNSW = "IVF_HNSW"; const char* NAME_ENGINE_TYPE_HNSW = "HNSW"; const char* NAME_ENGINE_TYPE_ANNOY = "ANNOY"; const char* NAME_ENGINE_TYPE_RHNSWFLAT = "RHNSW_FLAT"; diff --git a/core/src/server/web_impl/Constants.h b/core/src/server/web_impl/Constants.h index 21c74c7af8..a49008c751 100644 --- a/core/src/server/web_impl/Constants.h +++ b/core/src/server/web_impl/Constants.h @@ -24,6 +24,7 @@ extern const char* NAME_ENGINE_TYPE_IVFSQ8; extern const char* NAME_ENGINE_TYPE_IVFSQ8H; extern const char* NAME_ENGINE_TYPE_RNSG; extern const char* NAME_ENGINE_TYPE_IVFPQ; +extern const char* NAME_ENGINE_TYPE_IVFHNSW; extern const char* NAME_ENGINE_TYPE_HNSW; extern const char* NAME_ENGINE_TYPE_ANNOY; extern const char* NAME_ENGINE_TYPE_RHNSWFLAT; diff --git a/sdk/include/MilvusApi.h b/sdk/include/MilvusApi.h index f3be51fbff..818cd1e1dc 100644 --- a/sdk/include/MilvusApi.h +++ b/sdk/include/MilvusApi.h @@ -46,6 +46,7 @@ enum class IndexType { RHNSWSQ = 15, NGTPANNG = 16, NGTONNG = 17, + IVFHNSW = 18, }; enum class MetricType {