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}