Skip to content

Commit 4062549

Browse files
authored
Merge branch 'main' into java/int-types-tests
2 parents cd2ae5f + ea98506 commit 4062549

23 files changed

Lines changed: 1190 additions & 895 deletions

conda/environments/bench_ann_cuda-129_arch-aarch64.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ dependencies:
3535
- matplotlib-base>=3.9
3636
- nccl>=2.19
3737
- ninja
38-
- nlohmann_json>=3.11.2
38+
- nlohmann_json>=3.12.0
3939
- openblas
4040
- pandas
4141
- pylibraft==25.12.*,>=0.0.0a0

conda/environments/bench_ann_cuda-129_arch-x86_64.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ dependencies:
3838
- mkl-devel=2023
3939
- nccl>=2.19
4040
- ninja
41-
- nlohmann_json>=3.11.2
41+
- nlohmann_json>=3.12.0
4242
- openblas
4343
- pandas
4444
- pylibraft==25.12.*,>=0.0.0a0

conda/environments/bench_ann_cuda-130_arch-aarch64.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ dependencies:
3535
- matplotlib-base>=3.9
3636
- nccl>=2.19
3737
- ninja
38-
- nlohmann_json>=3.11.2
38+
- nlohmann_json>=3.12.0
3939
- openblas
4040
- pandas
4141
- pylibraft==25.12.*,>=0.0.0a0

conda/environments/bench_ann_cuda-130_arch-x86_64.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ dependencies:
3838
- mkl-devel=2023
3939
- nccl>=2.19
4040
- ninja
41-
- nlohmann_json>=3.11.2
41+
- nlohmann_json>=3.12.0
4242
- openblas
4343
- pandas
4444
- pylibraft==25.12.*,>=0.0.0a0

conda/recipes/cuvs-bench-cpu/conda_build_config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ h5py_version:
2020
- ">=3.8.0"
2121

2222
nlohmann_json_version:
23-
- ">=3.11.2"
23+
- ">=3.12.0"

cpp/cmake/thirdparty/get_nlohmann_json.cmake

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#=============================================================================
2-
# Copyright (c) 2024, NVIDIA CORPORATION.
2+
# Copyright (c) 2024-2025, NVIDIA CORPORATION.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -35,8 +35,8 @@ function(find_and_configure_nlohmann_json)
3535

3636
endfunction()
3737

