Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/knowhere/comp/index_param.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ constexpr const char* INDEX_HNSW = "HNSW";
constexpr const char* INDEX_HNSW_SQ = "HNSW_SQ";
constexpr const char* INDEX_HNSW_PQ = "HNSW_PQ";
constexpr const char* INDEX_HNSW_PRQ = "HNSW_PRQ";
constexpr const char* INDEX_HNSW_RABITQ = "HNSW_RABITQ";

constexpr const char* INDEX_DISKANN = "DISKANN";
constexpr const char* INDEX_AISAQ = "AISAQ";
Expand Down
5 changes: 5 additions & 0 deletions include/knowhere/index/index_node_data_mock_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ class IndexNodeDataMockWrapper : public IndexNode {
return index_node_->HasRawData(metric_type);
}

bool
IsIndexRefineEnabled() const override {
return index_node_->IsIndexRefineEnabled();
}

expected<DataSetPtr>
GetIndexMeta(std::unique_ptr<Config> cfg) const override {
return index_node_->GetIndexMeta(std::move(cfg));
Expand Down
5 changes: 5 additions & 0 deletions include/knowhere/index/index_node_thread_pool_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ class IndexNodeThreadPoolWrapper : public IndexNode {
return index_node_->HasRawData(metric_type);
}

bool
IsIndexRefineEnabled() const override {
return index_node_->IsIndexRefineEnabled();
}

expected<DataSetPtr>
GetIndexMeta(std::unique_ptr<Config> cfg) const override {
return index_node_->GetIndexMeta(std::move(cfg));
Expand Down
4 changes: 4 additions & 0 deletions include/knowhere/index/index_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ static std::set<std::pair<std::string, VecType>> legal_knowhere_index = {
{IndexEnum::INDEX_HNSW_PRQ, VecType::VECTOR_BFLOAT16},
{IndexEnum::INDEX_HNSW_PRQ, VecType::VECTOR_INT8},

{IndexEnum::INDEX_HNSW_RABITQ, VecType::VECTOR_FLOAT},
{IndexEnum::INDEX_HNSW_RABITQ, VecType::VECTOR_FLOAT16},
{IndexEnum::INDEX_HNSW_RABITQ, VecType::VECTOR_BFLOAT16},

// diskann
{IndexEnum::INDEX_DISKANN, VecType::VECTOR_FLOAT},
{IndexEnum::INDEX_DISKANN, VecType::VECTOR_FLOAT16},
Expand Down
1 change: 1 addition & 0 deletions src/common/prometheus_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ KnownIndexTypes() {
IndexEnum::INDEX_HNSW_SQ,
IndexEnum::INDEX_HNSW_PQ,
IndexEnum::INDEX_HNSW_PRQ,
IndexEnum::INDEX_HNSW_RABITQ,
IndexEnum::INDEX_DISKANN,
IndexEnum::INDEX_AISAQ,
IndexEnum::INDEX_MINHASH_LSH,
Expand Down
372 changes: 349 additions & 23 deletions src/index/hnsw/faiss_hnsw.cc

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions src/index/hnsw/faiss_hnsw_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,57 @@ class FaissHnswPqConfig : public FaissHnswConfig {
}
};

class FaissHnswRaBitQConfig : public FaissHnswConfig {
public:
// Number of bits per database vector dimension.
CFG_INT rbq_bits;
// Request-local coarse estimator query precision.
CFG_INT rbq_bits_query;

KNOWHERE_DECLARE_CONFIG(FaissHnswRaBitQConfig) {
KNOWHERE_CONFIG_DECLARE_FIELD(rbq_bits)
.description("number of RaBitQ bits per database vector dimension")
.set_default(1)
.set_range(1, 9)
.for_train()
.for_static();
KNOWHERE_CONFIG_DECLARE_FIELD(rbq_bits_query)
.description("query bits for the RaBitQ coarse estimator; 0 uses FP32")
.set_default(4)
.set_range(0, 8)
.for_search()
.for_range_search()
.for_iterator();
}

Status
CheckAndAdjust(PARAM_TYPE param_type, std::string* err_msg) override {
const auto base_status = FaissHnswConfig::CheckAndAdjust(param_type, err_msg);
if (base_status != Status::success) {
return base_status;
}

const auto metric = str_to_lower(metric_type.value_or(knowhere::metric::L2));
if (metric != "l2" && metric != "ip" && metric != "cosine") {
return HandleError(err_msg, "HNSW_RABITQ only supports L2, IP and COSINE metrics",
Status::invalid_metric_type);
}

if ((param_type == PARAM_TYPE::DESERIALIZE || param_type == PARAM_TYPE::DESERIALIZE_FROM_FILE) &&
enable_mmap.value_or(false)) {
return HandleError(err_msg, "HNSW_RABITQ does not support mmap loading", Status::invalid_args);
}
if (param_type == PARAM_TYPE::TRAIN && refine_type.has_value() &&
!WhetherAcceptableRefineType(refine_type.value())) {
return HandleError(err_msg,
"invalid refine type : " + refine_type.value() +
", optional types are [sq4u, sq6, sq8, fp16, bf16, fp32, flat]",
Status::invalid_args);
}
return Status::success;
}
};

class FaissHnswPrqConfig : public FaissHnswConfig {
public:
// number of subquantizer splits
Expand Down
44 changes: 44 additions & 0 deletions src/index/hnsw/impl/HnswSearchDispatch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (C) 2026 Zilliz. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <faiss/cppcontrib/knowhere/impl/HnswSearcher.h>
#include <faiss/cppcontrib/knowhere/utils/Bitset.h>

#include <type_traits>

#include "index/hnsw/impl/DummyVisitor.h"
#include "index/hnsw/impl/FederVisitor.h"
#include "index/hnsw/impl/IndexHNSWWrapper.h"
#include "knowhere/bitsetview_idselector.h"

namespace knowhere {

// Reuse selector/visitor dispatch without exposing codec types to common HNSW.
template <class DistanceEvaluationT = faiss::cppcontrib::knowhere::DefaultHnswDistanceEvaluation>
faiss::cppcontrib::knowhere::HNSWStats
search_hnsw_query(const faiss::cppcontrib::knowhere::HNSW& graph, faiss::DistanceComputer& distance,
faiss::cppcontrib::knowhere::Bitset& visited, faiss::idx_t k, float* distances, faiss::idx_t* labels,
const SearchParametersHNSWWrapper* params) {
auto run = [&](auto& visitor, const auto& selector) {
using Visitor = std::remove_reference_t<decltype(visitor)>;
using Selector = std::decay_t<decltype(selector)>;
faiss::cppcontrib::knowhere::v2_hnsw_searcher<
faiss::DistanceComputer, Visitor, faiss::cppcontrib::knowhere::Bitset, Selector, DistanceEvaluationT>
searcher{graph, distance, visitor, visited, selector, params ? params->kAlpha : 0.0f, params};
return searcher.search(k, distances, labels);
};
auto visit = [&](const auto& selector) {
if (params && params->feder) {
FederVisitor visitor(params->feder);
return run(visitor, selector);
}
DummyVisitor visitor;
return run(visitor, selector);
};
const auto* selector = params ? dynamic_cast<const BitsetViewIDSelector*>(params->sel) : nullptr;
if (selector && !selector->bitset_view.empty())
return visit(*selector);
faiss::IDSelectorAll all;
return visit(all);
}
} // namespace knowhere
9 changes: 7 additions & 2 deletions src/index/hnsw/impl/IndexBruteForceWrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <algorithm>
#include <memory>

#include "index/hnsw/impl/IndexHNSWWrapper.h"
#include "knowhere/bitsetview.h"
#include "knowhere/bitsetview_idselector.h"

Expand Down Expand Up @@ -56,7 +57,9 @@ IndexBruteForceWrapper::search(faiss::idx_t n, const float* __restrict x, faiss:
const faiss::SearchParameters* __restrict params) const {
FAISS_THROW_IF_NOT(k > 0);

std::unique_ptr<faiss::DistanceComputer> dis(index->get_distance_computer());
const auto* hnsw_params = dynamic_cast<const SearchParametersHNSWWrapper*>(params);
std::unique_ptr<faiss::DistanceComputer> dis(hnsw_params ? hnsw_params->storage_distance_computer(index)
: index->get_distance_computer());

// no parallelism by design
for (idx_t i = 0; i < n; i++) {
Expand Down Expand Up @@ -116,7 +119,9 @@ IndexBruteForceWrapper::range_search(faiss::idx_t n, const float* x, float radiu
RH_min bres_min(result, radius);
RH_max bres_max(result, radius);

std::unique_ptr<faiss::DistanceComputer> dis(index->get_distance_computer());
const auto* hnsw_params = dynamic_cast<const SearchParametersHNSWWrapper*>(params);
std::unique_ptr<faiss::DistanceComputer> dis(hnsw_params ? hnsw_params->storage_distance_computer(index)
: index->get_distance_computer());

// no parallelism by design
for (idx_t i = 0; i < n; i++) {
Expand Down
8 changes: 5 additions & 3 deletions src/index/hnsw/impl/IndexConditionalWrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ WhetherPerformBruteForceRangeSearch(const faiss::Index* index, const FaissHnswCo
// index was trained with the refine.
std::tuple<std::unique_ptr<faiss::Index>, bool>
create_conditional_hnsw_wrapper(faiss::Index* index, const FaissHnswConfig& hnsw_cfg, const bool whether_bf_search,
const bool whether_to_enable_refine) {
const bool whether_to_enable_refine, const SearchParametersHNSWWrapper* search_params) {
const bool is_cosine = IsMetricType(hnsw_cfg.metric_type.value(), knowhere::metric::COSINE);

// check if we have a refine available.
Expand Down Expand Up @@ -129,7 +129,8 @@ create_conditional_hnsw_wrapper(faiss::Index* index, const FaissHnswConfig& hnsw
base_wrapper = std::make_unique<knowhere::IndexBruteForceWrapper>(index_hnsw);
} else {
// use hnsw-search wrapper
base_wrapper = std::make_unique<knowhere::IndexHNSWWrapper>(index_hnsw);
base_wrapper = search_params ? search_params->create_hnsw_wrapper(index_hnsw)
: std::make_unique<knowhere::IndexHNSWWrapper>(index_hnsw);
}

// check if a user wants a refined result
Expand Down Expand Up @@ -201,7 +202,8 @@ create_conditional_hnsw_wrapper(faiss::Index* index, const FaissHnswConfig& hnsw
base_wrapper = std::make_unique<knowhere::IndexBruteForceWrapper>(index_hnsw);
} else {
// use hnsw-search wrapper
base_wrapper = std::make_unique<knowhere::IndexHNSWWrapper>(index_hnsw);
base_wrapper = search_params ? search_params->create_hnsw_wrapper(index_hnsw)
: std::make_unique<knowhere::IndexHNSWWrapper>(index_hnsw);
}

return {std::move(base_wrapper), false};
Expand Down
5 changes: 4 additions & 1 deletion src/index/hnsw/impl/IndexConditionalWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

namespace knowhere {

struct SearchParametersHNSWWrapper;

struct HnswSearchThresholds {
static constexpr float kHnswSearchKnnBFFilterThreshold = 0.93f;
static constexpr float kHnswSearchRangeBFFilterThreshold = 0.97f;
Expand All @@ -48,6 +50,7 @@ WhetherPerformBruteForceRangeSearch(const faiss::Index* index, const FaissHnswCo
// index was trained with the refine.
std::tuple<std::unique_ptr<faiss::Index>, bool>
create_conditional_hnsw_wrapper(faiss::Index* index, const FaissHnswConfig& hnsw_cfg, const bool whether_bf_search,
const bool whether_to_enable_refine);
const bool whether_to_enable_refine,
const SearchParametersHNSWWrapper* search_params = nullptr);

} // namespace knowhere
30 changes: 30 additions & 0 deletions src/index/hnsw/impl/IndexHNSWRaBitQWrapper.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (C) 2026 Zilliz. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include "index/hnsw/impl/IndexHNSWRaBitQWrapper.h"

#include <faiss/cppcontrib/knowhere/impl/RaBitQHnswDistanceEvaluation.h>

#include "index/hnsw/impl/HnswSearchDispatch.h"
#include "index/hnsw/impl/RaBitQSearchParameters.h"

namespace knowhere {
namespace rabitq_search = faiss::cppcontrib::knowhere::rabitq_search;

std::unique_ptr<faiss::DistanceComputer>
IndexHNSWRaBitQWrapper::storage_distance_computer(const faiss::cppcontrib::knowhere::IndexHNSW* index,
const SearchParametersHNSWWrapper* params) const {
const auto* rbq = dynamic_cast<const faiss::cppcontrib::knowhere::IndexHNSWRaBitQ*>(index);
FAISS_THROW_IF_NOT(rbq);
const auto* rbq_params = dynamic_cast<const SearchParametersHNSWRaBitQWrapper*>(params);
return std::unique_ptr<faiss::DistanceComputer>(
rbq->get_staged_distance_computer(rbq_params ? &rbq_params->storage_params : nullptr));
}

faiss::cppcontrib::knowhere::HNSWStats
IndexHNSWRaBitQWrapper::search_query(const faiss::cppcontrib::knowhere::HNSW& graph, faiss::DistanceComputer& dc,
faiss::cppcontrib::knowhere::Bitset& visited, faiss::idx_t k, float* distances,
faiss::idx_t* labels, const SearchParametersHNSWWrapper* params) const {
return search_hnsw_query<rabitq_search::RaBitQHnswDistanceEvaluation>(graph, dc, visited, k, distances, labels,
params);
}
} // namespace knowhere
20 changes: 20 additions & 0 deletions src/index/hnsw/impl/IndexHNSWRaBitQWrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2026 Zilliz. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include "index/hnsw/impl/IndexHNSWWrapper.h"

namespace knowhere {
struct IndexHNSWRaBitQWrapper : IndexHNSWWrapper {
using IndexHNSWWrapper::IndexHNSWWrapper;

protected:
std::unique_ptr<faiss::DistanceComputer>
storage_distance_computer(const faiss::cppcontrib::knowhere::IndexHNSW* index,
const SearchParametersHNSWWrapper* params) const override;
faiss::cppcontrib::knowhere::HNSWStats
search_query(const faiss::cppcontrib::knowhere::HNSW& graph, faiss::DistanceComputer& dc,
faiss::cppcontrib::knowhere::Bitset& visited, faiss::idx_t k, float* distances, faiss::idx_t* labels,
const SearchParametersHNSWWrapper* params) const override;
};
} // namespace knowhere
Loading