#![allow(unused_variables)]
#![allow(unused_assignments)]
#![allow(unused_mut)]
use scirs2_core::ndarray::Array1;
use scirs2_core::numeric::{Float, SparseElement};
use scirs2_core::random::seq::SliceRandom;
use scirs2_core::random::{Rng, RngExt, SeedableRng};
use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Sub};
use crate::coo_array::CooArray;
use crate::csr_array::CsrArray;
use crate::dok_array::DokArray;
use crate::error::{SparseError, SparseResult};
use crate::lil_array::LilArray;
use crate::sparray::SparseArray;
use scirs2_core::parallel_ops::*;
#[allow(dead_code)]
pub fn eye_array<T>(n: usize, format: &str) -> SparseResult<Box<dyn SparseArray<T>>>
where
T: SparseElement + Div<Output = T> + Float + 'static,
{
if n == 0 {
return Err(SparseError::ValueError(
"Matrix dimension must be positive".to_string(),
));
}
let mut rows = Vec::with_capacity(n);
let mut cols = Vec::with_capacity(n);
let mut data = Vec::with_capacity(n);
for i in 0..n {
rows.push(i);
cols.push(i);
data.push(T::sparse_one());
}
match format.to_lowercase().as_str() {
"csr" => CsrArray::from_triplets(&rows, &cols, &data, (n, n), true)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"coo" => CooArray::from_triplets(&rows, &cols, &data, (n, n), true)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"dok" => DokArray::from_triplets(&rows, &cols, &data, (n, n))
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"lil" => LilArray::from_triplets(&rows, &cols, &data, (n, n))
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
_ => Err(SparseError::ValueError(format!(
"Unknown sparse format: {format}. Supported formats are 'csr', 'coo', 'dok', and 'lil'"
))),
}
}
#[allow(dead_code)]
pub fn eye_array_k<T>(
m: usize,
n: usize,
k: isize,
format: &str,
) -> SparseResult<Box<dyn SparseArray<T>>>
where
T: SparseElement + Div<Output = T> + Float + 'static,
{
if m == 0 || n == 0 {
return Err(SparseError::ValueError(
"Matrix dimensions must be positive".to_string(),
));
}
let mut rows = Vec::new();
let mut cols = Vec::new();
let mut data = Vec::new();
if k >= 0 {
let k_usize = k as usize;
let len = std::cmp::min(m, n.saturating_sub(k_usize));
for i in 0..len {
rows.push(i);
cols.push(i + k_usize);
data.push(T::sparse_one());
}
} else {
let k_abs = (-k) as usize;
let len = std::cmp::min(m.saturating_sub(k_abs), n);
for i in 0..len {
rows.push(i + k_abs);
cols.push(i);
data.push(T::sparse_one());
}
}
match format.to_lowercase().as_str() {
"csr" => CsrArray::from_triplets(&rows, &cols, &data, (m, n), true)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"coo" => CooArray::from_triplets(&rows, &cols, &data, (m, n), true)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"dok" => DokArray::from_triplets(&rows, &cols, &data, (m, n))
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"lil" => LilArray::from_triplets(&rows, &cols, &data, (m, n))
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
_ => Err(SparseError::ValueError(format!(
"Unknown sparse format: {format}. Supported formats are 'csr', 'coo', 'dok', and 'lil'"
))),
}
}
#[allow(dead_code)]
pub fn diags_array<T>(
diagonals: &[Array1<T>],
offsets: &[isize],
shape: (usize, usize),
format: &str,
) -> SparseResult<Box<dyn SparseArray<T>>>
where
T: SparseElement + Div<Output = T> + Float + 'static,
{
if diagonals.len() != offsets.len() {
return Err(SparseError::InconsistentData {
reason: "Number of diagonals must match number of offsets".to_string(),
});
}
if shape.0 == 0 || shape.1 == 0 {
return Err(SparseError::ValueError(
"Matrix dimensions must be positive".to_string(),
));
}
let (m, n) = shape;
let mut rows = Vec::new();
let mut cols = Vec::new();
let mut data = Vec::new();
for (i, (diag, &offset)) in diagonals.iter().zip(offsets.iter()).enumerate() {
if offset >= 0 {
let offset_usize = offset as usize;
let max_len = std::cmp::min(m, n.saturating_sub(offset_usize));
if diag.len() > max_len {
return Err(SparseError::InconsistentData {
reason: format!("Diagonal {i} is too long ({} > {})", diag.len(), max_len),
});
}
for (j, &value) in diag.iter().enumerate() {
if !SparseElement::is_zero(&value) {
rows.push(j);
cols.push(j + offset_usize);
data.push(value);
}
}
} else {
let offset_abs = (-offset) as usize;
let max_len = std::cmp::min(m.saturating_sub(offset_abs), n);
if diag.len() > max_len {
return Err(SparseError::InconsistentData {
reason: format!("Diagonal {i} is too long ({} > {})", diag.len(), max_len),
});
}
for (j, &value) in diag.iter().enumerate() {
if !SparseElement::is_zero(&value) {
rows.push(j + offset_abs);
cols.push(j);
data.push(value);
}
}
}
}
match format.to_lowercase().as_str() {
"csr" => CsrArray::from_triplets(&rows, &cols, &data, shape, false)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"coo" => CooArray::from_triplets(&rows, &cols, &data, shape, false)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"dok" => DokArray::from_triplets(&rows, &cols, &data, shape)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"lil" => LilArray::from_triplets(&rows, &cols, &data, shape)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
_ => Err(SparseError::ValueError(format!(
"Unknown sparse format: {format}. Supported formats are 'csr', 'coo', 'dok', and 'lil'"
))),
}
}
#[allow(dead_code)]
pub fn random_array<T>(
shape: (usize, usize),
density: f64,
seed: Option<u64>,
format: &str,
) -> SparseResult<Box<dyn SparseArray<T>>>
where
T: Float + SparseElement + Div<Output = T> + 'static,
{
let (m, n) = shape;
if !(0.0..=1.0).contains(&density) {
return Err(SparseError::ValueError(
"Density must be between 0.0 and 1.0".to_string(),
));
}
if m == 0 || n == 0 {
return Err(SparseError::ValueError(
"Matrix dimensions must be positive".to_string(),
));
}
let nnz = (m * n) as f64 * density;
let nnz = nnz.round() as usize;
let mut rows = Vec::with_capacity(nnz);
let mut cols = Vec::with_capacity(nnz);
let mut data = Vec::with_capacity(nnz);
let mut rng = if let Some(seed_value) = seed {
scirs2_core::random::rngs::StdRng::seed_from_u64(seed_value)
} else {
let seed = scirs2_core::random::random::<u64>();
scirs2_core::random::rngs::StdRng::seed_from_u64(seed)
};
let total = m * n;
if density > 0.4 {
let mut indices: Vec<usize> = (0..total).collect();
indices.shuffle(&mut rng);
for &idx in indices.iter().take(nnz) {
let row = idx / n;
let col = idx % n;
rows.push(row);
cols.push(col);
let mut val: f64 = rng.random_range(-1.0..1.0);
while val.abs() < 1e-10 {
val = rng.random_range(-1.0..1.0);
}
data.push(T::from(val).expect("Operation failed"));
}
} else {
let mut positions = std::collections::HashSet::with_capacity(nnz);
while positions.len() < nnz {
let row = rng.random_range(0..m);
let col = rng.random_range(0..n);
let pos = row * n + col;
if positions.insert(pos) {
rows.push(row);
cols.push(col);
let mut val: f64 = rng.random_range(-1.0..1.0);
while val.abs() < 1e-10 {
val = rng.random_range(-1.0..1.0);
}
data.push(T::from(val).expect("Operation failed"));
}
}
}
match format.to_lowercase().as_str() {
"csr" => CsrArray::from_triplets(&rows, &cols, &data, shape, false)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"coo" => CooArray::from_triplets(&rows, &cols, &data, shape, false)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"dok" => DokArray::from_triplets(&rows, &cols, &data, shape)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"lil" => LilArray::from_triplets(&rows, &cols, &data, shape)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
_ => Err(SparseError::ValueError(format!(
"Unknown sparse format: {format}. Supported formats are 'csr', 'coo', 'dok', and 'lil'"
))),
}
}
#[allow(dead_code)]
pub fn random_array_parallel<T>(
shape: (usize, usize),
density: f64,
seed: Option<u64>,
format: &str,
parallel_threshold: usize,
) -> SparseResult<Box<dyn SparseArray<T>>>
where
T: Float + SparseElement + Div<Output = T> + Send + Sync + 'static,
{
if !(0.0..=1.0).contains(&density) {
return Err(SparseError::ValueError(
"Density must be between 0.0 and 1.0".to_string(),
));
}
let (rows, cols) = shape;
if rows == 0 || cols == 0 {
return Err(SparseError::ValueError(
"Matrix dimensions must be positive".to_string(),
));
}
let total_elements = rows * cols;
let expected_nnz = (total_elements as f64 * density) as usize;
if total_elements >= parallel_threshold && expected_nnz >= 1000 {
parallel_random_construction(shape, density, seed, format)
} else {
random_array(shape, density, seed, format)
}
}
#[allow(dead_code)]
fn parallel_random_construction<T>(
shape: (usize, usize),
density: f64,
seed: Option<u64>,
format: &str,
) -> SparseResult<Box<dyn SparseArray<T>>>
where
T: Float + SparseElement + Div<Output = T> + Send + Sync + 'static,
{
let (rows, cols) = shape;
let total_elements = rows * cols;
let expected_nnz = (total_elements as f64 * density) as usize;
let num_chunks = std::cmp::min(scirs2_core::parallel_ops::get_num_threads(), rows.min(cols));
let chunk_size = std::cmp::max(1, rows / num_chunks);
let row_chunks: Vec<_> = (0..rows)
.collect::<Vec<_>>()
.chunks(chunk_size)
.map(|chunk| chunk.to_vec())
.collect();
let chunk_data: Vec<_> = row_chunks.iter().enumerate().collect();
let results: Vec<_> = parallel_map(&chunk_data, |(chunk_idx, row_chunk)| {
let mut local_rows = Vec::new();
let mut local_cols = Vec::new();
let mut local_data = Vec::new();
let chunk_seed = seed.unwrap_or(42) + *chunk_idx as u64 * 1000007; let mut rng = scirs2_core::random::rngs::StdRng::seed_from_u64(chunk_seed);
for &row in row_chunk.iter() {
let row_elements = cols;
let row_expected_nnz = std::cmp::max(1, (row_elements as f64 * density) as usize);
let mut col_indices: Vec<usize> = (0..cols).collect();
col_indices.shuffle(&mut rng);
for &col in col_indices.iter().take(row_expected_nnz) {
let mut val = rng.random_range(-1.0..1.0);
while val.abs() < 1e-10 {
val = rng.random_range(-1.0..1.0);
}
local_rows.push(row);
local_cols.push(col);
local_data.push(T::from(val).expect("Operation failed"));
}
}
(local_rows, local_cols, local_data)
});
let mut all_rows = Vec::new();
let mut all_cols = Vec::new();
let mut all_data = Vec::new();
for (mut rowschunk, mut cols_chunk, mut data_chunk) in results {
all_rows.extend(rowschunk);
all_cols.append(&mut cols_chunk);
all_data.append(&mut data_chunk);
}
match format.to_lowercase().as_str() {
"csr" => CsrArray::from_triplets(&all_rows, &all_cols, &all_data, shape, false)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"coo" => CooArray::from_triplets(&all_rows, &all_cols, &all_data, shape, false)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"dok" => DokArray::from_triplets(&all_rows, &all_cols, &all_data, shape)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
"lil" => LilArray::from_triplets(&all_rows, &all_cols, &all_data, shape)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>),
_ => Err(SparseError::ValueError(format!(
"Unknown sparse format: {format}. Supported formats are 'csr', 'coo', 'dok', and 'lil'"
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_eye_array() {
let eye = eye_array::<f64>(3, "csr").expect("Operation failed");
assert_eq!(eye.shape(), (3, 3));
assert_eq!(eye.nnz(), 3);
assert_eq!(eye.get(0, 0), 1.0);
assert_eq!(eye.get(1, 1), 1.0);
assert_eq!(eye.get(2, 2), 1.0);
assert_eq!(eye.get(0, 1), 0.0);
let eye_coo = eye_array::<f64>(3, "coo").expect("Operation failed");
assert_eq!(eye_coo.shape(), (3, 3));
assert_eq!(eye_coo.nnz(), 3);
let eye_dok = eye_array::<f64>(3, "dok").expect("Operation failed");
assert_eq!(eye_dok.shape(), (3, 3));
assert_eq!(eye_dok.nnz(), 3);
assert_eq!(eye_dok.get(0, 0), 1.0);
assert_eq!(eye_dok.get(1, 1), 1.0);
assert_eq!(eye_dok.get(2, 2), 1.0);
let eye_lil = eye_array::<f64>(3, "lil").expect("Operation failed");
assert_eq!(eye_lil.shape(), (3, 3));
assert_eq!(eye_lil.nnz(), 3);
assert_eq!(eye_lil.get(0, 0), 1.0);
assert_eq!(eye_lil.get(1, 1), 1.0);
assert_eq!(eye_lil.get(2, 2), 1.0);
}
#[test]
fn test_eye_array_k() {
let eye = eye_array_k::<f64>(3, 3, 0, "csr").expect("Operation failed");
assert_eq!(eye.get(0, 0), 1.0);
assert_eq!(eye.get(1, 1), 1.0);
assert_eq!(eye.get(2, 2), 1.0);
let superdiag = eye_array_k::<f64>(3, 4, 1, "csr").expect("Operation failed");
assert_eq!(superdiag.get(0, 1), 1.0);
assert_eq!(superdiag.get(1, 2), 1.0);
assert_eq!(superdiag.get(2, 3), 1.0);
let subdiag = eye_array_k::<f64>(4, 3, -1, "csr").expect("Operation failed");
assert_eq!(subdiag.get(1, 0), 1.0);
assert_eq!(subdiag.get(2, 1), 1.0);
assert_eq!(subdiag.get(3, 2), 1.0);
let eye_lil = eye_array_k::<f64>(3, 3, 0, "lil").expect("Operation failed");
assert_eq!(eye_lil.get(0, 0), 1.0);
assert_eq!(eye_lil.get(1, 1), 1.0);
assert_eq!(eye_lil.get(2, 2), 1.0);
}
#[test]
fn test_diags_array() {
let diags = vec![
Array1::from_vec(vec![1.0, 2.0, 3.0]), Array1::from_vec(vec![4.0, 5.0]), ];
let offsets = vec![0, 1];
let shape = (3, 3);
let result = diags_array(&diags, &offsets, shape, "csr").expect("Operation failed");
assert_eq!(result.shape(), (3, 3));
assert_eq!(result.get(0, 0), 1.0);
assert_eq!(result.get(1, 1), 2.0);
assert_eq!(result.get(2, 2), 3.0);
assert_eq!(result.get(0, 1), 4.0);
assert_eq!(result.get(1, 2), 5.0);
let diags = vec![
Array1::from_vec(vec![1.0, 2.0, 3.0]), Array1::from_vec(vec![4.0, 5.0]), Array1::from_vec(vec![6.0, 7.0]), ];
let offsets = vec![0, 1, -1];
let shape = (3, 3);
let result = diags_array(&diags, &offsets, shape, "csr").expect("Operation failed");
assert_eq!(result.shape(), (3, 3));
assert_eq!(result.get(0, 0), 1.0);
assert_eq!(result.get(1, 1), 2.0);
assert_eq!(result.get(2, 2), 3.0);
assert_eq!(result.get(0, 1), 4.0);
assert_eq!(result.get(1, 2), 5.0);
assert_eq!(result.get(1, 0), 6.0);
assert_eq!(result.get(2, 1), 7.0);
let result_lil = diags_array(&diags, &offsets, shape, "lil").expect("Operation failed");
assert_eq!(result_lil.shape(), (3, 3));
assert_eq!(result_lil.get(0, 0), 1.0);
assert_eq!(result_lil.get(1, 1), 2.0);
assert_eq!(result_lil.get(2, 2), 3.0);
assert_eq!(result_lil.get(0, 1), 4.0);
assert_eq!(result_lil.get(1, 2), 5.0);
assert_eq!(result_lil.get(1, 0), 6.0);
assert_eq!(result_lil.get(2, 1), 7.0);
}
#[test]
fn test_random_array() {
let shape = (10, 10);
let density = 0.3;
let random = random_array::<f64>(shape, density, None, "csr").expect("Operation failed");
assert_eq!(random.shape(), shape);
let nnz = random.nnz();
let expected_nnz = (shape.0 * shape.1) as f64 * density;
assert!(
(nnz as f64) > expected_nnz * 0.7,
"Too few non-zeros: {nnz}"
);
assert!(
(nnz as f64) < expected_nnz * 1.3,
"Too many non-zeros: {nnz}"
);
let random_seeded =
random_array::<f64>(shape, density, Some(42), "csr").expect("Operation failed");
assert_eq!(random_seeded.shape(), shape);
let random_lil =
random_array::<f64>((5, 5), 0.5, Some(42), "lil").expect("Operation failed");
assert_eq!(random_lil.shape(), (5, 5));
let nnz_lil = random_lil.nnz();
let expected_nnz_lil = 25.0 * 0.5;
assert!(
(nnz_lil as f64) > expected_nnz_lil * 0.7,
"Too few non-zeros in LIL: {nnz_lil}"
);
assert!(
(nnz_lil as f64) < expected_nnz_lil * 1.3,
"Too many non-zeros in LIL: {nnz_lil}"
);
}
}