use ndarray::{Array1, ArrayView1, ArrayView2};
use crate::faer_ndarray::{fast_atv, fast_av};
#[derive(Clone, Copy, Debug)]
pub struct LowRankWeight<'a> {
pub diag: ArrayView1<'a, f64>,
pub u: ArrayView2<'a, f64>,
pub v: ArrayView2<'a, f64>,
}
impl<'a> LowRankWeight<'a> {
pub fn new(
diag: ArrayView1<'a, f64>,
u: ArrayView2<'a, f64>,
v: ArrayView2<'a, f64>,
) -> Result<Self, String> {
let n = diag.len();
if u.nrows() != n {
return Err(format!(
"LowRankWeight: u has {} rows but diag has {} entries",
u.nrows(),
n
));
}
if v.nrows() != n {
return Err(format!(
"LowRankWeight: v has {} rows but diag has {} entries",
v.nrows(),
n
));
}
if u.ncols() != v.ncols() {
return Err(format!(
"LowRankWeight: u has rank {} but v has rank {}",
u.ncols(),
v.ncols()
));
}
Ok(LowRankWeight { diag, u, v })
}
pub fn symmetric(diag: ArrayView1<'a, f64>, u: ArrayView2<'a, f64>) -> Result<Self, String> {
Self::new(diag, u, u)
}
#[inline]
pub fn nrows(&self) -> usize {
self.diag.len()
}
#[inline]
pub fn rank(&self) -> usize {
self.u.ncols()
}
#[inline]
pub fn is_rank_zero(&self) -> bool {
self.rank() == 0
}
pub fn apply(&self, x: ArrayView1<'_, f64>) -> Array1<f64> {
let n = self.nrows();
assert_eq!(
x.len(),
n,
"LowRankWeight::apply: x has {} entries but W has {} rows",
x.len(),
n
);
let mut out = Array1::<f64>::from_iter((0..n).map(|i| self.diag[i] * x[i]));
if self.is_rank_zero() {
return out;
}
let vtx = fast_atv(&self.v, &x);
let uvtx = fast_av(&self.u, &vtx);
out += &uvtx;
out
}
}