iterative_solvers/utils/sparse.rs
1//! Utility functions for creating sparse matrices.
2
3use crate::{
4 IterSolverError, IterSolverResult,
5 utils::{empty_spcsc, empty_spcsr},
6};
7#[cfg(not(feature = "ndarray"))]
8use crate::{SparseCscMatrix, SparseCsrMatrix};
9
10#[cfg(feature = "nalgebra")]
11use nalgebra_sparse::CooMatrix;
12
13#[cfg(feature = "faer")]
14use faer::sparse::Triplet;
15
16#[cfg(feature = "ndarray")]
17type SparseCsrMatrix<T> = sprs::CsMat<T>;
18#[cfg(feature = "ndarray")]
19type SparseCscMatrix<T> = sprs::CsMat<T>;
20
21#[cfg(feature = "ndarray")]
22use sprs::CsMat;
23
24/// Creates a diagonal sparse CSR matrix with the given data placed on a specified diagonal.
25///
26/// This function constructs a matrix where the provided data is placed on a diagonal
27/// that can be offset from the main diagonal. The resulting matrix size is determined
28/// by the length of the data and the absolute value of the offset.
29///
30/// # Arguments
31///
32/// * `data` - A slice of f64 values to be placed on the diagonal
33/// * `offset` - The diagonal offset:
34/// - `0`: Main diagonal
35/// - Positive: above the main diagonal (upper-diagonal)
36/// - Negative: below the main diagonal (lower-diagonal)
37///
38/// # Returns
39///
40/// A `CsrMatrix<f64>` containing the diagonal matrix. If `data` is empty, returns
41/// a 0×0 matrix.
42///
43/// # Examples
44///
45/// ```rust
46/// use iterative_solvers::utils::sparse::diagm_csr;
47///
48/// // Main diagonal
49/// let data = vec![1.0, 2.0, 3.0];
50/// let mat = diagm_csr(&data, 0);
51/// // Creates:
52/// // [1.0, 0.0, 0.0]
53/// // [0.0, 2.0, 0.0]
54/// // [0.0, 0.0, 3.0]
55///
56/// // Upper-diagonal (offset = 1)
57/// let mat = diagm_csr(&data, 1);
58/// // Creates:
59/// // [0.0, 1.0, 0.0, 0.0]
60/// // [0.0, 0.0, 2.0, 0.0]
61/// // [0.0, 0.0, 0.0, 3.0]
62/// // [0.0, 0.0, 0.0, 0.0]
63/// ```
64pub fn diagm_csr(data: &[f64], offset: isize) -> SparseCsrMatrix<f64> {
65 if data.is_empty() {
66 return empty_spcsr();
67 }
68
69 let offset_usize = offset.unsigned_abs();
70 let n = data.len() + offset_usize;
71
72 let tmp = match offset {
73 0 => {
74 #[cfg(feature = "nalgebra")]
75 {
76 let mut coo = CooMatrix::<f64>::new(n, n);
77
78 data.iter()
79 .enumerate()
80 .for_each(|(i, &val)| coo.push(i, i, val));
81 coo
82 }
83 #[cfg(feature = "faer")]
84 {
85 data.iter()
86 .enumerate()
87 .map(|(i, &val)| Triplet::new(i, i, val))
88 .collect::<Vec<_>>()
89 }
90 #[cfg(feature = "ndarray")]
91 {
92 let indptr = (0..=data.len())
93 .chain(std::iter::repeat_n(data.len(), n - data.len()))
94 .collect::<Vec<_>>();
95 let indices = (0..data.len()).collect::<Vec<_>>();
96
97 (indptr, indices)
98 }
99 }
100 offset => {
101 #[cfg(feature = "nalgebra")]
102 let mut coo = CooMatrix::<f64>::new(n, n);
103
104 if offset > 0 {
105 #[cfg(feature = "nalgebra")]
106 {
107 data.iter()
108 .enumerate()
109 .for_each(|(i, &val)| coo.push(i, i + offset_usize, val));
110 coo
111 }
112 #[cfg(feature = "faer")]
113 {
114 data.iter()
115 .enumerate()
116 .map(|(i, &val)| Triplet::new(i, i + offset_usize, val))
117 .collect::<Vec<_>>()
118 }
119 #[cfg(feature = "ndarray")]
120 {
121 // Each of the first `data.len()` rows has one element.
122 let indptr = (0..=data.len())
123 .chain(std::iter::repeat_n(data.len(), n - data.len()))
124 .collect::<Vec<_>>();
125 // The column indices are offset from the row indices.
126 let indices = (offset_usize..offset_usize + data.len()).collect::<Vec<_>>();
127 (indptr, indices)
128 }
129 } else {
130 #[cfg(feature = "nalgebra")]
131 {
132 data.iter()
133 .enumerate()
134 .for_each(|(i, &val)| coo.push(i + offset_usize, i, val));
135 coo
136 }
137 #[cfg(feature = "faer")]
138 {
139 data.iter()
140 .enumerate()
141 .map(|(i, &val)| Triplet::new(i + offset_usize, i, val))
142 .collect::<Vec<_>>()
143 }
144 #[cfg(feature = "ndarray")]
145 {
146 // The first `offset_usize` rows are empty.
147 let indptr = std::iter::repeat_n(0, offset_usize + 1)
148 .chain(1..=data.len())
149 .chain(std::iter::repeat_n(
150 data.len(),
151 n - (offset_usize + data.len()),
152 ))
153 .collect::<Vec<_>>();
154 // The column indices start from 0.
155 let indices = (0..data.len()).collect::<Vec<_>>();
156 (indptr, indices)
157 }
158 }
159 }
160 };
161 #[cfg(feature = "nalgebra")]
162 {
163 SparseCsrMatrix::from(&tmp)
164 }
165 #[cfg(feature = "faer")]
166 {
167 SparseCsrMatrix::try_new_from_triplets(n, n, &tmp).unwrap()
168 }
169 #[cfg(feature = "ndarray")]
170 {
171 CsMat::new((n, n), tmp.0, tmp.1, data.to_vec())
172 }
173}
174
175/// Creates a tridiagonal sparse CSR matrix from diagonal, lower diagonal, and upper diagonal vectors.
176///
177/// This function constructs a tridiagonal matrix where:
178/// - The main diagonal contains elements from the `diagonal` vector
179/// - The lower-diagonal (below main) contains elements from the `lower` vector
180/// - The upper-diagonal (above main) contains elements from the `upper` vector
181///
182/// # Arguments
183///
184/// * `diagonal` - A slice containing the main diagonal elements
185/// * `lower` - A slice containing the lower diagonal elements (lower-diagonal)
186/// * `upper` - A slice containing the upper diagonal elements (upper-diagonal)
187///
188/// # Dimension Requirements
189///
190/// For a valid tridiagonal matrix:
191/// - `diagonal.len()` must equal `lower.len() + 1`
192/// - `lower.len()` must equal `upper.len()`
193///
194/// This is because an n×n tridiagonal matrix has:
195/// - n diagonal elements
196/// - (n-1) lower-diagonal elements
197/// - (n-1) upper-diagonal elements
198///
199/// # Examples
200///
201/// ```rust
202/// use iterative_solvers::utils::sparse::tridiagonal_csr;
203///
204/// let diagonal = vec![2.0, 3.0, 4.0];
205/// let lower = vec![1.0, 1.0];
206/// let upper = vec![1.0, 1.0];
207///
208/// let result = tridiagonal_csr(&diagonal, &lower, &upper).unwrap();
209/// // Creates:
210/// // [2.0, 1.0, 0.0]
211/// // [1.0, 3.0, 1.0]
212/// // [0.0, 1.0, 4.0]
213/// ```
214///
215/// # Errors
216///
217/// Returns `IterSolverError::DimensionError` if the input vectors have incompatible dimensions.
218pub fn tridiagonal_csr(
219 diagonal: &[f64],
220 lower: &[f64],
221 upper: &[f64],
222) -> IterSolverResult<SparseCsrMatrix<f64>> {
223 if diagonal.len() != lower.len() + 1 || lower.len() != upper.len() {
224 return Err(IterSolverError::DimensionError(format!(
225 "For tridiagonal matrix, the length of `diagonal` {}, the length of `lower` {} and `upper` {} do not match",
226 diagonal.len(),
227 lower.len(),
228 upper.len()
229 )));
230 }
231 #[cfg(not(feature = "ndarray"))]
232 {
233 Ok(diagm_csr(diagonal, 0) + diagm_csr(lower, -1) + diagm_csr(upper, 1))
234 }
235 #[cfg(feature = "ndarray")]
236 {
237 Ok(&(&diagm_csr(diagonal, 0) + &diagm_csr(lower, -1)) + &diagm_csr(upper, 1))
238 }
239}
240
241/// Creates a symmetric tridiagonal sparse CSR matrix from diagonal and sub-diagonal vectors.
242///
243/// This function constructs a symmetric tridiagonal matrix where the lower-diagonal
244/// and upper-diagonal elements are identical. This is a common structure in numerical
245/// methods, particularly for solving differential equations and eigenvalue problems.
246///
247/// # Arguments
248///
249/// * `diagonal` - A slice containing the main diagonal elements
250/// * `sub_diagonal` - A slice containing the lower-diagonal elements, which will be
251/// mirrored to create the upper-diagonal
252///
253/// # Dimension Requirements
254///
255/// For a valid symmetric tridiagonal matrix:
256/// - `diagonal.len()` must equal `sub_diagonal.len() + 1`
257///
258/// # Examples
259///
260/// ```rust
261/// use iterative_solvers::utils::sparse::symmetric_tridiagonal_csr;
262///
263/// let diagonal = vec![2.0, 3.0, 4.0];
264/// let sub_diagonal = vec![1.0, 1.5];
265///
266/// let result = symmetric_tridiagonal_csr(&diagonal, &sub_diagonal).unwrap();
267/// // Creates:
268/// // [2.0, 1.0, 0.0]
269/// // [1.0, 3.0, 1.5]
270/// // [0.0, 1.5, 4.0]
271/// ```
272///
273/// # Errors
274///
275/// Returns `IterSolverError::DimensionError` if the input vectors have incompatible dimensions.
276///
277/// # Note
278///
279/// This function internally calls `tridiagonal(diagonal, sub_diagonal, sub_diagonal)`,
280/// ensuring that the lower and upper diagonals are identical.
281pub fn symmetric_tridiagonal_csr(
282 diagonal: &[f64],
283 sub_diagonal: &[f64],
284) -> IterSolverResult<SparseCsrMatrix<f64>> {
285 tridiagonal_csr(diagonal, sub_diagonal, sub_diagonal)
286}
287
288/// Creates a diagonal sparse CSC matrix with the given data placed on a specified diagonal.
289///
290/// This function constructs a matrix where the provided data is placed on a diagonal
291/// that can be offset from the main diagonal. The resulting matrix size is determined
292/// by the length of the data and the absolute value of the offset.
293///
294/// # Arguments
295///
296/// * `data` - A slice of f64 values to be placed on the diagonal
297/// * `offset` - The diagonal offset:
298/// - `0`: Main diagonal
299/// - Positive: above the main diagonal (upper-diagonal)
300/// - Negative: below the main diagonal (lower-diagonal)
301///
302/// # Returns
303///
304/// A `CscMatrix<f64>` containing the diagonal matrix. If `data` is empty, returns
305/// a 0×0 matrix.
306///
307/// # Examples
308///
309/// ```rust
310/// use iterative_solvers::utils::sparse::diagm_csr;
311///
312/// // Main diagonal
313/// let data = vec![1.0, 2.0, 3.0];
314/// let mat = diagm_csr(&data, 0);
315/// // Creates:
316/// // [1.0, 0.0, 0.0]
317/// // [0.0, 2.0, 0.0]
318/// // [0.0, 0.0, 3.0]
319///
320/// // Upper-diagonal (offset = 1)
321/// let mat = diagm_csr(&data, 1);
322/// // Creates:
323/// // [0.0, 1.0, 0.0, 0.0]
324/// // [0.0, 0.0, 2.0, 0.0]
325/// // [0.0, 0.0, 0.0, 3.0]
326/// // [0.0, 0.0, 0.0, 0.0]
327/// ```
328pub fn diagm_csc(data: &[f64], offset: isize) -> SparseCscMatrix<f64> {
329 if data.is_empty() {
330 return empty_spcsc();
331 }
332
333 let offset_usize = offset.unsigned_abs();
334 let n = data.len() + offset_usize;
335
336 let tmp = match offset {
337 0 => {
338 #[cfg(feature = "nalgebra")]
339 {
340 let mut coo = CooMatrix::<f64>::new(n, n);
341
342 data.iter()
343 .enumerate()
344 .for_each(|(i, &val)| coo.push(i, i, val));
345 coo
346 }
347 #[cfg(feature = "faer")]
348 {
349 data.iter()
350 .enumerate()
351 .map(|(i, &val)| Triplet::new(i, i, val))
352 .collect::<Vec<_>>()
353 }
354 #[cfg(feature = "ndarray")]
355 {
356 let indptr = (0..=data.len())
357 .chain(std::iter::repeat_n(data.len(), n - data.len()))
358 .collect::<Vec<_>>();
359 let indices = (0..data.len()).collect::<Vec<_>>();
360
361 (indptr, indices)
362 }
363 }
364 offset => {
365 if offset > 0 {
366 #[cfg(feature = "nalgebra")]
367 {
368 let mut coo = CooMatrix::<f64>::new(n, n);
369 data.iter()
370 .enumerate()
371 .for_each(|(i, &val)| coo.push(i, i + offset_usize, val));
372 coo
373 }
374 #[cfg(feature = "faer")]
375 {
376 data.iter()
377 .enumerate()
378 .map(|(i, &val)| Triplet::new(i, i + offset_usize, val))
379 .collect::<Vec<_>>()
380 }
381 #[cfg(feature = "ndarray")]
382 {
383 let indptr = std::iter::repeat_n(0, offset_usize + 1)
384 .chain(1..=data.len())
385 .chain(std::iter::repeat_n(
386 data.len(),
387 n - (offset_usize + data.len()),
388 ))
389 .collect::<Vec<_>>();
390 let indices = (0..data.len()).collect::<Vec<_>>();
391 (indptr, indices)
392 }
393 } else {
394 #[cfg(feature = "nalgebra")]
395 {
396 let mut coo = CooMatrix::<f64>::new(n, n);
397 data.iter()
398 .enumerate()
399 .for_each(|(i, &val)| coo.push(i + offset_usize, i, val));
400 coo
401 }
402 #[cfg(feature = "faer")]
403 {
404 data.iter()
405 .enumerate()
406 .map(|(i, &val)| Triplet::new(i + offset_usize, i, val))
407 .collect::<Vec<_>>()
408 }
409 #[cfg(feature = "ndarray")]
410 {
411 let indptr = std::iter::repeat_n(0, 1)
412 .chain(1..=data.len())
413 .chain(std::iter::repeat_n(data.len(), n - data.len()))
414 .collect::<Vec<_>>();
415 let indices = (offset_usize..offset_usize + data.len()).collect::<Vec<_>>();
416 (indptr, indices)
417 }
418 }
419 }
420 };
421 #[cfg(feature = "nalgebra")]
422 {
423 SparseCscMatrix::from(&tmp)
424 }
425 #[cfg(feature = "faer")]
426 {
427 SparseCscMatrix::try_new_from_triplets(n, n, &tmp).unwrap()
428 }
429 #[cfg(feature = "ndarray")]
430 {
431 CsMat::new_csc((n, n), tmp.0, tmp.1, data.to_vec())
432 }
433}
434
435/// Creates a tridiagonal sparse CSC matrix from diagonal, lower diagonal, and upper diagonal vectors.
436///
437/// This function constructs a tridiagonal matrix where:
438/// - The main diagonal contains elements from the `diagonal` vector
439/// - The lower-diagonal (below main) contains elements from the `lower` vector
440/// - The upper-diagonal (above main) contains elements from the `upper` vector
441///
442/// # Arguments
443///
444/// * `diagonal` - A slice containing the main diagonal elements
445/// * `lower` - A slice containing the lower diagonal elements (lower-diagonal)
446/// * `upper` - A slice containing the upper diagonal elements (upper-diagonal)
447///
448/// # Dimension Requirements
449///
450/// For a valid tridiagonal matrix:
451/// - `diagonal.len()` must equal `lower.len() + 1`
452/// - `lower.len()` must equal `upper.len()`
453///
454/// This is because an n×n tridiagonal matrix has:
455/// - n diagonal elements
456/// - (n-1) lower-diagonal elements
457/// - (n-1) upper-diagonal elements
458///
459/// # Examples
460///
461/// ```rust
462/// use iterative_solvers::utils::sparse::tridiagonal_csr;
463///
464/// let diagonal = vec![2.0, 3.0, 4.0];
465/// let lower = vec![1.0, 1.0];
466/// let upper = vec![1.0, 1.0];
467///
468/// let result = tridiagonal_csr(&diagonal, &lower, &upper).unwrap();
469/// // Creates:
470/// // [2.0, 1.0, 0.0]
471/// // [1.0, 3.0, 1.0]
472/// // [0.0, 1.0, 4.0]
473/// ```
474///
475/// # Errors
476///
477/// Returns `IterSolverError::DimensionError` if the input vectors have incompatible dimensions.
478pub fn tridiagonal_csc(
479 diagonal: &[f64],
480 lower: &[f64],
481 upper: &[f64],
482) -> IterSolverResult<SparseCscMatrix<f64>> {
483 if diagonal.len() != lower.len() + 1 || lower.len() != upper.len() {
484 return Err(IterSolverError::DimensionError(format!(
485 "For tridiagonal matrix, the length of `diagonal` {}, the length of `lower` {} and `upper` {} do not match",
486 diagonal.len(),
487 lower.len(),
488 upper.len()
489 )));
490 }
491 #[cfg(not(feature = "ndarray"))]
492 {
493 Ok(diagm_csc(diagonal, 0) + diagm_csc(lower, -1) + diagm_csc(upper, 1))
494 }
495 #[cfg(feature = "ndarray")]
496 {
497 Ok(&(&diagm_csc(diagonal, 0) + &diagm_csc(lower, -1)) + &diagm_csc(upper, 1))
498 }
499}
500
501/// Creates a symmetric tridiagonal sparse CSC matrix from diagonal and sub-diagonal vectors.
502///
503/// This function constructs a symmetric tridiagonal matrix where the lower-diagonal
504/// and upper-diagonal elements are identical. This is a common structure in numerical
505/// methods, particularly for solving differential equations and eigenvalue problems.
506///
507/// # Arguments
508///
509/// * `diagonal` - A slice containing the main diagonal elements
510/// * `sub_diagonal` - A slice containing the lower-diagonal elements, which will be
511/// mirrored to create the upper-diagonal
512///
513/// # Dimension Requirements
514///
515/// For a valid symmetric tridiagonal matrix:
516/// - `diagonal.len()` must equal `sub_diagonal.len() + 1`
517///
518/// # Examples
519///
520/// ```rust
521/// use iterative_solvers::utils::sparse::symmetric_tridiagonal_csc;
522///
523/// let diagonal = vec![2.0, 3.0, 4.0];
524/// let sub_diagonal = vec![1.0, 1.5];
525///
526/// let result = symmetric_tridiagonal_csc(&diagonal, &sub_diagonal).unwrap();
527/// // Creates:
528/// // [2.0, 1.0, 0.0]
529/// // [1.0, 3.0, 1.5]
530/// // [0.0, 1.5, 4.0]
531/// ```
532///
533/// # Errors
534///
535/// Returns `IterSolverError::DimensionError` if the input vectors have incompatible dimensions.
536///
537/// # Note
538///
539/// This function internally calls `tridiagonal(diagonal, sub_diagonal, sub_diagonal)`,
540/// ensuring that the lower and upper diagonals are identical.
541pub fn symmetric_tridiagonal_csc(
542 diagonal: &[f64],
543 sub_diagonal: &[f64],
544) -> IterSolverResult<SparseCscMatrix<f64>> {
545 tridiagonal_csc(diagonal, sub_diagonal, sub_diagonal)
546}
547
548/// Creates a sparse csr diagonal matrix from a list of diagonals and their offsets.
549///
550/// # Arguments
551///
552/// * `diagonals` - A vector of vectors, where each inner vector contains the diagonal elements
553/// * `offsets` - A vector of offsets for each diagonal
554///
555/// # Examples
556///
557/// ```rust
558/// use iterative_solvers::utils::sparse::diags_csr;
559///
560/// let diagonals = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0], vec![6.0]];
561/// let offsets = vec![0, 1, 2];
562///
563/// let result = diags_csr(diagonals, offsets);
564/// // Creates:
565/// // [1.0, 4.0, 6.0]
566/// // [0.0, 2.0, 5.0]
567/// // [0.0, 0.0, 3.0]
568/// ```
569pub fn diags_csr(
570 diagonals: Vec<Vec<f64>>,
571 offsets: Vec<isize>,
572) -> IterSolverResult<SparseCsrMatrix<f64>> {
573 if diagonals.len() != offsets.len() {
574 return Err(IterSolverError::DimensionError(format!(
575 "The length of `diagonals` {} and `offsets` {} do not match",
576 diagonals.len(),
577 offsets.len()
578 )));
579 }
580 if diagonals.is_empty() {
581 return Ok(empty_spcsr());
582 }
583
584 // Check for duplicate offsets
585 let n = diagonals[0].len() + offsets[0].unsigned_abs();
586 let mut unique_offsets = std::collections::HashSet::new();
587 for (index, (diag, offset)) in diagonals.iter().zip(offsets.iter()).enumerate() {
588 if !unique_offsets.insert(*offset) {
589 return Err(IterSolverError::InvalidInput(
590 "Duplicate offsets are not allowed".to_string(),
591 ));
592 }
593 if diag.len() + offset.unsigned_abs() != n {
594 return Err(IterSolverError::DimensionError(format!(
595 "The {}th diagonal's length {} and its offset {} do not match",
596 index,
597 diag.len(),
598 offset
599 )));
600 }
601 }
602
603 let mut res = diagm_csr(&diagonals[0], offsets[0]);
604
605 for (diag, offset) in diagonals.iter().zip(offsets.iter()) {
606 res = &res + &diagm_csr(diag, *offset);
607 }
608 Ok(res)
609}
610
611/// Creates a sparse csr diagonal matrix from a list of diagonals and their offsets.
612///
613/// # Arguments
614///
615/// * `diagonals` - A vector of vectors, where each inner vector contains the diagonal elements
616/// * `offsets` - A vector of offsets for each diagonal
617///
618/// # Examples
619///
620/// ```rust
621/// use iterative_solvers::utils::sparse::diags_csr;
622///
623/// let diagonals = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0], vec![6.0]];
624/// let offsets = vec![0, 1, 2];
625///
626/// let result = diags_csr(diagonals, offsets);
627/// // Creates:
628/// // [1.0, 4.0, 6.0]
629/// // [0.0, 2.0, 5.0]
630/// // [0.0, 0.0, 3.0]
631/// ```
632pub fn diags_csc(
633 diagonals: Vec<Vec<f64>>,
634 offsets: Vec<isize>,
635) -> IterSolverResult<SparseCscMatrix<f64>> {
636 if diagonals.len() != offsets.len() {
637 return Err(IterSolverError::DimensionError(format!(
638 "The length of `diagonals` {} and `offsets` {} do not match",
639 diagonals.len(),
640 offsets.len()
641 )));
642 }
643 if diagonals.is_empty() {
644 return Ok(empty_spcsc());
645 }
646
647 // Check for duplicate offsets
648 let n = diagonals[0].len() + offsets[0].unsigned_abs();
649 let mut unique_offsets = std::collections::HashSet::new();
650 for (index, (diag, offset)) in diagonals.iter().zip(offsets.iter()).enumerate() {
651 if !unique_offsets.insert(*offset) {
652 return Err(IterSolverError::InvalidInput(
653 "Duplicate offsets are not allowed".to_string(),
654 ));
655 }
656 if diag.len() + offset.unsigned_abs() != n {
657 return Err(IterSolverError::DimensionError(format!(
658 "The {}th diagonal's length {} and its offset {} do not match",
659 index,
660 diag.len(),
661 offset
662 )));
663 }
664 }
665
666 let mut res = diagm_csc(&diagonals[0], offsets[0]);
667
668 for (diag, offset) in diagonals.iter().zip(offsets.iter()) {
669 res = &res + &diagm_csc(diag, *offset);
670 }
671 Ok(res)
672}
673
674#[cfg(test)]
675mod tests {
676 use super::super::dense::diagm;
677 use super::*;
678 #[cfg(feature = "nalgebra")]
679 use nalgebra::DMatrix;
680
681 #[test]
682 #[cfg(feature = "nalgebra")]
683 fn test_diagm_csr_main_diagonal() {
684 let data = vec![1.0, 2.0, 3.0];
685 let mat = diagm_csr(&data, 0);
686
687 // 转换为稠密矩阵进行验证
688 let dense = DMatrix::from(&mat);
689 let expected = diagm(&data, 0);
690
691 assert_eq!(dense, expected);
692 }
693
694 #[test]
695 #[cfg(feature = "nalgebra")]
696 fn test_diagm_csr_upper_diagonal() {
697 let data = vec![1.0, 2.0, 3.0];
698 let mat = diagm_csr(&data, 1);
699
700 // 转换为稠密矩阵进行验证
701 let dense = DMatrix::from(&mat);
702 let expected = diagm(&data, 1);
703
704 assert_eq!(dense, expected);
705 }
706
707 #[test]
708 #[cfg(feature = "nalgebra")]
709 fn test_diagm_csr_lower_diagonal() {
710 let data = vec![1.0, 2.0, 3.0];
711 let mat = diagm_csr(&data, -1);
712
713 // 转换为稠密矩阵进行验证
714 let dense = DMatrix::from(&mat);
715 let expected = diagm(&data, -1);
716
717 assert_eq!(dense, expected);
718 }
719
720 #[test]
721 #[cfg(feature = "nalgebra")]
722 fn test_diagm_csr_empty() {
723 let data: Vec<f64> = vec![];
724 let mat = diagm_csr(&data, 0);
725
726 assert_eq!(mat.nrows(), 0);
727 assert_eq!(mat.ncols(), 0);
728 }
729
730 #[test]
731 #[cfg(feature = "nalgebra")]
732 fn test_diagm_csr_large_offset() {
733 let data = vec![1.0, 2.0];
734 let mat = diagm_csr(&data, 10);
735
736 // 转换为稠密矩阵进行验证
737 let dense = DMatrix::from(&mat);
738 let expected = diagm(&data, 10);
739
740 assert_eq!(dense, expected);
741 }
742
743 #[test]
744 #[cfg(any(feature = "faer", feature = "ndarray"))]
745 fn test_diagm_csr_main_diagonal() {
746 let data = vec![1.0, 2.0, 3.0];
747 let mat = diagm_csr(&data, 0);
748
749 // 转换为稠密矩阵进行验证
750 let dense = mat.to_dense();
751 let expected = diagm(&data, 0);
752
753 assert_eq!(dense, expected);
754 }
755
756 #[test]
757 #[cfg(any(feature = "faer", feature = "ndarray"))]
758 fn test_diagm_csr_upper_diagonal() {
759 let data = vec![1.0, 2.0, 3.0];
760 let mat = diagm_csr(&data, 1);
761
762 // 转换为稠密矩阵进行验证
763 let dense = mat.to_dense();
764 let expected = diagm(&data, 1);
765
766 assert_eq!(dense, expected);
767 }
768
769 #[test]
770 #[cfg(any(feature = "faer", feature = "ndarray"))]
771 fn test_diagm_csr_lower_diagonal() {
772 let data = vec![1.0, 2.0, 3.0];
773 let mat = diagm_csr(&data, -1);
774
775 // 转换为稠密矩阵进行验证
776 let dense = mat.to_dense();
777 let expected = diagm(&data, -1);
778
779 assert_eq!(dense, expected);
780 }
781
782 #[test]
783 #[cfg(any(feature = "faer", feature = "ndarray"))]
784 fn test_diagm_csr_empty() {
785 use crate::MatrixOp;
786
787 let data: Vec<f64> = vec![];
788 let mat = diagm_csr(&data, 0);
789
790 assert_eq!(mat.nrows(), 0);
791 assert_eq!(mat.ncols(), 0);
792 }
793
794 #[test]
795 #[cfg(any(feature = "faer", feature = "ndarray"))]
796 fn test_diagm_csr_large_offset() {
797 let data = vec![1.0, 2.0];
798 let mat = diagm_csr(&data, 10);
799
800 // 转换为稠密矩阵进行验证
801 let dense = mat.to_dense();
802 let expected = diagm(&data, 10);
803
804 assert_eq!(dense, expected);
805 }
806}