Skip to content
Open
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
80 changes: 80 additions & 0 deletions bindings/cpp/tests/runtime_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,86 @@ CATCH_TEST_CASE("RangeSearchFunctionalStatic", "[runtime][static_vamana]") {
svs::runtime::v0::VamanaIndex::destroy(index);
}

CATCH_TEST_CASE("SearchBufferCapacityRaisedToWindowSize", "[runtime]") {
const auto& test_data = get_test_data();
const int nq = 5;
const float* xq = test_data.data();
const int k = 20;
std::vector<float> distances(nq * k);
std::vector<size_t> result_labels(nq * k);

svs::runtime::v0::VamanaIndex::SearchParams small_capacity;
small_capacity.search_window_size = 50;
small_capacity.search_buffer_capacity = 10;

CATCH_SECTION("Static, per-search parameters") {
svs::runtime::v0::VamanaIndex* index = nullptr;
auto status = svs::runtime::v0::VamanaIndex::build(
&index,
test_d,
svs::runtime::v0::MetricType::L2,
svs::runtime::v0::StorageKind::FP32,
svs::runtime::v0::VamanaIndex::BuildParams{64}
);
CATCH_REQUIRE(status.ok());
status = index->add(test_n, test_data.data());
CATCH_REQUIRE(status.ok());

status = index->search(
nq, xq, k, distances.data(), result_labels.data(), &small_capacity
);
CATCH_REQUIRE(status.ok());
for (int i = 0; i < nq; ++i) {
CATCH_REQUIRE(result_labels[i * k] == static_cast<size_t>(i));
}
svs::runtime::v0::VamanaIndex::destroy(index);
}

CATCH_SECTION("Static, default search parameters") {
svs::runtime::v0::VamanaIndex* index = nullptr;
auto status = svs::runtime::v0::VamanaIndex::build(
&index,
test_d,
svs::runtime::v0::MetricType::L2,
svs::runtime::v0::StorageKind::FP32,
svs::runtime::v0::VamanaIndex::BuildParams{64},
small_capacity
);
CATCH_REQUIRE(status.ok());
status = index->add(test_n, test_data.data());
CATCH_REQUIRE(status.ok());

status = index->search(nq, xq, k, distances.data(), result_labels.data(), nullptr);
CATCH_REQUIRE(status.ok());
svs::runtime::v0::VamanaIndex::destroy(index);
}

CATCH_SECTION("Dynamic, per-search parameters") {
svs::runtime::v0::DynamicVamanaIndex* index = nullptr;
auto status = svs::runtime::v0::DynamicVamanaIndex::build(
&index,
test_d,
svs::runtime::v0::MetricType::L2,
svs::runtime::v0::StorageKind::FP32,
svs::runtime::v0::VamanaIndex::BuildParams{64}
);
CATCH_REQUIRE(status.ok());
std::vector<size_t> labels(test_n);
std::iota(labels.begin(), labels.end(), 0);
status = index->add(test_n, labels.data(), test_data.data());
CATCH_REQUIRE(status.ok());

status = index->search(
nq, xq, k, distances.data(), result_labels.data(), &small_capacity
);
CATCH_REQUIRE(status.ok());
for (int i = 0; i < nq; ++i) {
CATCH_REQUIRE(result_labels[i * k] == static_cast<size_t>(i));
}
svs::runtime::v0::DynamicVamanaIndex::destroy(index);
}
}

CATCH_TEST_CASE("GetDistanceDynamic", "[runtime]") {
const auto& test_data = get_test_data();
svs::runtime::v0::DynamicVamanaIndex* index = nullptr;
Expand Down
5 changes: 3 additions & 2 deletions bindings/python/src/vamana_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ See also: :py:class:`svs.VamanaSearchParameters`,
search_window_size (int, read-only): The number of valid entries in the buffer
that will be used to determine stopping conditions for graph search.
search_buffer_capacity (int, read-only): The (expected) number of valid entries that
will be available. Must be at least as large as `search_window_size`.
will be available. Raised to `search_window_size` if smaller.
)"};

config.def(py::init<>())
Expand All @@ -102,7 +102,8 @@ Configure with a given search window size. This constructor implicitly defaults
py::arg("search_buffer_capacity"),
R"(
Configure with a given search window size and capacity.
Raises ``svs.ANNException`` if ``search_buffer_capacity < search_window_size``.)"
If ``search_buffer_capacity < search_window_size``, the capacity is raised to
``search_window_size``.)"
)
.def_property_readonly(
"search_window_size", &SearchBufferConfig::get_search_window_size
Expand Down
34 changes: 6 additions & 28 deletions include/svs/index/vamana/search_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,17 @@ class SearchBufferConfig {
size_t search_window_size_{0};
size_t total_capacity_{0};

// Tags to bypass invariant checks.
struct BypassInvariantCheckTag {};

public:
constexpr SearchBufferConfig() = default;

SearchBufferConfig(
BypassInvariantCheckTag, size_t search_window_size, size_t total_capacity
)
: search_window_size_{search_window_size}
, total_capacity_{total_capacity} {}

// A capacity smaller than the window size is raised to the window size.
// Callers such as FAISS pass a default capacity alongside a user-chosen window
// size, so rejecting the pair would fail searches the user never misconfigured.
SearchBufferConfig(size_t search_window_size, size_t total_capacity)
: SearchBufferConfig(
BypassInvariantCheckTag(), search_window_size, total_capacity
) {
check_invariants();
}
: search_window_size_{search_window_size}
, total_capacity_{std::max(search_window_size, total_capacity)} {}
/*not explicit*/ SearchBufferConfig(size_t search_window_size)
: SearchBufferConfig(
BypassInvariantCheckTag(), search_window_size, search_window_size
) {}
: SearchBufferConfig(search_window_size, search_window_size) {}

// Increment both the window size and capacity.
void increment(size_t by) {
Expand All @@ -81,17 +70,6 @@ class SearchBufferConfig {
size_t get_search_window_size() const { return search_window_size_; }
size_t get_total_capacity() const { return total_capacity_; }

void check_invariants() const {
if (search_window_size_ > total_capacity_) {
throw ANNEXCEPTION(
"Improper configuration for search buffer! Effective size ({}) cannot be "
"less than capacity ({}).",
search_window_size_,
total_capacity_
);
}
}

friend bool operator==(SearchBufferConfig, SearchBufferConfig) = default;
};

Expand Down
7 changes: 4 additions & 3 deletions tests/svs/index/vamana/search_buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,10 @@ CATCH_TEST_CASE("Testing Search Buffer", "[core][search_buffer]") {
CATCH_REQUIRE(config.get_search_window_size() == 10);
CATCH_REQUIRE(config.get_total_capacity() == 20);

// Ensure we get an error if mis-configuring.
auto f = []() { return vamana::SearchBufferConfig{10, 9}; };
CATCH_REQUIRE_THROWS_AS(f(), svs::ANNException);
// A capacity smaller than the window size is raised to the window size.
config = vamana::SearchBufferConfig{10, 9};
CATCH_REQUIRE(config.get_search_window_size() == 10);
CATCH_REQUIRE(config.get_total_capacity() == 10);
}

// Type Traits
Expand Down
Loading