use crate::arithmetic::Fx;
use super::csr::CsrMatrix;
use super::focusing::FocusingParams;
#[inline]
fn fabs(x: Fx) -> Fx {
if x < Fx::ZERO {
Fx::ZERO - x
} else {
x
}
}
fn dot(a: &[Fx], b: &[Fx]) -> Fx {
let mut s = Fx::ZERO;
for i in 0..a.len() {
s = s + a[i] * b[i];
}
s
}
fn abs_max_normalize(v: &mut [Fx]) {
let mut m = Fx::ZERO;
for &x in v.iter() {
let a = fabs(x);
if a > m {
m = a;
}
}
if !m.is_zero() {
for x in v.iter_mut() {
*x = x.div(m);
}
}
}
fn deflate_mean(v: &mut [Fx]) {
let n = v.len();
let mut sum = Fx::ZERO;
for &x in v.iter() {
sum = sum + x;
}
let mean = sum.div(Fx::from_int(n as i64));
for x in v.iter_mut() {
*x = *x - mean;
}
}
fn l_matvec(sym: &CsrMatrix, degree: &[Fx], v: &[Fx], out: &mut [Fx]) {
sym.spmv(v, out);
for i in 0..v.len() {
out[i] = degree[i] * v[i] - out[i];
}
}
fn start(n: usize) -> Vec<Fx> {
let mut v: Vec<Fx> = (0..n).map(|i| Fx::from_int((i % 7 + 1) as i64)).collect();
abs_max_normalize(&mut v);
v
}
pub fn lambda_max(sym: &CsrMatrix, degree: &[Fx], n: usize, iters: usize) -> Fx {
if n == 0 {
return Fx::ZERO;
}
let mut v = start(n);
let mut lv = vec![Fx::ZERO; n];
let mut lam = Fx::ZERO;
for _ in 0..iters {
l_matvec(sym, degree, &v, &mut lv);
lam = dot(&v, &lv).div(dot(&v, &v)); v.copy_from_slice(&lv);
abs_max_normalize(&mut v);
}
lam
}
pub fn lambda_2(sym: &CsrMatrix, degree: &[Fx], n: usize, lambda_max: Fx, iters: usize) -> Fx {
if n < 2 {
return Fx::ZERO;
}
let mut v = start(n);
deflate_mean(&mut v);
abs_max_normalize(&mut v);
let mut mv = vec![Fx::ZERO; n];
let mut mu = Fx::ZERO;
for _ in 0..iters {
l_matvec(sym, degree, &v, &mut mv);
for i in 0..n {
mv[i] = lambda_max * v[i] - mv[i]; }
deflate_mean(&mut mv);
mu = dot(&v, &mv).div(dot(&v, &v));
v.copy_from_slice(&mv);
abs_max_normalize(&mut v);
}
let l2 = lambda_max - mu;
if l2 < Fx::ZERO {
Fx::ZERO
} else {
l2
}
}
pub fn spectral_vectors(
sym: &CsrMatrix,
degree: &[Fx],
n: usize,
lambda_max: Fx,
k: usize,
iters: usize,
) -> (Vec<Vec<Fx>>, Vec<Fx>) {
let k = k.min(n.saturating_sub(1));
if k == 0 {
return (vec![], vec![]);
}
let mut block: Vec<Vec<Fx>> = (0..k)
.map(|j| {
let mut v: Vec<Fx> = (0..n)
.map(|i| Fx::from_int(((i * (2 * j + 3) + j) % 13 + 1) as i64))
.collect();
deflate_mean(&mut v);
v
})
.collect();
orthonormalize(&mut block);
let mut mv = vec![Fx::ZERO; n];
for _ in 0..iters {
for col in block.iter_mut() {
l_matvec(sym, degree, col, &mut mv);
for i in 0..n {
col[i] = lambda_max * col[i] - mv[i]; }
deflate_mean(col);
}
orthonormalize(&mut block);
}
let mut ranked: Vec<(Fx, Vec<Fx>)> = block
.into_iter()
.map(|v| {
l_matvec(sym, degree, &v, &mut mv);
for i in 0..n {
mv[i] = lambda_max * v[i] - mv[i];
}
let mu = dot(&v, &mv).div(dot(&v, &v));
(mu, v)
})
.collect();
ranked.sort_by_key(|t| core::cmp::Reverse(t.0));
let mut vectors = Vec::with_capacity(k);
let mut eigenvalues = Vec::with_capacity(k);
for (mu, v) in ranked {
let lam = lambda_max - mu;
eigenvalues.push(if lam < Fx::ZERO { Fx::ZERO } else { lam });
vectors.push(v);
}
(vectors, eigenvalues)
}
fn orthonormalize(block: &mut [Vec<Fx>]) {
let k = block.len();
for j in 0..k {
for i in 0..j {
let denom = dot(&block[i], &block[i]);
if denom.is_zero() {
continue;
}
let coeff = dot(&block[i], &block[j]).div(denom);
for x in 0..block[j].len() {
block[j][x] = block[j][x] - coeff * block[i][x];
}
}
abs_max_normalize(&mut block[j]);
}
}
pub fn kappa(p: &FocusingParams, lambda_max: Fx, lambda_2: Fx) -> Fx {
let heat = (Fx::ZERO - p.tau * lambda_2).exp(); let springs = lambda_max.div(lambda_max + p.mu); p.lambda_d * p.alpha + p.lambda_s * springs + p.lambda_h * heat
}
pub fn steps_for(kappa: Fx, epsilon: Fx, cap: usize) -> usize {
if kappa >= Fx::ONE {
return cap;
}
let mut p = Fx::ONE;
let mut t = 0;
while p > epsilon && t < cap {
p = p * kappa;
t += 1;
}
t
}