use crate::real::Real;
use crate::{Col, CscMatrix, Row, SparseError};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Sign {
Plus,
Minus,
}
impl Sign {
#[inline]
pub fn value(self) -> f64 {
match self {
Sign::Plus => 1.0,
Sign::Minus => -1.0,
}
}
#[inline]
pub fn of<T: Real>(self) -> T {
T::from_f64(self.value())
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ColumnRef {
Structural(Col),
Logical(Row, Sign),
}
impl ColumnRef {
#[inline]
pub fn for_each_entry<T: Real>(self, a: &CscMatrix<T>, mut f: impl FnMut(u32, T)) {
match self {
ColumnRef::Structural(j) => {
let (rows, vals) = a.col(j);
for (&i, &v) in rows.iter().zip(vals) {
f(i, v);
}
}
ColumnRef::Logical(i, s) => f(i.0, s.of()),
}
}
pub(crate) fn validate<T: Real>(self, a: &CscMatrix<T>) -> Result<(), SparseError> {
match self {
ColumnRef::Structural(j) if j.index() >= a.n_cols() => {
Err(SparseError::IndexOutOfRange {
what: "structural basis column",
index: j.index(),
bound: a.n_cols(),
})
}
ColumnRef::Logical(i, _) if i.index() >= a.n_rows() => {
Err(SparseError::IndexOutOfRange {
what: "logical basis row",
index: i.index(),
bound: a.n_rows(),
})
}
_ => Ok(()),
}
}
}
pub trait ColumnSource<T: Real> {
fn m(&self) -> usize;
fn for_each_entry<F: FnMut(u32, T)>(&self, k: usize, f: F);
fn nnz_hint(&self) -> usize;
fn validate(&self) -> Result<(), SparseError>;
}
impl<T: Real> ColumnSource<T> for (&[ColumnRef], &CscMatrix<T>) {
#[inline]
fn m(&self) -> usize {
self.0.len()
}
#[inline]
fn for_each_entry<F: FnMut(u32, T)>(&self, k: usize, f: F) {
self.0[k].for_each_entry(self.1, f);
}
fn nnz_hint(&self) -> usize {
self.0
.iter()
.map(|c| match *c {
ColumnRef::Structural(j) => {
self.1.col_ptr[j.index() + 1] - self.1.col_ptr[j.index()]
}
ColumnRef::Logical(..) => 1,
})
.sum()
}
fn validate(&self) -> Result<(), SparseError> {
let (basis, a) = *self;
if a.n_rows() != basis.len() {
return Err(SparseError::Dimension {
what: "basis (must have one column per row of A)",
expected: a.n_rows(),
found: basis.len(),
});
}
for c in basis {
c.validate(a)?;
}
Ok(())
}
}
impl<T: Real> ColumnSource<T> for CscMatrix<T> {
#[inline]
fn m(&self) -> usize {
self.n_cols()
}
#[inline]
fn for_each_entry<F: FnMut(u32, T)>(&self, k: usize, mut f: F) {
let (rows, vals) = self.col(Col::from_usize(k));
for (&i, &v) in rows.iter().zip(vals) {
f(i, v);
}
}
fn nnz_hint(&self) -> usize {
self.nnz()
}
fn validate(&self) -> Result<(), SparseError> {
if self.n_rows() == self.n_cols() {
Ok(())
} else {
Err(SparseError::Dimension {
what: "matrix B (must be square)",
expected: self.n_rows(),
found: self.n_cols(),
})
}
}
}