oxicuda_sparse/format/
bsr.rs1use oxicuda_blas::GpuFloat;
14use oxicuda_memory::DeviceBuffer;
15
16use crate::error::{SparseError, SparseResult};
17
18pub struct BsrMatrix<T: GpuFloat> {
23 rows: u32,
25 cols: u32,
27 nnz_blocks: u32,
29 block_dim: u32,
31 row_ptr: DeviceBuffer<i32>,
33 col_idx: DeviceBuffer<i32>,
35 values: DeviceBuffer<T>,
37}
38
39impl<T: GpuFloat> BsrMatrix<T> {
40 #[allow(clippy::too_many_arguments)]
56 pub fn from_host(
57 rows: u32,
58 cols: u32,
59 block_dim: u32,
60 row_ptr: &[i32],
61 col_idx: &[i32],
62 values: &[T],
63 ) -> SparseResult<Self> {
64 if rows == 0 || cols == 0 || block_dim == 0 {
65 return Err(SparseError::InvalidFormat(
66 "rows, cols, and block_dim must be non-zero".to_string(),
67 ));
68 }
69 if rows % block_dim != 0 {
70 return Err(SparseError::InvalidFormat(format!(
71 "rows ({rows}) must be a multiple of block_dim ({block_dim})"
72 )));
73 }
74 if cols % block_dim != 0 {
75 return Err(SparseError::InvalidFormat(format!(
76 "cols ({cols}) must be a multiple of block_dim ({block_dim})"
77 )));
78 }
79
80 let block_rows = rows / block_dim;
81 let expected_row_ptr_len = block_rows as usize + 1;
82 if row_ptr.len() != expected_row_ptr_len {
83 return Err(SparseError::InvalidFormat(format!(
84 "row_ptr length ({}) must be block_rows + 1 ({})",
85 row_ptr.len(),
86 expected_row_ptr_len
87 )));
88 }
89
90 let nnz_blocks = col_idx.len() as u32;
91 if nnz_blocks == 0 {
92 return Err(SparseError::ZeroNnz);
93 }
94
95 let block_elems = block_dim as usize * block_dim as usize;
96 let expected_values_len = nnz_blocks as usize * block_elems;
97 if values.len() != expected_values_len {
98 return Err(SparseError::InvalidFormat(format!(
99 "values length ({}) must be nnz_blocks * block_dim^2 ({})",
100 values.len(),
101 expected_values_len
102 )));
103 }
104
105 if row_ptr[0] != 0 {
107 return Err(SparseError::InvalidFormat(
108 "row_ptr[0] must be 0".to_string(),
109 ));
110 }
111 if row_ptr[block_rows as usize] != nnz_blocks as i32 {
112 return Err(SparseError::InvalidFormat(format!(
113 "row_ptr[block_rows] ({}) must equal nnz_blocks ({})",
114 row_ptr[block_rows as usize], nnz_blocks
115 )));
116 }
117
118 let d_row_ptr = DeviceBuffer::from_host(row_ptr)?;
119 let d_col_idx = DeviceBuffer::from_host(col_idx)?;
120 let d_values = DeviceBuffer::from_host(values)?;
121
122 Ok(Self {
123 rows,
124 cols,
125 nnz_blocks,
126 block_dim,
127 row_ptr: d_row_ptr,
128 col_idx: d_col_idx,
129 values: d_values,
130 })
131 }
132
133 pub fn to_host(&self) -> SparseResult<(Vec<i32>, Vec<i32>, Vec<T>)> {
139 let mut h_row_ptr = vec![0i32; self.row_ptr.len()];
140 let mut h_col_idx = vec![0i32; self.col_idx.len()];
141 let mut h_values = vec![T::gpu_zero(); self.values.len()];
142
143 self.row_ptr.copy_to_host(&mut h_row_ptr)?;
144 self.col_idx.copy_to_host(&mut h_col_idx)?;
145 self.values.copy_to_host(&mut h_values)?;
146
147 Ok((h_row_ptr, h_col_idx, h_values))
148 }
149
150 #[inline]
152 pub fn rows(&self) -> u32 {
153 self.rows
154 }
155
156 #[inline]
158 pub fn cols(&self) -> u32 {
159 self.cols
160 }
161
162 #[inline]
164 pub fn nnz_blocks(&self) -> u32 {
165 self.nnz_blocks
166 }
167
168 #[inline]
170 pub fn block_dim(&self) -> u32 {
171 self.block_dim
172 }
173
174 #[inline]
176 pub fn block_rows(&self) -> u32 {
177 self.rows / self.block_dim
178 }
179
180 #[inline]
182 pub fn block_cols(&self) -> u32 {
183 self.cols / self.block_dim
184 }
185
186 #[inline]
188 pub fn scalar_nnz(&self) -> u32 {
189 self.nnz_blocks * self.block_dim * self.block_dim
190 }
191
192 #[inline]
194 pub fn row_ptr(&self) -> &DeviceBuffer<i32> {
195 &self.row_ptr
196 }
197
198 #[inline]
200 pub fn col_idx(&self) -> &DeviceBuffer<i32> {
201 &self.col_idx
202 }
203
204 #[inline]
206 pub fn values(&self) -> &DeviceBuffer<T> {
207 &self.values
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn bsr_validation_block_alignment() {
217 let result = BsrMatrix::<f32>::from_host(
219 5,
220 4,
221 2,
222 &[0, 1, 2, 3], &[0, 1, 0],
224 &[1.0; 12],
225 );
226 assert!(result.is_err());
227 }
228
229 #[test]
230 fn bsr_block_counts() {
231 let br = 4 / 2;
233 let bc = 4 / 2;
234 assert_eq!(br, 2);
235 assert_eq!(bc, 2);
236 }
237}