coordinate_descent_example/
coordinate_descent_example.rs

1use nalgebra::DVector;
2use optimization_solvers::{
3    BackTracking, CoordinateDescent, FuncEvalMultivariate, LineSearchSolver, Tracer,
4};
5
6fn main() {
7    // Setting up logging
8    std::env::set_var("RUST_LOG", "info");
9    let _ = Tracer::default().with_normal_stdout_layer().build();
10
11    // Separable convex function: f(x,y,z) = x^2 + 2y^2 + 3z^2
12    // This function is separable and has a minimum at (0, 0, 0)
13    let f_and_g = |x: &DVector<f64>| -> FuncEvalMultivariate {
14        let x1 = x[0];
15        let x2 = x[1];
16        let x3 = x[2];
17
18        // Function value
19        let f = x1.powi(2) + 2.0 * x2.powi(2) + 3.0 * x3.powi(2);
20
21        // Gradient
22        let g1 = 2.0 * x1;
23        let g2 = 4.0 * x2;
24        let g3 = 6.0 * x3;
25        let g = DVector::from_vec(vec![g1, g2, g3]);
26
27        FuncEvalMultivariate::new(f, g)
28    };
29
30    // Setting up the line search (backtracking)
31    let armijo_factor = 1e-4;
32    let beta = 0.5;
33    let mut ls = BackTracking::new(armijo_factor, beta);
34
35    // Setting up the solver
36    let tol = 1e-6;
37    let x0 = DVector::from_vec(vec![1.0, 1.0, 1.0]); // Starting point
38    let mut solver = CoordinateDescent::new(tol, x0.clone());
39
40    // Running the solver
41    let max_iter_solver = 100;
42    let max_iter_line_search = 10;
43
44    println!("=== Coordinate Descent Example ===");
45    println!("Objective: f(x,y,z) = x^2 + 2y^2 + 3z^2 (separable convex)");
46    println!("Global minimum: (0, 0, 0) with f(0,0,0) = 0");
47    println!("Starting point: {:?}", x0);
48    println!("Tolerance: {}", tol);
49    println!();
50
51    match solver.minimize(
52        &mut ls,
53        f_and_g,
54        max_iter_solver,
55        max_iter_line_search,
56        None,
57    ) {
58        Ok(()) => {
59            let x = solver.x();
60            let eval = f_and_g(x);
61            println!("✅ Optimization completed successfully!");
62            println!("Final iterate: {:?}", x);
63            println!("Function value: {:.6}", eval.f());
64            println!("Gradient norm: {:.6}", eval.g().norm());
65            println!("Iterations: {}", solver.k());
66
67            // Check if we're close to the known minimum
68            let true_min = DVector::from_vec(vec![0.0, 0.0, 0.0]);
69            let distance_to_min = (x - true_min).norm();
70            println!("Distance to true minimum: {:.6}", distance_to_min);
71            println!("Expected function value: 0.0");
72
73            // Verify optimality conditions
74            let gradient_at_solution = eval.g();
75            println!("Gradient at solution: {:?}", gradient_at_solution);
76            println!(
77                "Gradient norm should be close to 0: {}",
78                gradient_at_solution.norm()
79            );
80        }
81        Err(e) => {
82            println!("❌ Optimization failed: {:?}", e);
83        }
84    }
85}