1use super::{BlockedCoo, Csr, DenseWithMask, SellP, SparseView};
15use crate::arch::SimdArch;
16use crate::kernel::SimdKernel;
17use crate::scalar::Scalar;
18use crate::sparse::spmv::build_index_vector;
19
20pub trait SparseOps<T> {
22 fn sum_values(&self) -> T;
24
25 fn elementwise_mul_dense(&self, dense: &[T], out_values: &mut [T]);
28}
29
30impl<'a, T, Arch> SparseOps<T> for SparseView<'a, T, Csr, Arch>
31where
32 T: Scalar,
33 Arch: SimdArch + SimdKernel<T>,
34{
35 #[inline]
36 fn sum_values(&self) -> T {
37 if let Some(view) =
38 crate::view::SimdView::<T, Arch, crate::align::Unaligned>::new(self.data.values)
39 {
40 view.reduce(crate::ops::Sum)
41 } else {
42 T::ZERO
43 }
44 }
45
46 #[inline]
47 fn elementwise_mul_dense(&self, dense: &[T], out_values: &mut [T]) {
48 let d = &self.data;
49 let lane_count = Arch::LANE_COUNT;
50
51 use super::types::SparseValidate;
60 d.validate()
61 .expect("CSR matrix failed structural validation before elementwise_mul_dense");
62 assert!(
63 dense.len() >= d.ncols,
64 "CSR elementwise_mul_dense: dense len {} < ncols {}",
65 dense.len(),
66 d.ncols
67 );
68
69 for r in 0..d.nrows {
70 let start = d.row_ptr[r] as usize;
71 let end = d.row_ptr[r + 1] as usize;
72 let row_nnz = end - start;
73 let vals = &d.values[start..end];
74 let cols = &d.col_indices[start..end];
75 let out = &mut out_values[start..end];
76
77 let simd_len = (row_nnz / lane_count) * lane_count;
78 let mut j = 0usize;
84 unsafe {
85 while j < simd_len {
86 let idx = build_index_vector::<T, Arch>(&cols[j..j + lane_count]);
87 let res_vec = Arch::mul(
88 Arch::load_unaligned(vals[j..].as_ptr()),
89 Arch::gather(dense.as_ptr(), idx),
90 );
91 Arch::store_unaligned(out[j..].as_mut_ptr(), res_vec);
92 j += lane_count;
93 }
94 }
95 while j < row_nnz {
96 let c = cols[j] as usize;
97 out[j] = vals[j] * dense[c];
98 j += 1;
99 }
100 }
101 }
102}
103
104impl<'a, T, const C: usize, Arch> SparseOps<T> for SparseView<'a, T, SellP<C>, Arch>
105where
106 T: Scalar,
107 Arch: SimdArch + SimdKernel<T>,
108{
109 #[inline]
110 fn sum_values(&self) -> T {
111 if let Some(view) =
112 crate::view::SimdView::<T, Arch, crate::align::Unaligned>::new(self.data.values)
113 {
114 view.reduce(crate::ops::Sum)
115 } else {
116 T::ZERO
117 }
118 }
119
120 #[inline]
121 fn elementwise_mul_dense(&self, dense: &[T], out_values: &mut [T]) {
122 let d = &self.data;
123 let nslices = d.nslices();
124 let lane_count = Arch::LANE_COUNT;
125
126 if lane_count == C {
127 use super::types::SparseValidate;
134 d.validate()
135 .expect("SELL-p matrix failed structural validation before vectorized kernel");
136 assert!(
137 out_values.len() >= d.values.len(),
138 "SELL-p elementwise_mul_dense: out_values len {} < values len {}",
139 out_values.len(),
140 d.values.len()
141 );
142 for s in 0..nslices {
143 let col_count = d.slice_col_count[s] as usize;
144 let start_offset = d.slice_ptr[s] as usize;
145 let slice_base_r = s * C;
146
147 for col in 0..col_count {
148 let offset = start_offset + col * C;
149
150 let mut idx_arr = [0i32; 64];
151 let mut mask_arr = [false; 64];
152 for row in 0..C {
153 let r = slice_base_r + row;
154 let c = d.col_indices[offset + row] as usize;
155 let in_bounds = r < d.nrows && c < d.ncols;
156 mask_arr[row] = in_bounds;
157 if in_bounds {
158 idx_arr[row] = (r * d.ncols + c) as i32;
159 }
160 }
161
162 unsafe {
170 let idx = build_index_vector::<T, Arch>(&idx_arr[..C]);
171 let mask = Arch::mask_from_bools(&mask_arr[..C]);
172 let zero_vec = Arch::zero();
173 let dense_vec = Arch::gather_masked(dense.as_ptr(), idx, mask, zero_vec);
174 let res_vec =
175 Arch::mul(Arch::load_unaligned(d.values[offset..].as_ptr()), dense_vec);
176 Arch::masked_store_unaligned(
177 out_values[offset..].as_mut_ptr(),
178 mask,
179 res_vec,
180 );
181 }
182 }
183 }
184 } else {
185 for s in 0..nslices {
186 let col_count = d.slice_col_count[s] as usize;
187 let start_offset = d.slice_ptr[s] as usize;
188 for col in 0..col_count {
189 for row in 0..C {
190 let idx = start_offset + col * C + row;
191 let c = d.col_indices[idx] as usize;
192 let r = s * C + row;
193 if r < d.nrows && c < d.ncols {
194 out_values[idx] = d.values[idx] * dense[r * d.ncols + c];
195 }
196 }
197 }
198 }
199 }
200 }
201}
202
203impl<'a, T, const BM: usize, const BN: usize, Arch> SparseOps<T>
204 for SparseView<'a, T, BlockedCoo<BM, BN>, Arch>
205where
206 T: Scalar,
207 Arch: SimdArch + SimdKernel<T>,
208{
209 #[inline]
210 fn sum_values(&self) -> T {
211 if let Some(view) =
212 crate::view::SimdView::<T, Arch, crate::align::Unaligned>::new(self.data.blocks)
213 {
214 view.reduce(crate::ops::Sum)
215 } else {
216 T::ZERO
217 }
218 }
219
220 #[inline]
221 fn elementwise_mul_dense(&self, dense: &[T], out_values: &mut [T]) {
222 let d = &self.data;
223 let lane_count = Arch::LANE_COUNT;
224
225 let block_elems = d.nblocks * BM * BN;
230 assert!(
231 dense.len() >= d.nrows * d.ncols,
232 "dense buffer {} too small for {}x{}",
233 dense.len(),
234 d.nrows,
235 d.ncols
236 );
237 assert!(
238 out_values.len() >= block_elems && d.blocks.len() >= block_elems,
239 "block/output buffers too small for {} block elements",
240 block_elems
241 );
242 for b in 0..d.nblocks {
243 let br = d.block_row[b] as usize;
244 let bc = d.block_col[b] as usize;
245 assert!(
246 bc + BN <= d.ncols && br + BM <= d.nrows,
247 "BlockedCoo block {b} (row {br}+{BM}, col {bc}+{BN}) exceeds {}x{}",
248 d.nrows,
249 d.ncols
250 );
251 }
252
253 if BN == lane_count {
254 unsafe {
261 for b in 0..d.nblocks {
262 let br = d.block_row[b] as usize;
263 let bc = d.block_col[b] as usize;
264 for i in 0..BM {
265 let offset = b * (BM * BN) + i * BN;
266 let dense_idx = (br + i) * d.ncols + bc;
267 let res_vec = Arch::mul(
268 Arch::load_unaligned(d.blocks[offset..].as_ptr()),
269 Arch::load_unaligned(dense[dense_idx..].as_ptr()),
270 );
271 Arch::store_unaligned(out_values[offset..].as_mut_ptr(), res_vec);
272 }
273 }
274 }
275 } else if BN == lane_count * 2 {
276 unsafe {
281 for b in 0..d.nblocks {
282 let br = d.block_row[b] as usize;
283 let bc = d.block_col[b] as usize;
284 for i in 0..BM {
285 let offset = b * (BM * BN) + i * BN;
286 let dense_idx = (br + i) * d.ncols + bc;
287 let res_vec0 = Arch::mul(
288 Arch::load_unaligned(d.blocks[offset..].as_ptr()),
289 Arch::load_unaligned(dense[dense_idx..].as_ptr()),
290 );
291 let res_vec1 = Arch::mul(
292 Arch::load_unaligned(d.blocks[offset + lane_count..].as_ptr()),
293 Arch::load_unaligned(dense[dense_idx + lane_count..].as_ptr()),
294 );
295 Arch::store_unaligned(out_values[offset..].as_mut_ptr(), res_vec0);
296 Arch::store_unaligned(
297 out_values[offset + lane_count..].as_mut_ptr(),
298 res_vec1,
299 );
300 }
301 }
302 }
303 } else {
304 for b in 0..d.nblocks {
305 let br = d.block_row[b] as usize;
306 let bc = d.block_col[b] as usize;
307 for i in 0..BM {
308 for j in 0..BN {
309 let idx = b * (BM * BN) + i * BN + j;
310 out_values[idx] = d.blocks[idx] * dense[(br + i) * d.ncols + (bc + j)];
311 }
312 }
313 }
314 }
315 }
316}
317
318impl<'a, T, Arch> SparseOps<T> for SparseView<'a, T, DenseWithMask, Arch>
319where
320 T: Scalar,
321 Arch: SimdArch + SimdKernel<T>,
322{
323 #[inline]
324 fn sum_values(&self) -> T {
325 let lane_count = Arch::LANE_COUNT;
326 let len = self.data.values.len();
327 let simd_len = (len / lane_count) * lane_count;
328
329 let mut i = 0usize;
334 let acc_vec = unsafe {
335 let zero_vec = Arch::zero();
336 let mut acc_vec = zero_vec;
337 while i < simd_len {
338 let msk = Arch::mask_from_bools(&self.data.mask[i..i + lane_count]);
339 let v_vec =
340 Arch::masked_load_unaligned(self.data.values[i..].as_ptr(), msk, zero_vec);
341 acc_vec = Arch::add(acc_vec, v_vec);
342 i += lane_count;
343 }
344 acc_vec
345 };
346 let mut s = unsafe { Arch::sum_reduce(acc_vec) };
348 while i < len {
349 if self.data.mask[i] {
350 s += self.data.values[i];
351 }
352 i += 1;
353 }
354 s
355 }
356
357 #[inline]
358 fn elementwise_mul_dense(&self, dense: &[T], out_values: &mut [T]) {
359 let d = &self.data;
360 let len = d.values.len();
361 let lane_count = Arch::LANE_COUNT;
362 let simd_len = (len / lane_count) * lane_count;
363
364 assert!(
369 dense.len() >= len && out_values.len() >= len,
370 "DenseWithMask elementwise_mul_dense: dense {} / out {} shorter than values {}",
371 dense.len(),
372 out_values.len(),
373 len
374 );
375
376 let mut i = 0usize;
380 unsafe {
381 let zero_vec = Arch::zero();
382 while i < simd_len {
383 let msk = Arch::mask_from_bools(&d.mask[i..i + lane_count]);
384 let res_vec = Arch::masked_mul(
385 Arch::load_unaligned(d.values[i..].as_ptr()),
386 Arch::load_unaligned(dense[i..].as_ptr()),
387 msk,
388 zero_vec,
389 );
390 Arch::store_unaligned(out_values[i..].as_mut_ptr(), res_vec);
391 i += lane_count;
392 }
393 }
394 while i < len {
395 if d.mask[i] {
396 out_values[i] = d.values[i] * dense[i];
397 } else {
398 out_values[i] = T::ZERO;
399 }
400 i += 1;
401 }
402 }
403}