Skip to content

[SVS] SVS API and functionality update #676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jun 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d54ef7a
[SVS] Integrate VecSim and SVS logging systems
rfsaliev May 9, 2025
09462d7
[SVS] Changed SVS batch iterator API
rfsaliev May 9, 2025
515b6df
Refactor SVS_BatchIterator and remove max_batch_size
rfsaliev May 13, 2025
d77d12c
Update SVS submodule
rfsaliev May 13, 2025
6e7fa2d
Enable SVS for non-x86 platforms
rfsaliev May 13, 2025
5213198
Disable SVS for cmake >= v.4 due to robin-map incompatibility
rfsaliev May 13, 2025
6bd584b
Update SVS submodule and enable SVS for cmake v4
rfsaliev May 15, 2025
2cfca25
cleanup and fix allocation and construct_at issues
rfsaliev May 15, 2025
14fe420
Fix aggregare initilialization issue (C++ P0960R3)
rfsaliev May 15, 2025
4b85bce
Workaround C++ LWG 3436 issue
rfsaliev May 15, 2025
42de8a9
First draft of SVS scalar quantization support
rfsaliev May 14, 2025
8d122d5
Allow Scalar quantization for non-LVQ case
rfsaliev May 15, 2025
8d4f66d
Add scalar quantization query test
rfsaliev May 16, 2025
5b1244c
Filter debug log in svs tests and format
rfsaliev May 19, 2025
d7395f3
Update SVS shared library package
rfsaliev May 19, 2025
228b96a
Fix MacOS build
rfsaliev May 19, 2025
c49b714
Update SVS with eve compilation fix
rfsaliev May 20, 2025
ad1a500
Workaround SVS batch interator completion issue
rfsaliev May 21, 2025
7c9b6b8
Fix/update SVS flow test
rfsaliev May 21, 2025
01f3613
fixup! Workaround SVS batch interator completion issue
rfsaliev May 23, 2025
88b4f9e
Improve code coverage
rfsaliev May 23, 2025
5997492
Fix isSVSQuantBitsSupported() should return SQ as fallback quantization
rfsaliev Jun 3, 2025
2d65413
Post-rebase fix
rfsaliev Jun 10, 2025
a1467b5
Improve SQ tests coverage
rfsaliev Jun 11, 2025
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
20 changes: 11 additions & 9 deletions cmake/svs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@ if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG")
set(SVS_NO_AVX512 ON)
endif()

if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "(x86_64)|(AMD64|amd64)")
set(SVS_SUPPORTED 1)
else()
set(SVS_SUPPORTED 0)
message(STATUS "SVS is not supported on this architecture")
endif()
set(SVS_SUPPORTED 1)

# GCC < v11 does not support C++20 features required for SVS
# https://gcc.gnu.org/projects/cxx-status.html
Expand All @@ -39,6 +34,13 @@ if(USE_SVS)
# Configure SVS build
add_compile_definitions("HAVE_SVS=1")

if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "(x86_64)|(AMD64|amd64)")
set(SVS_LVQ_SUPPORTED 1)
else()
set(SVS_LVQ_SUPPORTED 0)
message(STATUS "SVS LVQ is not supported on this architecture")
endif()

# detect if build environment is using glibc
include(CheckSymbolExists)
unset(GLIBC_FOUND CACHE)
Expand All @@ -47,8 +49,8 @@ if(USE_SVS)
message(STATUS "GLIBC is not detected - SVS shared library is not supported")
endif()

cmake_dependent_option(SVS_SHARED_LIB "Use SVS pre-compiled shared library" ON "USE_SVS AND GLIBC_FOUND" OFF)
set(SVS_URL "https://github.com/intel/ScalableVectorSearch/releases/download/v0.0.8-dev/svs-shared-library-0.0.8-NIGHTLY-20250505.tar.gz" CACHE STRING "SVS URL")
cmake_dependent_option(SVS_SHARED_LIB "Use SVS pre-compiled shared library" ON "USE_SVS AND GLIBC_FOUND AND SVS_LVQ_SUPPORTED" OFF)
set(SVS_URL "https://github.com/intel/ScalableVectorSearch/releases/download/v0.0.8-dev/svs-shared-library-0.0.8-NIGHTLY-254.tar.gz" CACHE STRING "SVS URL")

