-
Notifications
You must be signed in to change notification settings - Fork 248
Lanczos dense fallback on n <= 3
#3122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION. | ||
| * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| #ifndef __DENSE_H | ||
|
|
@@ -8,46 +8,37 @@ | |
| #pragma once | ||
|
|
||
| #include <raft/core/detail/macros.hpp> | ||
| #include <raft/core/device_mdspan.hpp> | ||
| #include <raft/core/resources.hpp> | ||
| #include <raft/sparse/convert/detail/dense.cuh> | ||
|
|
||
| namespace raft { | ||
| namespace sparse { | ||
| namespace convert { | ||
|
|
||
| /** | ||
| * Convert CSR arrays to a dense matrix in either row- | ||
| * or column-major format. A custom kernel is used when | ||
| * row-major output is desired since cusparse does not | ||
| * output row-major. | ||
| * @tparam value_idx : data type of the CSR index arrays | ||
| * @tparam value_t : data type of the CSR value array | ||
| * @param[in] handle : cusparse handle for conversion | ||
| * @param[in] nrows : number of rows in CSR | ||
| * @param[in] ncols : number of columns in CSR | ||
| * @param[in] nnz : number of nonzeros in CSR | ||
| * @param[in] csr_indptr : CSR row index pointer array | ||
| * @param[in] csr_indices : CSR column indices array | ||
| * @param[in] csr_data : CSR data array | ||
| * @param[in] lda : Leading dimension (used for col-major only) | ||
| * @param[out] out : Dense output array of size nrows * ncols | ||
| * @param[in] stream : Cuda stream for ordering events | ||
| * @param[in] row_major : Is row-major output desired? | ||
| * Convert a sparse matrix view to a dense matrix view. | ||
| * | ||
| * Supports both COO and CSR sparse matrix views and row- or column-major dense output. | ||
| * | ||
| * @param[in] handle RAFT resources | ||
| * @param[in] sparse Sparse COO or CSR matrix view | ||
| * @param[out] dense Dense matrix view | ||
| */ | ||
| template <typename value_idx, typename value_t> | ||
| void csr_to_dense(cusparseHandle_t handle, | ||
| value_idx nrows, | ||
| value_idx ncols, | ||
| value_idx nnz, | ||
| const value_idx* csr_indptr, | ||
| const value_idx* csr_indices, | ||
| const value_t* csr_data, | ||
| value_idx lda, | ||
| value_t* out, | ||
| cudaStream_t stream, | ||
| bool row_major = true) | ||
| template <typename SparseMatrixViewType, | ||
| typename ValueType, | ||
| typename IndexType, | ||
| typename LayoutPolicy> | ||
| void sparse_to_dense(raft::resources const& handle, | ||
| SparseMatrixViewType sparse, | ||
| raft::device_matrix_view<ValueType, IndexType, LayoutPolicy> dense) | ||
| { | ||
| detail::csr_to_dense<value_idx, value_t>( | ||
| handle, nrows, ncols, nnz, csr_indptr, csr_indices, csr_data, lda, out, stream, row_major); | ||
| auto structure = sparse.structure_view(); | ||
| RAFT_EXPECTS(dense.extent(0) == static_cast<IndexType>(structure.get_n_rows()) && | ||
| dense.extent(1) == static_cast<IndexType>(structure.get_n_cols()), | ||
| "Sparse and dense matrix dimensions must match"); | ||
|
|
||
| detail::sparse_to_dense(handle, sparse, dense); | ||
| } | ||
|
Comment on lines
+28
to
42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find any remaining declarations/usages of csr_to_dense in the repository.
rg -nP --type-add 'cuda:*.{cu,cuh,hpp,cpp,h}' --type=cuda -C3 '\bcsr_to_dense\s*(<|\()'Repository: NVIDIA/raft Length of output: 149 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- tracked references ---'
rg -n -P --glob '*.{cu,cuh,cpp,hpp,h}' '\bcsr_to_dense\b|\bsparse_to_dense\b' cpp tests || true
printf '%s\n' '--- public header outline ---'
ast-grep outline cpp/include/raft/sparse/convert/dense.cuh --view compact || true
printf '%s\n' '--- relevant header ---'
cat -n cpp/include/raft/sparse/convert/dense.cuh
printf '%s\n' '--- current diff summary ---'
git diff --stat
printf '%s\n' '--- current diff for header ---'
git diff -- cpp/include/raft/sparse/convert/dense.cuhRepository: NVIDIA/raft Length of output: 2877 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- recent commits for the public conversion header ---'
git log -8 --oneline -- cpp/include/raft/sparse/convert/dense.cuh cpp/include/raft/sparse/convert/detail/dense.cuh
printf '%s\n' '--- parent public header ---'
git show HEAD^:cpp/include/raft/sparse/convert/dense.cuh | cat -n
printf '%s\n' '--- parent references ---'
git grep -n -E '\bcsr_to_dense\b|\bsparse_to_dense\b' HEAD^ -- cpp cpp/tests || true
printf '%s\n' '--- current file and test names ---'
git ls-files 'cpp/*' | grep -E 'sparse/(convert|.*csr_to_dense)|dense\.cuh$' | head -80Repository: NVIDIA/raft Length of output: 4149 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- conversion implementation APIs ---'
cat -n cpp/include/raft/sparse/convert/detail/dense.cuh
printf '%s\n' '--- commit change summary ---'
git show --stat --oneline c6fc1ee00
printf '%s\n' '--- public API and migration documentation references ---'
rg -n -i 'csr_to_dense|sparse_to_dense|migration|deprecated' README.md docs cpp/include/raft/sparse cpp/tests | head -160 || true
printf '%s\n' '--- current test call sites and declarations in parent ---'
git show HEAD^:cpp/tests/sparse/csr_to_dense.cu | sed -n '75,105p'
git show HEAD^:cpp/tests/util/preprocess_utils.cu | sed -n '140,165p'Repository: NVIDIA/raft Length of output: 7182 Preserve the
🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| }; // end NAMESPACE convert | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,134 +6,52 @@ | |
| #pragma once | ||
|
|
||
| #include <raft/core/detail/macros.hpp> | ||
| #include <raft/core/device_mdarray.hpp> | ||
| #include <raft/core/device_mdspan.hpp> | ||
| #include <raft/core/resource/cuda_stream.hpp> | ||
| #include <raft/core/resource/cusparse_handle.hpp> | ||
| #include <raft/core/resources.hpp> | ||
| #include <raft/sparse/detail/cusparse_wrappers.h> | ||
| #include <raft/sparse/detail/utils.h> | ||
| #include <raft/util/cuda_utils.cuh> | ||
| #include <raft/util/cudart_utils.hpp> | ||
| #include <raft/util/kernel_launch.hpp> | ||
|
|
||
| #include <rmm/device_uvector.hpp> | ||
| #include <raft/sparse/linalg/detail/cusparse_utils.hpp> | ||
|
|
||
| #include <cuda_runtime.h> | ||
| #include <thrust/device_ptr.h> | ||
| #include <thrust/scan.h> | ||
|
|
||
| #include <cusparse_v2.h> | ||
| #include <stdio.h> | ||
|
|
||
| #include <algorithm> | ||
| #include <iostream> | ||
|
|
||
| namespace raft { | ||
| namespace sparse { | ||
| namespace convert { | ||
| namespace detail { | ||
|
|
||
| template <typename value_t> | ||
| RAFT_KERNEL csr_to_dense_warp_per_row_kernel( | ||
| int n_cols, const value_t* csrVal, const int* csrRowPtr, const int* csrColInd, value_t* a) | ||
| template <typename SparseMatrixViewType, | ||
| typename ValueType, | ||
| typename IndexType, | ||
| typename LayoutPolicy> | ||
| void sparse_to_dense(raft::resources const& handle, | ||
| SparseMatrixViewType sparse, | ||
| raft::device_matrix_view<ValueType, IndexType, LayoutPolicy> dense) | ||
| { | ||
| int row = blockIdx.x; | ||
| int tid = threadIdx.x; | ||
|
|
||
| int colStart = csrRowPtr[row]; | ||
| int colEnd = csrRowPtr[row + 1]; | ||
| int rowNnz = colEnd - colStart; | ||
|
|
||
| for (int i = tid; i < rowNnz; i += blockDim.x) { | ||
| int colIdx = colStart + i; | ||
| if (colIdx < colEnd) { | ||
| int col = csrColInd[colIdx]; | ||
| a[row * n_cols + col] = csrVal[colIdx]; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Convert CSR arrays to a dense matrix in either row- | ||
| * or column-major format. A custom kernel is used when | ||
| * row-major output is desired since cusparse does not | ||
| * output row-major. | ||
| * @tparam value_idx : data type of the CSR index arrays | ||
| * @tparam value_t : data type of the CSR value array | ||
| * @param[in] handle : cusparse handle for conversion | ||
| * @param[in] nrows : number of rows in CSR | ||
| * @param[in] ncols : number of columns in CSR | ||
| * @param[in] nnz : the number of nonzeros in CSR | ||
| * @param[in] csr_indptr : CSR row index pointer array | ||
| * @param[in] csr_indices : CSR column indices array | ||
| * @param[in] csr_data : CSR data array | ||
| * @param[in] lda : Leading dimension (used for col-major only) | ||
| * @param[out] out : Dense output array of size nrows * ncols | ||
| * @param[in] stream : Cuda stream for ordering events | ||
| * @param[in] row_major : Is row-major output desired? | ||
| */ | ||
| template <typename value_idx, typename value_t> | ||
| void csr_to_dense(cusparseHandle_t handle, | ||
| value_idx nrows, | ||
| value_idx ncols, | ||
| value_idx nnz, | ||
| const value_idx* csr_indptr, | ||
| const value_idx* csr_indices, | ||
| const value_t* csr_data, | ||
| value_idx lda, | ||
| value_t* out, | ||
| cudaStream_t stream, | ||
| bool row_major = true) | ||
| { | ||
| if (!row_major) { | ||
| /** | ||
| * If we need col-major, use cusparse. | ||
| */ | ||
| cusparseMatDescr_t out_mat; | ||
| RAFT_CUSPARSE_TRY(cusparseCreateMatDescr(&out_mat)); | ||
| RAFT_CUSPARSE_TRY(cusparseSetMatIndexBase(out_mat, CUSPARSE_INDEX_BASE_ZERO)); | ||
| RAFT_CUSPARSE_TRY(cusparseSetMatType(out_mat, CUSPARSE_MATRIX_TYPE_GENERAL)); | ||
|
|
||
| size_t buffer_size; | ||
| RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsecsr2dense_buffersize(handle, | ||
| nrows, | ||
| ncols, | ||
| nnz, | ||
| out_mat, | ||
| csr_data, | ||
| csr_indptr, | ||
| csr_indices, | ||
| out, | ||
| lda, | ||
| &buffer_size, | ||
| stream)); | ||
|
|
||
| rmm::device_uvector<char> buffer(buffer_size, stream); | ||
|
|
||
| RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsecsr2dense(handle, | ||
| nrows, | ||
| ncols, | ||
| nnz, | ||
| out_mat, | ||
| csr_data, | ||
| csr_indptr, | ||
| csr_indices, | ||
| out, | ||
| lda, | ||
| buffer.data(), | ||
| stream)); | ||
|
|
||
| RAFT_CUSPARSE_TRY_NO_THROW(cusparseDestroyMatDescr(out_mat)); | ||
|
|
||
| } else { | ||
| int blockdim = block_dim(ncols); | ||
| RAFT_CUDA_TRY(cudaMemsetAsync(out, 0, nrows * ncols * sizeof(value_t), stream)); | ||
| raft::launch_kernel(stream, | ||
| nrows, | ||
| blockdim, | ||
| csr_to_dense_warp_per_row_kernel, | ||
| ncols, | ||
| csr_data, | ||
| csr_indptr, | ||
| csr_indices, | ||
| out); | ||
| } | ||
| auto stream = raft::resource::get_cuda_stream(handle); | ||
| auto cusparse_handle = raft::resource::get_cusparse_handle(handle); | ||
| auto sparse_descriptor = raft::sparse::linalg::detail::create_descriptor(sparse); | ||
| auto dense_descriptor = raft::sparse::linalg::detail::create_descriptor(dense); | ||
|
|
||
| RAFT_CUSPARSE_TRY(cusparseSetStream(cusparse_handle, stream)); | ||
| std::size_t buffer_size; | ||
| RAFT_CUSPARSE_TRY(cusparseSparseToDense_bufferSize(cusparse_handle, | ||
| sparse_descriptor, | ||
| dense_descriptor, | ||
| CUSPARSE_SPARSETODENSE_ALG_DEFAULT, | ||
| &buffer_size)); | ||
| auto buffer = raft::make_device_vector<char, std::size_t>(handle, buffer_size); | ||
| RAFT_CUSPARSE_TRY(cusparseSparseToDense(cusparse_handle, | ||
| sparse_descriptor, | ||
| dense_descriptor, | ||
| CUSPARSE_SPARSETODENSE_ALG_DEFAULT, | ||
| buffer.data_handle())); | ||
|
|
||
| RAFT_CUSPARSE_TRY_NO_THROW(cusparseDestroySpMat(sparse_descriptor)); | ||
| RAFT_CUSPARSE_TRY_NO_THROW(cusparseDestroyDnMat(dense_descriptor)); | ||
|
Comment on lines
+36
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Destroy the cuSPARSE descriptors on all paths.
Wrap each descriptor in an RAII holder so cleanup runs during stack unwinding. 🛡️ Proposed exception-safe cleanup+ auto sparse_guard = std::unique_ptr<std::remove_pointer_t<cusparseSpMatDescr_t>,
+ decltype(&cusparseDestroySpMat)>{sparse_descriptor,
+ &cusparseDestroySpMat};
+ auto dense_guard = std::unique_ptr<std::remove_pointer_t<cusparseDnMatDescr_t>,
+ decltype(&cusparseDestroyDnMat)>{dense_descriptor,
+ &cusparseDestroyDnMat};
+
RAFT_CUSPARSE_TRY(cusparseSetStream(cusparse_handle, stream));
@@
buffer.data_handle()));
-
- RAFT_CUSPARSE_TRY_NO_THROW(cusparseDestroySpMat(sparse_descriptor));
- RAFT_CUSPARSE_TRY_NO_THROW(cusparseDestroyDnMat(dense_descriptor));
}Add 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| } | ||
|
|
||
| }; // namespace detail | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Validate the dense view extents against the layout and add null checks.
The dimension check is good. Two gaps remain. First,
structure.get_n_rows()returns an unsigned index type in RAFT; thestatic_cast<IndexType>conversion can wrap whenIndexTypeis a narrower or signed type, and the comparison then passes for mismatched matrices. Second, the function does not reject a nulldense.data_handle()or a null sparse value pointer, which cuSPARSE reports only as a generic invalid-value error.Compare in a common wide type and add pointer validation.
🛡️ Proposed validation hardening
🤖 Prompt for AI Agents
Source: Coding guidelines