1use std::fmt::Debug;
2use std::ops::{Add, Mul, Sub};
3
4use super::extract_block::CscBlock;
5use super::sparsity::MatrixSparsityRef;
6use super::{Matrix, MatrixCommon, MatrixSparsity};
7use crate::context::broadcast_batch;
8use crate::error::{LaError, MatrixError};
9use crate::{Context, FaerContext, FaerVec, FaerVecIndex, Vector, VectorIndex};
10use crate::{DefaultSolver, FaerScalar, FaerSparseLU, IndexType, Scale};
11
12use faer::reborrow::{Reborrow, ReborrowMut};
13use faer::sparse::linalg::matmul::sparse_dense_matmul;
14use faer::sparse::ops::{ternary_op_assign_into, union_symbolic};
15use faer::sparse::{Pair, SparseColMat, SymbolicSparseColMat, SymbolicSparseColMatRef, Triplet};
16use faer::Accum;
17
18#[derive(Clone, Debug)]
19pub struct FaerSparseMat<T: FaerScalar> {
20 pub(crate) data: Vec<SparseColMat<IndexType, T>>,
21 pub(crate) context: FaerContext,
22}
23
24impl<T: FaerScalar> FaerSparseMat<T> {
25 #[inline]
30 pub(crate) fn batch(&self, batch: usize) -> &SparseColMat<IndexType, T> {
31 &self.data[batch]
32 }
33 #[inline]
36 pub(crate) fn batch_bcast(&self, batch: usize, nbatch: usize) -> &SparseColMat<IndexType, T> {
37 self.batch(broadcast_batch(batch, self.data.len(), nbatch))
38 }
39}
40
41impl<T: FaerScalar> DefaultSolver for FaerSparseMat<T> {
42 type LS = FaerSparseLU<T>;
43}
44
45impl<T: FaerScalar> MatrixCommon for FaerSparseMat<T> {
46 type T = T;
47 type V = FaerVec<T>;
48 type C = FaerContext;
49 type Inner = Vec<SparseColMat<IndexType, T>>;
50
51 fn nrows(&self) -> IndexType {
52 self.data[0].nrows()
53 }
54 fn ncols(&self) -> IndexType {
55 self.data[0].ncols()
56 }
57 fn inner(&self) -> &Self::Inner {
58 &self.data
59 }
60}
61
62macro_rules! impl_mul_scalar {
63 ($mat_type:ty, $out:ty) => {
64 impl<'a, T: FaerScalar> Mul<Scale<T>> for $mat_type {
65 type Output = $out;
66
67 fn mul(self, rhs: Scale<T>) -> Self::Output {
68 let scale: faer::Scale<T> = rhs.into();
69 Self::Output {
70 data: self.data.iter().map(|data| data * scale).collect(),
71 context: self.context,
72 }
73 }
74 }
75 };
76}
77
78impl_mul_scalar!(FaerSparseMat<T>, FaerSparseMat<T>);
79impl_mul_scalar!(&FaerSparseMat<T>, FaerSparseMat<T>);
80
81macro_rules! sparse_binary {
82 ($trait:ident, $method:ident, $binary:tt) => {
83 impl<T: FaerScalar> $trait<&FaerSparseMat<T>> for FaerSparseMat<T> {
84 type Output = FaerSparseMat<T>;
85
86 fn $method(self, rhs: &FaerSparseMat<T>) -> Self::Output {
87 self.context
90 .assert_broadcastable_into(rhs.context.nbatch(), stringify!($method));
91 let nbatch = self.data.len();
92 FaerSparseMat {
93 data: (0..nbatch)
94 .map(|b| self.batch(b) $binary rhs.batch_bcast(b, nbatch))
95 .collect(),
96 context: self.context,
97 }
98 }
99 }
100 };
101}
102sparse_binary!(Add, add, +);
103sparse_binary!(Sub, sub, -);
104
105impl<T: FaerScalar> MatrixSparsity<FaerSparseMat<T>> for SymbolicSparseColMat<IndexType> {
106 fn union(
107 self,
108 other: SymbolicSparseColMatRef<IndexType>,
109 ) -> Result<SymbolicSparseColMat<IndexType>, LaError> {
110 union_symbolic(self.rb(), other).map_err(|e| LaError::Other(e.to_string()))
111 }
112
113 fn as_ref(&self) -> SymbolicSparseColMatRef<'_, IndexType> {
114 self.rb()
115 }
116
117 fn nrows(&self) -> IndexType {
118 self.nrows()
119 }
120
121 fn ncols(&self) -> IndexType {
122 self.ncols()
123 }
124
125 fn is_sparse() -> bool {
126 true
127 }
128
129 fn indices(&self) -> Vec<(IndexType, IndexType)> {
130 let mut indices = Vec::with_capacity(self.compute_nnz());
131 for col_i in 0..self.ncols() {
132 for pos in self.col_range(col_i) {
133 indices.push((self.row_idx()[pos], col_i));
134 }
135 }
136 indices
137 }
138
139 fn new_diagonal(n: IndexType) -> Self {
140 let indices = (0..n).map(|i| Pair::new(i, i)).collect::<Vec<_>>();
141 SymbolicSparseColMat::try_new_from_indices(n, n, indices.as_slice())
142 .unwrap()
143 .0
144 }
145
146 fn try_from_indices(
147 nrows: IndexType,
148 ncols: IndexType,
149 indices: Vec<(IndexType, IndexType)>,
150 ) -> Result<Self, LaError> {
151 let indices = indices
152 .iter()
153 .map(|(i, j)| Pair::new(*i, *j))
154 .collect::<Vec<_>>();
155 match Self::try_new_from_indices(nrows, ncols, indices.as_slice()) {
156 Ok((sparsity, _)) => Ok(sparsity),
157 Err(e) => Err(LaError::Other(e.to_string())),
158 }
159 }
160
161 fn get_index(
162 &self,
163 indices: &[(IndexType, IndexType)],
164 ctx: FaerContext,
165 ) -> <<FaerSparseMat<T> as MatrixCommon>::V as Vector>::Index {
166 let col_ptrs = self.col_ptr();
167 let row_indices = self.row_idx();
168 let mut ret = Vec::with_capacity(indices.len());
169 for &(i, j) in indices.iter() {
170 let col_ptr = col_ptrs[j];
171 let next_col_ptr = col_ptrs[j + 1];
172 for (ii, &ri) in row_indices
173 .iter()
174 .enumerate()
175 .take(next_col_ptr)
176 .skip(col_ptr)
177 {
178 if ri == i {
179 ret.push(ii);
180 break;
181 }
182 }
183 }
184 FaerVecIndex {
185 data: ret,
186 context: ctx,
187 }
188 }
189}
190
191impl<'a, T: FaerScalar> MatrixSparsityRef<'a, FaerSparseMat<T>>
192 for SymbolicSparseColMatRef<'a, IndexType>
193{
194 fn to_owned(&self) -> SymbolicSparseColMat<IndexType> {
195 self.to_owned().unwrap()
196 }
197 fn nrows(&self) -> IndexType {
198 self.nrows()
199 }
200
201 fn ncols(&self) -> IndexType {
202 self.ncols()
203 }
204
205 fn is_sparse() -> bool {
206 true
207 }
208
209 fn split(
210 &self,
211 indices: &<<FaerSparseMat<T> as MatrixCommon>::V as Vector>::Index,
212 ) -> [(
213 SymbolicSparseColMat<IndexType>,
214 <<FaerSparseMat<T> as MatrixCommon>::V as Vector>::Index,
215 ); 4] {
216 let (_ni, _nj, col_ptrs, _col_nnz, row_idx) = self.parts();
217 let ctx = indices.context();
218 let (ul_blk, ur_blk, ll_blk, lr_blk) = CscBlock::split(row_idx, col_ptrs, indices);
219 let ul_sym = SymbolicSparseColMat::new_checked(
220 ul_blk.nrows,
221 ul_blk.ncols,
222 ul_blk.col_pointers,
223 None,
224 ul_blk.row_indices,
225 );
226 let ur_sym = SymbolicSparseColMat::new_checked(
227 ur_blk.nrows,
228 ur_blk.ncols,
229 ur_blk.col_pointers,
230 None,
231 ur_blk.row_indices,
232 );
233 let ll_sym = SymbolicSparseColMat::new_checked(
234 ll_blk.nrows,
235 ll_blk.ncols,
236 ll_blk.col_pointers,
237 None,
238 ll_blk.row_indices,
239 );
240 let lr_sym = SymbolicSparseColMat::new_checked(
241 lr_blk.nrows,
242 lr_blk.ncols,
243 lr_blk.col_pointers,
244 None,
245 lr_blk.row_indices,
246 );
247 [
248 (
249 ul_sym,
250 FaerVecIndex {
251 data: ul_blk.src_indices,
252 context: *ctx,
253 },
254 ),
255 (
256 ur_sym,
257 FaerVecIndex {
258 data: ur_blk.src_indices,
259 context: *ctx,
260 },
261 ),
262 (
263 ll_sym,
264 FaerVecIndex {
265 data: ll_blk.src_indices,
266 context: *ctx,
267 },
268 ),
269 (
270 lr_sym,
271 FaerVecIndex {
272 data: lr_blk.src_indices,
273 context: *ctx,
274 },
275 ),
276 ]
277 }
278
279 fn indices(&self) -> Vec<(IndexType, IndexType)> {
280 let mut indices = Vec::with_capacity(self.compute_nnz());
281 for col_i in 0..self.ncols() {
282 for pos in self.col_range(col_i) {
283 indices.push((self.row_idx()[pos], col_i));
284 }
285 }
286 indices
287 }
288}
289
290impl<T: FaerScalar> Matrix for FaerSparseMat<T> {
291 type Sparsity = SymbolicSparseColMat<IndexType>;
292 type SparsityRef<'a> = SymbolicSparseColMatRef<'a, IndexType>;
293
294 fn sparsity(&self) -> Option<Self::SparsityRef<'_>> {
295 Some(self.data[0].symbolic())
296 }
297 fn context(&self) -> &FaerContext {
298 &self.context
299 }
300 fn inner_mut(&mut self) -> &mut Self::Inner {
301 &mut self.data
302 }
303
304 fn gather(&mut self, other: &Self, indices: &<Self::V as Vector>::Index) {
305 self.context
306 .assert_broadcastable_into(other.context.nbatch(), "gather");
307 let nb = self.data.len();
308 for (batch, data) in self.data.iter_mut().enumerate() {
309 let src = other.batch_bcast(batch, nb).val();
310 for (dst, index) in data.val_mut().iter_mut().zip(&indices.data) {
311 *dst = src[*index];
312 }
313 }
314 }
315
316 fn set_data_with_indices(
317 &mut self,
318 dst_indices: &<Self::V as Vector>::Index,
319 src_indices: &<Self::V as Vector>::Index,
320 data: &Self::V,
321 ) {
322 self.context
323 .assert_broadcastable_into(data.context.nbatch(), "set_data_with_indices");
324 let nb = self.data.len();
325 for (batch, matrix) in self.data.iter_mut().enumerate() {
326 let values = matrix.val_mut();
327 let column = data.data.rb().col(data.batch(batch, nb));
328 for (dst, src) in dst_indices.data.iter().zip(&src_indices.data) {
329 values[*dst] = column[*src];
330 }
331 }
332 }
333
334 fn add_column_to_vector(&self, j: IndexType, v: &mut Self::V) {
335 v.context
336 .assert_broadcastable_into(self.context.nbatch(), "add_column_to_vector");
337 let nb = v.data.ncols();
338 for batch in 0..nb {
339 let matrix = self.batch_bcast(batch, nb);
340 let mut column = v.data.rb_mut().col_mut(batch);
341 for i in matrix.col_range(j) {
342 column[matrix.row_idx()[i]] += matrix.val()[i];
343 }
344 }
345 }
346
347 fn add_columns_to_batched_vector(&self, v: &mut Self::V) {
348 assert_eq!(v.len(), self.nrows(), "row count mismatch");
349 let ncols = self.ncols();
350 assert_eq!(
351 v.context.nbatch(),
352 self.context.nbatch() * ncols,
353 "batch count mismatch: the destination holds one lane per (batch, column)"
354 );
355 for batch in 0..self.context.nbatch() {
356 let matrix = self.batch(batch);
357 for j in 0..ncols {
358 let mut column = v.data.rb_mut().col_mut(batch * ncols + j);
359 for i in matrix.col_range(j) {
360 column[matrix.row_idx()[i]] += matrix.val()[i];
361 }
362 }
363 }
364 }
365 fn triplet_iter(
366 &self,
367 ) -> (
368 impl Iterator<Item = (IndexType, IndexType)> + '_,
369 impl Iterator<Item = Self::T> + '_,
370 ) {
371 let indices: Vec<_> = (0..self.ncols())
372 .flat_map(move |j| {
373 self.data[0]
374 .col_range(j)
375 .map(move |i| (self.data[0].row_idx()[i], j))
376 })
377 .collect();
378 let values: Vec<_> = self
379 .data
380 .iter()
381 .flat_map(|data| data.val().iter().copied())
382 .collect();
383 (indices.into_iter(), values.into_iter())
384 }
385
386 fn try_from_triplets(
387 nrows: IndexType,
388 ncols: IndexType,
389 indices: Vec<(IndexType, IndexType)>,
390 values: Vec<Self::T>,
391 ctx: Self::C,
392 ) -> Result<Self, LaError> {
393 assert_eq!(values.len(), indices.len() * ctx.nbatch());
394 if indices.is_empty() {
395 return Ok(Self::zeros(nrows, ncols, ctx));
397 }
398 let data = values
399 .chunks(indices.len())
400 .map(|values| {
401 let triplets = indices
402 .iter()
403 .zip(values)
404 .map(|((i, j), v)| Triplet::new(*i, *j, *v))
405 .collect::<Vec<_>>();
406 faer::sparse::SparseColMat::try_new_from_triplets(nrows, ncols, &triplets)
407 .map_err(|e| LaError::from(MatrixError::FailedToCreateMatrixFromTriplets(e)))
408 })
409 .collect::<Result<_, _>>()?;
410 Ok(Self { data, context: ctx })
411 }
412 fn gemv(&self, alpha: Self::T, x: &Self::V, beta: Self::T, y: &mut Self::V) {
413 y.context
414 .assert_broadcastable_into(self.context.nbatch(), "gemv");
415 y.context
416 .assert_broadcastable_into(x.context.nbatch(), "gemv");
417 let nb = y.data.ncols();
418 let par = self.context.par;
419 for batch in 0..nb {
420 let mut ycol = y.data.rb_mut().col_mut(batch);
421 let accum = if beta.is_zero() {
422 Accum::Replace
423 } else {
424 if !beta.is_one() {
425 ycol *= faer::Scale(beta);
426 }
427 Accum::Add
428 };
429 sparse_dense_matmul(
430 ycol.as_mat_mut(),
431 accum,
432 self.batch_bcast(batch, nb).rb(),
433 x.data.rb().col(x.batch(batch, nb)).as_mat(),
434 alpha,
435 par,
436 );
437 }
438 }
439 fn zeros(nrows: IndexType, ncols: IndexType, ctx: Self::C) -> Self {
440 Self {
441 data: (0..ctx.nbatch())
442 .map(|_| SparseColMat::try_new_from_triplets(nrows, ncols, &[]).unwrap())
443 .collect(),
444 context: ctx,
445 }
446 }
447 fn copy_from(&mut self, other: &Self) {
448 self.context
449 .assert_broadcastable_into(other.context.nbatch(), "copy_from");
450 let nb = self.data.len();
451 for (batch, data) in self.data.iter_mut().enumerate() {
452 let other = other.batch_bcast(batch, nb);
453 *data = SparseColMat::new(other.symbolic().to_owned().unwrap(), other.val().to_vec());
454 }
455 }
456 fn from_diagonal(v: &FaerVec<T>) -> Self {
457 let dim = v.len();
458 let data = (0..v.data.ncols())
459 .map(|b| {
460 let column = v.data.rb().col(b);
461 let triplets = (0..dim)
462 .map(|i| Triplet::new(i, i, column[i]))
463 .collect::<Vec<_>>();
464 SparseColMat::try_new_from_triplets(dim, dim, &triplets).unwrap()
465 })
466 .collect();
467 Self {
468 data,
469 context: *v.context(),
470 }
471 }
472
473 fn partition_indices_by_zero_diagonal(
474 &self,
475 ) -> (<Self::V as Vector>::Index, <Self::V as Vector>::Index) {
476 let mut indices_zero_diag = vec![];
477 let mut indices_non_zero_diag = vec![];
478 'outer: for j in 0..self.ncols() {
479 for (i, v) in self.data[0]
480 .row_idx_of_col(j)
481 .zip(self.data[0].val_of_col(j))
482 {
483 if i == j && *v != T::zero() {
484 indices_non_zero_diag.push(j);
485 continue 'outer;
486 } else if i > j {
487 break;
488 }
489 }
490 indices_zero_diag.push(j);
491 }
492 (
493 <Self::V as Vector>::Index::from_vec(indices_zero_diag, self.context),
494 <Self::V as Vector>::Index::from_vec(indices_non_zero_diag, self.context),
495 )
496 }
497
498 fn set_column(&mut self, j: IndexType, v: &Self::V) {
499 assert_eq!(v.len(), self.nrows());
500 self.context
501 .assert_broadcastable_into(v.context.nbatch(), "set_column");
502 let nb = self.data.len();
503 for (batch, data) in self.data.iter_mut().enumerate() {
504 let column = v.data.rb().col(v.batch(batch, nb));
505 for i in data.col_range(j) {
506 data.val_mut()[i] = column[data.row_idx()[i]];
507 }
508 }
509 }
510
511 fn scale_add_and_assign(&mut self, x: &Self, beta: Self::T, y: &Self) {
512 self.context
513 .assert_broadcastable_into(x.context.nbatch(), "scale_add_and_assign");
514 self.context
515 .assert_broadcastable_into(y.context.nbatch(), "scale_add_and_assign");
516 let nb = self.data.len();
517 for (batch, data) in self.data.iter_mut().enumerate() {
518 ternary_op_assign_into(
519 data.rb_mut(),
520 x.batch_bcast(batch, nb).rb(),
521 y.batch_bcast(batch, nb).rb(),
522 |s, x, y| *s = *x.unwrap_or(&T::zero()) + beta * *y.unwrap_or(&T::zero()),
523 );
524 }
525 }
526
527 fn new_from_sparsity(
528 nrows: IndexType,
529 ncols: IndexType,
530 sparsity: Option<Self::Sparsity>,
531 ctx: Self::C,
532 ) -> Self {
533 let sparsity = sparsity.expect("Sparsity pattern required for sparse matrix");
534 assert_eq!(sparsity.nrows(), nrows);
535 assert_eq!(sparsity.ncols(), ncols);
536 let nnz = sparsity.row_idx().len();
537 Self {
538 data: (0..ctx.nbatch())
539 .map(|_| SparseColMat::new(sparsity.clone(), vec![T::zero(); nnz]))
540 .collect(),
541 context: ctx,
542 }
543 }
544}
545
546#[cfg(test)]
547mod tests {
548 use crate::{FaerContext, FaerSparseMat, Matrix, MatrixSparsity, MatrixSparsityRef};
549 use faer::sparse::SymbolicSparseColMat;
550
551 #[test]
552 fn sparsity_indices_round_trip() {
553 let indices = vec![(0, 0), (3, 0), (1, 2), (2, 2)];
554 let sparsity =
555 <SymbolicSparseColMat<usize> as MatrixSparsity<FaerSparseMat<f64>>>::try_from_indices(
556 4,
557 3,
558 indices.clone(),
559 )
560 .unwrap();
561
562 let mut round_tripped =
563 <SymbolicSparseColMat<usize> as MatrixSparsity<FaerSparseMat<f64>>>::indices(&sparsity);
564 round_tripped.sort_unstable();
565 let mut expected = indices;
566 expected.sort_unstable();
567
568 assert_eq!(round_tripped, expected);
569
570 let sparsity_ref =
571 <SymbolicSparseColMat<usize> as MatrixSparsity<FaerSparseMat<f64>>>::as_ref(&sparsity);
572 let mut ref_round_tripped = MatrixSparsityRef::<FaerSparseMat<f64>>::indices(&sparsity_ref);
573 ref_round_tripped.sort_unstable();
574
575 assert_eq!(ref_round_tripped, expected);
576 }
577
578 #[test]
579 fn test_triplet_iter() {
580 let indices = vec![(0, 0), (1, 0), (2, 2), (3, 2)];
581 let values = vec![1.0, 2.0, 3.0, 4.0];
582 let mat = FaerSparseMat::<f64>::try_from_triplets(
583 4,
584 3,
585 indices.clone(),
586 values.clone(),
587 Default::default(),
588 )
589 .unwrap();
590 let (iter_indices, iter_values): (Vec<_>, Vec<_>) = {
591 let (i, v) = mat.triplet_iter();
592 (i.collect(), v.collect())
593 };
594 assert_eq!(iter_indices, indices);
595 assert_eq!(iter_values, values);
596 }
597
598 #[test]
599 fn test_set_data_with_indices() {
600 use crate::{FaerVec, FaerVecIndex, Vector, VectorIndex};
601
602 let triplets_full = [
604 (0, 0, 1.0),
605 (2, 0, 2.0),
606 (1, 1, 3.0),
607 (0, 2, 4.0),
608 (2, 2, 5.0),
609 ];
610 let (indices, init_values): (Vec<_>, Vec<_>) =
611 triplets_full.iter().map(|&(i, j, v)| ((i, j), v)).unzip();
612 let mut mat = FaerSparseMat::<f64>::try_from_triplets(
613 3,
614 3,
615 indices.clone(),
616 init_values,
617 Default::default(),
618 )
619 .unwrap();
620
621 assert_eq!(mat.inner_mut()[0].val_mut().len(), indices.len());
622
623 let new_values = [10.0, 11.0, 12.0, 13.0, 14.0];
624 mat.inner_mut()[0].val_mut().copy_from_slice(&new_values);
625 let (indices_iter, values_iter) = mat.triplet_iter();
626 let got: Vec<(usize, usize, f64)> = indices_iter
627 .zip(values_iter)
628 .map(|((i, j), v)| (i, j, v))
629 .collect();
630 let expected: Vec<(usize, usize, f64)> = triplets_full
631 .iter()
632 .zip(new_values.iter())
633 .map(|(&(i, j, _), &v)| (i, j, v))
634 .collect();
635 assert_eq!(got, expected);
636
637 let mut via_set_data = FaerSparseMat::<f64>::try_from_triplets(
638 3,
639 3,
640 indices,
641 new_values.iter().map(|_| 0.0).collect(),
642 Default::default(),
643 )
644 .unwrap();
645 let nnz = new_values.len();
646 let identity = FaerVecIndex::from_vec((0..nnz).collect(), Default::default());
647 let data = FaerVec::from_vec(new_values.to_vec(), Default::default());
648 via_set_data.set_data_with_indices(&identity, &identity, &data);
649 assert_eq!(
650 mat.inner_mut()[0].val_mut(),
651 via_set_data.inner_mut()[0].val_mut()
652 );
653 }
654
655 #[test]
656 fn test_partition_indices_by_zero_diagonal() {
657 super::super::tests::test_partition_indices_by_zero_diagonal::<FaerSparseMat<f64>>();
658 }
659
660 super::super::generate_matrix_tests_nonbatched!(faer_sparse, FaerSparseMat<f64>);
661 super::super::generate_matrix_tests_nonbatched!(faer_sparse_f32, FaerSparseMat<f32>);
662 super::super::generate_matrix_tests_batched!(
663 faer_sparse,
664 FaerSparseMat<f64>,
665 FaerContext::default(),
666 FaerContext::with_nbatch(2)
667 );
668 super::super::generate_matrix_tests_batched!(
669 faer_sparse_f32,
670 FaerSparseMat<f32>,
671 FaerContext::default(),
672 FaerContext::with_nbatch(2)
673 );
674}