if(SVS_SHARED_LIB)
include(FetchContent)
Expand All @@ -71,7 +73,7 @@ if(USE_SVS)
set(SVS_LVQ_HEADER "svs/quantization/lvq/impl/lvq_impl.h")
endif()

if(EXISTS "${svs_SOURCE_DIR}/include/${SVS_LVQ_HEADER}")
if(SVS_LVQ_SUPPORTED AND EXISTS "${svs_SOURCE_DIR}/include/${SVS_LVQ_HEADER}")
message("SVS LVQ implementation found")
add_compile_definitions(VectorSimilarity PUBLIC "HAVE_SVS_LVQ=1" PUBLIC "SVS_LVQ_HEADER=\"${SVS_LVQ_HEADER}\"")
else()
Expand Down
2 changes: 1 addition & 1 deletion deps/ScalableVectorSearch
Submodule ScalableVectorSearch updated 69 files
+72 −0 .github/workflows/build-linux-arm.yml
+5 −0 .github/workflows/build-linux.yml
+85 −0 .github/workflows/build-macos.yaml
+1 −1 THIRD-PARTY-PROGRAMS
+13 −1 benchmark/include/svs-benchmark/benchmark.h
+23 −95 benchmark/include/svs-benchmark/vamana/iterator.h
+23 −0 bindings/python/include/svs/python/vamana.h
+24 −0 bindings/python/src/flat.cpp
+50 −0 bindings/python/tests/common.py
+13 −8 bindings/python/tests/test_flat.py
+5 −1 bindings/python/tests/test_vamana.py
+0 −29 cmake/FindSphinx.cmake
+2 −2 cmake/mkl.cmake
+1 −0 cmake/mkl_functions
+3 −1 cmake/robin-map.cmake
+15 −1 examples/cpp/CMakeLists.txt
+35 −3 examples/cpp/vamana.cpp
+18 −29 examples/cpp/vamana_iterator.cpp
+41 −16 include/svs/core/allocator.h
+0 −1 include/svs/core/distance/cosine.h
+0 −1 include/svs/core/distance/euclidean.h
+0 −1 include/svs/core/distance/inner_product.h
+4 −0 include/svs/core/distance/simd_utils.h
+15 −6 include/svs/core/kmeans.h
+41 −0 include/svs/extensions/vamana/scalar.h
+57 −0 include/svs/index/flat/flat.h
+4 −2 include/svs/index/inverted/clustering.h
+2 −1 include/svs/index/inverted/memory_based.h
+14 −10 include/svs/index/vamana/calibrate.h
+57 −7 include/svs/index/vamana/dynamic_index.h
+33 −0 include/svs/index/vamana/extensions.h
+38 −1 include/svs/index/vamana/index.h
+100 −192 include/svs/index/vamana/iterator.h
+589 −0 include/svs/index/vamana/multi.h
+7 −0 include/svs/lib/datatype.h
+3 −1 include/svs/lib/memory.h
+1 −1 include/svs/lib/prefetch.h
+5 −1 include/svs/lib/preprocessor.h
+7 −1 include/svs/lib/spinlock.h
+2 −1 include/svs/lib/threads/thunks.h
+3 −2 include/svs/misc/dynamic_helper.h
+30 −7 include/svs/orchestrators/dynamic_vamana.h
+26 −0 include/svs/orchestrators/exhaustive.h
+42 −17 include/svs/orchestrators/vamana.h
+44 −103 include/svs/orchestrators/vamana_iterator.h
+473 −0 include/svs/quantization/scalar/scalar.h
+8 −1 tests/CMakeLists.txt
+8 −6 tests/integration/cancel.cpp
+37 −26 tests/integration/exhaustive.cpp
+2 −1 tests/integration/inverted/build.cpp
+8 −0 tests/integration/vamana/index_build.cpp
+118 −0 tests/integration/vamana/scalar_build.cpp
+294 −0 tests/integration/vamana/scalar_iterator.cpp
+164 −0 tests/integration/vamana/scalar_search.cpp
+4 −0 tests/svs/core/allocator.cpp
+4 −0 tests/svs/core/distances/simd_utils.cpp
+11 −0 tests/svs/index/flat/flat.cpp
+17 −4 tests/svs/index/inverted/clustering.cpp
+12 −1 tests/svs/index/inverted/memory_based.cpp
+57 −8 tests/svs/index/vamana/dynamic_index_2.cpp
+18 −4 tests/svs/index/vamana/index.cpp
+77 −17 tests/svs/index/vamana/iterator.cpp
+84 −42 tests/svs/index/vamana/iterator_example.cpp
+232 −0 tests/svs/index/vamana/multi.cpp
+1 −1 tests/svs/lib/threads/threadpool.cpp
+19 −3 tests/svs/lib/timing.cpp
+211 −0 tests/svs/quantization/scalar/scalar.cpp
+66 −0 tests/utils/utils.h
+2 −61 tools/benchmark_inputs/vamana/iterator-input.toml
73 changes: 60 additions & 13 deletions src/VecSim/algorithms/svs/svs.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <vector>

