use crate::csr::CsrMatrix;
use crate::error::{SparseError, SparseResult};
use scirs2_core::numeric::{SparseElement, Zero};
#[allow(dead_code)]
pub fn identity(n: usize) -> SparseResult<CsrMatrix<f64>> {
if n == 0 {
return Err(SparseError::ValueError(
"Matrix size must be positive".to_string(),
));
}
let mut data = Vec::with_capacity(n);
let mut row_indices = Vec::with_capacity(n);
let mut col_indices = Vec::with_capacity(n);
for i in 0..n {
data.push(1.0);
row_indices.push(i);
col_indices.push(i);
}
CsrMatrix::new(data, row_indices, col_indices, (n, n))
}
#[allow(dead_code)]
pub fn diag(diag: &[f64]) -> SparseResult<CsrMatrix<f64>> {
if diag.is_empty() {
return Err(SparseError::ValueError(
"Diagonal vector must not be empty".to_string(),
));
}
let n = diag.len();
let mut data = Vec::with_capacity(n);
let mut row_indices = Vec::with_capacity(n);
let mut col_indices = Vec::with_capacity(n);
for (i, &val) in diag.iter().enumerate() {
if val != 0.0 {
data.push(val);
row_indices.push(i);
col_indices.push(i);
}
}
CsrMatrix::new(data, row_indices, col_indices, (n, n))
}
#[allow(dead_code)]
pub fn density(shape: (usize, usize), nnz: usize) -> f64 {
let (rows, cols) = shape;
if rows == 0 || cols == 0 {
return 0.0;
}
nnz as f64 / (rows * cols) as f64
}
#[allow(dead_code)]
pub fn is_symmetric(matrix: &CsrMatrix<f64>) -> bool {
let (rows, cols) = matrix.shape();
if rows != cols {
return false;
}
let transposed = matrix.transpose();
let a_dense = matrix.to_dense();
let at_dense = transposed.to_dense();
for i in 0..rows {
for j in 0..cols {
if (a_dense[i][j] - at_dense[i][j]).abs() > 1e-10 {
return false;
}
}
}
true
}
#[allow(dead_code)]
pub fn random(shape: (usize, usize), density: f64) -> SparseResult<CsrMatrix<f64>> {
if !(0.0..=1.0).contains(&density) {
return Err(SparseError::ValueError(format!(
"Density must be between 0 and 1, got {density}"
)));
}
let (rows, cols) = shape;
if rows == 0 || cols == 0 {
return Err(SparseError::ValueError(
"Matrix dimensions must be positive".to_string(),
));
}
let nnz = (rows * cols) as f64 * density;
let nnz = nnz.round() as usize;
if nnz == 0 {
return CsrMatrix::new(Vec::new(), Vec::new(), Vec::new(), shape);
}
let mut data = Vec::with_capacity(nnz);
let mut row_indices = Vec::with_capacity(nnz);
let mut col_indices = Vec::with_capacity(nnz);
let mut used = vec![vec![false; cols]; rows];
let mut count = 0;
use scirs2_core::random::{Rng, RngExt};
let mut rng = scirs2_core::random::rng();
while count < nnz {
let i = rng.random_range(0..rows);
let j = rng.random_range(0..cols);
if !used[i][j] {
used[i][j] = true;
data.push(rng.random_range(-1.0..1.0));
row_indices.push(i);
col_indices.push(j);
count += 1;
}
}
CsrMatrix::new(data, row_indices, col_indices, shape)
}
#[allow(dead_code)]
pub fn sparsity_pattern<T>(matrix: &CsrMatrix<T>) -> Vec<Vec<usize>>
where
T: Clone + Copy + Zero + PartialEq + SparseElement,
{
let (rows, cols) = matrix.shape();
let dense = matrix.to_dense();
let mut pattern = vec![vec![0; cols]; rows];
for i in 0..rows {
for j in 0..cols {
if dense[i][j] != T::sparse_zero() {
pattern[i][j] = 1;
}
}
}
pattern
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_identity() {
let n = 3;
let eye = identity(n).expect("Operation failed");
assert_eq!(eye.shape(), (n, n));
assert_eq!(eye.nnz(), n);
let dense = eye.to_dense();
for (i, row) in dense.iter().enumerate() {
for (j, &value) in row.iter().enumerate() {
let expected = if i == j { 1.0 } else { 0.0 };
assert_eq!(value, expected);
}
}
}
#[test]
fn test_diag() {
let diag_elements = [1.0, 2.0, 3.0];
let d = diag(&diag_elements).expect("Operation failed");
assert_eq!(d.shape(), (3, 3));
assert_eq!(d.nnz(), 3);
let dense = d.to_dense();
for i in 0..3 {
for j in 0..3 {
let expected = if i == j { diag_elements[i] } else { 0.0 };
assert_eq!(dense[i][j], expected);
}
}
}
#[test]
fn test_density() {
assert_relative_eq!(density((4, 4), 4), 0.25, epsilon = 1e-10);
assert_relative_eq!(density((10, 10), 0), 0.0, epsilon = 1e-10);
assert_relative_eq!(density((5, 5), 25), 1.0, epsilon = 1e-10);
}
#[test]
fn test_is_symmetric() {
let rows = vec![0, 0, 1, 1, 2, 2];
let cols = vec![0, 1, 0, 1, 0, 2];
let data = vec![1.0, 2.0, 2.0, 3.0, 0.0, 4.0]; let shape = (3, 3);
let matrix = CsrMatrix::new(data, rows, cols, shape).expect("Operation failed");
assert!(is_symmetric(&matrix));
let rows = vec![0, 0, 1, 1, 2, 2];
let cols = vec![0, 1, 0, 1, 0, 2];
let data = vec![1.0, 2.0, 3.0, 3.0, 0.0, 4.0]; let shape = (3, 3);
let matrix = CsrMatrix::new(data, rows, cols, shape).expect("Operation failed");
assert!(!is_symmetric(&matrix));
}
#[test]
fn test_sparsity_pattern() {
let rows = vec![0, 0, 1, 2, 2];
let cols = vec![0, 2, 2, 0, 1];
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let shape = (3, 3);
let matrix = CsrMatrix::new(data, rows, cols, shape).expect("Operation failed");
let pattern = sparsity_pattern(&matrix);
let expected = vec![vec![1, 0, 1], vec![0, 0, 1], vec![1, 1, 0]];
assert_eq!(pattern, expected);
}
}