optimization_engine 0.12.0

A pure Rust framework for embedded nonconvex optimization. Ideal for robotics!
Documentation
//! # Constrained minimisation with penalty method
//!
//! In this example we solve the problem:
//!
//! $$
//! \begin{aligned}
//! \text{Minimise  }  f(u) = \tfrac{1}{2}\\|u\\|^2 + \sum_{i=1}^{n}u_i
//! \\\\
//! F_2(u) = \\|u\\|^2 - 1 = 0
//! \end{aligned}
//! $$

use optimization_engine::{
    alm::*,
    core::{constraints::*, panoc::*},
    matrix_operations, SolverError,
};

/// Smooth cost function
///
/// This is a quadratic function given by
/// $$
/// \begin{aligned}
/// f(u) = \tfrac{1}{2}\\|u\\|^2 + \sum_{i=1}^{n}u_i
/// \end{aligned}
/// $$
pub fn f(u: &[f64], cost: &mut f64) -> Result<(), SolverError> {
    *cost = 0.5 * matrix_operations::norm2_squared(u) + matrix_operations::sum(u);
    Ok(())
}

/// Gradient of the cost function
///
/// $$\begin{aligned}
/// f(u) = \tfrac{1}{2}\\|u\\|^2 + \sum_{i=1}^{n}u_i
/// \Rightarrow
/// \nabla f(u) = u + 1_n
/// \end{aligned}
/// $$
pub fn df(u: &[f64], grad: &mut [f64]) -> Result<(), SolverError> {
    grad.iter_mut()
        .zip(u.iter())
        .for_each(|(grad_i, u_i)| *grad_i = u_i + 1.0);
    Ok(())
}

/// Function $F_2 = \\|u\\|^2 - 1$
///
///
pub fn f2(u: &[f64], res: &mut [f64]) -> Result<(), SolverError> {
    res[0] = matrix_operations::norm2_squared(u) - 1.;
    Ok(())
}

/// Function $JF_2(u)^\top d$
pub fn jf2t(u: &[f64], d: &[f64], res: &mut [f64]) -> Result<(), crate::SolverError> {
    res.iter_mut()
        .zip(u.iter())
        .for_each(|(res_i, u_i)| *res_i = u_i * d[0]);
    Ok(())
}

fn main() {
    let tolerance = 1e-4;
    let nx = 3;
    let n1 = 0;
    let n2 = 1;
    let lbfgs_mem = 5;
    let panoc_cache = PANOCCache::new(nx, tolerance, lbfgs_mem);
    let mut alm_cache = AlmCache::new(panoc_cache, n1, n2);

    let bounds = NoConstraints::new();

    let factory = AlmFactory::new(
        f,
        df,
        NO_MAPPING,
        NO_JACOBIAN_MAPPING,
        Some(f2),
        Some(jf2t),
        NO_SET,
        n2,
    );

    let alm_problem = AlmProblem::new(
        bounds,
        NO_SET,
        NO_SET,
        |u: &[f64], xi: &[f64], cost: &mut f64| -> Result<(), SolverError> {
            factory.psi(u, xi, cost)
        },
        |u: &[f64], xi: &[f64], grad: &mut [f64]| -> Result<(), SolverError> {
            factory.d_psi(u, xi, grad)
        },
        NO_MAPPING,
        Some(f2),
        n1,
        n2,
    );

    let mut alm_optimizer = AlmOptimizer::new(&mut alm_cache, alm_problem)
        .with_delta_tolerance(1e-6)
        .with_epsilon_tolerance(1e-5)
        .with_max_outer_iterations(20)
        .with_max_inner_iterations(1000)
        .with_initial_penalty(5000.0)
        .with_penalty_update_factor(2.2);

    let mut u = vec![0.1; nx];
    let solver_result = alm_optimizer.solve(&mut u);
    let r = solver_result.unwrap();
    println!("\n\nSolver result : {:#.7?}\n", r);
    println!("Solution u = {:#.6?}", u);
}