use crate::amp::AmpResult;
use crate::error::{CsError, CsResult};
use crate::linalg::normal_equations::normal_equations_solve;
use crate::linalg::{mat_t_vec, mat_vec, norm2};
use crate::thresholding::iht::soft_threshold;
pub fn vamp(
phi: &[f64],
m: usize,
n: usize,
y: &[f64],
tau_lambda: f64,
max_iter: usize,
tol: f64,
) -> CsResult<AmpResult> {
if phi.len() != m * n {
return Err(CsError::ShapeMismatch {
expected: vec![m, n],
got: vec![phi.len()],
});
}
if y.len() != m {
return Err(CsError::DimensionMismatch { a: y.len(), b: m });
}
if tau_lambda <= 0.0 {
return Err(CsError::InvalidParameter("tau_lambda must be > 0".into()));
}
let mut r1 = vec![0.0_f64; n];
let mut tau1 = 1.0_f64;
let mut iter = 0usize;
let mut x_hat = vec![0.0_f64; n];
let phi_t_y = mat_t_vec(phi, m, n, y)?;
for _ in 0..max_iter {
if !tau1.is_finite() {
tau1 = 1.0;
}
for v in r1.iter_mut() {
if !v.is_finite() {
*v = 0.0;
}
}
let mut rhs = vec![0.0_f64; n];
for j in 0..n {
rhs[j] = phi_t_y[j] + tau1 * r1[j];
}
let tau1_pos = tau1.max(1.0e-6);
let mut g = vec![0.0_f64; n * n];
for i in 0..m {
for a in 0..n {
let pai = phi[i * n + a];
for b in 0..n {
g[a * n + b] += pai * phi[i * n + b];
}
}
}
for i in 0..n {
g[i * n + i] += tau1_pos;
}
let l = crate::linalg::cholesky::cholesky_factor(&g, n)?;
let x2 = crate::linalg::cholesky::cholesky_solve(&l, n, &rhs)?;
let alpha1 =
((n as f64) / ((m as f64) + tau1_pos * (n as f64))).clamp(1.0e-6, 1.0 - 1.0e-6);
let beta1 = (1.0 - alpha1).max(1.0e-6);
let mut r2 = vec![0.0_f64; n];
for j in 0..n {
let v = (x2[j] - alpha1 * r1[j]) / beta1;
r2[j] = if v.is_finite() { v } else { 0.0 };
}
let tau2 = (tau1_pos * (1.0 - alpha1) / alpha1).clamp(1.0e-6, 1.0e12);
let lam = tau_lambda / tau2.sqrt();
let x_new = soft_threshold(&r2, lam);
let nz = x_new.iter().filter(|v| v.abs() > 1.0e-300).count();
let alpha2 = ((nz as f64) / (n as f64)).clamp(1.0e-6, 1.0 - 1.0e-6);
let beta2 = (1.0 - alpha2).max(1.0e-6);
let mut r1_new = vec![0.0_f64; n];
for j in 0..n {
r1_new[j] = (x_new[j] - alpha2 * r2[j]) / beta2;
}
let tau1_new_raw = tau2 * (1.0 - alpha2) / alpha2;
let tau1_new = if tau1_new_raw.is_finite() {
tau1_new_raw.clamp(1.0e-6, 1.0e12)
} else {
1.0
};
let mut delta = 0.0_f64;
for j in 0..n {
let d = x_new[j] - x_hat[j];
delta += d * d;
}
for v in r1_new.iter_mut() {
if !v.is_finite() {
*v = 0.0;
}
}
let mut x_safe = x_new;
for v in x_safe.iter_mut() {
if !v.is_finite() {
*v = 0.0;
}
}
x_hat = x_safe;
r1 = r1_new;
tau1 = tau1_new;
iter += 1;
if delta.sqrt() / norm2(&x_hat).max(1.0e-300) < tol {
break;
}
}
for v in x_hat.iter_mut() {
if !v.is_finite() {
*v = 0.0;
}
}
let ax = mat_vec(phi, m, n, &x_hat)?;
let mut residual = vec![0.0_f64; m];
for i in 0..m {
residual[i] = y[i] - ax[i];
}
let _ = normal_equations_solve;
Ok(AmpResult {
x: x_hat,
residual_norm: norm2(&residual),
iterations: iter,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vamp_runs() {
let phi = vec![
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
];
let y = vec![1.0, 0.0, 0.5, 0.0];
let r = vamp(&phi, 4, 4, &y, 0.1, 50, 1.0e-9).expect("ok");
assert!(r.iterations > 0);
}
}