Skip to content

Commit ad9bd20

Browse files
committed
Add new FlatIndex API and fix abstract interface descructors
1 parent f7fbdfc commit ad9bd20

File tree

6 files changed

+310
-1
lines changed

6 files changed

+310
-1
lines changed

bindings/cpp/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@ set(SVS_RUNTIME_HEADERS
2323
include/training.h
2424
include/vamana_index.h
2525
include/dynamic_vamana_index.h
26+
include/flat_index.h
2627
)
2728

2829
set(SVS_RUNTIME_SOURCES
2930
src/IndexSVSImplUtils.h
3031
src/svs_runtime_utils.h
3132
src/dynamic_vamana_index_impl.h
33+
src/flat_index_impl.h
3234
src/IndexSVSFlatImpl.cpp
3335
src/IndexSVSVamanaImpl.cpp
3436
src/training.cpp
3537
src/dynamic_vamana_index.cpp
38+
src/flat_index.cpp
3639
)
3740

3841
option(SVS_RUNTIME_ENABLE_LVQ_LEANVEC "Enable compilation of SVS runtime with LVQ and LeanVec support" ON)

bindings/cpp/include/flat_index.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2025 Intel Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
#include "IndexSVSImplDefs.h"
19+
20+
#include <cstddef>
21+
#include <istream>
22+
#include <ostream>
23+
24+
namespace svs {
25+
namespace runtime {
26+
27+
// Abstract interface for Flat indices.
28+
struct SVS_RUNTIME_API FlatIndex {
29+
// Static constructors and destructors
30+
static Status build(FlatIndex** index, size_t dim, MetricType metric) noexcept;
31+
static Status destroy(FlatIndex* index) noexcept;
32+
virtual ~FlatIndex();
33+
34+
virtual Status search(
35+
size_t n, const float* x, size_t k, float* distances, size_t* labels
36+
) const noexcept = 0;
37+
38+
virtual Status add(size_t n, const float* x) noexcept = 0;
39+
virtual Status reset() noexcept = 0;
40+
41+
virtual Status save(std::ostream& out) const noexcept = 0;
42+
static Status load(FlatIndex** index, std::istream& in, MetricType metric) noexcept;
43+
};
44+
} // namespace runtime
45+
} // namespace svs

