Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion c/include/cuvs/core/c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ CUVS_EXPORT cuvsError_t cuvsMatrixCopy(cuvsResources_t res, DLManagedTensor* src
* @param[in] res cuvsResources_t opaque C handle
* @param[in] src Pointer to DLManagedTensor to copy
* @param[in] start First row index to include in the output
* @param[in] end Last row index to include in the output
* @param[in] end One past the last row index to include in the output
* @param[out] dst Pointer to DLManagedTensor to receive slice from matrix
*/
CUVS_EXPORT cuvsError_t cuvsMatrixSliceRows(
Expand Down
46 changes: 32 additions & 14 deletions c/src/core/c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,33 +339,51 @@ extern "C" cuvsError_t cuvsMatrixSliceRows(cuvsResources_t res,
DLManagedTensor* dst_managed)
{
return cuvs::core::translate_exceptions([=] {
RAFT_EXPECTS(end >= start, "end index must be greater than start index");
RAFT_EXPECTS(dst_managed != nullptr, "dst tensor should be initialized");

dst_managed->dl_tensor = DLTensor{};
dst_managed->manager_ctx = nullptr;
dst_managed->deleter = nullptr;

RAFT_EXPECTS(src_managed != nullptr, "src tensor should be initialized");

DLTensor& src = src_managed->dl_tensor;
DLTensor& dst = dst_managed->dl_tensor;
RAFT_EXPECTS(src.ndim <= 2, "src should be a 1 or 2 dimensional tensor");
RAFT_EXPECTS(src.ndim == 1 || src.ndim == 2, "src should be a 1 or 2 dimensional tensor");
RAFT_EXPECTS(src.shape != nullptr, "shape should be initialized in the src tensor");
RAFT_EXPECTS(src.data != nullptr, "data should be initialized in the src tensor");
RAFT_EXPECTS(start >= 0 && end >= start && end <= src.shape[0],
"row slice range must satisfy 0 <= start <= end <= src.shape[0]");

dst.dtype = src.dtype;
dst.device = src.device;
dst.ndim = src.ndim;
dst.shape = new int64_t[dst.ndim];
dst.shape[0] = end - start;
auto shape = std::make_unique<int64_t[]>(src.ndim);
std::unique_ptr<int64_t[]> strides;
shape[0] = end - start;

int64_t row_strides = 1;

if (dst.ndim == 2) {
dst.shape[1] = src.shape[1];
row_strides = dst.shape[1];
if (src.ndim == 1 && src.strides) {
strides = std::make_unique<int64_t[]>(1);
row_strides = strides[0] = src.strides[0];
}

if (src.ndim == 2) {
shape[1] = src.shape[1];
row_strides = shape[1];

if (src.strides) {
dst.strides = new int64_t[2];
row_strides = dst.strides[0] = src.strides[0];
dst.strides[1] = src.strides[1];
strides = std::make_unique<int64_t[]>(2);
row_strides = strides[0] = src.strides[0];
strides[1] = src.strides[1];
}
}

dst.data = static_cast<char*>(src.data) + start * row_strides * (dst.dtype.bits / 8);
dst.dtype = src.dtype;
dst.device = src.device;
dst.ndim = src.ndim;
dst.shape = shape.release();
dst.strides = strides.release();
dst.byte_offset = src.byte_offset;
dst.data = static_cast<char*>(src.data) + start * row_strides * (dst.dtype.bits / 8);
dst_managed->deleter = cuvsMatrixDestroy;
});
}
71 changes: 70 additions & 1 deletion c/tests/core/c_api.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION.
* SPDX-License-Identifier: Apache-2.0
*/

Expand All @@ -9,6 +9,73 @@
#include <stdio.h>
#include <stdlib.h>

static void expect_matrix_slice_error(cuvsResources_t res,
DLManagedTensor* src,
int64_t start,
int64_t end)
{
int64_t sentinel_stride = 0;
DLManagedTensor dst = {0};
dst.dl_tensor.strides = &sentinel_stride;

if (cuvsMatrixSliceRows(res, src, start, end, &dst) != CUVS_ERROR) { exit(EXIT_FAILURE); }
if (dst.dl_tensor.shape != NULL || dst.dl_tensor.strides != NULL || dst.deleter != NULL) {
exit(EXIT_FAILURE);
}
}

static void test_matrix_slice_rows(cuvsResources_t res)
{
int32_t data[] = {0, 1, 2, 3, 4, 5};
int64_t shape_2d[] = {3, 2};
int64_t sentinel_stride = 0;
DLManagedTensor src_2d = {0};
src_2d.dl_tensor.data = data;
src_2d.dl_tensor.device = (DLDevice){kDLCPU, 0};
src_2d.dl_tensor.ndim = 2;
src_2d.dl_tensor.dtype = (DLDataType){kDLInt, 32, 1};
src_2d.dl_tensor.shape = shape_2d;
src_2d.dl_tensor.byte_offset = 0;

DLManagedTensor dst_2d = {0};
dst_2d.dl_tensor.strides = &sentinel_stride;
if (cuvsMatrixSliceRows(res, &src_2d, 1, 3, &dst_2d) != CUVS_SUCCESS) {
exit(EXIT_FAILURE);
}
if (dst_2d.dl_tensor.ndim != 2 || dst_2d.dl_tensor.shape[0] != 2 ||
dst_2d.dl_tensor.shape[1] != 2 || dst_2d.dl_tensor.data != (void*)(data + 2) ||
dst_2d.dl_tensor.strides != NULL || dst_2d.deleter == NULL) {
exit(EXIT_FAILURE);
}
dst_2d.deleter(&dst_2d);

int64_t shape_1d[] = {6};
DLManagedTensor src_1d = {0};
src_1d.dl_tensor.data = data;
src_1d.dl_tensor.device = (DLDevice){kDLCPU, 0};
src_1d.dl_tensor.ndim = 1;
src_1d.dl_tensor.dtype = (DLDataType){kDLInt, 32, 1};
src_1d.dl_tensor.shape = shape_1d;

DLManagedTensor dst_1d = {0};
if (cuvsMatrixSliceRows(res, &src_1d, 1, 4, &dst_1d) != CUVS_SUCCESS) {
exit(EXIT_FAILURE);
}
if (dst_1d.dl_tensor.ndim != 1 || dst_1d.dl_tensor.shape[0] != 3 ||
dst_1d.dl_tensor.data != (void*)(data + 1) || dst_1d.dl_tensor.strides != NULL ||
dst_1d.deleter == NULL) {
exit(EXIT_FAILURE);
}
dst_1d.deleter(&dst_1d);

expect_matrix_slice_error(res, &src_2d, -1, 1);
expect_matrix_slice_error(res, &src_2d, 0, 4);

DLManagedTensor src_0d = src_2d;
src_0d.dl_tensor.ndim = 0;
expect_matrix_slice_error(res, &src_0d, 0, 0);
}

int main()
{
// Create resources
Expand Down Expand Up @@ -73,6 +140,8 @@ int main()
cuvsError_t free_error_pinned = cuvsRMMHostFree(ptr3, 1024);
if (free_error_pinned == CUVS_ERROR) { exit(EXIT_FAILURE); }

test_matrix_slice_rows(res);

// Destroy resources
error = cuvsResourcesDestroy(res);
if (error == CUVS_ERROR) { exit(EXIT_FAILURE); }
Expand Down
Loading