Skip to main content

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}