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
25 changes: 13 additions & 12 deletions c/src/neighbors/cagra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,13 @@ static void merge_indices_for_layout(
cuvs::neighbors::cagra::detail::merged_dataset_size<T, uint32_t, DatasetViewT>(
*res_ptr, index_ptrs, row_filter);
auto const dim = static_cast<uint32_t>(index_ptrs.front()->dim());
auto const stride = static_cast<int64_t>(index_ptrs.front()->dataset().stride());
auto const stride = static_cast<int64_t>(index_ptrs.front()->dataset().data_const_view().stride(0));

try {
auto matrix = raft::make_device_matrix<T, int64_t>(*res_ptr, final_row_count, stride);
using owner_t = cuvs::neighbors::owning_dataset_for_view_t<DatasetViewT>;
auto owner = std::make_unique<owner_t>(std::move(matrix), dim);
auto view = owner->as_dataset_view();
auto owner = std::make_unique<owner_t>(
cuvs::neighbors::detail::make_dense_array<owner_t>(*res_ptr, final_row_count, dim, stride));
auto view = std::as_const(*owner).view();
auto merged_idx =
cuvs::neighbors::cagra::merge(
*res_ptr, params_cpp, index_ptrs, view, merge_params, row_filter);
Expand Down Expand Up @@ -218,26 +218,27 @@ static void merge_indices_for_layout(
cuvs::neighbors::host_standard_dataset_view<T, int64_t>>;
using host_owner_t = cuvs::neighbors::owning_dataset_for_view_t<host_view_t>;

auto matrix = raft::make_host_matrix<T, int64_t>(final_row_count, stride);
std::fill_n(matrix.data_handle(), static_cast<std::size_t>(matrix.size()), T{});
auto matrix = cuvs::neighbors::detail::make_dense_array<host_owner_t>(
*res_ptr, final_row_count, dim, stride);
std::fill_n(matrix.data_handle(), matrix.mapping().required_span_size(), T{});

std::size_t row_offset = 0;
auto stream = raft::resource::get_cuda_stream(*res_ptr);
for (auto* index : index_ptrs) {
auto const& input = index->dataset();
raft::copy_matrix(matrix.data_handle() + row_offset * static_cast<std::size_t>(stride),
static_cast<std::size_t>(stride),
input.view().data_handle(),
static_cast<std::size_t>(input.stride()),
input.data_const_view().data_handle(),
static_cast<std::size_t>(input.data_const_view().stride(0)),
static_cast<std::size_t>(dim),
static_cast<std::size_t>(input.n_rows()),
stream);
row_offset += static_cast<std::size_t>(input.n_rows());
}
raft::resource::sync_stream(*res_ptr);

auto owner = std::make_unique<host_owner_t>(std::move(matrix), dim);
auto view = owner->as_dataset_view();
auto owner = std::make_unique<host_owner_t>(std::move(matrix));
auto view = std::as_const(*owner).view();
auto merged_idx = cuvs::neighbors::cagra::build(*res_ptr, params_cpp, view);
auto* holder =
new cuvs_cagra_c_api_index_lifetime_holder<T, host_view_t>{std::move(merged_idx)};
Expand Down Expand Up @@ -402,7 +403,7 @@ static void with_dataset_view(cuvsDataset_t dataset, Fn&& fn)
RAFT_EXPECTS(dataset->addr != 0, "null dataset storage");
if (dataset->is_owning) {
auto* owner = reinterpret_cast<OwnerT*>(dataset->addr);
auto view = owner->as_dataset_view();
auto view = std::as_const(*owner).view();
fn(view);
} else {
fn(*reinterpret_cast<ViewT*>(dataset->addr));
Expand Down Expand Up @@ -1383,7 +1384,7 @@ void get_dataset_view(cuvsCagraIndex_t index, DLManagedTensor* dataset)
RAFT_FAIL(
"cuvsCagraIndexGetDataset does not expose quantized datasets as dense DLPack tensors");
} else {
cuvs::core::to_dlpack(idx.dataset().view(), dataset);
cuvs::core::to_dlpack(cuvs::neighbors::dense_row_major_view(idx.dataset()), dataset);
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion c/src/neighbors/mg_cagra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ static void with_device_padded_dataset_view(cuvsDataset_t dataset, Fn&& fn)
using view_t = cuvs::neighbors::device_padded_dataset_view<T, int64_t>;
if (dataset->is_owning) {
auto* owner = reinterpret_cast<owner_t*>(dataset->addr);
auto view = owner->as_dataset_view();
auto view = std::as_const(*owner).view();
fn(view);
} else {
fn(*reinterpret_cast<view_t*>(dataset->addr));
Expand Down
6 changes: 3 additions & 3 deletions c/src/preprocessing/quantize/pq.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -244,7 +244,7 @@ extern "C" cuvsError_t cuvsProductQuantizerGetPqCodebook(cuvsProductQuantizer_t
if (quantizer->dtype.code == kDLFloat && quantizer->dtype.bits == 32) {
auto pq_mdspan =
(reinterpret_cast<cuvs::preprocessing::quantize::pq::quantizer<float>*>(quant_addr))
->vpq_codebooks.pq_code_book.view();
->vpq_codebooks.dictionary_view().pq_code_book;
cuvs::core::to_dlpack(pq_mdspan, pq_codebook);
} else {
RAFT_FAIL("Unsupported quantizer dtype: %d and bits: %d",
Expand All @@ -266,7 +266,7 @@ extern "C" cuvsError_t cuvsProductQuantizerGetVqCodebook(cuvsProductQuantizer_t
if (quantizer->dtype.code == kDLFloat && quantizer->dtype.bits == 32) {
auto pq_mdspan =
(reinterpret_cast<cuvs::preprocessing::quantize::pq::quantizer<float>*>(quant_addr))
->vpq_codebooks.vq_code_book.view();
->vpq_codebooks.dictionary_view().vq_code_book;
cuvs::core::to_dlpack(pq_mdspan, vq_codebook);
} else {
RAFT_FAIL("Unsupported quantizer dtype: %d and bits: %d",
Expand Down
31 changes: 18 additions & 13 deletions cpp/bench/ann/src/cuvs/cuvs_cagra_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ auto make_padded_view(const raft::resources& res,
}
}
cuvs::neighbors::cagra::detail::copy_with_padding(res, buffer, src);
return {raft::make_const_mdspan(buffer.view()), static_cast<uint32_t>(src.extent(1))};
return cuvs::neighbors::detail::make_dense_view<
cuvs::neighbors::device_padded_dataset_view<T, int64_t>>(
buffer.data_handle(),
buffer.extent(0),
static_cast<uint32_t>(src.extent(1)),
static_cast<uint32_t>(buffer.extent(1)));
}

/** Grow `buffers` to `size` empty matrices (device matrices are not default-constructible). */
Expand Down Expand Up @@ -373,8 +378,9 @@ void cuvs_cagra<T, IdxT>::build(const T* dataset, size_t nrow)
auto const stride = static_cast<int64_t>(
cuvs::neighbors::cagra_required_row_width<T>(static_cast<uint32_t>(dim_)));
*dataset_ = raft::make_device_matrix<T, int64_t>(handle_, merged_rows, stride);
auto merged_dataset_view = cuvs::neighbors::device_padded_dataset_view<T, int64_t>(
raft::make_const_mdspan(dataset_->view()), static_cast<uint32_t>(dim_));
auto merged_dataset_view = cuvs::neighbors::detail::make_dense_view<
cuvs::neighbors::device_padded_dataset_view<T, int64_t>>(
dataset_->data_handle(), merged_rows, static_cast<uint32_t>(dim_), stride);
index_ =
std::make_shared<index_type>(cuvs::neighbors::cagra::merge(handle_,
params,
Expand Down Expand Up @@ -413,11 +419,11 @@ void cuvs_cagra<T, IdxT>::compress_dataset(const T* dataset, size_t nrow)
vpq_dataset_ = std::make_shared<cuvs::neighbors::device_vpq_dataset<half, int64_t>>(
cuvs::preprocessing::quantize::pq::make_vpq_dataset(handle_, *index_params_.compression, src));
vpq_index_ = std::make_shared<cuvs::neighbors::cagra::device_pq_index<T, IdxT, half>>(
handle_, parse_metric_type(metric_), vpq_dataset_->as_dataset_view(), index_->graph());
handle_, parse_metric_type(metric_), vpq_dataset_->view(), index_->graph());

// Search runs on the compressed rows and the graph, so release the dense copy of the dataset.
cuvs::neighbors::device_padded_dataset_view<T, int64_t> empty_dv(
raft::make_device_matrix_view(static_cast<T const*>(nullptr), 0, this->dim_), this->dim_);
auto empty_dv = cuvs::neighbors::detail::make_empty_dataset_view<
cuvs::neighbors::device_padded_dataset_view<T, int64_t>>(this->dim_);
*index_ = cuvs::neighbors::cagra::update_dataset(handle_, std::move(*index_), empty_dv);
*dataset_ = raft::make_device_matrix<T, int64_t>(handle_, 0, 0);
need_dataset_update_ = false;
Expand Down Expand Up @@ -482,9 +488,9 @@ void cuvs_cagra<T, IdxT>::set_search_param(const search_param_base& param,
dataset_mem_ = sp.dataset_mem;

// First free up existing memory
*dataset_ = raft::make_device_matrix<T, int64_t>(handle_, 0, 0);
cuvs::neighbors::device_padded_dataset_view<T, int64_t> empty_dv(
raft::make_device_matrix_view(static_cast<T const*>(nullptr), 0, this->dim_), this->dim_);
*dataset_ = raft::make_device_matrix<T, int64_t>(handle_, 0, 0);
auto empty_dv = cuvs::neighbors::detail::make_empty_dataset_view<
cuvs::neighbors::device_padded_dataset_view<T, int64_t>>(this->dim_);
*index_ = cuvs::neighbors::cagra::update_dataset(handle_, std::move(*index_), empty_dv);

// Allocate space using the correct memory resource.
Expand All @@ -494,10 +500,9 @@ void cuvs_cagra<T, IdxT>::set_search_param(const search_param_base& param,
auto mr = get_mr(dataset_mem_);
cuvs::neighbors::cagra::detail::copy_with_padding(handle_, *dataset_, *input_dataset_v_, mr);

cuvs::neighbors::device_padded_dataset_view<T, int64_t> dv(
raft::make_device_matrix_view(
dataset_->data_handle(), dataset_->extent(0), dataset_->extent(1)),
this->dim_);
auto dv = cuvs::neighbors::detail::make_dense_view<
cuvs::neighbors::device_padded_dataset_view<T, int64_t>>(
dataset_->data_handle(), dataset_->extent(0), this->dim_, dataset_->extent(1));
*index_ = cuvs::neighbors::cagra::update_dataset(handle_, std::move(*index_), dv);

need_dataset_update_ = false;
Expand Down
13 changes: 6 additions & 7 deletions cpp/bench/ann/src/cuvs/cuvs_mg_cagra_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,12 @@ void cuvs_mg_cagra<T, IdxT>::build(const T* dataset, size_t nrow)

auto dataset_mds =
raft::make_host_matrix_view<const T, int64_t, raft::row_major>(dataset, nrow, dim_);
// The row alignment of the host view is irrelevant: every per-rank device shard is padded
// individually during the multi-GPU build.
cuvs::neighbors::host_padded_dataset_view<T, int64_t> dataset_view(dataset_mds,
static_cast<uint32_t>(dim_));
auto idx = cuvs::neighbors::cagra::build(clique_, build_params, dataset_view);
index_ = std::make_shared<
cuvs::neighbors::mg_index<cuvs::neighbors::cagra::device_padded_index<T, IdxT>, T, IdxT>>(
// A padded view cannot wrap rows that are not aligned, so the host rows get padded in a copy.
auto dataset_padded = cuvs::neighbors::make_host_padded_dataset(clique_, dataset_mds);
auto idx =
cuvs::neighbors::cagra::build(clique_, build_params, std::as_const(*dataset_padded).view());
index_ = std::make_shared<
cuvs::neighbors::mg_index<cuvs::neighbors::cagra::device_padded_index<T, IdxT>, T, IdxT>>(
std::move(idx));
}

Expand Down
12 changes: 6 additions & 6 deletions cpp/cmake/patches/faiss-1.14-cuvs-26.08.diff
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ index 4be7cebb8..42315d027 100644
raft_handle,
cuvs::distance::DistanceType::BitwiseHamming,
- dataset_mds,
+ host_to_device_dataset_->as_dataset_view(),
+ std::as_const(*host_to_device_dataset_).view(),
raft::make_const_mdspan(knn_graph_copy.view()));
} else {
FAISS_THROW_MSG(
Expand All @@ -72,7 +72,7 @@ index 4be7cebb8..42315d027 100644
cuvs::neighbors::cagra::build(
- raft_handle, index_params_, dataset));
+ raft_handle, index_params_,
+ host_to_device_dataset_->as_dataset_view()));
+ std::as_const(*host_to_device_dataset_).view()));
}
}

Expand All @@ -99,7 +99,7 @@ index 4be7cebb8..42315d027 100644
+ *cuvs_index = cuvs::neighbors::cagra::update_dataset(
+ raft_handle,
+ std::move(*cuvs_index),
+ host_to_device_dataset_->as_dataset_view());
+ std::as_const(*host_to_device_dataset_).view());
}
store_dataset_ = true;
}
Expand Down Expand Up @@ -189,7 +189,7 @@ index 6ff3419b7..f03a5e5f0 100644
raft_handle,
metricFaissToCuvs(metric_, false),
- dataset_mds,
+ host_to_device_dataset_->as_dataset_view(),
+ std::as_const(*host_to_device_dataset_).view(),
raft::make_const_mdspan(knn_graph_copy.view()));
} else {
FAISS_THROW_MSG(
Expand All @@ -215,7 +215,7 @@ index 6ff3419b7..f03a5e5f0 100644
cuvs::neighbors::cagra::build(
- raft_handle, index_params_, dataset));
+ raft_handle, index_params_,
+ host_to_device_dataset_->as_dataset_view()));
+ std::as_const(*host_to_device_dataset_).view()));
}
}