#include "svs/index/vamana/dynamic_index.h"
#include "spdlog/sinks/callback_sink.h"

#include "VecSim/algorithms/svs/svs_utils.h"
#include "VecSim/algorithms/svs/svs_batch_iterator.h"
Expand All @@ -28,6 +29,9 @@
virtual ~SVSIndexBase() = default;
virtual int addVectors(const void *vectors_data, const labelType *labels, size_t n) = 0;
virtual int deleteVectors(const labelType *labels, size_t n) = 0;
#ifdef BUILD_TESTS
virtual svs::logging::logger_ptr getLogger() const = 0;
#endif
};

template <typename MetricType, typename DataType, size_t QuantBits, size_t ResidualBits,
Expand Down Expand Up @@ -65,7 +69,7 @@
// SVS Index implementation instance
std::unique_ptr<impl_type> impl_;

static float toVecSimDistance(float v) { return svs_details::toVecSimDistance<distance_f>(v); }
static double toVecSimDistance(float v) { return svs_details::toVecSimDistance<distance_f>(v); }

template <typename T, typename U>
static T getOrDefault(T v, U def) {
Expand Down Expand Up @@ -109,12 +113,51 @@
// clang-format on
}

svs::logging::logger_ptr makeLogger() {
spdlog::custom_log_callback callback = [this](const spdlog::details::log_msg &msg) {
if (!VecSimIndexInterface::logCallback) {
return; // No callback function provided

Check warning on line 119 in src/VecSim/algorithms/svs/svs.h

View check run for this annotation

Codecov / codecov/patch

src/VecSim/algorithms/svs/svs.h#L119

Added line #L119 was not covered by tests
}
// Custom callback implementation
const char *vecsim_level = [msg]() {
switch (msg.level) {
case spdlog::level::trace:
return VecSimCommonStrings::LOG_DEBUG_STRING;
case spdlog::level::debug:
return VecSimCommonStrings::LOG_VERBOSE_STRING;
case spdlog::level::info:
return VecSimCommonStrings::LOG_NOTICE_STRING;
case spdlog::level::warn:
case spdlog::level::err:
case spdlog::level::critical:
return VecSimCommonStrings::LOG_WARNING_STRING;
default:
return "UNKNOWN";

Check warning on line 135 in src/VecSim/algorithms/svs/svs.h

View check run for this annotation

Codecov / codecov/patch

src/VecSim/algorithms/svs/svs.h#L134-L135

Added lines #L134 - L135 were not covered by tests
}
}();

std::string msg_str{msg.payload.data(), msg.payload.size()};
// Log the message using the custom callback
VecSimIndexInterface::logCallback(this->logCallbackCtx, vecsim_level, msg_str.c_str());
};

// Create a logger with the custom callback
auto sink = std::make_shared<spdlog::sinks::callback_sink_mt>(callback);
auto logger = std::make_shared<spdlog::logger>("SVSIndex", sink);
// Sink all messages to VecSim
logger->set_level(spdlog::level::trace);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alonre24, this line causes printing al lot of tracing/debug information in flow tests (unit tests use custom log callback to filter-out "debug" logs).
Do you have an idea how can we properly avoid tracing/debug information to be dumped in tests keeping ability for SVS to sink needed logs to VecSim/Redisearch?
I can set logger level to spdlog::level::info here but it will prevent possibility to trace SVS index.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we redirect the logs output to a log file in flow tests somehow?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, there are different techniques could be used to manage VecSim logs in flow tests:

  1. Extend VecSim python bindings to allow changing VecSim log call back and modify flow tests. See pybind11 docs
  2. Manage pytest capture mode
  3. Modify default callback to print "debug" logs to stdlog + p.2

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@meiravgri can we address this issue in a separate PR? I believe that options 2 / 3 are better (so we still have full logs for failing tests if needed)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I fully follow — if these logs are too verbose for flow tests, wouldn't they also be noisy in production, which we probably want to avoid as well? Or is there a difference in how logging is handled between test and production environments?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In flow tests, all logs at every level are printed, while in production, the default level is "notice".
@dor-forer, can you please handle the logs redirection in tests?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean implementing option 2/3 in a separate pr?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

return logger;
}