bindings/cpp/include/vamana_index.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace runtime {
2525
// Abstract interface for Vamana-based indices.
2626
// NOTE VamanaIndex is not implemented directly, only DynamicVamanaIndex is implemented.
2727
struct SVS_RUNTIME_API VamanaIndex {
28-
virtual ~VamanaIndex() = 0;
28+
virtual ~VamanaIndex();
2929

3030
struct BuildParams {
3131
size_t graph_max_degree;

bindings/cpp/src/dynamic_vamana_index.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ using DynamicVamanaIndexLeanVecImplManager =
133133

134134
} // namespace
135135

136+
// VamanaIndex interface implementation
137+
VamanaIndex::~VamanaIndex() = default;
138+
139+
// DynamicVamanaIndex interface implementation
136140
Status DynamicVamanaIndex::build(
137141
DynamicVamanaIndex** index,
138142
size_t dim,

bindings/cpp/src/flat_index.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2025 Intel Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "flat_index.h"
18+
#include "flat_index_impl.h"
19+
#include "svs_runtime_utils.h"
20+
21+
#include <algorithm>
22+
#include <memory>
23+
#include <span>
24+
#include <variant>
25+
26+
#include <svs/core/data.h>
27+
#include <svs/core/distance.h>
28+
#include <svs/core/query_result.h>
29+
#include <svs/cpuid.h>
30+
#include <svs/extensions/vamana/scalar.h>
31+
32+
namespace svs {
33+
namespace runtime {
34+
35+
namespace {
36+
struct FlatIndexManager : public FlatIndex {
37+
std::unique_ptr<FlatIndexImpl> impl_;
38+
39+
FlatIndexManager(std::unique_ptr<FlatIndexImpl> impl)
40+
: impl_{std::move(impl)} {
41+
assert(impl_ != nullptr);
42+
}
43+
44+
~FlatIndexManager() override = default;
45+
46+
Status add(size_t n, const float* x) noexcept override {
47+
SVS_RUNTIME_TRY_BEGIN
48+
svs::data::ConstSimpleDataView<float> data{x, n, impl_->dimensions()};
49+
impl_->add(data);
50+
return Status_Ok;
51+
SVS_RUNTIME_TRY_END
52+
}
53+
54+
Status search(size_t n, const float* x, size_t k, float* distances, size_t* labels)
55+
const noexcept override {
56+
SVS_RUNTIME_TRY_BEGIN
57+
// TODO wrap arguments into proper data structures in FlatIndexImpl and
58+
// here
59+
impl_->search(n, x, k, distances, labels);
60+
return Status_Ok;
61+
SVS_RUNTIME_TRY_END
62+
}
63+
64+
Status reset() noexcept override {
65+
SVS_RUNTIME_TRY_BEGIN
66+
impl_->reset();
67+
return Status_Ok;
68+
SVS_RUNTIME_TRY_END
69+
}
70+
71+
Status save(std::ostream& out) const noexcept override {
72+
SVS_RUNTIME_TRY_BEGIN
73+
impl_->save(out);
74+
return Status_Ok;
75+
SVS_RUNTIME_TRY_END
76+
}
77+
};
78+
} // namespace
79+
80+
// FlatIndex interface implementation
81+
FlatIndex::~FlatIndex() = default;
82+
83+
Status FlatIndex::build(FlatIndex** index, size_t dim, MetricType metric) noexcept {
84+
*index = nullptr;
85+
SVS_RUNTIME_TRY_BEGIN
86+
auto impl = std::make_unique<FlatIndexImpl>(dim, metric);
87+
*index = new FlatIndexManager{std::move(impl)};
88+
return Status_Ok;
89+
SVS_RUNTIME_TRY_END
90+
}
91+
92+
Status FlatIndex::destroy(FlatIndex* index) noexcept {
93+
SVS_RUNTIME_TRY_BEGIN
94+
delete index;
95+
return Status_Ok;
96+
SVS_RUNTIME_TRY_END
97+
}
98+
99+
Status FlatIndex::load(FlatIndex** index, std::istream& in, MetricType metric) noexcept {
100+
*index = nullptr;
101+
SVS_RUNTIME_TRY_BEGIN
102+
std::unique_ptr<FlatIndexImpl> impl{FlatIndexImpl::load(in, metric)};
103+
*index = new FlatIndexManager{std::move(impl)};
104+
return Status_Ok;
105+
SVS_RUNTIME_TRY_END
106+
}
107+
} // namespace runtime
108+
} // namespace svs

bindings/cpp/src/flat_index_impl.h

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright 2025 Intel Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "flat_index.h"
20+
#include "svs_runtime_utils.h"
21+
22+
#include <algorithm>
23+
#include <memory>
24+
#include <variant>
25+
#include <vector>
26+
27+
#include <svs/core/data.h>
28+
#include <svs/core/distance.h>
29+
#include <svs/core/query_result.h>
30+
#include <svs/orchestrators/exhaustive.h>
31+
32+
namespace svs {
33+
namespace runtime {
34+
35+
// Vamana index implementation
36+
class FlatIndexImpl {
37+
public:
38+
FlatIndexImpl(size_t dim, MetricType metric)
39+
: dim_{dim}
40+
, metric_type_{metric} {}
41+
42+
size_t size() const { return impl_ ? impl_->size() : 0; }
43+
44+
size_t dimensions() const { return dim_; }
45+
46+
MetricType metric_type() const { return metric_type_; }
47+
48+
void add(data::ConstSimpleDataView<float> data) {
49+
if (!impl_) {
50+
return init_impl(data);
51+
}
52+
53+
throw StatusException{
54+
ErrorCode::NOT_IMPLEMENTED,
55+
"Flat index does not support adding points after initialization"};
56+
}
57+
58+
void search(
59+
size_t n,
60+
const float* x,
61+
size_t k,
62+
float* distances,
63+
size_t* labels,
64+
IDFilter* filter = nullptr
65+
) const {
66+
if (!impl_) {
67+
for (size_t i = 0; i < n; ++i) {
68+
distances[i] = std::numeric_limits<float>::infinity();
69+
labels[i] = -1;
70+
}
71+
throw StatusException{ErrorCode::NOT_INITIALIZED, "Index not initialized"};
72+
}
73+
74+
if (k == 0) {
75+
throw StatusException{ErrorCode::INVALID_ARGUMENT, "k must be greater than 0"};
76+
}
77+
78+
// Simple search
79+
if (filter == nullptr) {
80+
auto queries = svs::data::ConstSimpleDataView<float>(x, n, dim_);
81+
82+
// TODO: faiss use int64_t as label whereas SVS uses size_t?
83+
auto results = svs::QueryResultView<size_t>{
84+
svs::MatrixView<size_t>{
85+
svs::make_dims(n, k), static_cast<size_t*>(static_cast<void*>(labels))},
86+
svs::MatrixView<float>{svs::make_dims(n, k), distances}};
87+
impl_->search(results, queries, {});
88+
} else {
89+
throw StatusException{
90+
ErrorCode::NOT_IMPLEMENTED, "Filtered search not implemented yet"};
91+
}
92+
}
93+
94+
void reset() { impl_.reset(); }
95+
96+
void save(std::ostream& out) const {
97+
if (!impl_) {
98+
throw StatusException{
99+
ErrorCode::NOT_INITIALIZED, "Cannot serialize: SVS index not initialized."};
100+
}
101+
102+
impl_->save(out);
103+
}
104+
105+
static FlatIndexImpl* load(std::istream& in, MetricType metric) {
106+
auto threadpool = default_threadpool();
107+
using storage_type = svs::runtime::storage::StorageType_t<storage::FP32Tag>;
108+
109+
svs::DistanceDispatcher distance_dispatcher(to_svs_distance(metric));
110+
return distance_dispatcher([&](auto&& distance) {
111+
auto impl = new svs::Flat{svs::Flat::assemble<float, storage_type>(
112+
in, std::forward<decltype(distance)>(distance), std::move(threadpool)
113+
)};
114+
115+
return new FlatIndexImpl(std::unique_ptr<svs::Flat>{impl}, metric);
116+
});
117+
}
118+
119+
protected:
120+
// Constructor used during loading
121+
FlatIndexImpl(std::unique_ptr<svs::Flat>&& impl, MetricType metric)
122+
: dim_{impl->dimensions()}
123+
, metric_type_{metric}
124+
, impl_{std::move(impl)} {}
125+
126+
void init_impl(data::ConstSimpleDataView<float> data) {
127+
auto threadpool = default_threadpool();
128+
129+
auto storage = svs::runtime::storage::make_storage(
130+
svs::runtime::storage::FP32Tag{}, data, threadpool
131+
);
132+
133+
svs::DistanceDispatcher distance_dispatcher(to_svs_distance(metric_type_));
134+
impl_.reset(distance_dispatcher([&](auto&& distance) {
135+
return new svs::Flat(svs::Flat::assemble<float>(
136+
std::move(storage),
137+
std::forward<decltype(distance)>(distance),
138+
std::move(threadpool)
139+
));
140+
}));
141+
}
142+
143+
// Data members
144+
size_t dim_;
145+
MetricType metric_type_;
146+
std::unique_ptr<svs::Flat> impl_;
147+
};
148+
} // namespace runtime
149+
} // namespace svs

0 commit comments

Comments
 (0)