use crate::optimizers::{ConvergenceStatus, Objective, OptimizeResult, norm};
#[must_use]
pub fn gradient_descent(
obj: &impl Objective,
x0: &[f64],
lr: f64,
max_iter: usize,
tol: f64,
) -> OptimizeResult {
let mut x = x0.to_vec();
let mut status = ConvergenceStatus::MaxIterReached;
let mut iterations = 0;
for step in 0..max_iter {
iterations = step + 1;
let g = obj.grad(&x);
if norm(&g) < tol {
status = ConvergenceStatus::Converged;
break;
}
for (xi, gi) in x.iter_mut().zip(&g) {
*xi -= lr * gi;
}
}
let fx = obj.value(&x);
OptimizeResult {
x,
fx,
iterations,
status,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::optimizers::objectives::Quadratic;
#[test]
fn budget_limited_run_reports_max_iter() {
let obj = Quadratic::new(vec![3.0, -2.0]);
let r = gradient_descent(&obj, &[0.0, 0.0], 0.1, 1, 1e-12);
assert_eq!(r.status, ConvergenceStatus::MaxIterReached);
assert_eq!(r.iterations, 1, "iterations was {}", r.iterations);
}
}