// Create SVS index instance with initial data
// Data should not be empty
template <svs::data::ImmutableMemoryDataset Dataset>
void initImpl(const Dataset &points, std::span<const labelType> ids) {
VecSimSVSThreadPool threadpool;
svs::threads::ThreadPoolHandle threadpool_handle{VecSimSVSThreadPool{threadpool}};

auto logger = makeLogger();

// Construct SVS index initial storage with compression if needed
auto data = storage_traits_t::create_storage(points, this->blockSize, threadpool_handle,
this->getAllocator());
Expand All @@ -129,11 +172,12 @@
// Construct initial Vamana Graph
auto graph =
graph_builder_t::build_graph(parameters, data, distance, threadpool, entry_point,
this->blockSize, this->getAllocator());
this->blockSize, this->getAllocator(), logger);

// Create SVS MutableIndex instance
impl_ = std::make_unique<impl_type>(std::move(graph), std::move(data), entry_point,
std::move(distance), ids, std::move(threadpool));
impl_ =
std::make_unique<impl_type>(std::move(graph), std::move(data), entry_point,
std::move(distance), ids, std::move(threadpool), logger);

// Set SVS MutableIndex build parameters to be used in future updates
impl_->set_construction_window_size(parameters.window_size);
Expand Down Expand Up @@ -361,7 +405,8 @@
rep->results.reserve(n_neighbors);

for (size_t i = 0; i < n_neighbors; i++) {
rep->results.emplace_back(result.index(0, i), toVecSimDistance(result.distance(0, i)));
rep->results.push_back(
VecSimQueryResult{result.index(0, i), toVecSimDistance(result.distance(0, i))});
}
return rep;
}
Expand All @@ -388,13 +433,11 @@
// SVS BatchIterator handles the search in batches
// The batch size is set to the index search window size by default
const size_t batch_size = sp.buffer_config_.get_search_window_size();
auto schedule = svs::index::vamana::DefaultSchedule{sp, batch_size};

