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
59unsafe impl Send for MatrixDescriptor {}
63unsafe impl Sync for MatrixDescriptor {}
64unsafe impl Send for SparseMatrixDescriptor<'_> {}
65unsafe impl Send for DenseMatrixDescriptor<'_> {}
66
67impl MatrixDescriptor {
68 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 pub fn matrix_type(&self) -> MatrixType {
91 unsafe { sys::cusparseGetMatType(self.as_raw()).into() }
92 }
93
94 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 pub fn fill_mode(&self) -> FillMode {
108 unsafe { sys::cusparseGetMatFillMode(self.as_raw()).into() }
109 }
110
111 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 pub fn diagonal_type(&self) -> DiagonalType {
125 unsafe { sys::cusparseGetMatDiagType(self.as_raw()).into() }
126 }
127
128 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 pub fn index_base(&self) -> IndexBase {
145 unsafe { sys::cusparseGetMatIndexBase(self.as_raw()).into() }
146 }
147
148 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
973pub 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
1064pub 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
1232pub 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
1289pub 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}