use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
use scirs2_core::numeric::{Float, SparseElement};
use std::fmt::{self, 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, SparseSum};
#[derive(Clone)]
pub struct DiaArray<T>
where
T: SparseElement + Div<Output = T> + Float + 'static + std::ops::AddAssign,
{
data: Vec<Array1<T>>,
offsets: Vec<isize>,
shape: (usize, usize),
}
impl<T> DiaArray<T>
where
T: SparseElement + Div<Output = T> + Float + 'static + std::ops::AddAssign,
{
pub fn new(
data: Vec<Array1<T>>,
offsets: Vec<isize>,
shape: (usize, usize),
) -> SparseResult<Self> {
let (rows, cols) = shape;
let max_dim = rows.max(cols);
if data.len() != offsets.len() {
return Err(SparseError::DimensionMismatch {
expected: data.len(),
found: offsets.len(),
});
}
for diag in data.iter() {
if diag.len() != max_dim {
return Err(SparseError::DimensionMismatch {
expected: max_dim,
found: diag.len(),
});
}
}
Ok(DiaArray {
data,
offsets,
shape,
})
}
pub fn empty(shape: (usize, usize)) -> Self {
DiaArray {
data: Vec::new(),
offsets: Vec::new(),
shape,
}
}
pub fn from_triplets(
row: &[usize],
col: &[usize],
data: &[T],
shape: (usize, usize),
) -> SparseResult<Self> {
if row.len() != col.len() || row.len() != data.len() {
return Err(SparseError::InconsistentData {
reason: "Lengths of row, col, and data arrays must be equal".to_string(),
});
}
let (rows, cols) = shape;
let max_dim = rows.max(cols);
let mut diagonal_offsets = std::collections::HashSet::new();
for (&r, &c) in row.iter().zip(col.iter()) {
if r >= rows || c >= cols {
return Err(SparseError::IndexOutOfBounds {
index: (r, c),
shape,
});
}
let offset = c as isize - r as isize;
diagonal_offsets.insert(offset);
}
let mut offsets: Vec<isize> = diagonal_offsets.into_iter().collect();
offsets.sort();
let mut diag_data = Vec::with_capacity(offsets.len());
for _ in 0..offsets.len() {
diag_data.push(Array1::zeros(max_dim));
}
for (&r, (&c, &val)) in row.iter().zip(col.iter().zip(data.iter())) {
let offset = c as isize - r as isize;
let diag_idx = offsets
.iter()
.position(|&o| o == offset)
.expect("Operation failed");
let index = if offset >= 0 { r } else { c };
diag_data[diag_idx][index] = val;
}
DiaArray::new(diag_data, offsets, shape)
}
fn to_coo_internal(&self) -> (Vec<usize>, Vec<usize>, Vec<T>) {
let (rows, cols) = self.shape;
let mut row_indices = Vec::new();
let mut col_indices = Vec::new();
let mut values = Vec::new();
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
if offset >= 0 {
let offset_usize = offset as usize;
let length = rows.min(cols.saturating_sub(offset_usize));
for i in 0..length {
let value = diag[i];
if !SparseElement::is_zero(&value) {
row_indices.push(i);
col_indices.push(i + offset_usize);
values.push(value);
}
}
} else {
let offset_usize = (-offset) as usize;
let length = cols.min(rows.saturating_sub(offset_usize));
for i in 0..length {
let value = diag[i];
if !SparseElement::is_zero(&value) {
row_indices.push(i + offset_usize);
col_indices.push(i);
values.push(value);
}
}
}
}
(row_indices, col_indices, values)
}
}
impl<T> SparseArray<T> for DiaArray<T>
where
T: SparseElement + Div<Output = T> + Float + 'static + std::ops::AddAssign,
{
fn shape(&self) -> (usize, usize) {
self.shape
}
fn nnz(&self) -> usize {
let (rows, cols) = self.shape;
let mut count = 0;
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
let length = if offset >= 0 {
rows.min(cols.saturating_sub(offset as usize))
} else {
cols.min(rows.saturating_sub((-offset) as usize))
};
let start_idx = 0; for i in start_idx..start_idx + length {
if !SparseElement::is_zero(&diag[i]) {
count += 1;
}
}
}
count
}
fn dtype(&self) -> &str {
std::any::type_name::<T>()
}
fn to_array(&self) -> Array2<T> {
let (rows, cols) = self.shape;
let mut result = Array2::zeros((rows, cols));
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
if offset >= 0 {
let offset_usize = offset as usize;
for i in 0..rows.min(cols.saturating_sub(offset_usize)) {
result[[i, i + offset_usize]] = diag[i];
}
} else {
let offset_usize = (-offset) as usize;
for i in 0..cols.min(rows.saturating_sub(offset_usize)) {
result[[i + offset_usize, i]] = diag[i];
}
}
}
result
}
fn toarray(&self) -> Array2<T> {
self.to_array()
}
fn to_coo(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let (row_indices, col_indices, values) = self.to_coo_internal();
let row_array = Array1::from_vec(row_indices);
let col_array = Array1::from_vec(col_indices);
let data_array = Array1::from_vec(values);
CooArray::from_triplets(
&row_array.to_vec(),
&col_array.to_vec(),
&data_array.to_vec(),
self.shape,
false,
)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>)
}
fn to_csr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let (row_indices, col_indices, values) = self.to_coo_internal();
CsrArray::from_triplets(&row_indices, &col_indices, &values, self.shape, false)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>)
}
fn to_csc(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
self.to_coo()?.to_csc()
}
fn to_dok(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let (row_indices, col_indices, values) = self.to_coo_internal();
DokArray::from_triplets(&row_indices, &col_indices, &values, self.shape)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>)
}
fn to_lil(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let (row_indices, col_indices, values) = self.to_coo_internal();
LilArray::from_triplets(&row_indices, &col_indices, &values, self.shape)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>)
}
fn to_dia(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
Ok(Box::new(self.clone()))
}
fn to_bsr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
self.to_coo()?.to_bsr()
}
fn add(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr_self = self.to_csr()?;
let csr_other = other.to_csr()?;
csr_self.add(&*csr_other)
}
fn sub(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr_self = self.to_csr()?;
let csr_other = other.to_csr()?;
csr_self.sub(&*csr_other)
}
fn mul(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr_self = self.to_csr()?;
let csr_other = other.to_csr()?;
csr_self.mul(&*csr_other)
}
fn div(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let csr_self = self.to_csr()?;
let csr_other = other.to_csr()?;
csr_self.div(&*csr_other)
}
fn dot(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
let (_, n) = self.shape();
let (p, q) = other.shape();
if n != p {
return Err(SparseError::DimensionMismatch {
expected: n,
found: p,
});
}
if q == 1 {
let other_array = other.to_array();
let vec_view = other_array.column(0);
let result = self.dot_vector(&vec_view)?;
let mut rows = Vec::new();
let mut cols = Vec::new();
let mut values = Vec::new();
for (i, &val) in result.iter().enumerate() {
if !SparseElement::is_zero(&val) {
rows.push(i);
cols.push(0);
values.push(val);
}
}
CooArray::from_triplets(&rows, &cols, &values, (result.len(), 1), false)
.map(|array| Box::new(array) as Box<dyn SparseArray<T>>)
} else {
let csr_self = self.to_csr()?;
csr_self.dot(other)
}
}
fn dot_vector(&self, other: &ArrayView1<T>) -> SparseResult<Array1<T>> {
let (rows, cols) = self.shape;
if cols != other.len() {
return Err(SparseError::DimensionMismatch {
expected: cols,
found: other.len(),
});
}
let mut result = Array1::zeros(rows);
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
if offset >= 0 {
let offset_usize = offset as usize;
let length = rows.min(cols.saturating_sub(offset_usize));
for i in 0..length {
result[i] += diag[i] * other[i + offset_usize];
}
} else {
let offset_usize = (-offset) as usize;
let length = cols.min(rows.saturating_sub(offset_usize));
for i in 0..length {
result[i + offset_usize] += diag[i] * other[i];
}
}
}
Ok(result)
}
fn transpose(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
let (row_indices, col_indices, values) = self.to_coo_internal();
let transposed_rows = col_indices;
let transposed_cols = row_indices;
CooArray::from_triplets(
&transposed_rows,
&transposed_cols,
&values,
(self.shape.1, self.shape.0),
false,
)?
.to_dia()
}
fn copy(&self) -> Box<dyn SparseArray<T>> {
Box::new(self.clone())
}
fn get(&self, i: usize, j: usize) -> T {
if i >= self.shape.0 || j >= self.shape.1 {
return T::sparse_zero();
}
let offset = j as isize - i as isize;
if let Some(diag_idx) = self.offsets.iter().position(|&o| o == offset) {
let diag = &self.data[diag_idx];
let index = if offset >= 0 { i } else { j };
if index < diag.len() {
return diag[index];
}
}
T::sparse_zero()
}
fn set(&mut self, i: usize, j: usize, value: T) -> SparseResult<()> {
if i >= self.shape.0 || j >= self.shape.1 {
return Err(SparseError::IndexOutOfBounds {
index: (i, j),
shape: self.shape,
});
}
let offset = j as isize - i as isize;
let diag_idx = match self.offsets.iter().position(|&o| o == offset) {
Some(idx) => idx,
None => {
self.offsets.push(offset);
self.data
.push(Array1::zeros(self.shape.0.max(self.shape.1)));
let mut offset_data: Vec<(isize, Array1<T>)> = self
.offsets
.iter()
.cloned()
.zip(self.data.drain(..))
.collect();
offset_data.sort_by_key(|&(offset_, _)| offset_);
self.offsets = offset_data.iter().map(|&(offset_, _)| offset_).collect();
self.data = offset_data.into_iter().map(|(_, data)| data).collect();
self.offsets
.iter()
.position(|&o| o == offset)
.expect("Operation failed")
}
};
let index = if offset >= 0 { i } else { j };
self.data[diag_idx][index] = value;
Ok(())
}
fn eliminate_zeros(&mut self) {
let mut new_offsets = Vec::new();
let mut new_data = Vec::new();
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
let length = if offset >= 0 {
self.shape
.0
.min(self.shape.1.saturating_sub(offset as usize))
} else {
self.shape
.1
.min(self.shape.0.saturating_sub((-offset) as usize))
};
let has_nonzero = (0..length).any(|i| !SparseElement::is_zero(&diag[i]));
if has_nonzero {
new_offsets.push(offset);
new_data.push(diag.clone());
}
}
self.offsets = new_offsets;
self.data = new_data;
}
fn sort_indices(&mut self) {
let mut offset_data: Vec<(isize, Array1<T>)> = self
.offsets
.iter()
.cloned()
.zip(self.data.drain(..))
.collect();
offset_data.sort_by_key(|&(offset_, _)| offset_);
self.offsets = offset_data.iter().map(|&(offset_, _)| offset_).collect();
self.data = offset_data.into_iter().map(|(_, data)| data).collect();
}
fn sorted_indices(&self) -> Box<dyn SparseArray<T>> {
let mut result = self.clone();
result.sort_indices();
Box::new(result)
}
fn has_sorted_indices(&self) -> bool {
self.offsets.windows(2).all(|w| w[0] <= w[1])
}
fn sum(&self, axis: Option<usize>) -> SparseResult<SparseSum<T>> {
match axis {
None => {
let mut total = T::sparse_zero();
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
let length = if offset >= 0 {
self.shape
.0
.min(self.shape.1.saturating_sub(offset as usize))
} else {
self.shape
.1
.min(self.shape.0.saturating_sub((-offset) as usize))
};
for i in 0..length {
total += diag[i];
}
}
Ok(SparseSum::Scalar(total))
}
Some(0) => {
let mut result = Array1::zeros(self.shape.1);
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
if offset >= 0 {
let offset_usize = offset as usize;
let length = self.shape.0.min(self.shape.1.saturating_sub(offset_usize));
for i in 0..length {
result[i + offset_usize] += diag[i];
}
} else {
let offset_usize = (-offset) as usize;
let length = self.shape.1.min(self.shape.0.saturating_sub(offset_usize));
for i in 0..length {
result[i] += diag[i];
}
}
}
match Array2::from_shape_vec((1, self.shape.1), result.to_vec()) {
Ok(result_2d) => {
let mut row_indices = Vec::new();
let mut col_indices = Vec::new();
let mut values = Vec::new();
for j in 0..self.shape.1 {
let val: T = result_2d[[0, j]];
if !SparseElement::is_zero(&val) {
row_indices.push(0);
col_indices.push(j);
values.push(val);
}
}
match CooArray::from_triplets(
&row_indices,
&col_indices,
&values,
(1, self.shape.1),
false,
) {
Ok(coo_array) => Ok(SparseSum::SparseArray(Box::new(coo_array))),
Err(e) => Err(e),
}
}
Err(_) => Err(SparseError::InconsistentData {
reason: "Failed to create 2D array from result vector".to_string(),
}),
}
}
Some(1) => {
let mut result = Array1::zeros(self.shape.0);
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
if offset >= 0 {
let offset_usize = offset as usize;
let length = self.shape.0.min(self.shape.1.saturating_sub(offset_usize));
for i in 0..length {
result[i] += diag[i];
}
} else {
let offset_usize = (-offset) as usize;
let length = self.shape.1.min(self.shape.0.saturating_sub(offset_usize));
for i in 0..length {
result[i + offset_usize] += diag[i];
}
}
}
match Array2::from_shape_vec((self.shape.0, 1), result.to_vec()) {
Ok(result_2d) => {
let mut row_indices = Vec::new();
let mut col_indices = Vec::new();
let mut values = Vec::new();
for i in 0..self.shape.0 {
let val: T = result_2d[[i, 0]];
if !SparseElement::is_zero(&val) {
row_indices.push(i);
col_indices.push(0);
values.push(val);
}
}
match CooArray::from_triplets(
&row_indices,
&col_indices,
&values,
(self.shape.0, 1),
false,
) {
Ok(coo_array) => Ok(SparseSum::SparseArray(Box::new(coo_array))),
Err(e) => Err(e),
}
}
Err(_) => Err(SparseError::InconsistentData {
reason: "Failed to create 2D array from result vector".to_string(),
}),
}
}
_ => Err(SparseError::InvalidAxis),
}
}
fn max(&self) -> T {
let mut max_val = T::neg_infinity();
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
let length = if offset >= 0 {
self.shape
.0
.min(self.shape.1.saturating_sub(offset as usize))
} else {
self.shape
.1
.min(self.shape.0.saturating_sub((-offset) as usize))
};
for i in 0..length {
max_val = max_val.max(diag[i]);
}
}
if max_val == T::neg_infinity() {
T::sparse_zero()
} else {
max_val
}
}
fn min(&self) -> T {
let mut min_val = T::sparse_zero();
let mut has_nonzero = false;
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
let length = if offset >= 0 {
self.shape
.0
.min(self.shape.1.saturating_sub(offset as usize))
} else {
self.shape
.1
.min(self.shape.0.saturating_sub((-offset) as usize))
};
for i in 0..length {
if !SparseElement::is_zero(&diag[i]) {
has_nonzero = true;
min_val = min_val.min(diag[i]);
}
}
}
if !has_nonzero {
T::sparse_zero()
} else {
min_val
}
}
fn find(&self) -> (Array1<usize>, Array1<usize>, Array1<T>) {
let (row_indices, col_indices, values) = self.to_coo_internal();
(
Array1::from_vec(row_indices),
Array1::from_vec(col_indices),
Array1::from_vec(values),
)
}
fn slice(
&self,
row_range: (usize, usize),
col_range: (usize, usize),
) -> SparseResult<Box<dyn SparseArray<T>>> {
let (start_row, end_row) = row_range;
let (start_col, end_col) = col_range;
let (rows, cols) = self.shape;
if start_row >= rows || end_row > rows || start_col >= cols || end_col > cols {
return Err(SparseError::IndexOutOfBounds {
index: (start_row.max(end_row), start_col.max(end_col)),
shape: (rows, cols),
});
}
if start_row >= end_row || start_col >= end_col {
return Err(SparseError::InvalidSliceRange);
}
let coo = self.to_coo()?;
coo.slice(row_range, col_range)?.to_dia()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl<T> fmt::Display for DiaArray<T>
where
T: SparseElement + Div<Output = T> + Float + 'static + std::ops::AddAssign,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"DiaArray of shape {:?} with {} stored elements",
self.shape,
self.nnz()
)?;
writeln!(f, "Offsets: {:?}", self.offsets)?;
if self.offsets.len() <= 5 {
for (i, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[i];
let length = if offset >= 0 {
self.shape
.0
.min(self.shape.1.saturating_sub(offset as usize))
} else {
self.shape
.1
.min(self.shape.0.saturating_sub((-offset) as usize))
};
write!(f, "Diagonal {offset}: [")?;
for j in 0..length.min(10) {
if j > 0 {
write!(f, ", ")?;
}
write!(f, "{:?}", diag[j])?;
}
if length > 10 {
write!(f, ", ...")?;
}
writeln!(f, "]")?;
}
} else {
writeln!(f, "({} diagonals)", self.offsets.len())?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dia_array_dtype_reflects_actual_element_type() {
let data_f64 = vec![Array1::from_vec(vec![1.0f64, 2.0, 3.0])];
let data_f32 = vec![Array1::from_vec(vec![1.0f32, 2.0, 3.0])];
let offsets = vec![0];
let shape = (3, 3);
let dia_f64 = DiaArray::new(data_f64, offsets.clone(), shape).expect("Operation failed");
let dia_f32 = DiaArray::new(data_f32, offsets, shape).expect("Operation failed");
assert_eq!(dia_f64.dtype(), "f64");
assert_eq!(dia_f32.dtype(), "f32");
assert_ne!(dia_f64.dtype(), "float");
assert_ne!(dia_f32.dtype(), dia_f64.dtype());
}
#[test]
fn test_dia_array_create() {
let data = vec![
Array1::from_vec(vec![1.0, 2.0, 3.0]), Array1::from_vec(vec![4.0, 5.0, 0.0]), ];
let offsets = vec![0, 1]; let shape = (3, 3);
let array = DiaArray::new(data, offsets, shape).expect("Operation failed");
assert_eq!(array.shape(), (3, 3));
assert_eq!(array.nnz(), 5);
assert_eq!(array.get(0, 0), 1.0);
assert_eq!(array.get(1, 1), 2.0);
assert_eq!(array.get(2, 2), 3.0);
assert_eq!(array.get(0, 1), 4.0);
assert_eq!(array.get(1, 2), 5.0);
assert_eq!(array.get(0, 2), 0.0);
}
#[test]
fn test_dia_array_from_triplets() {
let row = vec![0, 0, 1, 1, 1, 2, 2];
let col = vec![0, 1, 0, 1, 2, 1, 2];
let data = vec![1.0, 4.0, 2.0, 3.0, 5.0, 6.0, 7.0];
let shape = (3, 3);
let array = DiaArray::from_triplets(&row, &col, &data, shape).expect("Operation failed");
assert_eq!(array.offsets.len(), 3);
assert!(array.offsets.contains(&0));
assert!(array.offsets.contains(&1));
assert!(array.offsets.contains(&-1));
assert_eq!(array.get(0, 0), 1.0);
assert_eq!(array.get(0, 1), 4.0);
assert_eq!(array.get(1, 0), 2.0);
assert_eq!(array.get(1, 1), 3.0);
assert_eq!(array.get(1, 2), 5.0);
assert_eq!(array.get(2, 1), 6.0);
assert_eq!(array.get(2, 2), 7.0);
}
#[test]
fn test_dia_array_conversion() {
let data = vec![
Array1::from_vec(vec![1.0, 3.0, 7.0]), Array1::from_vec(vec![4.0, 5.0, 0.0]), Array1::from_vec(vec![0.0, 2.0, 0.0]), ];
let offsets = vec![0, 1, -1]; let shape = (3, 3);
let array = DiaArray::new(data, offsets, shape).expect("Operation failed");
let coo = array.to_coo().expect("Operation failed");
assert_eq!(coo.shape(), (3, 3));
assert_eq!(coo.nnz(), 6);
let dense = array.to_array();
let expected =
Array2::from_shape_vec((3, 3), vec![1.0, 4.0, 0.0, 0.0, 3.0, 5.0, 0.0, 2.0, 7.0])
.expect("Operation failed");
assert_eq!(dense, expected);
}
#[test]
fn test_dia_array_operations() {
let data1 = vec![Array1::from_vec(vec![1.0, 2.0, 3.0])]; let offsets1 = vec![0];
let shape1 = (3, 3);
let array1 = DiaArray::new(data1, offsets1, shape1).expect("Operation failed");
let data2 = vec![Array1::from_vec(vec![4.0, 5.0, 6.0])]; let offsets2 = vec![0];
let shape2 = (3, 3);
let array2 = DiaArray::new(data2, offsets2, shape2).expect("Operation failed");
let sum = array1.add(&array2).expect("Operation failed");
assert_eq!(sum.get(0, 0), 5.0);
assert_eq!(sum.get(1, 1), 7.0);
assert_eq!(sum.get(2, 2), 9.0);
let product = array1.mul(&array2).expect("Operation failed");
assert_eq!(product.get(0, 0), 4.0);
assert_eq!(product.get(1, 1), 10.0);
assert_eq!(product.get(2, 2), 18.0);
let dot = array1.dot(&array2).expect("Operation failed");
assert_eq!(dot.get(0, 0), 4.0);
assert_eq!(dot.get(1, 1), 10.0);
assert_eq!(dot.get(2, 2), 18.0);
}
#[test]
fn test_dia_array_dot_vector() {
let data = vec![
Array1::from_vec(vec![1.0, 2.0, 3.0]), Array1::from_vec(vec![4.0, 5.0, 0.0]), Array1::from_vec(vec![0.0, 6.0, 7.0]), ];
let offsets = vec![0, 1, -1]; let shape = (3, 3);
let array = DiaArray::new(data, offsets, shape).expect("Operation failed");
let vector = Array1::from_vec(vec![1.0, 2.0, 3.0]);
let result = array.dot_vector(&vector.view()).expect("Operation failed");
let expected = Array1::from_vec(vec![9.0, 19.0, 21.0]);
assert_eq!(result, expected);
}
#[test]
fn test_dia_array_transpose() {
let data = vec![
Array1::from_vec(vec![1.0, 2.0, 3.0]), Array1::from_vec(vec![4.0, 5.0, 0.0]), Array1::from_vec(vec![0.0, 6.0, 7.0]), ];
let offsets = vec![0, 1, -1]; let shape = (3, 3);
let array = DiaArray::new(data, offsets, shape).expect("Operation failed");
let transposed = array.transpose().expect("Operation failed");
assert_eq!(transposed.shape(), (3, 3));
let original_dense = array.to_array();
let transposed_dense = transposed.to_array();
for i in 0..3 {
for j in 0..3 {
assert_eq!(transposed_dense[[i, j]], original_dense[[j, i]]);
}
}
}
#[test]
fn test_dia_array_sum() {
let data = vec![
Array1::from_vec(vec![1.0, 2.0, 3.0]), Array1::from_vec(vec![4.0, 5.0, 0.0]), ];
let offsets = vec![0, 1]; let shape = (3, 3);
let array = DiaArray::new(data, offsets, shape).expect("Operation failed");
if let SparseSum::Scalar(sum) = array.sum(None).expect("Operation failed") {
assert_eq!(sum, 15.0); } else {
panic!("Expected SparseSum::Scalar");
}
if let SparseSum::SparseArray(row_sum) = array.sum(Some(0)).expect("Operation failed") {
assert_eq!(row_sum.shape(), (1, 3));
assert_eq!(row_sum.get(0, 0), 1.0);
assert_eq!(row_sum.get(0, 1), 6.0); assert_eq!(row_sum.get(0, 2), 8.0); } else {
panic!("Expected SparseSum::SparseArray");
}
if let SparseSum::SparseArray(col_sum) = array.sum(Some(1)).expect("Operation failed") {
assert_eq!(col_sum.shape(), (3, 1));
assert_eq!(col_sum.get(0, 0), 5.0); assert_eq!(col_sum.get(1, 0), 7.0); assert_eq!(col_sum.get(2, 0), 3.0);
} else {
panic!("Expected SparseSum::SparseArray");
}
}
}