// Create SVS BatchIterator for range search
// SVS BatchIterator executes first batch of search at construction
// Search result is cached in the iterator and can be accessed by the user
svs::index::vamana::BatchIterator<impl_type, data_type> svs_it{*impl_, query, schedule,
cancel};
svs::index::vamana::BatchIterator<impl_type, data_type> svs_it{*impl_, query};
svs_it.next(batch_size, cancel);
if (cancel()) {
rep->code = VecSim_QueryReply_TimedOut;
return rep;
Expand All @@ -414,15 +457,15 @@
for (auto &neighbor : svs_it) {
const auto dist = toVecSimDistance(neighbor.distance());
if (dist <= radius) {
rep->results.emplace_back(neighbor.id(), dist);
rep->results.push_back(VecSimQueryResult{neighbor.id(), dist});
} else if (dist > range_search_boundaries) {
keep_searching = false;
}
}
// If search radius + epsilon is not exceeded, request SVS BatchIterator for the next
// batch
if (keep_searching) {
svs_it.next(cancel);
svs_it.next(batch_size, cancel);
if (cancel()) {
rep->code = VecSim_QueryReply_TimedOut;
return rep;
Expand Down Expand Up @@ -481,13 +524,17 @@
#ifdef BUILD_TESTS
void fitMemory() override {}
std::vector<std::vector<char>> getStoredVectorDataByLabel(labelType label) const override {
assert(nullptr && "Not implemented");
assert(false && "Not implemented");

Check warning on line 527 in src/VecSim/algorithms/svs/svs.h

View check run for this annotation

Codecov / codecov/patch

src/VecSim/algorithms/svs/svs.h#L527

Added line #L527 was not covered by tests
return {};
}
void getDataByLabel(
labelType label,
std::vector<std::vector<svs_details::vecsim_dt<DataType>>> &vectors_output) const override {
assert(nullptr && "Not implemented");
assert(false && "Not implemented");

Check warning on line 533 in src/VecSim/algorithms/svs/svs.h

View check run for this annotation

Codecov / codecov/patch

src/VecSim/algorithms/svs/svs.h#L533

Added line #L533 was not covered by tests
}

svs::logging::logger_ptr getLogger() const override {
return impl_ ? impl_->get_logger() : nullptr;
}
#endif
};
51 changes: 22 additions & 29 deletions src/VecSim/algorithms/svs/svs_batch_iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,12 @@
private:
using impl_type = svs::index::vamana::BatchIterator<Index, DataType>;
using dist_type = typename Index::distance_type;
bool done;
size_t dim;
const Index *index_; // Pointer to the index, used for reset and other operations.
std::unique_ptr<impl_type> impl_;
decltype(impl_->begin()) curr_it;

static std::unique_ptr<impl_type> makeImpl(const Index *index, void *query_vector,
VecSimQueryParams *queryParams) {
auto sp = svs_details::joinSearchParams(index->get_search_parameters(), queryParams);
const size_t batch_size = queryParams && queryParams->batchSize
? queryParams->batchSize
: sp.buffer_config_.get_search_window_size();
// Base search parameters for the iterator schedule.
auto schedule = svs::index::vamana::DefaultSchedule{sp, batch_size};
std::span<const DataType> query{reinterpret_cast<DataType *>(query_vector),
index->dimensions()};

auto timeoutCtx = queryParams ? queryParams->timeoutCtx : nullptr;
auto cancel = [timeoutCtx]() { return VECSIM_TIMEOUT(timeoutCtx); };
return std::make_unique<svs::index::vamana::BatchIterator<Index, DataType>>(
*index, query, schedule, cancel);
}
typename impl_type::const_iterator curr_it;
size_t batch_size;

VecSimQueryReply *getNextResultsImpl(size_t n_res) {
auto rep = new VecSimQueryReply(this->allocator);
Expand All @@ -56,16 +42,19 @@
return rep;
}

const auto bs = std::max(n_res, batch_size);

for (size_t i = 0; i < n_res; i++) {
if (curr_it == impl_->end()) {
impl_->next(cancel);
impl_->next(bs, cancel);
if (cancel()) {
rep->code = VecSim_QueryReply_TimedOut;
rep->results.clear();
return rep;
}
curr_it = impl_->begin();
if (impl_->size() == 0) {
this->done = true;
return rep;
}
}
Expand All @@ -77,12 +66,18 @@
}

public:
SVS_BatchIterator(void *query_vector, const Index *index, VecSimQueryParams *queryParams,
SVS_BatchIterator(void *query_vector, const Index *index, const VecSimQueryParams *queryParams,
std::shared_ptr<VecSimAllocator> allocator)
: VecSimBatchIterator{query_vector, queryParams ? queryParams->timeoutCtx : nullptr,
std::move(allocator)},
dim{index->dimensions()}, impl_{makeImpl(index, query_vector, queryParams)} {
curr_it = impl_->begin();
done{false}, dim{index->dimensions()}, index_{index},
impl_{std::make_unique<impl_type>(*index_,
std::span{static_cast<DataType *>(query_vector), dim})},
curr_it{impl_->begin()} {
auto sp = svs_details::joinSearchParams(index->get_search_parameters(), queryParams);
batch_size = queryParams && queryParams->batchSize

Check warning on line 78 in src/VecSim/algorithms/svs/svs_batch_iterator.h

View check run for this annotation

Codecov / codecov/patch

src/VecSim/algorithms/svs/svs_batch_iterator.h#L78

Added line #L78 was not covered by tests
? queryParams->batchSize
: sp.buffer_config_.get_search_window_size();
}

VecSimQueryReply *getNextResults(size_t n_res, VecSimQueryReply_Order order) override {
Expand All @@ -92,23 +87,21 @@
return rep;
}

bool isDepleted() override { return curr_it == impl_->end() && impl_->done(); }
bool isDepleted() override { return curr_it == impl_->end() && (this->done || impl_->done()); }

void reset() override {
std::span<const DataType> query{reinterpret_cast<const DataType *>(this->getQueryBlob()),
dim};
auto timeoutCtx = this->getTimeoutCtx();
auto cancel = [timeoutCtx]() { return VECSIM_TIMEOUT(timeoutCtx); };
impl_->update(query, cancel);
impl_.reset(new impl_type{
*index_, std::span{static_cast<const DataType *>(this->getQueryBlob()), dim}});
curr_it = impl_->begin();
this->done = false;
}
};

// Empty index iterator
class NullSVS_BatchIterator : public VecSimBatchIterator {
private:
public:
NullSVS_BatchIterator(void *query_vector, VecSimQueryParams *queryParams,
NullSVS_BatchIterator(void *query_vector, const VecSimQueryParams *queryParams,
std::shared_ptr<VecSimAllocator> allocator)
: VecSimBatchIterator{query_vector, queryParams ? queryParams->timeoutCtx : nullptr,
allocator} {}
Expand Down
49 changes: 48 additions & 1 deletion src/VecSim/algorithms/svs/svs_extensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,58 @@

#pragma once
#include "VecSim/algorithms/svs/svs_utils.h"
#include "svs/extensions/vamana/scalar.h"

#if HAVE_SVS_LVQ
#include SVS_LVQ_HEADER
#include "svs/extensions/vamana/leanvec.h"
#endif // HAVE_SVS_LVQ

// Scalar Quantization traits for SVS
template <typename DataType>
struct SVSStorageTraits<DataType, 1, 0, false> {
using element_type = std::int8_t;
using allocator_type = svs_details::SVSAllocator<element_type>;
using blocked_type = svs::data::Blocked<svs::AllocatorHandle<element_type>>;
using index_storage_type =
svs::quantization::scalar::SQDataset<element_type, svs::Dynamic, blocked_type>;

template <svs::data::ImmutableMemoryDataset Dataset, svs::threads::ThreadPool Pool>
static index_storage_type create_storage(const Dataset &data, size_t block_size, Pool &pool,
std::shared_ptr<VecSimAllocator> allocator) {
const auto dim = data.dimensions();
auto svs_bs = svs_details::SVSBlockSize(block_size, element_size(dim));

allocator_type data_allocator{std::move(allocator)};
auto blocked_alloc = svs::make_blocked_allocator_handle({svs_bs}, data_allocator);

return index_storage_type::compress(data, pool, blocked_alloc);
}

static constexpr size_t element_size(size_t dims, size_t alignment = 0) {
return dims * sizeof(element_type);
}

static size_t storage_capacity(const index_storage_type &storage) {
// SQDataset does not provide a capacity method
return storage.size();
}

template <typename Distance, typename E, size_t N>
static float compute_distance_by_id(const index_storage_type &storage, const Distance &distance,
size_t id, std::span<E, N> query) {
auto dist_f = svs::index::vamana::extensions::single_search_setup(storage, distance);

// SVS distance function may require to fix/pre-process one of arguments
svs::distance::maybe_fix_argument(dist_f, query);

// Get the datum from the storage using the storage ID
auto datum = storage.get_datum(id);
return svs::distance::compute(dist_f, query, datum);
}
};

#if HAVE_SVS_LVQ
namespace svs_details {
template <size_t Primary>
struct LVQSelector {
Expand All @@ -29,7 +76,7 @@ struct LVQSelector<4> {
// LVQDataset traits for SVS
template <typename DataType, size_t QuantBits, size_t ResidualBits>
struct SVSStorageTraits<DataType, QuantBits, ResidualBits, false,
std::enable_if_t<(QuantBits > 0)>> {
std::enable_if_t<(QuantBits > 1)>> {
using allocator_type = svs_details::SVSAllocator<std::byte>;
using blocked_type = svs::data::Blocked<svs::AllocatorHandle<std::byte>>;
using strategy_type = typename svs_details::LVQSelector<QuantBits>::strategy;
Expand Down
Loading
Loading