Expand All @@ -241,7 +241,7 @@ index 6ff3419b7..f03a5e5f0 100644
+ *cuvs_index = cuvs::neighbors::cagra::update_dataset(
+ raft_handle,
+ std::move(*cuvs_index),
+ host_to_device_dataset_->as_dataset_view());
+ std::as_const(*host_to_device_dataset_).view());
}
store_dataset_ = true;
}
Expand Down
17 changes: 17 additions & 0 deletions cpp/include/cuvs/core/dataset.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

/**
* The minimal viable dataset API: the owning dataset, its view, and the one spec that needs no
* storage backend at all. A dataset that does keep data around takes the spec that describes how -
* `cuvs/core/dataset/dense.hpp`, `sparse.hpp`, `vpq.hpp`, `sq.hpp` - so that including this header
* costs nothing beyond the types themselves.
*/

#include <cuvs/core/dataset/empty.hpp>
#include <cuvs/core/dataset/type.hpp>
#include <cuvs/core/dataset/view.hpp>
99 changes: 99 additions & 0 deletions cpp/include/cuvs/core/dataset/dense.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <cuvs/core/dataset/type.hpp>
#include <cuvs/core/export.hpp>

#include <raft/core/device_mdarray.hpp>
#include <raft/core/host_mdarray.hpp>
#include <raft/core/mdarray.hpp>
#include <raft/core/mdspan.hpp>

