use crate::error::{CsError, CsResult};
use crate::handle::LcgRng;
use crate::linalg::jacobi_svd::jacobi_svd_thin;
use crate::linalg::submat_columns;
pub fn rip_estimator(
phi: &[f64],
m: usize,
n: usize,
k: usize,
num_samples: usize,
rng: &mut LcgRng,
) -> CsResult<f64> {
if phi.len() != m * n {
return Err(CsError::ShapeMismatch {
expected: vec![m, n],
got: vec![phi.len()],
});
}
if k == 0 || k > n.min(m) {
return Err(CsError::InvalidSparsity(k));
}
if num_samples == 0 {
return Err(CsError::InvalidParameter("num_samples = 0".into()));
}
let mut worst = 0.0_f64;
let mut indices: Vec<usize> = (0..n).collect();
for _ in 0..num_samples {
for i in 0..k {
let j = i + rng.next_usize(n - i);
indices.swap(i, j);
}
let mut subset = indices[..k].to_vec();
subset.sort();
let sub = submat_columns(phi, m, n, &subset)?;
let (_u, s, _v) = jacobi_svd_thin(&sub, m, k)?;
let sigma_max = s[0];
let sigma_min = *s.last().unwrap_or(&0.0);
let d_hi = (sigma_max * sigma_max - 1.0).abs();
let d_lo = (1.0 - sigma_min * sigma_min).abs();
let d = d_hi.max(d_lo);
if d > worst {
worst = d;
}
}
Ok(worst)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::measurement::gaussian_matrix::gaussian_matrix;
#[test]
fn rip_estimator_runs() {
let mut rng = LcgRng::new(2);
let m = 20;
let n = 50;
let phi = gaussian_matrix(m, n, &mut rng).expect("ok");
let mut rng2 = LcgRng::new(7);
let d = rip_estimator(&phi, m, n, 3, 20, &mut rng2).expect("ok");
assert!(d.is_finite());
assert!(d < 2.0);
}
}