use optirs_core::optimizers::{Adam, Lion, Optimizer, LAMB, SGD};
use scirs2_core::ndarray::Array1;
#[allow(dead_code)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("LAMB Optimizer Example");
println!("=====================");
let rastrigin_fn = |x: &Array1<f64>| -> f64 {
let a = 10.0;
let n = x.len() as f64;
let sum: f64 = x
.iter()
.map(|&xi| xi.powi(2) - a * (2.0 * std::f64::consts::PI * xi).cos())
.sum();
a * n + sum
};
let rastrigin_grad = |x: &Array1<f64>| -> Array1<f64> {
let a = 10.0;
let two_pi = 2.0 * std::f64::consts::PI;
Array1::from_shape_fn(x.len(), |i| 2.0 * x[i] + a * two_pi * (two_pi * x[i]).sin())
};
let initial_params = Array1::from_vec(vec![2.5, -1.8, 3.2]);
println!("Minimizing 3D Rastrigin function");
println!("Initial point: {:?}", initial_params);
println!("Target minimum: [0.0, 0.0, 0.0]");
println!();
let learning_rate = 0.01;
let optimizers = vec![
(
"LAMB",
Box::new(LAMB::new(learning_rate))
as Box<dyn Optimizer<f64, scirs2_core::ndarray::Ix1>>,
),
(
"Lion",
Box::new(Lion::new(learning_rate))
as Box<dyn Optimizer<f64, scirs2_core::ndarray::Ix1>>,
),
(
"Adam",
Box::new(Adam::new(learning_rate))
as Box<dyn Optimizer<f64, scirs2_core::ndarray::Ix1>>,
),
(
"SGD",
Box::new(SGD::new(learning_rate)) as Box<dyn Optimizer<f64, scirs2_core::ndarray::Ix1>>,
),
];
for (name, mut optimizer) in optimizers {
println!("Running {} optimizer:", name);
let mut params = initial_params.clone();
let initial_value = rastrigin_fn(¶ms);
println!("Initial function value: {:.6}", initial_value);
let num_iterations = 200;
let mut values = vec![initial_value];
for i in 0..num_iterations {
let gradients = rastrigin_grad(¶ms);
params = optimizer.step(¶ms, &gradients)?;
let value = rastrigin_fn(¶ms);
values.push(value);
if i % 40 == 0 || i == num_iterations - 1 {
println!(
" Iteration {:3}: x = [{:6.3}, {:6.3}, {:6.3}], f(x) = {:.6}",
i + 1,
params[0],
params[1],
params[2],
value
);
}
}
let final_value = values.last().expect("unwrap failed");
println!(
"Final point: [{:.6}, {:.6}, {:.6}]",
params[0], params[1], params[2]
);
println!("Final function value: {:.6}", final_value);
let improvements: Vec<f64> = values
.windows(2)
.filter_map(|w| {
let improvement = w[0] - w[1];
if improvement > 0.0 {
Some(improvement)
} else {
None
}
})
.collect();
if !improvements.is_empty() {
let avg_improvement = improvements.iter().sum::<f64>() / improvements.len() as f64;
println!("Average improvement per step: {:.6}", avg_improvement);
println!(
"Steps with improvement: {}/{}",
improvements.len(),
num_iterations
);
}
println!(
"Distance from optimum: {:.6}",
params.mapv(|x| x.abs()).sum()
);
println!();
}
println!("LAMB with Weight Decay");
println!("======================");
let mut lamb_wd = LAMB::new_with_config(
learning_rate,
0.9, 0.999, 1e-8, 0.01, true, );
let mut params = initial_params.clone();
for i in 0..100 {
let gradients = rastrigin_grad(¶ms);
params = lamb_wd.step(¶ms, &gradients)?;
if i % 20 == 0 {
let value = rastrigin_fn(¶ms);
println!(" Iteration {:3}: f(x) = {:.6}", i + 1, value);
}
}
println!("Final parameters with weight decay: {:?}", params);
println!();
Ok(())
}
#[test]
#[allow(dead_code)]
fn test_lamb_on_rastrigin() {
let mut optimizer = LAMB::new(0.1);
let mut params = Array1::from_vec(vec![2.0, 1.5]);
let grad_fn =
|x: &Array1<f64>| -> Array1<f64> { Array1::from_vec(vec![2.0 * x[0], 2.0 * x[1]]) };
for _ in 0..100 {
let gradients = grad_fn(¶ms);
params = optimizer.step(¶ms, &gradients).expect("unwrap failed");
}
assert!(params[0].abs() < 0.1);
assert!(params[1].abs() < 0.1);
}