use crate::optimizers::{ConvergenceStatus, Objective, OptimizeResult, norm};
const BETA1: f64 = 0.9;
const BETA2: f64 = 0.999;
const EPS: f64 = 1e-8;
#[allow(clippy::many_single_char_names)]
#[must_use]
pub fn adam(
obj: &impl Objective,
x0: &[f64],
lr: f64,
max_iter: usize,
tol: f64,
) -> OptimizeResult {
let mut x = x0.to_vec();
let n = x.len();
let mut m = vec![0.0; n];
let mut v = vec![0.0; n];
let mut status = ConvergenceStatus::MaxIterReached;
let mut iterations = 0;
let mut t = 0.0_f64;
for step in 0..max_iter {
iterations = step + 1;
let g = obj.grad(&x);
if norm(&g) < tol {
status = ConvergenceStatus::Converged;
break;
}
t += 1.0;
let bc1 = 1.0 - BETA1.powf(t);
let bc2 = 1.0 - BETA2.powf(t);
for (((xi, gi), mi), vi) in x.iter_mut().zip(&g).zip(m.iter_mut()).zip(v.iter_mut()) {
*mi = BETA1.mul_add(*mi, (1.0 - BETA1) * gi);
*vi = BETA2.mul_add(*vi, (1.0 - BETA2) * gi * gi);
let m_hat = *mi / bc1;
let v_hat = *vi / bc2;
*xi -= lr * m_hat / (v_hat.sqrt() + EPS);
}
}
let fx = obj.value(&x);
OptimizeResult {
x,
fx,
iterations,
status,
}
}