use crate::infrastructure::sparse::coo::CooArray;
use crate::infrastructure::sparse::traits::{SparseFormat, SparseStorage, SparseStorageMut};
use eunomia::NumericElement;
use super::CsrArray;
#[derive(Debug, Clone)]
pub struct CscArray<T: NumericElement> {
nrows: usize,
ncols: usize,
data: Vec<T>,
row_indices: Vec<usize>,
col_ptr: Vec<usize>,
}
impl<T: NumericElement> CscArray<T> {
pub fn new(nrows: usize, ncols: usize) -> Self {
Self {
nrows,
ncols,
data: Vec::new(),
row_indices: Vec::new(),
col_ptr: vec![0; ncols + 1],
}
}
pub fn from_coo(coo: CooArray<T>) -> Self {
let nrows = coo.nrows();
let ncols = coo.ncols();
let nnz = coo.nnz();
let mut entries: Vec<_> = coo
.entries()
.map(|(row, col, value)| (row, col, *value))
.collect();
entries.sort_by_key(|&(row, col, _)| (col, row));
let mut data = Vec::with_capacity(nnz);
let mut row_indices = Vec::with_capacity(nnz);
let mut col_ptr = vec![0usize; ncols + 1];
let mut current_col = 0;
for (row, col, value) in entries {
while current_col < col {
current_col += 1;
col_ptr[current_col] = data.len();
}
data.push(value);
row_indices.push(row);
}
while current_col < ncols {
current_col += 1;
col_ptr[current_col] = data.len();
}
Self {
nrows,
ncols,
data,
row_indices,
col_ptr,
}
}
pub fn col_entries(&self, col: usize) -> impl Iterator<Item = (usize, &T)> {
let start = self.col_ptr[col];
let end = self.col_ptr[col + 1];
self.row_indices[start..end]
.iter()
.zip(self.data[start..end].iter())
.map(|(&row, value)| (row, value))
}
pub fn col_nnz(&self, col: usize) -> usize {
self.col_ptr[col + 1] - self.col_ptr[col]
}
pub fn to_csr(&self) -> CsrArray<T> {
let mut coo = CooArray::with_capacity(self.ncols, self.nrows, self.nnz());
for col in 0..self.ncols {
for (row, value) in self.col_entries(col) {
coo.add(col, row, *value);
}
}
coo.sort_by_row_column();
CsrArray::from_coo(coo)
}
pub fn to_coo(&self) -> CooArray<T> {
let mut triplets = Vec::with_capacity(self.nnz());
for col in 0..self.ncols {
for (row, value) in self.col_entries(col) {
triplets.push((row, col, *value));
}
}
CooArray::from_triplets(self.nrows, self.ncols, triplets)
}
}
impl<T: NumericElement> SparseStorage<T> for CscArray<T> {
fn nrows(&self) -> usize {
self.nrows
}
fn ncols(&self) -> usize {
self.ncols
}
fn nnz(&self) -> usize {
self.data.len()
}
fn format(&self) -> SparseFormat {
SparseFormat::Csc
}
fn get(&self, row: usize, col: usize) -> Option<T> {
if row >= self.nrows || col >= self.ncols {
return None;
}
for (r, value) in self.col_entries(col) {
if r == row {
return Some(*value);
}
if r > row {
break; }
}
None
}
}
impl<T: NumericElement> SparseStorageMut<T> for CscArray<T> {
fn set(&mut self, row: usize, col: usize, value: T) {
let mut coo = self.to_coo();
coo.set(row, col, value);
*self = CscArray::from_coo(coo);
}
fn add(&mut self, row: usize, col: usize, delta: T) {
let mut coo = self.to_coo();
coo.add(row, col, delta);
*self = CscArray::from_coo(coo);
}
fn remove(&mut self, row: usize, col: usize) {
let mut coo = self.to_coo();
coo.remove(row, col);
*self = CscArray::from_coo(coo);
}
fn reserve(&mut self, _additional: usize) {
}
fn clear(&mut self) {
self.data.clear();
self.row_indices.clear();
for ptr in &mut self.col_ptr {
*ptr = 0;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_csc_from_coo() {
let triplets = vec![(1, 1, 3.0), (1, 0, 2.0), (0, 0, 1.0)];
let coo = CooArray::from_triplets(2, 2, triplets);
let csc = CscArray::from_coo(coo);
assert_eq!(csc.nrows(), 2);
assert_eq!(csc.ncols(), 2);
assert_eq!(csc.nnz(), 3);
assert_eq!(csc.get(0, 0), Some(1.0));
assert_eq!(csc.get(1, 0), Some(2.0));
assert_eq!(csc.get(1, 1), Some(3.0));
}
#[test]
fn test_csc_col_entries() {
let triplets = vec![(0, 0, 1.0), (1, 0, 2.0), (1, 1, 3.0)];
let coo = CooArray::from_triplets(2, 2, triplets);
let csc = CscArray::from_coo(coo);
let col0: Vec<_> = csc
.col_entries(0)
.map(|(row, value)| (row, *value))
.collect();
let col1: Vec<_> = csc
.col_entries(1)
.map(|(row, value)| (row, *value))
.collect();
assert_eq!(col0, [(0, 1.0), (1, 2.0)]);
assert_eq!(col1, [(1, 3.0)]);
}
#[test]
fn test_csc_get() {
let triplets = vec![(0, 0, 1.0), (1, 0, 2.0), (1, 1, 3.0)];
let coo = CooArray::from_triplets(2, 2, triplets);
let csc = CscArray::from_coo(coo);
assert_eq!(csc.get(0, 0), Some(1.0));
assert_eq!(csc.get(1, 0), Some(2.0));
assert_eq!(csc.get(1, 1), Some(3.0));
assert_eq!(csc.get(0, 1), None);
}
}