use crate::scalar::Scalar;
use super::add::DimensionMismatch;
use super::{CscMatrix, CsrMatrix};
pub trait SparseLinearOp<T: Scalar> {
fn rows(&self) -> usize;
fn cols(&self) -> usize;
fn apply(&self, x: &[T], out: &mut [T]) -> Result<(), DimensionMismatch>;
}
impl<T: Scalar> SparseLinearOp<T> for CsrMatrix<T> {
fn rows(&self) -> usize {
CsrMatrix::rows(self)
}
fn cols(&self) -> usize {
CsrMatrix::cols(self)
}
fn apply(&self, x: &[T], out: &mut [T]) -> Result<(), DimensionMismatch> {
if x.len() != CsrMatrix::cols(self) || out.len() != CsrMatrix::rows(self) {
return Err(DimensionMismatch::Shape);
}
out.fill(T::zero());
let row_ptr = self.row_ptr();
let col_idx = self.col_indices();
let vals = self.values();
for r in 0..CsrMatrix::rows(self) {
for k in row_ptr[r] as usize..row_ptr[r + 1] as usize {
let prev = out[r];
out[r] = prev.add(vals[k].mul(x[col_idx[k] as usize]));
}
}
Ok(())
}
}
impl<T: Scalar> SparseLinearOp<T> for CscMatrix<T> {
fn rows(&self) -> usize {
CscMatrix::rows(self)
}
fn cols(&self) -> usize {
CscMatrix::cols(self)
}
fn apply(&self, x: &[T], out: &mut [T]) -> Result<(), DimensionMismatch> {
if x.len() != CscMatrix::cols(self) || out.len() != CscMatrix::rows(self) {
return Err(DimensionMismatch::Shape);
}
out.fill(T::zero());
let col_ptr = self.col_ptr();
let row_idx = self.row_indices();
let vals = self.values();
for c in 0..CscMatrix::cols(self) {
for k in col_ptr[c] as usize..col_ptr[c + 1] as usize {
let r = row_idx[k] as usize;
let prev = out[r];
out[r] = prev.add(vals[k].mul(x[c]));
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use super::*;
#[test]
fn apply_overwrites_out_for_csr() {
let m =
CsrMatrix::new(2, 3, vec![0, 2, 3], vec![0, 1, 2], vec![1.0_f64, 2.0, 3.0]).unwrap();
let x = [1.0, 2.0, 3.0];
let mut out = [7.0; 2];
m.apply(&x, &mut out).unwrap();
assert_eq!(out, [5.0, 9.0]);
}
#[test]
fn apply_overwrites_out_for_csc() {
let m =
CscMatrix::new(2, 2, vec![0, 2, 3], vec![0, 1, 1], vec![1.0_f64, 4.0, 3.0]).unwrap();
let x = [2.0, 4.0];
let mut out = [7.0; 2];
m.apply(&x, &mut out).unwrap();
assert_eq!(out, [2.0, 20.0]);
}
#[test]
fn apply_rejects_wrong_input_length_for_csr() {
let m =
CsrMatrix::new(2, 3, vec![0, 2, 3], vec![0, 1, 2], vec![1.0_f64, 2.0, 3.0]).unwrap();
let mut out = [0.0; 2];
assert_eq!(
m.apply(&[1.0, 2.0], &mut out),
Err(DimensionMismatch::Shape)
);
}
#[test]
fn apply_rejects_wrong_output_length_for_csc() {
let m =
CscMatrix::new(2, 2, vec![0, 2, 3], vec![0, 1, 1], vec![1.0_f64, 4.0, 3.0]).unwrap();
let mut out = [0.0; 3];
assert_eq!(
m.apply(&[2.0, 4.0], &mut out),
Err(DimensionMismatch::Shape)
);
}
}