38-
find_and_configure_nlohmann_json(VERSION 3.11.2
38+
find_and_configure_nlohmann_json(VERSION 3.12.0
3939
FORK nlohmann
40-
PINNED_TAG v3.11.2
40+
PINNED_TAG v3.12.0
4141
EXCLUDE_FROM_ALL ON
4242
)

cpp/include/cuvs/neighbors/cagra.hpp

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ struct index : cuvs::neighbors::index {
291291
using index_type = IdxT;
292292
using value_type = T;
293293
using dataset_index_type = int64_t;
294+
using graph_index_type = uint32_t;
294295

295296
static_assert(!raft::is_narrowing_v<uint32_t, IdxT>,
296297
"IdxT must be able to represent all values of uint32_t");
@@ -334,11 +335,21 @@ struct index : cuvs::neighbors::index {
334335

335336
/** neighborhood graph [size, graph-degree] */
336337
[[nodiscard]] inline auto graph() const noexcept
337-
-> raft::device_matrix_view<const IdxT, int64_t, raft::row_major>
338+
-> raft::device_matrix_view<const graph_index_type, int64_t, raft::row_major>
338339
{
339340
return graph_view_;
340341
}
341342

343+
/** Mapping from internal graph node indices to the original user-provided indices. */
344+
[[nodiscard]] inline auto source_indices() const noexcept
345+
-> std::optional<raft::device_vector_view<const index_type, int64_t>>
346+
{
347+
return source_indices_.has_value()
348+
? std::optional<raft::device_vector_view<const index_type, int64_t>>(
349+
source_indices_->view())
350+
: std::nullopt;
351+
}
352+
342353
/** Dataset norms for cosine distance [size] */
343354
[[nodiscard]] inline auto dataset_norms() const noexcept
344355
-> std::optional<raft::device_vector_view<const float, int64_t>>
@@ -361,7 +372,7 @@ struct index : cuvs::neighbors::index {
361372
cuvs::distance::DistanceType metric = cuvs::distance::DistanceType::L2Expanded)
362373
: cuvs::neighbors::index(),
363374
metric_(metric),
364-
graph_(raft::make_device_matrix<IdxT, int64_t>(res, 0, 0)),
375+
graph_(raft::make_device_matrix<graph_index_type, int64_t>(res, 0, 0)),
365376
dataset_(new cuvs::neighbors::empty_dataset<int64_t>(0)),
366377
dataset_norms_(std::nullopt)
367378
{
@@ -405,7 +416,7 @@ struct index : cuvs::neighbors::index {
405416
* using namespace raft::neighbors::experimental;
406417
*
407418
* auto dataset = raft::make_device_matrix<float, int64_t>(res, n_rows, n_cols);
408-
* auto knn_graph = raft::make_device_matrix<uint32_n, int64_t>(res, n_rows, graph_degree);
419+
* auto knn_graph = raft::make_device_matrix<uint32_t, int64_t>(res, n_rows, graph_degree);
409420
*
410421
* // custom loading and graph creation
411422
* // load_dataset(dataset.view());
@@ -424,11 +435,13 @@ struct index : cuvs::neighbors::index {
424435
index(raft::resources const& res,
425436
cuvs::distance::DistanceType metric,
426437
raft::mdspan<const T, raft::matrix_extent<int64_t>, raft::row_major, data_accessor> dataset,
427-
raft::mdspan<const IdxT, raft::matrix_extent<int64_t>, raft::row_major, graph_accessor>
428-
knn_graph)
438+
raft::mdspan<const graph_index_type,
439+
raft::matrix_extent<int64_t>,
440+
raft::row_major,
441+
graph_accessor> knn_graph)
429442
: cuvs::neighbors::index(),
430443
metric_(metric),
431-
graph_(raft::make_device_matrix<IdxT, int64_t>(res, 0, 0)),
444+
graph_(raft::make_device_matrix<graph_index_type, int64_t>(res, 0, 0)),
432445
dataset_(make_aligned_dataset(res, dataset, 16)),
433446
dataset_norms_(std::nullopt)
434447
{
@@ -536,8 +549,9 @@ struct index : cuvs::neighbors::index {
536549
* Since the new graph is a device array, we store a reference to that, and it is
537550
* the caller's responsibility to ensure that knn_graph stays alive as long as the index.
538551
*/
539-
void update_graph(raft::resources const& res,
540-
raft::device_matrix_view<const IdxT, int64_t, raft::row_major> knn_graph)
552+
void update_graph(
553+
raft::resources const& res,
554+
raft::device_matrix_view<const graph_index_type, int64_t, raft::row_major> knn_graph)
541555
{
542556
graph_view_ = knn_graph;
543557
}
@@ -547,16 +561,19 @@ struct index : cuvs::neighbors::index {
547561
*
548562
* We create a copy of the graph on the device. The index manages the lifetime of this copy.
549563
*/
550-
void update_graph(raft::resources const& res,
551-
raft::host_matrix_view<const IdxT, int64_t, raft::row_major> knn_graph)
564+
void update_graph(
565+
raft::resources const& res,
566+
raft::host_matrix_view<const graph_index_type, int64_t, raft::row_major> knn_graph)
552567
{
553568
RAFT_LOG_DEBUG("Copying CAGRA knn graph from host to device");
554569

555570
if ((graph_.extent(0) != knn_graph.extent(0)) || (graph_.extent(1) != knn_graph.extent(1))) {
556571
// clear existing memory before allocating to prevent OOM errors on large graphs
557-
if (graph_.size()) { graph_ = raft::make_device_matrix<IdxT, int64_t>(res, 0, 0); }
558-
graph_ =
559-
raft::make_device_matrix<IdxT, int64_t>(res, knn_graph.extent(0), knn_graph.extent(1));
572+
if (graph_.size()) {
573+
graph_ = raft::make_device_matrix<graph_index_type, int64_t>(res, 0, 0);
574+
}
575+
graph_ = raft::make_device_matrix<graph_index_type, int64_t>(
576+
res, knn_graph.extent(0), knn_graph.extent(1));
560577
}
561578
raft::copy(graph_.data_handle(),
562579
knn_graph.data_handle(),
@@ -565,11 +582,52 @@ struct index : cuvs::neighbors::index {
565582
graph_view_ = graph_.view();
566583
}
567584

585+
/**
586+
* Replace the source indices with a new source indices taking the ownership of the passed vector.
587+
*/
588+
void update_source_indices(raft::device_vector<index_type, int64_t>&& source_indices)
589+
{
590+
RAFT_EXPECTS(source_indices.extent(0) == size(),
591+
"Source indices must have the same number of rows as the index");
592+
source_indices_.emplace(std::move(source_indices));
593+
}
594+
595+
/**
596+
* Copy the provided source indices into the index.
597+
*/
598+
template <typename Accessor>
599+
void update_source_indices(
600+
raft::resources const& res,
601+
raft::mdspan<const index_type, raft::vector_extent<int64_t>, raft::row_major, Accessor>
602+
source_indices)
603+
{
604+
RAFT_EXPECTS(source_indices.extent(0) == size(),
605+
"Source indices must have the same number of rows as the index");
606+
// Reset the array if it's not compatible to avoid using more memory than necessary.
607+
// NB: this likely is never triggered because we check the invariant above (but it doesn't
608+
// hurt).
609+
if (source_indices_.has_value()) {
610+
if (source_indices_->extent(0) != source_indices.extent(0)) { source_indices_.reset(); }
611+
}
612+
// Allocate the new array if needed.
613+
if (!source_indices_.has_value()) {
614+
source_indices_.emplace(
615+
raft::make_device_vector<index_type, int64_t>(res, source_indices.extent(0)));
616+
}
617+
// Copy the data.
618+
raft::copy(source_indices_->data_handle(),
619+
source_indices.data_handle(),
620+
source_indices.extent(0),
621+
raft::resource::get_cuda_stream(res));
622+
}
623+
568624
private:
569625
cuvs::distance::DistanceType metric_;
570-
raft::device_matrix<IdxT, int64_t, raft::row_major> graph_;
571-
raft::device_matrix_view<const IdxT, int64_t, raft::row_major> graph_view_;
626+
raft::device_matrix<graph_index_type, int64_t, raft::row_major> graph_;
627+
raft::device_matrix_view<const graph_index_type, int64_t, raft::row_major> graph_view_;
572628
std::unique_ptr<neighbors::dataset<dataset_index_type>> dataset_;
629+
// Mapping from internal graph node indices to the original user-provided indices.
630+
std::optional<raft::device_vector<IdxT, int64_t>> source_indices_;
573631
// only float distances supported at the moment
574632
std::optional<raft::device_vector<float, int64_t>> dataset_norms_;
575633

cpp/src/neighbors/detail/cagra/cagra_build.cuh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ void build_knn_graph(
133133

134134
uint32_t node_degree = knn_graph.extent(1);
135135
raft::common::nvtx::range<cuvs::common::nvtx::domain::cuvs> fun_scope(
136-
"cagra::build_graph(%zu, %zu, %u)",
136+
"cagra::build_knn_graph<IVF-PQ>(%zu, %zu, %u)",
137137
size_t(dataset.extent(0)),
138138
size_t(dataset.extent(1)),
139139
node_degree);
@@ -391,6 +391,12 @@ void build_knn_graph(
391391
raft::host_matrix_view<IdxT, int64_t, raft::row_major> knn_graph,
392392
cuvs::neighbors::nn_descent::index_params build_params)
393393
{
394+
raft::common::nvtx::range<cuvs::common::nvtx::domain::cuvs> fun_scope(
395+
"cagra::build_knn_graph<NN-DESCENT>(%zu, %zu, %u)",
396+
size_t(dataset.extent(0)),
397+
size_t(dataset.extent(1)),
398+
size_t(knn_graph.extent(1)));
399+
394400
std::optional<raft::host_matrix_view<IdxT, int64_t, row_major>> graph_view = knn_graph;
395401
auto nn_descent_idx = cuvs::neighbors::nn_descent::build(res, build_params, dataset, graph_view);
396402

cpp/src/neighbors/detail/cagra/cagra_search.cuh

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,21 @@ template <typename DataT,
5050
typename IndexT,
5151
typename DistanceT,
5252
typename CagraSampleFilterT,
53-
typename OutputIdxT = IndexT>
54-
void search_main_core(raft::resources const& res,
55-
search_params params,
56-
const dataset_descriptor_host<DataT, IndexT, DistanceT>& dataset_desc,
57-
raft::device_matrix_view<const IndexT, int64_t, raft::row_major> graph,
58-
raft::device_matrix_view<const DataT, int64_t, raft::row_major> queries,
59-
raft::device_matrix_view<OutputIdxT, int64_t, raft::row_major> neighbors,
60-
raft::device_matrix_view<DistanceT, int64_t, raft::row_major> distances,
61-
CagraSampleFilterT sample_filter = CagraSampleFilterT())
53+
typename SourceIdxT = IndexT,
54+
typename OutputIdxT = SourceIdxT>
55+
void search_main_core(
56+
raft::resources const& res,
57+
search_params params,
58+
const dataset_descriptor_host<DataT, IndexT, DistanceT>& dataset_desc,
59+
raft::device_matrix_view<const IndexT, int64_t, raft::row_major> graph,
60+
std::optional<raft::device_vector_view<const SourceIdxT, int64_t>> source_indices,
61+
raft::device_matrix_view<const DataT, int64_t, raft::row_major> queries,
62+
raft::device_matrix_view<OutputIdxT, int64_t, raft::row_major> neighbors,
63+
raft::device_matrix_view<DistanceT, int64_t, raft::row_major> distances,
64+
CagraSampleFilterT sample_filter = CagraSampleFilterT())
6265
{
66+
static_assert(std::is_same_v<IndexT, uint32_t>,
67+
"Only uint32_t is supported as the graph element type (internal index type)");
6368
RAFT_LOG_DEBUG("# dataset size = %lu, dim = %lu\n",
6469
static_cast<size_t>(graph.extent(0)),
6570
static_cast<size_t>(queries.extent(1)));
@@ -80,8 +85,9 @@ void search_main_core(raft::resources const& res,
8085
queries.extent(1));
8186

8287
using CagraSampleFilterT_s = typename CagraSampleFilterT_Selector<CagraSampleFilterT>::type;
83-
std::unique_ptr<search_plan_impl<DataT, IndexT, DistanceT, CagraSampleFilterT_s, OutputIdxT>>
84-
plan = factory<DataT, IndexT, DistanceT, CagraSampleFilterT_s, OutputIdxT>::create(
88+
std::unique_ptr<
89+
search_plan_impl<DataT, IndexT, DistanceT, CagraSampleFilterT_s, SourceIdxT, OutputIdxT>>
90+
plan = factory<DataT, IndexT, DistanceT, CagraSampleFilterT_s, SourceIdxT, OutputIdxT>::create(
8591
res, params, dataset_desc, queries.extent(1), graph.extent(0), graph.extent(1), topk);
8692

8793
plan->check(topk);
@@ -104,6 +110,7 @@ void search_main_core(raft::resources const& res,
104110

105111
(*plan)(res,
106112
graph,
113+
source_indices,
107114
_topk_indices_ptr,
108115
_topk_distances_ptr,
109116
_query_ptr,
@@ -147,10 +154,12 @@ void search_main(raft::resources const& res,
147154
CagraSampleFilterT sample_filter = CagraSampleFilterT())
148155
{
149156
// n_rows has the same type as the dataset index (the array extents type)
150-
using ds_idx_type = decltype(index.data().n_rows());
157+
using ds_idx_type = decltype(index.data().n_rows());
158+
using graph_idx_type = uint32_t;
151159
// Dispatch search parameters based on the dataset kind.
152160
if (auto* strided_dset = dynamic_cast<const strided_dataset<T, ds_idx_type>*>(&index.data());
153161
strided_dset != nullptr) {
162+
// Search using a plain (strided) row-major dataset
154163
RAFT_EXPECTS(index.metric() != cuvs::distance::DistanceType::CosineExpanded ||
155164
index.dataset_norms().has_value(),
156165
"Dataset norms must be provided for CosineExpanded metric");
@@ -159,20 +168,36 @@ void search_main(raft::resources const& res,
159168
if (index.metric() == cuvs::distance::DistanceType::CosineExpanded) {
160169
dataset_norms_ptr = index.dataset_norms().value().data_handle();
161170
}
162-
auto desc = dataset_descriptor_init_with_cache<T, IdxT, DistanceT>(
171+
auto desc = dataset_descriptor_init_with_cache<T, graph_idx_type, DistanceT>(
163172
res, params, *strided_dset, index.metric(), dataset_norms_ptr);
164-
search_main_core<T, IdxT, DistanceT, CagraSampleFilterT, OutputIdxT>(
165-
res, params, desc, index.graph(), queries, neighbors, distances, sample_filter);
173+
search_main_core<T, graph_idx_type, DistanceT, CagraSampleFilterT, IdxT, OutputIdxT>(
174+
res,
175+
params,
176+
desc,
177+
index.graph(),
178+
index.source_indices(),
179+
queries,
180+
neighbors,
181+
distances,
182+
sample_filter);
166183
} else if (auto* vpq_dset = dynamic_cast<const vpq_dataset<float, ds_idx_type>*>(&index.data());
167184
vpq_dset != nullptr) {
168185
// Search using a compressed dataset
169186
RAFT_FAIL("FP32 VPQ dataset support is coming soon");
170187
} else if (auto* vpq_dset = dynamic_cast<const vpq_dataset<half, ds_idx_type>*>(&index.data());
171188
vpq_dset != nullptr) {
172-
auto desc = dataset_descriptor_init_with_cache<T, IdxT, DistanceT>(
189+
auto desc = dataset_descriptor_init_with_cache<T, graph_idx_type, DistanceT>(
173190
res, params, *vpq_dset, index.metric(), nullptr);
174-
search_main_core<T, IdxT, DistanceT, CagraSampleFilterT, OutputIdxT>(
175-
res, params, desc, index.graph(), queries, neighbors, distances, sample_filter);
191+
search_main_core<T, graph_idx_type, DistanceT, CagraSampleFilterT, IdxT, OutputIdxT>(
192+
res,
193+
params,
194+
desc,
195+
index.graph(),
196+
index.source_indices(),
197+
queries,
198+
neighbors,
199+
distances,
200+
sample_filter);
176201
} else if (auto* empty_dset = dynamic_cast<const empty_dataset<ds_idx_type>*>(&index.data());
177202
empty_dset != nullptr) {
178203
// Forgot to add a dataset.

cpp/src/neighbors/detail/cagra/cagra_serialize.cuh

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
namespace cuvs::neighbors::cagra::detail {
3737

38-
constexpr int serialization_version = 4;
38+
constexpr int serialization_version = 5;
3939

4040
/**
4141
* Save the index to file.
@@ -71,14 +71,18 @@ void serialize(raft::resources const& res,
7171
raft::serialize_mdspan(res, os, index_.graph());
7272

7373
include_dataset &= (index_.data().n_rows() > 0);
74+
bool has_source_indices = index_.source_indices().has_value();
75+
uint32_t content_map = 0x1u * include_dataset + 0x2u * has_source_indices;
7476

75-
raft::serialize_scalar(res, os, include_dataset);
77+
raft::serialize_scalar(res, os, content_map);
7678
if (include_dataset) {
7779
RAFT_LOG_DEBUG("Saving CAGRA index with dataset");
7880
neighbors::detail::serialize(res, os, index_.data());
7981
} else {
8082
RAFT_LOG_DEBUG("Saving CAGRA index WITHOUT dataset");
8183
}
84+
85+
if (has_source_indices) { raft::serialize_mdspan(res, os, index_.source_indices().value()); }
8286
}
8387

8488
template <typename T, typename IdxT>
@@ -284,10 +288,21 @@ void deserialize(raft::resources const& res, std::istream& is, index<T, IdxT>* i
284288

285289
*index_ = index<T, IdxT>(res, metric);
286290
index_->update_graph(res, raft::make_const_mdspan(graph.view()));
287-
bool has_dataset = raft::deserialize_scalar<bool>(res, is);
291+
292+
auto content_map = raft::deserialize_scalar<uint32_t>(res, is);
293+
bool has_dataset = content_map & 0x1u;
288294
if (has_dataset) {
289295
index_->update_dataset(res, cuvs::neighbors::detail::deserialize_dataset<int64_t>(res, is));
290296
}
297+
298+
bool has_source_indices = content_map & 0x2u;
299+
if (has_source_indices) {
300+
auto source_indices = raft::make_host_vector<IdxT, int64_t>(n_rows);
301+
deserialize_mdspan(res, is, source_indices.view());
302+
index_->update_source_indices(res, raft::make_const_mdspan(source_indices.view()));
303+
raft::resource::sync_stream(
304+
res); // Don't let the vector out of the scope before the copy is finished
305+
}
291306
}
292307

293308
template <typename T, typename IdxT>

0 commit comments

Comments
 (0)