#include <cuda/std/mdspan>

#include <cstddef>
#include <cstdint>
#include <numeric>
#include <type_traits>

namespace CUVS_EXPORT cuvs {

/**
* Dense plain/padded dataset implemented via raft::mdarray.
*/
template <typename LayoutPolicy, typename ContainerPolicy>
struct mdarray_spec {
template <typename T, typename IdxT>
struct apply {
// NOTE: index type != extents type
// The index type can vary depending on the use case; we often may want to store indices in
// 32-bit slots to save memory on large datasets. The extents are always fixed to 64-bit signed
// integers to simplify the conversion and avoid integer overflow issues.
using value_type = std::remove_cv_t<T>;
using index_type = std::remove_cv_t<IdxT>;
using data_type =
raft::mdarray<value_type, raft::matrix_extent<int64_t>, LayoutPolicy, ContainerPolicy>;
using data_view_type = typename data_type::view_type;
using const_data_view_type = typename data_type::const_view_type;

[[nodiscard]] static auto get_data_view(data_type& data) noexcept -> data_view_type
{
return data.view();
}

[[nodiscard]] static auto get_data_const_view(const data_type& data) noexcept
-> const_data_view_type
{
return data.view();
}
// A mutable mdspan does not decay to a read-only one on its own; it has to be rebuilt over an
// accessor of `const T`.
[[nodiscard]] static auto get_data_const_view(data_view_type data) noexcept
-> const_data_view_type
{
return raft::make_const_mdspan(data);
}
// NOTE: the shape accessors take the data in either form - the storage an owning dataset holds,
// or the view a `dataset_view` holds. A plain spec defines no dictionary either, so it ignores
// whatever the dataset puts in that slot instead.
[[nodiscard]] static auto get_n_rows(const auto& data) noexcept -> index_type
{
return static_cast<index_type>(data.extent(0));
}
[[nodiscard]] static auto get_dim(const auto& data, const auto&) noexcept -> uint32_t
{
return static_cast<uint32_t>(data.extent(1));
}
};
};

/**
* Row-major rows padded to 16 bytes, the alignment the CAGRA kernels require. A padded matrix keeps
* the logical dimension as its second extent; the row stride follows from the layout.
*/
template <typename T>
using padded_layout = cuda::std::layout_right_padded<16 / std::gcd<int, int>(16, sizeof(T))>;

template <typename T, typename IdxT, typename ContainerPolicy>
using contiguous_dataset =
dataset<T, IdxT, mdarray_spec<raft::layout_c_contiguous, ContainerPolicy>>;
template <typename T, typename IdxT, typename ContainerPolicy>
using padded_dataset = dataset<T, IdxT, mdarray_spec<padded_layout<T>, ContainerPolicy>>;
template <typename T, typename IdxT>
using device_contiguous_dataset =
contiguous_dataset<T, IdxT, raft::device_accessor<raft::device_container_policy<T>>>;
template <typename T, typename IdxT>
using device_padded_dataset =
padded_dataset<T, IdxT, raft::device_accessor<raft::device_container_policy<T>>>;
template <typename T, typename IdxT>
using host_contiguous_dataset =
contiguous_dataset<T, IdxT, raft::host_accessor<raft::host_container_policy<T>>>;
template <typename T, typename IdxT>
using host_padded_dataset =
padded_dataset<T, IdxT, raft::host_accessor<raft::host_container_policy<T>>>;

} // namespace CUVS_EXPORT cuvs
Loading
Loading