singe_cusparse/sddmm.rs
1use singe_cuda::{data_type::DataTypeLike, types::DevicePtr};
2
3use singe_cusparse_sys as sys;
4
5use crate::{
6 context::Context,
7 error::Result,
8 matrix::{DenseMatrixDescriptor, SparseMatrixDescriptor},
9 scalar::Scalar,
10 try_ffi,
11 types::{Operation, SddmmAlgorithm},
12 utility::to_usize,
13};
14
15pub fn sddmm_buffer_size<Compute: DataTypeLike>(
16 ctx: &Context,
17 op_a: Operation,
18 op_b: Operation,
19 alpha: Scalar<'_, Compute>,
20 matrix_a: &DenseMatrixDescriptor,
21 matrix_b: &DenseMatrixDescriptor,
22 beta: Scalar<'_, Compute>,
23 matrix_c: &mut SparseMatrixDescriptor,
24 algorithm: SddmmAlgorithm,
25) -> Result<usize> {
26 ctx.bind()?;
27 ctx.require_scalar_pointer_mode(alpha, beta)?;
28
29 let mut size = 0;
30 unsafe {
31 try_ffi!(sys::cusparseSDDMM_bufferSize(
32 ctx.as_raw(),
33 op_a.into(),
34 op_b.into(),
35 alpha.ptr().cast(),
36 matrix_a.as_raw_const(),
37 matrix_b.as_raw_const(),
38 beta.ptr().cast(),
39 matrix_c.as_raw(),
40 Compute::data_type().into(),
41 algorithm.into(),
42 &raw mut size,
43 ))?;
44 }
45 to_usize(size, "sddmm buffer size")
46}
47
48pub fn sddmm_preprocess<Compute: DataTypeLike>(
49 ctx: &Context,
50 op_a: Operation,
51 op_b: Operation,
52 alpha: Scalar<'_, Compute>,
53 matrix_a: &DenseMatrixDescriptor,
54 matrix_b: &DenseMatrixDescriptor,
55 beta: Scalar<'_, Compute>,
56 matrix_c: &mut SparseMatrixDescriptor,
57 algorithm: SddmmAlgorithm,
58 external_buffer: Option<DevicePtr>,
59) -> Result<()> {
60 ctx.bind()?;
61 ctx.require_scalar_pointer_mode(alpha, beta)?;
62
63 unsafe {
64 try_ffi!(sys::cusparseSDDMM_preprocess(
65 ctx.as_raw(),
66 op_a.into(),
67 op_b.into(),
68 alpha.ptr().cast(),
69 matrix_a.as_raw_const(),
70 matrix_b.as_raw_const(),
71 beta.ptr().cast(),
72 matrix_c.as_raw(),
73 Compute::data_type().into(),
74 algorithm.into(),
75 external_buffer.unwrap_or(DevicePtr::null()).as_ptr() as _,
76 ))?;
77 }
78 Ok(())
79}
80
81/// Multiplies `matrix_a` and `matrix_b`, then applies the sparsity pattern of `matrix_c`.
82/// Formally, it computes $C = \alpha (op(A) op(B)) \circ spy(C) + \beta C$,
83/// where:
84///
85/// * `op(A)` is a dense matrix of size $m \times k$.
86/// * `op(B)` is a dense matrix of size $k \times n$.
87/// * `C` is a sparse matrix of size $m \times n$.
88/// * `alpha` and `beta` are scalars.
89/// * $\circ$ denotes the Hadamard (entry-wise) matrix product, and `spy(C)` is the structural sparsity pattern of `C`, equal to `1` where `C` stores an entry and `0` elsewhere.
90///
91/// `op(A)` is selected by `op_a` and may be `A`, `A^T`, or `A^H`.
92/// `op(B)` is selected by `op_b` and may be `B`, `B^T`, or `B^H`.
93///
94/// [`sddmm_buffer_size`] returns the workspace size needed by [`sddmm`] or [`sddmm_preprocess`].
95///
96/// Calling [`sddmm_preprocess`] is optional.
97/// It may accelerate subsequent calls to [`sddmm`].
98/// Useful when [`sddmm`] is called multiple times with the same sparsity
99/// pattern (`matrix_c`).
100///
101/// Calling [`sddmm_preprocess`] with `buffer` makes that buffer active for `matrix_c` SDDMM calls.
102/// Subsequent calls to [`sddmm`] with `matrix_c` and the active buffer must use the same values for all parameters as the call to [`sddmm_preprocess`].
103/// The exceptions are: `alpha`, `beta`, `matrix_a`, `matrix_b`, and the values (but not indices) of `matrix_c` may be different.
104/// Importantly, the buffer contents must be unmodified since the call to [`sddmm_preprocess`].
105/// When [`sddmm`] is called with `matrix_c` and its active buffer, it may read acceleration data from the buffer.
106///
107/// Calling [`sddmm_preprocess`] again with `matrix_c` and a new buffer makes the
108/// new buffer active and makes the previously active buffer inactive.
109/// For [`sddmm`], there can only be one active buffer per sparse matrix at a time.
110/// To get the effect of multiple active buffers for a single sparse matrix, create multiple matrix handles that all point to the same index and value buffers, and call [`sddmm_preprocess`] once per handle with different workspace buffers.
111///
112/// Calling [`sddmm`] with an inactive buffer is always permitted.
113/// However, there may be no acceleration from the preprocessing in that case.
114///
115/// For the purposes of thread safety, [`sddmm_preprocess`] is writing to `matrix_c` internal state.
116///
117/// Currently supported sparse matrix formats:
118///
119/// * [`Format::Csr`](crate::types::Format::Csr)
120/// * [`Format::Bsr`](crate::types::Format::Bsr)
121///
122/// [`sddmm`] supports the following index type for representing `matrix_c`:
123///
124/// * 32-bit indices ([`IndexType::I32`](crate::types::IndexType::I32))
125/// * 64-bit indices ([`IndexType::I64`](crate::types::IndexType::I64))
126///
127/// The data type combinations currently supported for [`sddmm`] are listed below:
128///
129/// Uniform-precision computation:
130///
131/// | `A`/`B`/`C`/`compute_type` |
132/// | --- |
133/// | [`DataType::F32`](singe_cuda::data_type::DataType::F32) |
134/// | [`DataType::F64`](singe_cuda::data_type::DataType::F64) |
135/// | [`DataType::ComplexF32`](singe_cuda::data_type::DataType::ComplexF32) |
136/// | [`DataType::ComplexF64`](singe_cuda::data_type::DataType::ComplexF64) |
137///
138/// Mixed-precision computation:
139///
140/// | `A`/`B` | `C` | `compute_type` |
141/// | --- | --- | --- |
142/// | [`DataType::F16`](singe_cuda::data_type::DataType::F16) | [`DataType::F32`](singe_cuda::data_type::DataType::F32) | [`DataType::F32`](singe_cuda::data_type::DataType::F32) |
143/// | [`DataType::F16`](singe_cuda::data_type::DataType::F16) | [`DataType::F16`](singe_cuda::data_type::DataType::F16) | |
144///
145/// [`sddmm`] for [`Format::Bsr`](crate::types::Format::Bsr) also supports the following mixed-precision computation:
146///
147/// | `A`/`B` | `C` | `compute_type` |
148/// | --- | --- | --- |
149/// | [`DataType::Bf16`](singe_cuda::data_type::DataType::Bf16) | [`DataType::F32`](singe_cuda::data_type::DataType::F32) | [`DataType::F32`](singe_cuda::data_type::DataType::F32) |
150/// | [`DataType::Bf16`](singe_cuda::data_type::DataType::Bf16) | [`DataType::Bf16`](singe_cuda::data_type::DataType::Bf16) | |
151///
152/// [`DataType::F16`](singe_cuda::data_type::DataType::F16) and [`DataType::Bf16`](singe_cuda::data_type::DataType::Bf16) always imply mixed-precision computation.
153///
154/// [`sddmm`] for [`Format::Bsr`](crate::types::Format::Bsr) supports block sizes of 2, 4, 8, 16, 32, 64 and 128.
155///
156/// [`sddmm`] supports the following algorithms:
157///
158/// | Algorithm | Notes |
159/// | --- | --- |
160/// | [`SddmmAlgorithm::Default`] | Default algorithm. |
161///
162/// It supports batched computation.
163///
164/// Performance notes: [`sddmm`] for [`Format::Csr`](crate::types::Format::Csr) provides the best performance when `matrix_a` and `matrix_b` satisfy:
165///
166/// * For `matrix_a`:
167///
168/// * `matrix_a` is in row-major order and `op_a` is [`Operation::NonTranspose`], or
169/// * `matrix_a` is in col-major order and `op_a` is not [`Operation::NonTranspose`].
170/// * For `matrix_b`:
171///
172/// * `matrix_b` is in col-major order and `op_b` is [`Operation::NonTranspose`], or
173/// * `matrix_b` is in row-major order and `op_b` is not [`Operation::NonTranspose`].
174///
175/// [`sddmm`] for [`Format::Bsr`](crate::types::Format::Bsr) provides the best performance when `matrix_a` and `matrix_b` satisfy:
176///
177/// * For `matrix_a`:
178///
179/// * `matrix_a` is in row-major order and `op_a` is [`Operation::NonTranspose`], or
180/// * `matrix_a` is in col-major order and `op_a` is not [`Operation::NonTranspose`].
181/// * For `matrix_b`:
182///
183/// * `matrix_b` is in row-major order and `op_b` is [`Operation::NonTranspose`], or
184/// * `matrix_b` is in col-major order and `op_b` is not [`Operation::NonTranspose`].
185///
186/// [`sddmm`] supports the following batch modes:
187///
188/// * $C\_{i} = (A \cdot B) \circ C\_{i}$
189/// * $C\_{i} = \left( A\_{i} \cdot B \right) \circ C\_{i}$
190/// * $C\_{i} = \left( A \cdot B\_{i} \right) \circ C\_{i}$
191/// * $C\_{i} = \left( A\_{i} \cdot B\_{i} \right) \circ C\_{i}$
192///
193/// The number of batches and their strides can be set by using [`SparseMatrixDescriptor::set_csr_strided_batch`] and [`DenseMatrixDescriptor::set_strided_batch`].
194/// The maximum number of batches for [`sddmm`] is 65,535.
195///
196/// [`sddmm`] has the following properties:
197///
198/// * Requires no extra storage.
199/// * Provides deterministic (bitwise) results for each run.
200/// * Supports asynchronous execution.
201/// * Allows the indices of `matrix_c` to be unsorted.
202///
203/// [`sddmm`] supports the following optimizations:
204///
205/// * CUDA graph capture.
206/// * Hardware Memory Compression.
207pub fn sddmm<Compute: DataTypeLike>(
208 ctx: &Context,
209 op_a: Operation,
210 op_b: Operation,
211 alpha: Scalar<'_, Compute>,
212 matrix_a: &DenseMatrixDescriptor,
213 matrix_b: &DenseMatrixDescriptor,
214 beta: Scalar<'_, Compute>,
215 matrix_c: &mut SparseMatrixDescriptor,
216 algorithm: SddmmAlgorithm,
217 external_buffer: Option<DevicePtr>,
218) -> Result<()> {
219 ctx.bind()?;
220 ctx.require_scalar_pointer_mode(alpha, beta)?;
221
222 unsafe {
223 try_ffi!(sys::cusparseSDDMM(
224 ctx.as_raw(),
225 op_a.into(),
226 op_b.into(),
227 alpha.ptr().cast(),
228 matrix_a.as_raw_const(),
229 matrix_b.as_raw_const(),
230 beta.ptr().cast(),
231 matrix_c.as_raw(),
232 Compute::data_type().into(),
233 algorithm.into(),
234 external_buffer.unwrap_or(DevicePtr::null()).as_ptr() as _,
235 ))?;
236 }
237 Ok(())
238}