use crate::coo_array::CooArray;
use crate::csr_array::CsrArray;
use crate::error::{SparseError, SparseResult};
use crate::sparray::{SparseArray, SparseSum};
use crate::sym_coo::SymCooArray;
use crate::sym_csr::SymCsrArray;
use scirs2_core::numeric::{Float, SparseElement};
use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Sub};
pub trait SymSparseArray<T>: SparseArray<T>
where
T: SparseElement + Div<Output = T> + Float + PartialOrd + 'static,
{
fn nnz_stored(&self) -> usize;
fn is_symmetric(&self) -> bool {
true
}
fn to_csr(&self) -> SparseResult<CsrArray<T>>;
fn to_coo(&self) -> SparseResult<CooArray<T>>;
fn to_sym_csr(&self) -> SparseResult<SymCsrArray<T>>;
fn to_sym_coo(&self) -> SparseResult<SymCooArray<T>>;
}
impl<T> SymSparseArray<T> for SymCsrArray<T>
where
T: Float
+ SparseElement
+ Debug
+ Copy
+ 'static
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ scirs2_core::simd_ops::SimdUnifiedOps
+ SparseElement
+ Send
+ Sync,
{
fn nnz_stored(&self) -> usize {
self.inner().nnz_stored()
}
fn to_csr(&self) -> SparseResult<CsrArray<T>> {
self.to_csr_array()
}
fn to_coo(&self) -> SparseResult<CooArray<T>> {
let csr_inner = self.inner();
let shape = csr_inner.shape;
let mut rows = Vec::new();
let mut cols = Vec::new();
let mut data = Vec::new();
for i in 0..shape.0 {
for j_ptr in csr_inner.indptr[i]..csr_inner.indptr[i + 1] {
let j = csr_inner.indices[j_ptr];
let val = csr_inner.data[j_ptr];
rows.push(i);
cols.push(j);
data.push(val);
if i != j {
rows.push(j);
cols.push(i);
data.push(val);
}
}
}
CooArray::from_triplets(&rows, &cols, &data, shape, false)
}
fn to_sym_csr(&self) -> SparseResult<SymCsrArray<T>> {
Ok(Clone::clone(self))
}
fn to_sym_coo(&self) -> SparseResult<SymCooArray<T>> {
let csr_inner = self.inner();
let mut data = Vec::new();
let mut rows = Vec::new();
let mut cols = Vec::new();
for i in 0..csr_inner.shape.0 {
for j in csr_inner.indptr[i]..csr_inner.indptr[i + 1] {
let col = csr_inner.indices[j];
let val = csr_inner.data[j];
data.push(val);
rows.push(i);
cols.push(col);
}
}
use crate::sym_coo::SymCooMatrix;
let sym_coo = SymCooMatrix::new(data, rows, cols, csr_inner.shape)?;
Ok(SymCooArray::new(sym_coo))
}
}
impl<T> SymSparseArray<T> for SymCooArray<T>
where
T: Float
+ SparseElement
+ Debug
+ Copy
+ 'static
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ scirs2_core::simd_ops::SimdUnifiedOps
+ SparseElement
+ Send
+ Sync,
{
fn nnz_stored(&self) -> usize {
self.inner().nnz_stored()
}
fn to_csr(&self) -> SparseResult<CsrArray<T>> {
let coo = self.to_coo_array()?;
match coo.to_csr() {
Ok(boxed_csr) => {
match boxed_csr.as_any().downcast_ref::<CsrArray<T>>() {
Some(csr_array) => Ok(csr_array.clone()),
None => Err(SparseError::ConversionError(
"Failed to downcast to CsrArray".to_string(),
)),
}
}
Err(e) => Err(e),
}
}
fn to_coo(&self) -> SparseResult<CooArray<T>> {
self.to_coo_array()
}
fn to_sym_csr(&self) -> SparseResult<SymCsrArray<T>> {
let coo_inner = self.inner();
let data = coo_inner.data.clone();
let rows = coo_inner.rows.clone();
let cols = coo_inner.cols.clone();
let shape = coo_inner.shape;
let csr = crate::csr::CsrMatrix::new(data, rows, cols, shape)?;
use crate::sym_csr::SymCsrMatrix;
let mut sym_data = Vec::new();
let mut sym_indices = Vec::new();
let mut sym_indptr = vec![0];
let n = shape.0;
for i in 0..n {
for j_ptr in csr.indptr[i]..csr.indptr[i + 1] {
let j = csr.indices[j_ptr];
let val = csr.data[j_ptr];
if j <= i {
sym_data.push(val);
sym_indices.push(j);
}
}
sym_indptr.push(sym_data.len());
}
let sym_csr = SymCsrMatrix::new(sym_data, sym_indptr, sym_indices, shape)?;
Ok(SymCsrArray::new(sym_csr))
}
fn to_sym_coo(&self) -> SparseResult<SymCooArray<T>> {
Ok(Clone::clone(self))
}
}
impl<T> SparseArray<T> for SymCsrArray<T>
where
T: Float
+ SparseElement
+ Debug
+ Copy
+ 'static
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ scirs2_core::simd_ops::SimdUnifiedOps
+ SparseElement
+ Send
+ Sync,
{
fn to_coo(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let coo_array = <Self as SymSparseArray<T>>::to_coo(self)?;
Ok(Box::new(coo_array))
}
fn to_csr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
Ok(Box::new(csr))
}
fn shape(&self) -> (usize, usize) {
self.inner().shape()
}
fn nnz(&self) -> usize {
self.inner().nnz()
}
fn dtype(&self) -> &str {
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f32>() {
"f32"
} else {
"f64"
}
}
fn get(&self, row: usize, col: usize) -> T {
self.inner().get(row, col)
}
fn to_array(&self) -> scirs2_core::ndarray::Array2<T> {
let dense = self.inner().to_dense();
let mut array = scirs2_core::ndarray::Array2::zeros(self.shape());
for i in 0..dense.len() {
for j in 0..dense[i].len() {
array[[i, j]] = dense[i][j];
}
}
array
}
fn toarray(&self) -> scirs2_core::ndarray::Array2<T> {
self.to_array()
}
fn set(&mut self, i: usize, j: usize, value: T) -> SparseResult<()> {
Err(SparseError::NotImplemented(
"Setting individual elements in SymCsrArray is not supported. Convert to another format first.".to_string()
))
}
fn dot_vector(
&self,
other: &scirs2_core::ndarray::ArrayView1<T>,
) -> SparseResult<scirs2_core::ndarray::Array1<T>> {
crate::sym_ops::sym_csr_matvec(self.inner(), other)
}
fn copy(&self) -> Box<dyn SparseArray<T>> {
Box::new(Clone::clone(self))
}
fn sub(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let result = self_csr.sub(other)?;
Ok(result)
}
fn div(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
self_csr.div(other)
}
fn eliminate_zeros(&mut self) {
}
fn sort_indices(&mut self) {
}
fn sorted_indices(&self) -> Box<dyn SparseArray<T>> {
Box::new(Clone::clone(self))
}
fn has_sorted_indices(&self) -> bool {
true
}
fn sum(&self, axis: Option<usize>) -> SparseResult<SparseSum<T>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
SparseArray::<T>::sum(&csr, axis)
}
fn max(&self) -> T {
match <Self as SymSparseArray<T>>::to_csr(self) {
Ok(csr) => SparseArray::<T>::max(&csr),
Err(_) => T::nan(), }
}
fn min(&self) -> T {
match <Self as SymSparseArray<T>>::to_csr(self) {
Ok(csr) => SparseArray::<T>::min(&csr),
Err(_) => T::nan(), }
}
fn slice(
&self,
rows: (usize, usize),
cols: (usize, usize),
) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
csr.slice(rows, cols)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn find(
&self,
) -> (
scirs2_core::ndarray::Array1<usize>,
scirs2_core::ndarray::Array1<usize>,
scirs2_core::ndarray::Array1<T>,
) {
match <Self as SymSparseArray<T>>::to_csr(self) {
Ok(csr) => csr.find(),
Err(_) => (
scirs2_core::ndarray::Array1::from_vec(Vec::new()),
scirs2_core::ndarray::Array1::from_vec(Vec::new()),
scirs2_core::ndarray::Array1::from_vec(Vec::new()),
),
}
}
fn add(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let selfshape = self.shape();
let othershape = other.shape();
if selfshape != othershape {
return Err(crate::error::SparseError::DimensionMismatch {
expected: selfshape.0,
found: othershape.0,
});
}
let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let other_csr_box = other.to_csr()?;
match other_csr_box.as_any().downcast_ref::<CsrArray<T>>() {
Some(other_csr) => {
let result = self_csr.add(other_csr)?;
use crate::csr::CsrMatrix;
let (rows, cols, data) = result.find();
let csr_matrix =
CsrMatrix::new(data.to_vec(), rows.to_vec(), cols.to_vec(), result.shape())?;
use crate::sym_csr::SymCsrMatrix;
let sym_csr = SymCsrMatrix::from_csr(&csr_matrix)?;
let sym_csr_array = SymCsrArray::new(sym_csr);
Ok(Box::new(sym_csr_array) as Box<dyn SparseArray<T>>)
}
None => {
let self_dense = self.to_array();
let other_dense = other.to_array();
let mut result_dense = scirs2_core::ndarray::Array2::zeros(selfshape);
for i in 0..selfshape.0 {
for j in 0..selfshape.1 {
result_dense[[i, j]] = self_dense[[i, j]] + other_dense[[i, j]];
}
}
let mut rows = Vec::new();
let mut cols = Vec::new();
let mut values = Vec::new();
for i in 0..selfshape.0 {
for j in 0..selfshape.1 {
let val = result_dense[[i, j]];
if val != T::sparse_zero() {
rows.push(i);
cols.push(j);
values.push(val);
}
}
}
let csr = CsrArray::from_triplets(&rows, &cols, &values, selfshape, false)?;
Ok(Box::new(csr) as Box<dyn SparseArray<T>>)
}
}
}
fn mul(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let selfshape = self.shape();
let othershape = other.shape();
if selfshape != othershape {
return Err(crate::error::SparseError::DimensionMismatch {
expected: selfshape.0,
found: othershape.0,
});
}
let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let other_csr_box = other.to_csr()?;
match other_csr_box.as_any().downcast_ref::<CsrArray<T>>() {
Some(other_csr) => {
let result = self_csr.mul(other_csr)?;
let (rows, cols, data) = result.find();
use crate::csr::CsrMatrix;
let csr_matrix =
CsrMatrix::new(data.to_vec(), rows.to_vec(), cols.to_vec(), result.shape())?;
use crate::sym_csr::SymCsrMatrix;
let sym_csr = SymCsrMatrix::from_csr(&csr_matrix)?;
let sym_csr_array = SymCsrArray::new(sym_csr);
Ok(Box::new(sym_csr_array) as Box<dyn SparseArray<T>>)
}
None => {
let self_dense = self.to_array();
let other_dense = other.to_array();
let mut result_dense = scirs2_core::ndarray::Array2::zeros(selfshape);
for i in 0..selfshape.0 {
for j in 0..selfshape.1 {
result_dense[[i, j]] = self_dense[[i, j]] * other_dense[[i, j]];
}
}
let mut rows = Vec::new();
let mut cols = Vec::new();
let mut values = Vec::new();
for i in 0..selfshape.0 {
for j in 0..selfshape.1 {
let val = result_dense[[i, j]];
if val != T::sparse_zero() {
rows.push(i);
cols.push(j);
values.push(val);
}
}
}
let csr = CsrArray::from_triplets(&rows, &cols, &values, selfshape, false)?;
Ok(Box::new(csr) as Box<dyn SparseArray<T>>)
}
}
}
fn dot(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let result = self_csr.dot(other)?;
Ok(result)
}
fn transpose(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
Ok(Box::new(Clone::clone(self)))
}
fn to_csc(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_csc()
}
fn to_dia(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_dia()
}
fn to_dok(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_dok()
}
fn to_lil(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_lil()
}
fn to_bsr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_bsr()
}
}
impl<T> SparseArray<T> for SymCooArray<T>
where
T: Float
+ SparseElement
+ Debug
+ Copy
+ 'static
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ scirs2_core::simd_ops::SimdUnifiedOps
+ SparseElement
+ Send
+ Sync,
{
fn to_coo(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let coo_array = <Self as SymSparseArray<T>>::to_coo(self)?;
Ok(Box::new(coo_array))
}
fn to_csr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
Ok(Box::new(csr))
}
fn shape(&self) -> (usize, usize) {
self.inner().shape()
}
fn nnz(&self) -> usize {
self.inner().nnz()
}
fn dtype(&self) -> &str {
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f32>() {
"f32"
} else {
"f64"
}
}
fn get(&self, row: usize, col: usize) -> T {
self.inner().get(row, col)
}
fn to_array(&self) -> scirs2_core::ndarray::Array2<T> {
let dense = self.inner().to_dense();
let mut array = scirs2_core::ndarray::Array2::zeros(self.shape());
for i in 0..dense.len() {
for j in 0..dense[i].len() {
array[[i, j]] = dense[i][j];
}
}
array
}
fn toarray(&self) -> scirs2_core::ndarray::Array2<T> {
self.to_array()
}
fn set(&mut self, i: usize, j: usize, value: T) -> SparseResult<()> {
Err(SparseError::NotImplemented(
"Setting individual elements in SymCooArray is not supported. Convert to another format first.".to_string()
))
}
fn dot_vector(
&self,
other: &scirs2_core::ndarray::ArrayView1<T>,
) -> SparseResult<scirs2_core::ndarray::Array1<T>> {
crate::sym_ops::sym_coo_matvec(self.inner(), other)
}
fn copy(&self) -> Box<dyn SparseArray<T>> {
Box::new(Clone::clone(self))
}
fn sub(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
self_csr.sub(other)
}
fn div(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
self_csr.div(other)
}
fn eliminate_zeros(&mut self) {
}
fn sort_indices(&mut self) {
}
fn sorted_indices(&self) -> Box<dyn SparseArray<T>> {
match self.to_sym_csr() {
Ok(csr) => Box::new(csr),
Err(_) => Box::new(Clone::clone(self)), }
}
fn has_sorted_indices(&self) -> bool {
false
}
fn sum(&self, axis: Option<usize>) -> SparseResult<SparseSum<T>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
SparseArray::<T>::sum(&csr, axis)
}
fn max(&self) -> T {
match <Self as SymSparseArray<T>>::to_csr(self) {
Ok(csr) => SparseArray::<T>::max(&csr),
Err(_) => T::nan(), }
}
fn min(&self) -> T {
match <Self as SymSparseArray<T>>::to_csr(self) {
Ok(csr) => SparseArray::<T>::min(&csr),
Err(_) => T::nan(), }
}
fn slice(
&self,
rows: (usize, usize),
cols: (usize, usize),
) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
csr.slice(rows, cols)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn find(
&self,
) -> (
scirs2_core::ndarray::Array1<usize>,
scirs2_core::ndarray::Array1<usize>,
scirs2_core::ndarray::Array1<T>,
) {
match <Self as SymSparseArray<T>>::to_coo(self) {
Ok(coo) => coo.find(),
Err(_) => (
scirs2_core::ndarray::Array1::from_vec(Vec::new()),
scirs2_core::ndarray::Array1::from_vec(Vec::new()),
scirs2_core::ndarray::Array1::from_vec(Vec::new()),
),
}
}
fn add(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
self.to_sym_csr()?.add(other)
}
fn mul(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
self.to_sym_csr()?.mul(other)
}
fn dot(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
<Self as SymSparseArray<T>>::to_csr(self)?.dot(other)
}
fn transpose(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
Ok(Box::new(Clone::clone(self)))
}
fn to_csc(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_csc()
}
fn to_dia(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_dia()
}
fn to_dok(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_dok()
}
fn to_lil(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_lil()
}
fn to_bsr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
csr_box.to_bsr()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sym_csr::{SymCsrArray, SymCsrMatrix};
fn create_test_sym_csr() -> SymCsrArray<f64> {
let data = vec![2.0, 1.0, 2.0, 3.0, 1.0];
let indices = vec![0, 0, 1, 1, 2];
let indptr = vec![0, 1, 3, 5];
let sym_matrix =
SymCsrMatrix::new(data, indptr, indices, (3, 3)).expect("Operation failed");
SymCsrArray::new(sym_matrix)
}
#[test]
fn test_sym_sparse_array_trait() {
let sym_csr = create_test_sym_csr();
assert_eq!(sym_csr.shape(), (3, 3));
assert!(sym_csr.is_symmetric());
assert_eq!(sym_csr.get(0, 0), 2.0);
assert_eq!(sym_csr.get(0, 1), 1.0);
assert_eq!(sym_csr.get(1, 0), 1.0);
assert_eq!(sym_csr.nnz_stored(), 5); assert_eq!(sym_csr.nnz(), 7);
let sym_coo = sym_csr.to_sym_coo().expect("Operation failed");
assert_eq!(sym_coo.shape(), (3, 3));
assert!(sym_coo.is_symmetric());
let csr = SymSparseArray::<f64>::to_csr(&sym_csr).expect("Operation failed");
assert_eq!(csr.shape(), (3, 3));
let coo = SymSparseArray::<f64>::to_coo(&sym_csr).expect("Operation failed");
assert_eq!(coo.shape(), (3, 3));
let (rows, _cols, _data) = sym_csr.find();
assert!(rows.len() > sym_csr.nnz_stored()); }
#[test]
fn test_sym_sparse_array_operations() {
let sym_csr = create_test_sym_csr();
let sym_csr2 = create_test_sym_csr();
let sum = sym_csr.add(&sym_csr2).expect("Operation failed");
assert_eq!(sum.shape(), (3, 3));
assert_eq!(sum.get(0, 0), 4.0); assert_eq!(sum.get(0, 1), 2.0); assert_eq!(sum.get(1, 0), 2.0);
let prod = sym_csr.mul(&sym_csr2).expect("Operation failed");
assert_eq!(prod.shape(), (3, 3));
assert_eq!(prod.get(0, 0), 4.0); assert_eq!(prod.get(0, 1), 1.0); assert_eq!(prod.get(1, 0), 1.0);
let dot = sym_csr.dot(&sym_csr2).expect("Operation failed");
assert_eq!(dot.shape(), (3, 3));
let trans = sym_csr.transpose().expect("Operation failed");
assert_eq!(trans.shape(), sym_csr.shape());
assert_eq!(SparseArray::get(&*trans, 0, 1), sym_csr.get(0, 1));
assert_eq!(SparseArray::get(&*trans, 1, 0), sym_csr.get(1, 0));
}
}