Skip to content
Merged
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
4 changes: 3 additions & 1 deletion bindings/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ if (SVS_RUNTIME_ENABLE_LVQ_LEANVEC)
svs::svs
svs_compile_options
)
link_mkl_static(${TARGET_NAME})
if(SVS_EXPERIMENTAL_LINK_STATIC_MKL)
link_mkl_static(${TARGET_NAME})
endif()
if(SVS_LVQ_HEADER)
target_compile_definitions(${TARGET_NAME} PRIVATE
SVS_LVQ_HEADER="${SVS_LVQ_HEADER}"
Expand Down
8 changes: 8 additions & 0 deletions bindings/cpp/include/svs/runtime/dynamic_vamana_index.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ struct SVS_RUNTIME_API DynamicVamanaIndex : public VamanaIndex {
) noexcept;

virtual size_t blocksize_bytes() const noexcept = 0;

// Override for VamanaIndex interface
Status add(size_t, const float*) noexcept override {
return Status(
ErrorCode::NOT_IMPLEMENTED,
"Use add(size_t n, const size_t* labels, const float* x) for DynamicVamanaIndex"
);
}
};

struct SVS_RUNTIME_API DynamicVamanaIndexLeanVec : public DynamicVamanaIndex {
Expand Down
85 changes: 69 additions & 16 deletions bindings/cpp/include/svs/runtime/vamana_index.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,47 @@

#pragma once
#include <svs/runtime/api_defs.h>
#include <svs/runtime/training.h>

#include <cstddef>
#include <iosfwd>

namespace svs {
namespace runtime {
namespace v0 {

namespace detail {
struct VamanaBuildParameters {
size_t graph_max_degree = Unspecify<size_t>();
size_t prune_to = Unspecify<size_t>();
float alpha = Unspecify<float>();
size_t construction_window_size = Unspecify<size_t>();
size_t max_candidate_pool_size = Unspecify<size_t>();
OptionalBool use_full_search_history = Unspecify<bool>();
};

struct VamanaSearchParameters {
size_t search_window_size = Unspecify<size_t>();
size_t search_buffer_capacity = Unspecify<size_t>();
size_t prefetch_lookahead = Unspecify<size_t>();
size_t prefetch_step = Unspecify<size_t>();
};
} // namespace detail

// Abstract interface for Vamana-based indices.
// NOTE VamanaIndex is not implemented directly, only DynamicVamanaIndex is implemented.
struct SVS_RUNTIME_API VamanaIndex {
virtual ~VamanaIndex();

struct BuildParams {
size_t graph_max_degree = Unspecify<size_t>();
size_t prune_to = Unspecify<size_t>();
float alpha = Unspecify<float>();
size_t construction_window_size = Unspecify<size_t>();
size_t max_candidate_pool_size = Unspecify<size_t>();
OptionalBool use_full_search_history = Unspecify<bool>();
};

struct SearchParams {
size_t search_window_size = Unspecify<size_t>();
size_t search_buffer_capacity = Unspecify<size_t>();
size_t prefetch_lookahead = Unspecify<size_t>();
size_t prefetch_step = Unspecify<size_t>();
};
using BuildParams = detail::VamanaBuildParameters;
using SearchParams = detail::VamanaSearchParameters;

struct DynamicIndexParams {
size_t blocksize_exp = 30;
};

virtual Status add(size_t n, const float* x) noexcept = 0;
virtual Status reset() noexcept = 0;

virtual Status search(
size_t n,
const float* x,
Expand All @@ -66,6 +75,50 @@ struct SVS_RUNTIME_API VamanaIndex {
const SearchParams* params = nullptr,
IDFilter* filter = nullptr
) const noexcept = 0;

// Utility function to check storage kind support
static Status check_storage_kind(StorageKind storage_kind) noexcept;

// Static constructors and destructors
static Status build(
VamanaIndex** index,
size_t dim,
MetricType metric,
StorageKind storage_kind,
const VamanaIndex::BuildParams& params = VamanaIndex::BuildParams{},
const VamanaIndex::SearchParams& default_search_params = VamanaIndex::SearchParams{}
) noexcept;

static Status destroy(VamanaIndex* index) noexcept;

virtual Status save(std::ostream& out) const noexcept = 0;
static Status load(
VamanaIndex** index, std::istream& in, MetricType metric, StorageKind storage_kind
) noexcept;
};

struct SVS_RUNTIME_API VamanaIndexLeanVec : public VamanaIndex {
// Specialization to build LeanVec-based Vamana index with specified leanvec dims
static Status build(
VamanaIndex** index,
size_t dim,
MetricType metric,
StorageKind storage_kind,
size_t leanvec_dims,
const VamanaIndex::BuildParams& params = {},
const VamanaIndex::SearchParams& default_search_params = {}
) noexcept;

// Specialization to build LeanVec-based Vamana index with provided training data
static Status build(
VamanaIndex** index,
size_t dim,
MetricType metric,
StorageKind storage_kind,
const LeanVecTrainingData* training_data,
const VamanaIndex::BuildParams& params = {},
const VamanaIndex::SearchParams& default_search_params = {}
) noexcept;
};

} // namespace v0
Expand Down
121 changes: 72 additions & 49 deletions bindings/cpp/src/dynamic_ivf_index_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace runtime {

// Dynamic IVF index implementation (non-LeanVec storage kinds)
class DynamicIVFIndexImpl {
using allocator_type = svs::data::Blocked<svs::lib::Allocator<float>>;

public:
DynamicIVFIndexImpl(
size_t dim,
Expand Down Expand Up @@ -184,25 +186,32 @@ class DynamicIVFIndexImpl {
}

// Dispatch on storage kind to load with correct data type
return ivf_storage::dispatch_ivf_storage_kind(storage_kind, [&](auto tag) {
using Tag = decltype(tag);
using DataType = ivf_storage::IVFBlockedStorageType_t<Tag>;

svs::DistanceDispatcher distance_dispatcher(to_svs_distance(metric));
return distance_dispatcher([&](auto&& distance) {
auto impl = std::make_unique<svs::DynamicIVF>(
svs::DynamicIVF::assemble<float, svs::BFloat16, DataType>(
in,
std::forward<decltype(distance)>(distance),
return ivf_storage::dispatch_ivf_storage_kind<allocator_type>(
storage_kind,
[&](auto tag) {
using Tag = decltype(tag);
using DataType = typename Tag::type;

svs::DistanceDispatcher distance_dispatcher(to_svs_distance(metric));
return distance_dispatcher([&](auto&& distance) {
auto impl = std::make_unique<svs::DynamicIVF>(
svs::DynamicIVF::assemble<float, svs::BFloat16, DataType>(
in,
std::forward<decltype(distance)>(distance),
num_threads,
intra_query_threads
)
);
return new DynamicIVFIndexImpl(
std::move(impl),
metric,
storage_kind,
num_threads,
intra_query_threads
)
);
return new DynamicIVFIndexImpl(
std::move(impl), metric, storage_kind, num_threads, intra_query_threads
);
});
});
);
});
}
);
}

protected:
Expand Down Expand Up @@ -297,20 +306,25 @@ class DynamicIVFIndexImpl {
);

// Dispatch on storage kind to compress and assemble
return ivf_storage::dispatch_ivf_storage_kind(storage_kind_, [&](auto tag) {
// Compress data to target storage type using the factory
auto compressed_data =
ivf_storage::make_ivf_blocked_storage(tag, data, threadpool);

return new svs::DynamicIVF(svs::DynamicIVF::assemble_from_clustering<float>(
std::move(clustering),
std::move(compressed_data),
ids,
std::forward<decltype(distance)>(distance),
num_threads_,
intra_query_threads_
));
});
return ivf_storage::dispatch_ivf_storage_kind<allocator_type>(
storage_kind_,
[&](auto tag) {
// Compress data to target storage type using the factory
auto compressed_data =
ivf_storage::make_ivf_blocked_storage(tag, data, threadpool);

return new svs::DynamicIVF(
svs::DynamicIVF::assemble_from_clustering<float>(
std::move(clustering),
std::move(compressed_data),
ids,
std::forward<decltype(distance)>(distance),
num_threads_,
intra_query_threads_
)
);
}
);
}));
}

Expand All @@ -328,6 +342,8 @@ class DynamicIVFIndexImpl {
#ifdef SVS_RUNTIME_HAVE_LVQ_LEANVEC
// Dynamic IVF index implementation for LeanVec storage kinds
class DynamicIVFIndexLeanVecImpl : public DynamicIVFIndexImpl {
using allocator_type = svs::data::Blocked<svs::lib::Allocator<std::byte>>;

public:
using LeanVecMatricesType = LeanVecTrainingDataImpl::LeanVecMatricesType;

Expand Down Expand Up @@ -397,25 +413,32 @@ class DynamicIVFIndexLeanVecImpl : public DynamicIVFIndexImpl {
num_threads = static_cast<size_t>(omp_get_max_threads());
}

return ivf_storage::dispatch_ivf_leanvec_storage_kind(storage_kind, [&](auto tag) {
using Tag = decltype(tag);
using DataType = ivf_storage::IVFBlockedStorageType_t<Tag>;

svs::DistanceDispatcher distance_dispatcher(to_svs_distance(metric));
return distance_dispatcher([&](auto&& distance) {
auto impl = std::make_unique<svs::DynamicIVF>(
svs::DynamicIVF::assemble<float, svs::BFloat16, DataType>(
in,
std::forward<decltype(distance)>(distance),
return ivf_storage::dispatch_ivf_leanvec_storage_kind<allocator_type>(
storage_kind,
[&](auto tag) {
using Tag = decltype(tag);
using DataType = typename Tag::type;

svs::DistanceDispatcher distance_dispatcher(to_svs_distance(metric));
return distance_dispatcher([&](auto&& distance) {
auto impl = std::make_unique<svs::DynamicIVF>(
svs::DynamicIVF::assemble<float, svs::BFloat16, DataType>(
in,
std::forward<decltype(distance)>(distance),
num_threads,
intra_query_threads
)
);
return new DynamicIVFIndexLeanVecImpl(
std::move(impl),
metric,
storage_kind,
num_threads,
intra_query_threads
)
);
return new DynamicIVFIndexLeanVecImpl(
std::move(impl), metric, storage_kind, num_threads, intra_query_threads
);
});
});
);
});
}
);
}

protected:
Expand Down Expand Up @@ -451,7 +474,7 @@ class DynamicIVFIndexLeanVecImpl : public DynamicIVFIndexImpl {
);

// Dispatch on LeanVec storage kind to compress and assemble
return ivf_storage::dispatch_ivf_leanvec_storage_kind(
return ivf_storage::dispatch_ivf_leanvec_storage_kind<allocator_type>(
storage_kind_,
[&](auto tag) {
// Compress data to LeanVec storage type using the factory with matrices
Expand Down
21 changes: 12 additions & 9 deletions bindings/cpp/src/dynamic_vamana_index_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@
namespace svs {
namespace runtime {

// Vamana index implementation
// Dynamic Vamana index implementation
class DynamicVamanaIndexImpl {
using allocator_type = svs::data::Blocked<svs::lib::Allocator<float>>;

public:
DynamicVamanaIndexImpl(
size_t dim,
Expand Down Expand Up @@ -138,10 +140,9 @@ class DynamicVamanaIndexImpl {

// Pad results if not enough neighbors found
if (found < k) {
auto& dists = result.distances();
std::fill(dists.begin() + found, dists.end(), Unspecify<float>());
auto& inds = result.indices();
std::fill(inds.begin() + found, inds.end(), Unspecify<size_t>());
for (size_t j = found; j < k; ++j) {
result.set(Neighbor{Unspecify<size_t>(), Unspecify<float>()}, i, j);
}
}
}
};
Expand Down Expand Up @@ -394,12 +395,14 @@ class DynamicVamanaIndexImpl {
StorageArgs&&... storage_args
) {
auto threadpool = default_threadpool();
using storage_alloc_t = typename Tag::allocator_type;
auto allocator = storage::make_allocator<storage_alloc_t>(blocksize_bytes);

auto storage = make_storage(
std::forward<Tag>(tag),
data,
threadpool,
blocksize_bytes,
allocator,
std::forward<StorageArgs>(storage_args)...
);

Expand All @@ -420,7 +423,7 @@ class DynamicVamanaIndexImpl {
std::span<const size_t> labels,
lib::PowerOfTwo blocksize_bytes
) {
impl_.reset(storage::dispatch_storage_kind(
impl_.reset(storage::dispatch_storage_kind<allocator_type>(
get_storage_kind(),
[this](
auto&& tag,
Expand Down Expand Up @@ -466,7 +469,7 @@ class DynamicVamanaIndexImpl {
impl_->get_full_search_history()};
}

template <storage::StorageTag Tag>
template <typename Tag>
static svs::DynamicVamana*
load_impl_t(Tag&& tag, std::istream& stream, MetricType metric) {
namespace fs = std::filesystem;
Expand Down Expand Up @@ -514,7 +517,7 @@ class DynamicVamanaIndexImpl {
public:
static DynamicVamanaIndexImpl*
load(std::istream& stream, MetricType metric, StorageKind storage_kind) {
return storage::dispatch_storage_kind(
return storage::dispatch_storage_kind<allocator_type>(
storage_kind,
[&](auto&& tag, std::istream& stream, MetricType metric) {
using Tag = std::decay_t<decltype(tag)>;
Expand Down
Loading
Loading