use cartan_core::{Manifold, Real, Retraction};
use crate::result::OptResult;
#[derive(Debug, Clone)]
pub struct RGDConfig {
pub max_iters: usize,
pub grad_tol: Real,
pub init_step: Real,
pub armijo_c: Real,
pub armijo_beta: Real,
pub max_ls_iters: usize,
}
impl Default for RGDConfig {
fn default() -> Self {
Self {
max_iters: 1000,
grad_tol: 1e-6,
init_step: 1.0,
armijo_c: 1e-4,
armijo_beta: 0.5,
max_ls_iters: 50,
}
}
}
pub fn minimize_rgd<M, F, G>(
manifold: &M,
cost: F,
rgrad: G,
x0: M::Point,
config: &RGDConfig,
) -> OptResult<M::Point>
where
M: Manifold + Retraction,
F: Fn(&M::Point) -> Real,
G: Fn(&M::Point) -> M::Tangent,
{
let mut x = x0;
let mut f_x = cost(&x);
let mut g = rgrad(&x);
let mut g_norm = manifold.norm(&x, &g);
for iter in 0..config.max_iters {
if g_norm < config.grad_tol {
return OptResult {
point: x,
value: f_x,
grad_norm: g_norm,
iterations: iter,
converged: true,
};
}
let d = -g.clone();
let slope = manifold.inner(&x, &g, &d); let mut t = config.init_step;
let f_threshold_base = f_x + config.armijo_c * slope;
for _ in 0..config.max_ls_iters {
let x_trial = manifold.retract(&x, &(d.clone() * t));
let f_trial = cost(&x_trial);
if f_trial <= f_x + config.armijo_c * t * slope {
x = x_trial;
f_x = f_trial;
break;
}
t *= config.armijo_beta;
}
let _ = f_threshold_base;
g = rgrad(&x);
g_norm = manifold.norm(&x, &g);
}
OptResult {
point: x,
value: f_x,
grad_norm: g_norm,
iterations: config.max_iters,
converged: false,
}
}