optimization_engine 0.4.0-alpha

A pure Rust framework for embedded nonconvex optimization. Ideal for robotics!
Documentation
use crate::core::panoc::panoc_engine::PANOCEngine;
use crate::core::panoc::*;
use crate::core::*;
use crate::{mocks, SolverError};
use std::num::NonZeroUsize;

const N_DIM: usize = 2;
#[test]
fn t_panoc_init() {
    let radius = 0.2;
    let ball = constraints::Ball2::new(None, radius);
    let problem = Problem::new(&ball, mocks::my_gradient, mocks::my_cost);
    let mut panoc_cache = PANOCCache::new(
        NonZeroUsize::new(N_DIM).unwrap(),
        1e-6,
        NonZeroUsize::new(5).unwrap(),
    );

    {
        let mut panoc_engine = PANOCEngine::new(problem, &mut panoc_cache);
        let mut u = [0.75, -1.4];
        panoc_engine.init(&mut u).unwrap();
        assert!(2.549509967743775 > panoc_engine.cache.lipschitz_constant);
        assert!(0.372620625931781 < panoc_engine.cache.gamma, "gamma");
        println!("----------- {} ", panoc_engine.cache.cost_value);
        unit_test_utils::assert_nearly_equal(
            6.34125,
            panoc_engine.cache.cost_value,
            1e-4,
            1e-10,
            "cost value",
        );
        unit_test_utils::assert_nearly_equal_array(
            &[0.35, -3.05],
            &panoc_engine.cache.gradient_u,
            1e-4,
            1e-10,
            "gradient at u",
        );
    }
    println!("cache = {:#?}", &panoc_cache);
}

fn print_panoc_engine<'a, GradientType, ConstraintType, CostType>(
    panoc_engine: &PANOCEngine<'a, GradientType, ConstraintType, CostType>,
) where
    GradientType: Fn(&[f64], &mut [f64]) -> Result<(), SolverError>,
    CostType: Fn(&[f64], &mut f64) -> Result<(), SolverError>,
    ConstraintType: constraints::Constraint,
{
    println!("> fpr       = {:?}", &panoc_engine.cache.gamma_fpr);
    println!("> fpr       = {:.2e}", panoc_engine.cache.norm_gamma_fpr);
    println!("> L         = {:.3}", panoc_engine.cache.lipschitz_constant);
    println!("> gamma     = {:.10}", panoc_engine.cache.gamma);
    println!("> tau       = {:.3}", panoc_engine.cache.tau);
    println!("> lbfgs dir = {:.11?}", panoc_engine.cache.direction_lbfgs);
}

#[test]
fn t_test_panoc_basic() {
    let bounds = constraints::Ball2::new(None, 0.2);
    let problem = Problem::new(&bounds, mocks::my_gradient, mocks::my_cost);
    let tolerance = 1e-9;
    let mut panoc_cache = PANOCCache::new(
        NonZeroUsize::new(2).unwrap(),
        tolerance,
        NonZeroUsize::new(5).unwrap(),
    );
    let mut panoc_engine = PANOCEngine::new(problem, &mut panoc_cache);

    let mut u = [0.0, 0.0];
    panoc_engine.init(&mut u).unwrap();
    panoc_engine.step(&mut u).unwrap();
    let fpr0 = panoc_engine.cache.norm_gamma_fpr;
    println!("fpr0 = {}", fpr0);

    for i in 1..=100 {
        println!("----------------------------------------------------");
        println!("> iter      = {}", i);
        print_panoc_engine(&panoc_engine);
        println!("> u         = {:.14?}", u);
        if panoc_engine.step(&mut u) != Ok(true) {
            break;
        }
    }
    println!("final |fpr| = {}", panoc_engine.cache.norm_gamma_fpr);
    assert!(panoc_engine.cache.norm_gamma_fpr <= tolerance);
    unit_test_utils::assert_nearly_equal_array(&u, &mocks::SOLUTION_A, 1e-6, 1e-8, "");
}

#[test]
fn t_test_panoc_hard() {
    let radius: f64 = 0.05;
    let bounds = constraints::Ball2::new(None, radius);
    let problem = Problem::new(
        &bounds,
        mocks::hard_quadratic_gradient,
        mocks::hard_quadratic_cost,
    );
    let n: usize = 3;
    let lbfgs_memory: usize = 10;
    let tolerance_fpr: f64 = 1e-12;
    let mut panoc_cache = PANOCCache::new(
        NonZeroUsize::new(n).unwrap(),
        tolerance_fpr,
        NonZeroUsize::new(lbfgs_memory).unwrap(),
    );
    let mut panoc_engine = PANOCEngine::new(problem, &mut panoc_cache);

    let mut u = [-20., 10., 0.2];
    panoc_engine.init(&mut u).unwrap();

    println!("L     = {}", panoc_engine.cache.lipschitz_constant);
    println!("gamma = {}", panoc_engine.cache.gamma);
    println!("sigma = {}", panoc_engine.cache.sigma);

    let mut i = 1;
    println!("\n*** ITERATION   1");
    while panoc_engine.step(&mut u) == Ok(true) && i < 100 {
        i += 1;
        println!("+ u_plus               = {:?}", u);
        println!("\n*** ITERATION {:3}", i);
    }

    println!("\nsol = {:?}", u);
    assert!(panoc_engine.cache.norm_gamma_fpr <= tolerance_fpr);
    unit_test_utils::assert_nearly_equal_array(&u, &mocks::SOLUTION_HARD, 1e-6, 1e-8, "");
}

#[test]
fn t_test_panoc_rosenbrock() {
    let tolerance = 1e-12;
    let a = 1.0;
    let b = 100.0;
    let df = |u: &[f64], grad: &mut [f64]| -> Result<(), SolverError> {
        mocks::rosenbrock_grad(a, b, u, grad);
        Ok(())
    };
    let f = |u: &[f64], c: &mut f64| -> Result<(), SolverError> {
        *c = mocks::rosenbrock_cost(a, b, u);
        Ok(())
    };
    let bounds = constraints::Ball2::new(None, 1.0);
    let problem = Problem::new(&bounds, df, f);
    let mut panoc_cache = PANOCCache::new(
        NonZeroUsize::new(2).unwrap(),
        tolerance,
        NonZeroUsize::new(2).unwrap(),
    );
    let mut panoc_engine = PANOCEngine::new(problem, &mut panoc_cache);
    let mut u = [-1.5, 0.9];
    panoc_engine.init(&mut u).unwrap();
    let mut i = 1;
    while panoc_engine.step(&mut u) == Ok(true) && i < 50 {
        i += 1;
    }
    assert!(panoc_engine.cache.norm_gamma_fpr <= tolerance);
    println!("u = {:?}", u);
}