use super::operator::QuadraticOperator;
use super::types::LipschitzMethod;
use super::utils::{DenseVec, l2_norm};
const AUTO_POWER_ITERS: usize = 30;
const AUTO_POWER_SAFETY: f64 = 1.2;
pub fn estimate_lipschitz(q: &dyn QuadraticOperator, method: &LipschitzMethod) -> f64 {
match method {
LipschitzMethod::Gershgorin => estimate_lipschitz_gershgorin(q),
LipschitzMethod::Auto => {
let power = AUTO_POWER_SAFETY * estimate_lipschitz_power(q, AUTO_POWER_ITERS);
match q.gershgorin_upper_bound() {
Some(gershgorin) => gershgorin.min(power),
None => power,
}
}
}
}
pub fn estimate_lipschitz_gershgorin(q: &dyn QuadraticOperator) -> f64 {
q.gershgorin_upper_bound()
.unwrap_or_else(|| estimate_lipschitz_power(q, AUTO_POWER_ITERS))
}
fn estimate_lipschitz_power(q: &dyn QuadraticOperator, n_iter: usize) -> f64 {
let n = q.n();
let mut v = DenseVec::from_elem(n, 1.0 / (n as f64).sqrt());
let mut w = DenseVec::zeros(n);
for _ in 0..n_iter {
q.matvec_into(&v, &mut w);
let nw = l2_norm(&w);
if nw == 0.0 {
return 1.0;
}
for i in 0..n {
v[i] = w[i] / nw;
}
}
q.matvec_into(&v, &mut w);
v.dot(&w).max(1e-12)
}