Skip to main content

singe_cusparse/
matrix.rs

1use std::{
2    marker::PhantomData,
3    mem::{ManuallyDrop, MaybeUninit, size_of},
4    ptr,
5    sync::Arc,
6};
7
8use singe_cuda::{
9    context::Context as CudaContext, data_type::DataTypeLike, memory::DeviceMemory,
10    types::DevicePtr,
11};
12
13use crate::{
14    context::Context,
15    error::{Error, Result},
16    sys, try_ffi,
17    types::{
18        CsrToCscAlgorithm, DenseToSparseAlgorithm, DiagonalType, FillMode, Format, IndexBase,
19        IndexTypeLike, MatrixType, Order, SparseMatrixAttribute, SparseToDenseAlgorithm,
20    },
21    utility::{to_i32, to_i64, to_usize},
22};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub struct SparseMatrixShape {
26    pub rows: usize,
27    pub cols: usize,
28    pub nonzero_count: usize,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct DenseMatrixInfo {
33    pub rows: usize,
34    pub cols: usize,
35    pub leading_dimension: usize,
36    pub values: DevicePtr,
37    pub order: Order,
38}
39
40#[derive(Debug)]
41pub struct MatrixDescriptor {
42    handle: sys::cusparseMatDescr_t,
43}
44
45#[derive(Debug)]
46pub struct SparseMatrixDescriptor<'a> {
47    handle: sys::cusparseSpMatDescr_t,
48    cuda_ctx: Arc<CudaContext>,
49    _buffers: PhantomData<&'a mut ()>,
50}
51
52#[derive(Debug)]
53pub struct DenseMatrixDescriptor<'a> {
54    handle: sys::cusparseDnMatDescr_t,
55    cuda_ctx: Arc<CudaContext>,
56    _buffers: PhantomData<&'a mut ()>,
57}
58
59// Legacy matrix descriptors contain descriptor attributes only, so immutable
60// sharing follows the cuSPARSE descriptor contract. Data-bearing descriptors
61// can rebind borrowed pointers and therefore require exclusive access.
62unsafe impl Send for MatrixDescriptor {}
63unsafe impl Sync for MatrixDescriptor {}
64unsafe impl Send for SparseMatrixDescriptor<'_> {}
65unsafe impl Send for DenseMatrixDescriptor<'_> {}
66
67impl MatrixDescriptor {
68    /// Initializes a matrix descriptor.
69    /// It sets [`MatrixType`] and [`IndexBase`] to [`MatrixType::General`] and
70    /// [`IndexBase::Zero`], respectively, while leaving other fields uninitialized.
71    ///
72    /// # Errors
73    ///
74    /// Returns an error if cuSPARSE cannot create the descriptor or if it returns
75    /// a null handle.
76    pub fn create() -> Result<Self> {
77        let mut handle = ptr::null_mut();
78        unsafe {
79            try_ffi!(sys::cusparseCreateMatDescr(&raw mut handle))?;
80        }
81
82        if handle.is_null() {
83            return Err(Error::NullHandle);
84        }
85
86        Ok(Self { handle })
87    }
88
89    /// Returns the [`MatrixType`] field of this matrix descriptor.
90    pub fn matrix_type(&self) -> MatrixType {
91        unsafe { sys::cusparseGetMatType(self.as_raw()).into() }
92    }
93
94    /// Sets the [`MatrixType`] field of this matrix descriptor.
95    ///
96    /// # Errors
97    ///
98    /// Returns an error if cuSPARSE rejects `matrix_type`.
99    pub fn set_matrix_type(&mut self, matrix_type: MatrixType) -> Result<()> {
100        unsafe {
101            try_ffi!(sys::cusparseSetMatType(self.as_raw(), matrix_type.into()))?;
102        }
103        Ok(())
104    }
105
106    /// Returns the [`FillMode`] field of this matrix descriptor.
107    pub fn fill_mode(&self) -> FillMode {
108        unsafe { sys::cusparseGetMatFillMode(self.as_raw()).into() }
109    }
110
111    /// Sets the [`FillMode`] field of this matrix descriptor.
112    ///
113    /// # Errors
114    ///
115    /// Returns an error if cuSPARSE rejects `fill_mode`.
116    pub fn set_fill_mode(&mut self, fill_mode: FillMode) -> Result<()> {
117        unsafe {
118            try_ffi!(sys::cusparseSetMatFillMode(self.as_raw(), fill_mode.into()))?;
119        }
120        Ok(())
121    }
122
123    /// Returns the [`DiagonalType`] field of this matrix descriptor.
124    pub fn diagonal_type(&self) -> DiagonalType {
125        unsafe { sys::cusparseGetMatDiagType(self.as_raw()).into() }
126    }
127
128    /// Sets the [`DiagonalType`] field of this matrix descriptor.
129    ///
130    /// # Errors
131    ///
132    /// Returns an error if cuSPARSE rejects `diagonal_type`.
133    pub fn set_diagonal_type(&mut self, diagonal_type: DiagonalType) -> Result<()> {
134        unsafe {
135            try_ffi!(sys::cusparseSetMatDiagType(
136                self.as_raw(),
137                diagonal_type.into(),
138            ))?;
139        }
140        Ok(())
141    }
142
143    /// Returns the [`IndexBase`] field of this matrix descriptor.
144    pub fn index_base(&self) -> IndexBase {
145        unsafe { sys::cusparseGetMatIndexBase(self.as_raw()).into() }
146    }
147
148    /// Sets the [`IndexBase`] field of this matrix descriptor.
149    ///
150    /// # Errors
151    ///
152    /// Returns an error if cuSPARSE rejects `index_base`.
153    pub fn set_index_base(&mut self, index_base: IndexBase) -> Result<()> {
154        unsafe {
155            try_ffi!(sys::cusparseSetMatIndexBase(
156                self.as_raw(),
157                index_base.into(),
158            ))?;
159        }
160        Ok(())
161    }
162
163    pub fn as_raw(&self) -> sys::cusparseMatDescr_t {
164        self.handle
165    }
166
167    /// Wraps an existing cuSPARSE legacy matrix descriptor and takes ownership
168    /// of it.
169    ///
170    /// # Safety
171    ///
172    /// `handle` must be a valid `cusparseMatDescr_t`. Ownership of `handle` is
173    /// transferred to the returned descriptor, and the handle must not be
174    /// destroyed elsewhere after calling this function.
175    pub unsafe fn from_raw(handle: sys::cusparseMatDescr_t) -> Result<Self> {
176        if handle.is_null() {
177            return Err(Error::NullHandle);
178        }
179
180        Ok(Self { handle })
181    }
182
183    /// Consumes the descriptor and returns the raw cuSPARSE handle without
184    /// destroying it.
185    ///
186    /// The caller becomes responsible for eventually destroying the returned
187    /// handle with `cusparseDestroyMatDescr`.
188    pub fn into_raw(self) -> sys::cusparseMatDescr_t {
189        let descriptor = ManuallyDrop::new(self);
190        descriptor.handle
191    }
192}
193
194impl Drop for MatrixDescriptor {
195    fn drop(&mut self) {
196        unsafe {
197            if let Err(err) = try_ffi!(sys::cusparseDestroyMatDescr(self.handle)) {
198                #[cfg(debug_assertions)]
199                eprintln!("failed to destroy cusparse matrix descriptor: {err}");
200            }
201        }
202    }
203}
204
205impl<'a> SparseMatrixDescriptor<'a> {
206    /// Initializes a sparse matrix descriptor in the CSR format.
207    ///
208    /// The descriptor borrows the row-offset, column-index, and value buffers by
209    /// pointer; those buffers must remain valid while the descriptor uses them.
210    pub fn create_csr<Offsets: IndexTypeLike, Columns: IndexTypeLike, T: DataTypeLike>(
211        ctx: &Context,
212        rows: usize,
213        cols: usize,
214        nonzero_count: usize,
215        row_offsets: &'a mut DeviceMemory<Offsets>,
216        column_indices: &'a mut DeviceMemory<Columns>,
217        values: &'a mut DeviceMemory<T>,
218        index_base: IndexBase,
219    ) -> Result<Self> {
220        ctx.bind()?;
221        let mut raw = ptr::null_mut();
222        unsafe {
223            try_ffi!(sys::cusparseCreateCsr(
224                &raw mut raw,
225                to_i64(rows, "rows")?,
226                to_i64(cols, "cols")?,
227                to_i64(nonzero_count, "nonzero_count")?,
228                row_offsets.as_mut_ptr().cast(),
229                column_indices.as_mut_ptr().cast(),
230                values.as_mut_ptr().cast(),
231                Offsets::index_type().into(),
232                Columns::index_type().into(),
233                index_base.into(),
234                T::data_type().into(),
235            ))?;
236        }
237        ensure_spmat(raw, ctx)
238    }
239
240    /// Initializes a sparse matrix descriptor in the CSC format.
241    ///
242    /// The descriptor borrows the column-offset, row-index, and value buffers by
243    /// pointer; those buffers must remain valid while the descriptor uses them.
244    pub fn create_csc<Offsets: IndexTypeLike, Rows: IndexTypeLike, T: DataTypeLike>(
245        ctx: &Context,
246        rows: usize,
247        cols: usize,
248        nonzero_count: usize,
249        column_offsets: &'a mut DeviceMemory<Offsets>,
250        row_indices: &'a mut DeviceMemory<Rows>,
251        values: &'a mut DeviceMemory<T>,
252        index_base: IndexBase,
253    ) -> Result<Self> {
254        ctx.bind()?;
255        let mut raw = ptr::null_mut();
256        unsafe {
257            try_ffi!(sys::cusparseCreateCsc(
258                &raw mut raw,
259                to_i64(rows, "rows")?,
260                to_i64(cols, "cols")?,
261                to_i64(nonzero_count, "nonzero_count")?,
262                column_offsets.as_mut_ptr().cast(),
263                row_indices.as_mut_ptr().cast(),
264                values.as_mut_ptr().cast(),
265                Offsets::index_type().into(),
266                Rows::index_type().into(),
267                index_base.into(),
268                T::data_type().into(),
269            ))?;
270        }
271        ensure_spmat(raw, ctx)
272    }
273
274    /// Initializes a sparse matrix descriptor in the COO format (Structure of Arrays layout).
275    ///
276    /// The descriptor borrows the row-index, column-index, and value buffers by
277    /// pointer; those buffers must remain valid while the descriptor uses them.
278    pub fn create_coo<I: IndexTypeLike, T: DataTypeLike>(
279        ctx: &Context,
280        rows: usize,
281        cols: usize,
282        nonzero_count: usize,
283        row_indices: &'a mut DeviceMemory<I>,
284        column_indices: &'a mut DeviceMemory<I>,
285        values: &'a mut DeviceMemory<T>,
286        index_base: IndexBase,
287    ) -> Result<Self> {
288        ctx.bind()?;
289        let mut raw = ptr::null_mut();
290        unsafe {
291            try_ffi!(sys::cusparseCreateCoo(
292                &raw mut raw,
293                to_i64(rows, "rows")?,
294                to_i64(cols, "cols")?,
295                to_i64(nonzero_count, "nonzero_count")?,
296                row_indices.as_mut_ptr().cast(),
297                column_indices.as_mut_ptr().cast(),
298                values.as_mut_ptr().cast(),
299                I::index_type().into(),
300                index_base.into(),
301                T::data_type().into(),
302            ))?;
303        }
304        ensure_spmat(raw, ctx)
305    }
306
307    /// Initializes a sparse matrix descriptor for the Block Compressed Row (BSR) format.
308    ///
309    /// The descriptor borrows the block row-offset, column-index, and value
310    /// buffers by pointer; those buffers must remain valid while the descriptor uses them.
311    pub fn create_bsr<Offsets: IndexTypeLike, Columns: IndexTypeLike, T: DataTypeLike>(
312        ctx: &Context,
313        block_rows: usize,
314        block_cols: usize,
315        block_nnz: usize,
316        row_block_size: usize,
317        col_block_size: usize,
318        row_offsets: &'a mut DeviceMemory<Offsets>,
319        column_indices: &'a mut DeviceMemory<Columns>,
320        values: &'a mut DeviceMemory<T>,
321        index_base: IndexBase,
322        order: Order,
323    ) -> Result<Self> {
324        ctx.bind()?;
325        let mut raw = ptr::null_mut();
326        unsafe {
327            try_ffi!(sys::cusparseCreateBsr(
328                &raw mut raw,
329                to_i64(block_rows, "block_rows")?,
330                to_i64(block_cols, "block_cols")?,
331                to_i64(block_nnz, "block_nnz")?,
332                to_i64(row_block_size, "row_block_size")?,
333                to_i64(col_block_size, "col_block_size")?,
334                row_offsets.as_mut_ptr().cast(),
335                column_indices.as_mut_ptr().cast(),
336                values.as_mut_ptr().cast(),
337                Offsets::index_type().into(),
338                Columns::index_type().into(),
339                index_base.into(),
340                T::data_type().into(),
341                order.into(),
342            ))?;
343        }
344        ensure_spmat(raw, ctx)
345    }
346
347    /// Initializes a sparse matrix descriptor for the Blocked-Ellpack (ELL) format.
348    ///
349    /// Blocked-ELL column indices are in the range `0..cols / ell_block_size`.
350    /// The array can contain `-1` values for indicating empty blocks.
351    /// The descriptor borrows the column-index and value buffers by pointer;
352    /// those buffers must remain valid while the descriptor uses them.
353    pub fn create_blocked_ell<I: IndexTypeLike, T: DataTypeLike>(
354        ctx: &Context,
355        rows: usize,
356        cols: usize,
357        block_size: usize,
358        ell_cols: usize,
359        column_indices: &'a mut DeviceMemory<I>,
360        values: &'a mut DeviceMemory<T>,
361        index_base: IndexBase,
362    ) -> Result<Self> {
363        ctx.bind()?;
364        let mut raw = ptr::null_mut();
365        unsafe {
366            try_ffi!(sys::cusparseCreateBlockedEll(
367                &raw mut raw,
368                to_i64(rows, "rows")?,
369                to_i64(cols, "cols")?,
370                to_i64(block_size, "block_size")?,
371                to_i64(ell_cols, "ell_cols")?,
372                column_indices.as_mut_ptr().cast(),
373                values.as_mut_ptr().cast(),
374                I::index_type().into(),
375                index_base.into(),
376                T::data_type().into(),
377            ))?;
378        }
379        ensure_spmat(raw, ctx)
380    }
381
382    /// Initializes a sparse matrix descriptor for the Sliced Ellpack (SELL) format.
383    ///
384    /// The descriptor borrows the slice-offset, column-index, and value buffers
385    /// by pointer; those buffers must remain valid while the descriptor uses them.
386    pub fn create_sliced_ell<
387        SliceOffsets: IndexTypeLike,
388        Columns: IndexTypeLike,
389        T: DataTypeLike,
390    >(
391        ctx: &Context,
392        rows: usize,
393        cols: usize,
394        nonzero_count: usize,
395        values_size: usize,
396        slice_size: usize,
397        slice_offsets: &'a mut DeviceMemory<SliceOffsets>,
398        column_indices: &'a mut DeviceMemory<Columns>,
399        values: &'a mut DeviceMemory<T>,
400        index_base: IndexBase,
401    ) -> Result<Self> {
402        ctx.bind()?;
403        let mut raw = ptr::null_mut();
404        unsafe {
405            try_ffi!(sys::cusparseCreateSlicedEll(
406                &raw mut raw,
407                to_i64(rows, "rows")?,
408                to_i64(cols, "cols")?,
409                to_i64(nonzero_count, "nonzero_count")?,
410                to_i64(values_size, "values_size")?,
411                to_i64(slice_size, "slice_size")?,
412                slice_offsets.as_mut_ptr().cast(),
413                column_indices.as_mut_ptr().cast(),
414                values.as_mut_ptr().cast(),
415                SliceOffsets::index_type().into(),
416                Columns::index_type().into(),
417                index_base.into(),
418                T::data_type().into(),
419            ))?;
420        }
421        ensure_spmat(raw, ctx)
422    }
423
424    /// Returns the format of this sparse matrix descriptor.
425    ///
426    /// # Errors
427    ///
428    /// Returns an error if cuSPARSE cannot report the sparse matrix format.
429    pub fn format(&self) -> Result<Format> {
430        let mut format = sys::cusparseFormat_t::CUSPARSE_FORMAT_CSR;
431        unsafe {
432            try_ffi!(sys::cusparseSpMatGetFormat(
433                self.as_raw_const(),
434                &raw mut format,
435            ))?;
436        }
437        Ok(format.into())
438    }
439
440    /// Returns the index base of this sparse matrix descriptor.
441    ///
442    /// # Errors
443    ///
444    /// Returns an error if cuSPARSE cannot report the sparse matrix index base.
445    pub fn index_base(&self) -> Result<IndexBase> {
446        let mut index_base = sys::cusparseIndexBase_t::CUSPARSE_INDEX_BASE_ZERO;
447        unsafe {
448            try_ffi!(sys::cusparseSpMatGetIndexBase(
449                self.as_raw_const(),
450                &raw mut index_base,
451            ))?;
452        }
453        Ok(index_base.into())
454    }
455
456    /// Returns the values pointer of this sparse matrix descriptor.
457    ///
458    /// # Errors
459    ///
460    /// Returns an error if cuSPARSE cannot report the values pointer.
461    pub fn values(&self) -> Result<DevicePtr> {
462        let mut values = ptr::null_mut();
463        unsafe {
464            try_ffi!(sys::cusparseSpMatGetValues(self.as_raw(), &raw mut values))?;
465        }
466        Ok(unsafe { DevicePtr::from_raw(values.cast()) })
467    }
468
469    /// Sets the values pointer of this sparse matrix descriptor.
470    ///
471    /// The pointed-to device buffer must remain valid while the descriptor uses it.
472    ///
473    /// # Errors
474    ///
475    /// Returns an error if cuSPARSE rejects the values pointer.
476    pub fn set_values<T: DataTypeLike>(&mut self, values: &'a mut DeviceMemory<T>) -> Result<()> {
477        unsafe {
478            try_ffi!(sys::cusparseSpMatSetValues(
479                self.as_raw(),
480                values.as_mut_ptr().cast(),
481            ))?;
482        }
483        Ok(())
484    }
485
486    /// Returns the shape of this sparse matrix descriptor.
487    ///
488    /// # Errors
489    ///
490    /// Returns an error if cuSPARSE cannot report the sparse matrix shape or if
491    /// the reported values cannot be represented as `usize`.
492    pub fn shape(&self) -> Result<SparseMatrixShape> {
493        let mut rows = 0_i64;
494        let mut cols = 0_i64;
495        let mut nnz = 0_i64;
496        unsafe {
497            try_ffi!(sys::cusparseSpMatGetSize(
498                self.as_raw_const(),
499                &raw mut rows,
500                &raw mut cols,
501                &raw mut nnz,
502            ))?;
503        }
504        Ok(SparseMatrixShape {
505            rows: to_usize(rows, "rows")?,
506            cols: to_usize(cols, "cols")?,
507            nonzero_count: to_usize(nnz, "nonzero_count")?,
508        })
509    }
510
511    /// Returns the batch count of this sparse matrix descriptor.
512    ///
513    /// # Errors
514    ///
515    /// Returns an error if cuSPARSE cannot report the strided batch settings.
516    pub fn strided_batch_count(&self) -> Result<i32> {
517        let mut batch_count = 0;
518        unsafe {
519            try_ffi!(sys::cusparseSpMatGetStridedBatch(
520                self.as_raw_const(),
521                &raw mut batch_count,
522            ))?;
523        }
524        Ok(batch_count)
525    }
526
527    /// Sets the batch count and batch stride fields of this COO sparse matrix descriptor.
528    ///
529    /// # Errors
530    ///
531    /// Returns an error if either argument cannot be represented by cuSPARSE or
532    /// if cuSPARSE rejects the strided batch settings.
533    pub fn set_coo_strided_batch(&mut self, batch_count: usize, batch_stride: usize) -> Result<()> {
534        unsafe {
535            try_ffi!(sys::cusparseCooSetStridedBatch(
536                self.as_raw(),
537                to_i32(batch_count, "batch_count")?,
538                to_i64(batch_stride, "batch_stride")?,
539            ))?;
540        }
541        Ok(())
542    }
543
544    /// Sets the batch count and batch stride fields of this CSR sparse matrix descriptor.
545    pub fn set_csr_strided_batch(
546        &mut self,
547        batch_count: usize,
548        offsets_batch_stride: usize,
549        columns_values_batch_stride: usize,
550    ) -> Result<()> {
551        unsafe {
552            try_ffi!(sys::cusparseCsrSetStridedBatch(
553                self.as_raw(),
554                to_i32(batch_count, "batch_count")?,
555                to_i64(offsets_batch_stride, "offsets_batch_stride")?,
556                to_i64(columns_values_batch_stride, "columns_values_batch_stride")?,
557            ))?;
558        }
559        Ok(())
560    }
561
562    /// Sets the batch count and batch stride fields of this BSR sparse matrix descriptor.
563    pub fn set_bsr_strided_batch(
564        &mut self,
565        batch_count: usize,
566        offsets_batch_stride: usize,
567        columns_batch_stride: usize,
568        values_batch_stride: usize,
569    ) -> Result<()> {
570        unsafe {
571            try_ffi!(sys::cusparseBsrSetStridedBatch(
572                self.as_raw(),
573                to_i32(batch_count, "batch_count")?,
574                to_i64(offsets_batch_stride, "offsets_batch_stride")?,
575                to_i64(columns_batch_stride, "columns_batch_stride")?,
576                to_i64(values_batch_stride, "values_batch_stride")?,
577            ))?;
578        }
579        Ok(())
580    }
581
582    pub fn fill_mode(&self) -> Result<FillMode> {
583        let raw: sys::cusparseFillMode_t = self.raw_attribute(SparseMatrixAttribute::FillMode)?;
584        Ok(raw.into())
585    }
586
587    pub fn set_fill_mode(&mut self, fill_mode: FillMode) -> Result<()> {
588        self.set_attribute_raw::<sys::cusparseFillMode_t>(
589            SparseMatrixAttribute::FillMode,
590            fill_mode.into(),
591        )
592    }
593
594    pub fn diagonal_type(&self) -> Result<DiagonalType> {
595        let raw: sys::cusparseDiagType_t =
596            self.raw_attribute(SparseMatrixAttribute::DiagonalType)?;
597        Ok(raw.into())
598    }
599
600    pub fn set_diagonal_type(&mut self, diagonal_type: DiagonalType) -> Result<()> {
601        self.set_attribute_raw::<sys::cusparseDiagType_t>(
602            SparseMatrixAttribute::DiagonalType,
603            diagonal_type.into(),
604        )
605    }
606
607    /// Sets the pointers of this CSR sparse matrix descriptor.
608    ///
609    /// The pointed-to device buffers must remain valid while the descriptor uses them.
610    pub fn set_csr_pointers(
611        &mut self,
612        row_offsets: &'a mut DeviceMemory<impl IndexTypeLike>,
613        column_indices: &'a mut DeviceMemory<impl IndexTypeLike>,
614        values: &'a mut DeviceMemory<impl DataTypeLike>,
615    ) -> Result<()> {
616        unsafe {
617            try_ffi!(sys::cusparseCsrSetPointers(
618                self.as_raw(),
619                row_offsets.as_mut_ptr().cast(),
620                column_indices.as_mut_ptr().cast(),
621                values.as_mut_ptr().cast(),
622            ))?;
623        }
624        Ok(())
625    }
626
627    /// Sets the pointers of this CSC sparse matrix descriptor.
628    ///
629    /// The pointed-to device buffers must remain valid while the descriptor uses them.
630    pub fn set_csc_pointers(
631        &mut self,
632        column_offsets: &'a mut DeviceMemory<impl IndexTypeLike>,
633        row_indices: &'a mut DeviceMemory<impl IndexTypeLike>,
634        values: &'a mut DeviceMemory<impl DataTypeLike>,
635    ) -> Result<()> {
636        unsafe {
637            try_ffi!(sys::cusparseCscSetPointers(
638                self.as_raw(),
639                column_offsets.as_mut_ptr().cast(),
640                row_indices.as_mut_ptr().cast(),
641                values.as_mut_ptr().cast(),
642            ))?;
643        }
644        Ok(())
645    }
646
647    /// Sets the pointers of this COO sparse matrix descriptor.
648    ///
649    /// The pointed-to device buffers must remain valid while the descriptor uses them.
650    pub fn set_coo_pointers(
651        &mut self,
652        row_indices: &'a mut DeviceMemory<impl IndexTypeLike>,
653        column_indices: &'a mut DeviceMemory<impl IndexTypeLike>,
654        values: &'a mut DeviceMemory<impl DataTypeLike>,
655    ) -> Result<()> {
656        unsafe {
657            try_ffi!(sys::cusparseCooSetPointers(
658                self.as_raw(),
659                row_indices.as_mut_ptr().cast(),
660                column_indices.as_mut_ptr().cast(),
661                values.as_mut_ptr().cast(),
662            ))?;
663        }
664        Ok(())
665    }
666
667    pub fn as_raw(&self) -> sys::cusparseSpMatDescr_t {
668        self.handle
669    }
670
671    pub fn as_raw_const(&self) -> sys::cusparseConstSpMatDescr_t {
672        self.handle.cast_const()
673    }
674
675    pub fn cuda_context(&self) -> &Arc<CudaContext> {
676        &self.cuda_ctx
677    }
678
679    pub(crate) fn ensure_context(&self, ctx: &Context) -> Result<()> {
680        if self.cuda_ctx.as_ref() != ctx.cuda_context().as_ref() {
681            return Err(Error::PlanContextMismatch);
682        }
683        Ok(())
684    }
685
686    /// Wraps an existing cuSPARSE sparse-matrix descriptor and takes ownership
687    /// of it.
688    ///
689    /// # Safety
690    ///
691    /// `handle` must be a valid `cusparseSpMatDescr_t`. Any device buffers
692    /// referenced by the descriptor must remain valid for lifetime `'a`.
693    /// Ownership of `handle` is transferred to the returned descriptor, and the
694    /// handle must not be destroyed elsewhere after calling this function.
695    pub unsafe fn from_raw(handle: sys::cusparseSpMatDescr_t, ctx: &Context) -> Result<Self> {
696        if handle.is_null() {
697            return Err(Error::NullHandle);
698        }
699
700        Ok(Self {
701            handle,
702            cuda_ctx: Arc::clone(ctx.cuda_context()),
703            _buffers: PhantomData,
704        })
705    }
706
707    /// Consumes the descriptor and returns the raw cuSPARSE handle without
708    /// destroying it.
709    ///
710    /// The caller becomes responsible for eventually destroying the returned
711    /// handle with `cusparseDestroySpMat`.
712    pub fn into_raw(self) -> sys::cusparseSpMatDescr_t {
713        let descriptor = ManuallyDrop::new(self);
714        descriptor.handle
715    }
716
717    fn raw_attribute<T: Copy>(&self, attribute: SparseMatrixAttribute) -> Result<T> {
718        let mut value = MaybeUninit::<T>::uninit();
719        unsafe {
720            try_ffi!(sys::cusparseSpMatGetAttribute(
721                self.as_raw_const(),
722                attribute.into(),
723                value.as_mut_ptr().cast(),
724                u64::try_from(size_of::<T>()).map_err(|_| Error::OutOfRange {
725                    name: "attribute size".into(),
726                })?,
727            ))?;
728            Ok(value.assume_init())
729        }
730    }
731
732    fn set_attribute_raw<T>(&mut self, attribute: SparseMatrixAttribute, value: T) -> Result<()> {
733        let mut value = value;
734        unsafe {
735            try_ffi!(sys::cusparseSpMatSetAttribute(
736                self.as_raw(),
737                attribute.into(),
738                &raw mut value as *mut _,
739                u64::try_from(size_of::<T>()).map_err(|_| Error::OutOfRange {
740                    name: "attribute size".into(),
741                })?,
742            ))?;
743        }
744        Ok(())
745    }
746}
747
748impl Drop for SparseMatrixDescriptor<'_> {
749    fn drop(&mut self) {
750        unsafe {
751            if let Err(err) = try_ffi!(sys::cusparseDestroySpMat(self.as_raw_const())) {
752                #[cfg(debug_assertions)]
753                eprintln!("failed to destroy cusparse sparse matrix descriptor: {err}");
754            }
755        }
756    }
757}
758
759impl<'a> DenseMatrixDescriptor<'a> {
760    /// Initializes a dense matrix descriptor.
761    ///
762    /// The descriptor borrows the device value buffer by pointer; the buffer must
763    /// remain valid while the descriptor uses it.
764    pub fn create<T: DataTypeLike>(
765        ctx: &Context,
766        rows: usize,
767        cols: usize,
768        leading_dimension: usize,
769        values: &'a mut DeviceMemory<T>,
770        order: Order,
771    ) -> Result<Self> {
772        ctx.bind()?;
773        let mut handle = ptr::null_mut();
774        unsafe {
775            try_ffi!(sys::cusparseCreateDnMat(
776                &raw mut handle,
777                to_i64(rows, "rows")?,
778                to_i64(cols, "cols")?,
779                to_i64(leading_dimension, "leading_dimension")?,
780                values.as_mut_ptr().cast(),
781                T::data_type().into(),
782                order.into(),
783            ))?;
784        }
785
786        if handle.is_null() {
787            return Err(Error::NullHandle);
788        }
789
790        Ok(Self {
791            handle,
792            cuda_ctx: Arc::clone(ctx.cuda_context()),
793            _buffers: PhantomData,
794        })
795    }
796
797    /// Returns the values pointer of this dense matrix descriptor.
798    ///
799    /// # Errors
800    ///
801    /// Returns an error if cuSPARSE cannot report the values pointer.
802    pub fn values(&self) -> Result<DevicePtr> {
803        let mut values = ptr::null_mut();
804        unsafe {
805            try_ffi!(sys::cusparseDnMatGetValues(self.as_raw(), &raw mut values))?;
806        }
807        Ok(unsafe { DevicePtr::from_raw(values.cast()) })
808    }
809
810    /// Sets the values pointer of this dense matrix descriptor.
811    ///
812    /// The pointed-to device buffer must remain valid while the descriptor uses it.
813    ///
814    /// # Errors
815    ///
816    /// Returns an error if cuSPARSE rejects the values pointer.
817    pub fn set_values<T: DataTypeLike>(&mut self, values: &'a mut DeviceMemory<T>) -> Result<()> {
818        unsafe {
819            try_ffi!(sys::cusparseDnMatSetValues(
820                self.as_raw(),
821                values.as_mut_ptr().cast(),
822            ))?;
823        }
824        Ok(())
825    }
826
827    /// Returns the fields of this dense matrix descriptor.
828    ///
829    /// # Errors
830    ///
831    /// Returns an error if cuSPARSE cannot report the descriptor fields or if
832    /// reported dimensions cannot be represented as `usize`.
833    pub fn info(&self) -> Result<DenseMatrixInfo> {
834        let mut rows = 0_i64;
835        let mut cols = 0_i64;
836        let mut leading_dimension = 0_i64;
837        let mut values = ptr::null_mut();
838        let mut value_type = MaybeUninit::uninit();
839        let mut order = sys::cusparseOrder_t::CUSPARSE_ORDER_ROW;
840        unsafe {
841            try_ffi!(sys::cusparseDnMatGet(
842                self.as_raw(),
843                &raw mut rows,
844                &raw mut cols,
845                &raw mut leading_dimension,
846                &raw mut values,
847                value_type.as_mut_ptr(),
848                &raw mut order,
849            ))?;
850        }
851        Ok(DenseMatrixInfo {
852            rows: to_usize(rows, "rows")?,
853            cols: to_usize(cols, "cols")?,
854            leading_dimension: to_usize(leading_dimension, "leading_dimension")?,
855            values: unsafe { DevicePtr::from_raw(values.cast()) },
856            order: order.into(),
857        })
858    }
859
860    /// Sets the number of batches and the batch stride of this dense matrix descriptor.
861    ///
862    /// # Errors
863    ///
864    /// Returns an error if either argument cannot be represented by cuSPARSE or
865    /// if cuSPARSE rejects the strided batch settings.
866    pub fn set_strided_batch(&mut self, batch_count: usize, batch_stride: usize) -> Result<()> {
867        unsafe {
868            try_ffi!(sys::cusparseDnMatSetStridedBatch(
869                self.as_raw(),
870                to_i32(batch_count, "batch_count")?,
871                to_i64(batch_stride, "batch_stride")?,
872            ))?;
873        }
874        Ok(())
875    }
876
877    /// Returns the number of batches and the batch stride of this dense matrix descriptor.
878    ///
879    /// # Errors
880    ///
881    /// Returns an error if cuSPARSE cannot report the strided batch settings or
882    /// if the reported stride cannot be represented as `usize`.
883    pub fn strided_batch(&self) -> Result<(i32, usize)> {
884        let mut batch_count = 0;
885        let mut batch_stride = 0_i64;
886        unsafe {
887            try_ffi!(sys::cusparseDnMatGetStridedBatch(
888                self.as_raw_const(),
889                &raw mut batch_count,
890                &raw mut batch_stride,
891            ))?;
892        }
893        Ok((batch_count, to_usize(batch_stride, "batch_stride")?))
894    }
895
896    pub fn as_raw(&self) -> sys::cusparseDnMatDescr_t {
897        self.handle
898    }
899
900    pub fn as_raw_const(&self) -> sys::cusparseConstDnMatDescr_t {
901        self.handle.cast_const()
902    }
903
904    pub fn cuda_context(&self) -> &Arc<CudaContext> {
905        &self.cuda_ctx
906    }
907
908    pub(crate) fn ensure_context(&self, ctx: &Context) -> Result<()> {
909        if self.cuda_ctx.as_ref() != ctx.cuda_context().as_ref() {
910            return Err(Error::PlanContextMismatch);
911        }
912        Ok(())
913    }
914
915    /// Wraps an existing cuSPARSE dense-matrix descriptor and takes ownership
916    /// of it.
917    ///
918    /// # Safety
919    ///
920    /// `handle` must be a valid `cusparseDnMatDescr_t`. Any device buffer
921    /// referenced by the descriptor must remain valid for lifetime `'a`.
922    /// Ownership of `handle` is transferred to the returned descriptor, and the
923    /// handle must not be destroyed elsewhere after calling this function.
924    pub unsafe fn from_raw(handle: sys::cusparseDnMatDescr_t, ctx: &Context) -> Result<Self> {
925        if handle.is_null() {
926            return Err(Error::NullHandle);
927        }
928
929        Ok(Self {
930            handle,
931            cuda_ctx: Arc::clone(ctx.cuda_context()),
932            _buffers: PhantomData,
933        })
934    }
935
936    /// Consumes the descriptor and returns the raw cuSPARSE handle without
937    /// destroying it.
938    ///
939    /// The caller becomes responsible for eventually destroying the returned
940    /// handle with `cusparseDestroyDnMat`.
941    pub fn into_raw(self) -> sys::cusparseDnMatDescr_t {
942        let descriptor = ManuallyDrop::new(self);
943        descriptor.handle
944    }
945}
946
947impl Drop for DenseMatrixDescriptor<'_> {
948    fn drop(&mut self) {
949        unsafe {
950            if let Err(err) = try_ffi!(sys::cusparseDestroyDnMat(self.as_raw_const())) {
951                #[cfg(debug_assertions)]
952                eprintln!("failed to destroy cusparse dense matrix descriptor: {err}");
953            }
954        }
955    }
956}
957
958fn ensure_spmat<'a>(
959    handle: sys::cusparseSpMatDescr_t,
960    ctx: &Context,
961) -> Result<SparseMatrixDescriptor<'a>> {
962    if handle.is_null() {
963        Err(Error::NullHandle)
964    } else {
965        Ok(SparseMatrixDescriptor {
966            handle,
967            cuda_ctx: Arc::clone(ctx.cuda_context()),
968            _buffers: PhantomData,
969        })
970    }
971}
972
973/// Converts a sparse matrix in CSR format into a sparse matrix in CSC format.
974/// The resulting matrix can also be seen as the transpose of the original sparse matrix.
975/// This can also convert a matrix in CSC format into a matrix in CSR format.
976///
977/// This operation requires extra storage proportional to `nonzero_count`.
978/// It always produces the same matrix values in the destination format.
979///
980/// The operation executes asynchronously with respect to the host and may return before the result is ready.
981///
982/// [`csr_to_csc_buffer_size`] returns the size of the workspace needed by [`csr_to_csc`].
983/// Pass a buffer of this size to [`csr_to_csc`].
984///
985/// If `nonzero_count` is `0`, then the CSR column indices, CSR values, CSC
986/// values, and CSC row indices may be zero-length buffers.
987/// In this case, all CSC column pointers are initialized to the configured index base.
988///
989/// If `m` or `n` is `0`, the pointers are not checked and the operation succeeds with `Ok`.
990///
991/// [`csr_to_csc`] supports the following data types:
992///
993/// | `X`/`Y` | Notes |
994/// | --- | --- |
995/// | [`DataType::I8`](singe_cuda::data_type::DataType::I8) |  |
996/// | [`DataType::F16`](singe_cuda::data_type::DataType::F16) |  |
997/// | [`DataType::Bf16`](singe_cuda::data_type::DataType::Bf16) |  |
998/// | [`DataType::F32`](singe_cuda::data_type::DataType::F32) |  |
999/// | [`DataType::F64`](singe_cuda::data_type::DataType::F64) |  |
1000/// | [`DataType::ComplexF16`](singe_cuda::data_type::DataType::ComplexF16) | Deprecated. |
1001/// | [`DataType::ComplexBf16`](singe_cuda::data_type::DataType::ComplexBf16) | Deprecated. |
1002/// | [`DataType::ComplexF32`](singe_cuda::data_type::DataType::ComplexF32) |  |
1003/// | [`DataType::ComplexF64`](singe_cuda::data_type::DataType::ComplexF64) |  |
1004///
1005/// [`csr_to_csc`] supports the following algorithms ([`CsrToCscAlgorithm`]):
1006///
1007/// | Algorithm | Notes |
1008/// | --- | --- |
1009/// | [`CsrToCscAlgorithm::Default`] | Default algorithm. |
1010///
1011/// | Action | Notes |
1012/// | --- | --- |
1013/// | [`Action::Symbolic`](crate::types::Action::Symbolic) | Compute the structure of the CSC output matrix (offset, row indices). |
1014/// | [`Action::Numeric`](crate::types::Action::Numeric) | Compute the structure of the CSC output matrix and copy the values. |
1015///
1016/// [`csr_to_csc`] has the following properties:
1017///
1018/// * Requires no extra storage.
1019/// * Supports asynchronous execution.
1020///
1021/// [`csr_to_csc`] supports the following optimizations:
1022///
1023/// * CUDA graph capture.
1024/// * Hardware Memory Compression.
1025pub fn csr_to_csc<T: DataTypeLike>(
1026    ctx: &Context,
1027    row_count: usize,
1028    col_count: usize,
1029    nonzero_count: usize,
1030    csr_values: &DeviceMemory<T>,
1031    csr_row_offsets: &DeviceMemory<i32>,
1032    csr_column_indices: &DeviceMemory<i32>,
1033    csc_values: &mut DeviceMemory<T>,
1034    csc_column_offsets: &mut DeviceMemory<i32>,
1035    csc_row_indices: &mut DeviceMemory<i32>,
1036    action: crate::types::Action,
1037    index_base: IndexBase,
1038    algorithm: CsrToCscAlgorithm,
1039    external_buffer: &mut DeviceMemory<u8>,
1040) -> Result<()> {
1041    ctx.bind()?;
1042    unsafe {
1043        try_ffi!(sys::cusparseCsr2cscEx2(
1044            ctx.as_raw(),
1045            to_i32(row_count, "row_count")?,
1046            to_i32(col_count, "col_count")?,
1047            to_i32(nonzero_count, "nonzero_count")?,
1048            csr_values.as_ptr().cast(),
1049            csr_row_offsets.as_ptr() as _,
1050            csr_column_indices.as_ptr() as _,
1051            csc_values.as_mut_ptr().cast(),
1052            csc_column_offsets.as_mut_ptr(),
1053            csc_row_indices.as_mut_ptr(),
1054            T::data_type().into(),
1055            action.into(),
1056            index_base.into(),
1057            algorithm.into(),
1058            external_buffer.as_mut_ptr().cast(),
1059        ))?;
1060    }
1061    Ok(())
1062}
1063
1064/// Converts `matrix_a` in CSR, CSC, or COO format into its dense representation `matrix_b`.
1065/// Blocked-ELL is not currently supported.
1066///
1067/// [`sparse_to_dense_buffer_size`] returns the size of the workspace needed by [`sparse_to_dense`].
1068///
1069/// [`sparse_to_dense`] supports the following index type for representing `matrix_a`:
1070///
1071/// * 32-bit indices ([`IndexType::I32`](crate::types::IndexType::I32))
1072/// * 64-bit indices ([`IndexType::I64`](crate::types::IndexType::I64))
1073///
1074/// [`sparse_to_dense`] supports the following data types:
1075///
1076/// | `A`/`B` | Notes |
1077/// | --- | --- |
1078/// | [`DataType::I8`](singe_cuda::data_type::DataType::I8) |  |
1079/// | [`DataType::F16`](singe_cuda::data_type::DataType::F16) |  |
1080/// | [`DataType::Bf16`](singe_cuda::data_type::DataType::Bf16) |  |
1081/// | [`DataType::F32`](singe_cuda::data_type::DataType::F32) |  |
1082/// | [`DataType::F64`](singe_cuda::data_type::DataType::F64) |  |
1083/// | [`DataType::ComplexF16`](singe_cuda::data_type::DataType::ComplexF16) | Deprecated. |
1084/// | [`DataType::ComplexBf16`](singe_cuda::data_type::DataType::ComplexBf16) | Deprecated. |
1085/// | [`DataType::ComplexF32`](singe_cuda::data_type::DataType::ComplexF32) |  |
1086/// | [`DataType::ComplexF64`](singe_cuda::data_type::DataType::ComplexF64) |  |
1087///
1088/// [`sparse_to_dense`] supports the following algorithm:
1089///
1090/// | Algorithm | Notes |
1091/// | --- | --- |
1092/// | [`SparseToDenseAlgorithm::Default`] | Default algorithm. |
1093///
1094/// [`sparse_to_dense`] has the following properties:
1095///
1096/// * Requires no extra storage.
1097/// * Supports asynchronous execution.
1098/// * Provides deterministic (bitwise) results for each run.
1099/// * Allows the indices of `matrix_a` to be unsorted.
1100///
1101/// [`sparse_to_dense`] supports the following optimizations:
1102///
1103/// * CUDA graph capture.
1104/// * Hardware Memory Compression.
1105pub fn sparse_to_dense(
1106    ctx: &Context,
1107    matrix_a: &SparseMatrixDescriptor,
1108    matrix_b: &mut DenseMatrixDescriptor,
1109    algorithm: SparseToDenseAlgorithm,
1110    external_buffer: Option<DevicePtr>,
1111) -> Result<()> {
1112    matrix_a.ensure_context(ctx)?;
1113    matrix_b.ensure_context(ctx)?;
1114    ctx.bind()?;
1115    unsafe {
1116        try_ffi!(sys::cusparseSparseToDense(
1117            ctx.as_raw(),
1118            matrix_a.as_raw_const(),
1119            matrix_b.as_raw(),
1120            algorithm.into(),
1121            external_buffer.unwrap_or(DevicePtr::null()).as_ptr() as _,
1122        ))?;
1123    }
1124    Ok(())
1125}
1126
1127pub fn dense_to_sparse_buffer_size(
1128    ctx: &Context,
1129    matrix_a: &DenseMatrixDescriptor,
1130    matrix_b: &mut SparseMatrixDescriptor,
1131    algorithm: DenseToSparseAlgorithm,
1132) -> Result<usize> {
1133    matrix_a.ensure_context(ctx)?;
1134    matrix_b.ensure_context(ctx)?;
1135    ctx.bind()?;
1136    let mut size = 0;
1137    unsafe {
1138        try_ffi!(sys::cusparseDenseToSparse_bufferSize(
1139            ctx.as_raw(),
1140            matrix_a.as_raw_const(),
1141            matrix_b.as_raw(),
1142            algorithm.into(),
1143            &raw mut size,
1144        ))?;
1145    }
1146    to_usize(size, "dense-to-sparse buffer size")
1147}
1148
1149pub fn dense_to_sparse_analysis(
1150    ctx: &Context,
1151    matrix_a: &DenseMatrixDescriptor,
1152    matrix_b: &mut SparseMatrixDescriptor,
1153    algorithm: DenseToSparseAlgorithm,
1154    external_buffer: Option<DevicePtr>,
1155) -> Result<()> {
1156    matrix_a.ensure_context(ctx)?;
1157    matrix_b.ensure_context(ctx)?;
1158    ctx.bind()?;
1159    unsafe {
1160        try_ffi!(sys::cusparseDenseToSparse_analysis(
1161            ctx.as_raw(),
1162            matrix_a.as_raw_const(),
1163            matrix_b.as_raw(),
1164            algorithm.into(),
1165            external_buffer.unwrap_or(DevicePtr::null()).as_ptr() as _,
1166        ))?;
1167    }
1168    Ok(())
1169}
1170
1171pub fn dense_to_sparse_convert(
1172    ctx: &Context,
1173    matrix_a: &DenseMatrixDescriptor,
1174    matrix_b: &mut SparseMatrixDescriptor,
1175    algorithm: DenseToSparseAlgorithm,
1176    external_buffer: Option<DevicePtr>,
1177) -> Result<()> {
1178    matrix_a.ensure_context(ctx)?;
1179    matrix_b.ensure_context(ctx)?;
1180    ctx.bind()?;
1181    unsafe {
1182        try_ffi!(sys::cusparseDenseToSparse_convert(
1183            ctx.as_raw(),
1184            matrix_a.as_raw_const(),
1185            matrix_b.as_raw(),
1186            algorithm.into(),
1187            external_buffer.unwrap_or(DevicePtr::null()).as_ptr() as _,
1188        ))?;
1189    }
1190    Ok(())
1191}
1192
1193pub fn csr_to_csc_buffer_size<T: DataTypeLike>(
1194    ctx: &Context,
1195    row_count: usize,
1196    col_count: usize,
1197    nonzero_count: usize,
1198    csr_values: &DeviceMemory<T>,
1199    csr_row_offsets: &DeviceMemory<i32>,
1200    csr_column_indices: &DeviceMemory<i32>,
1201    csc_values: &mut DeviceMemory<T>,
1202    csc_column_offsets: &mut DeviceMemory<i32>,
1203    csc_row_indices: &mut DeviceMemory<i32>,
1204    action: crate::types::Action,
1205    index_base: IndexBase,
1206    algorithm: CsrToCscAlgorithm,
1207) -> Result<usize> {
1208    ctx.bind()?;
1209    let mut size = 0;
1210    unsafe {
1211        try_ffi!(sys::cusparseCsr2cscEx2_bufferSize(
1212            ctx.as_raw(),
1213            to_i32(row_count, "row_count")?,
1214            to_i32(col_count, "col_count")?,
1215            to_i32(nonzero_count, "nonzero_count")?,
1216            csr_values.as_ptr().cast(),
1217            csr_row_offsets.as_ptr() as _,
1218            csr_column_indices.as_ptr() as _,
1219            csc_values.as_mut_ptr().cast(),
1220            csc_column_offsets.as_mut_ptr(),
1221            csc_row_indices.as_mut_ptr(),
1222            T::data_type().into(),
1223            action.into(),
1224            index_base.into(),
1225            algorithm.into(),
1226            &raw mut size,
1227        ))?;
1228    }
1229    to_usize(size, "csr-to-csc buffer size")
1230}
1231
1232/// Converts compressed row pointers in CSR format into uncompressed row indices in COO format.
1233///
1234/// It can also be used to convert the array containing the compressed column indices (corresponding to CSC format) into an array of uncompressed column indices (corresponding to COO format).
1235///
1236/// * Requires no extra storage.
1237/// * Supports asynchronous execution.
1238/// * Supports CUDA graph capture.
1239pub fn csr_to_coo(
1240    ctx: &Context,
1241    csr_row_offsets: &DeviceMemory<i32>,
1242    nonzero_count: usize,
1243    row_count: usize,
1244    coo_row_indices: &mut DeviceMemory<i32>,
1245    index_base: IndexBase,
1246) -> Result<()> {
1247    ctx.bind()?;
1248    if csr_row_offsets.len() < row_count.saturating_add(1) || coo_row_indices.len() < nonzero_count
1249    {
1250        return Err(Error::OutOfRange {
1251            name: "csr/coo buffer length".into(),
1252        });
1253    }
1254    unsafe {
1255        try_ffi!(sys::cusparseXcsr2coo(
1256            ctx.as_raw(),
1257            csr_row_offsets.as_ptr() as _,
1258            to_i32(nonzero_count, "nonzero_count")?,
1259            to_i32(row_count, "row_count")?,
1260            coo_row_indices.as_mut_ptr(),
1261            index_base.into(),
1262        ))?;
1263    }
1264    Ok(())
1265}
1266
1267pub fn sparse_to_dense_buffer_size(
1268    ctx: &Context,
1269    matrix_a: &SparseMatrixDescriptor,
1270    matrix_b: &mut DenseMatrixDescriptor,
1271    algorithm: SparseToDenseAlgorithm,
1272) -> Result<usize> {
1273    matrix_a.ensure_context(ctx)?;
1274    matrix_b.ensure_context(ctx)?;
1275    ctx.bind()?;
1276    let mut size = 0;
1277    unsafe {
1278        try_ffi!(sys::cusparseSparseToDense_bufferSize(
1279            ctx.as_raw(),
1280            matrix_a.as_raw_const(),
1281            matrix_b.as_raw(),
1282            algorithm.into(),
1283            &raw mut size,
1284        ))?;
1285    }
1286    to_usize(size, "sparse-to-dense buffer size")
1287}
1288
1289/// Converts uncompressed row indices in COO format into compressed row pointers in CSR format.
1290///
1291/// It can also be used to convert the array containing the uncompressed column indices (corresponding to COO format) into an array of column pointers (corresponding to CSC format).
1292///
1293/// * Requires no extra storage.
1294/// * Supports asynchronous execution.
1295/// * Supports CUDA graph capture.
1296pub fn coo_to_csr(
1297    ctx: &Context,
1298    coo_row_indices: &DeviceMemory<i32>,
1299    row_count: usize,
1300    csr_row_offsets: &mut DeviceMemory<i32>,
1301    index_base: IndexBase,
1302) -> Result<()> {
1303    ctx.bind()?;
1304    let nonzero_count = coo_row_indices.len();
1305    if csr_row_offsets.len() < row_count.saturating_add(1) {
1306        return Err(Error::OutOfRange {
1307            name: "csr_row_offsets length".into(),
1308        });
1309    }
1310    unsafe {
1311        try_ffi!(sys::cusparseXcoo2csr(
1312            ctx.as_raw(),
1313            coo_row_indices.as_ptr() as _,
1314            to_i32(nonzero_count, "nonzero_count")?,
1315            to_i32(row_count, "row_count")?,
1316            csr_row_offsets.as_mut_ptr(),
1317            index_base.into(),
1318        ))?;
1319    }
1320    Ok(())
1321}