ipopt_bindgen 0.2.3

Rust bindings to the C interface of Ipopt, a library for large-scale, constrained, nonlinear optimization.
Documentation
//! HS071 example as per the
//! [Ipopt documentation](https://coin-or.github.io/Ipopt/INTERFACES.html).
//!
//! > ⚠️ This library should probably be used to implement your own convenient
//! > Rust interfaces to Ipopt. This example is just to show how that it *is*
//! > possible to use the raw C API.

use ipopt_bindgen::*;

unsafe extern "C" fn objective_callback(
    n: ipindex,
    x: *mut ipnumber,
    _new_x: bool,
    obj_value: *mut ipnumber,
    _user_data: UserDataPtr,
) -> bool {
    let x_slice = std::slice::from_raw_parts(x, n as usize);
    *obj_value = x_slice[0] * x_slice[3] * (x_slice[0] + x_slice[1] + x_slice[2]) + x_slice[2];
    true
}

unsafe extern "C" fn constraints_callback(
    n: ipindex,
    x: *mut ipnumber,
    _new_x: bool,
    m: ipindex,
    g: *mut ipnumber,
    _user_data: UserDataPtr,
) -> bool {
    let x_slice = std::slice::from_raw_parts(x, n as usize);
    let g_slice = std::slice::from_raw_parts_mut(g, m as usize);
    g_slice[0] = x_slice[0] * x_slice[1] * x_slice[2] * x_slice[3];
    g_slice[1] = x_slice[0] * x_slice[0]
        + x_slice[1] * x_slice[1]
        + x_slice[2] * x_slice[2]
        + x_slice[3] * x_slice[3];
    true
}

unsafe extern "C" fn gradient_callback(
    n: ipindex,
    x: *mut ipnumber,
    _new_x: bool,
    grad_f: *mut ipnumber,
    _user_data: UserDataPtr,
) -> bool {
    let x_slice = std::slice::from_raw_parts(x, n as usize);
    let grad_slice = std::slice::from_raw_parts_mut(grad_f, n as usize);
    grad_slice[0] = x_slice[0] * x_slice[3] + x_slice[3] * (x_slice[0] + x_slice[1] + x_slice[2]);
    grad_slice[1] = x_slice[0] * x_slice[3];
    grad_slice[2] = x_slice[0] * x_slice[3] + 1.0;
    grad_slice[3] = x_slice[0] * (x_slice[0] + x_slice[1] + x_slice[2]);
    true
}

unsafe extern "C" fn jacobian_callback(
    n: ipindex,
    x: *mut ipnumber,
    _new_x: bool,
    _m: ipindex,
    nele_jac: ipindex,
    i_row: *mut ipindex,
    j_col: *mut ipindex,
    values: *mut ipnumber,
    _user_data: UserDataPtr,
) -> bool {
    if x.is_null() {
        // Set Jacobian sparsity structure
        let i_row = std::slice::from_raw_parts_mut(i_row, nele_jac as usize);
        let j_col = std::slice::from_raw_parts_mut(j_col, nele_jac as usize);
        // this particular Jacobian is dense
        i_row[0] = 0;
        j_col[0] = 0;
        i_row[1] = 0;
        j_col[1] = 1;
        i_row[2] = 0;
        j_col[2] = 2;
        i_row[3] = 0;
        j_col[3] = 3;
        i_row[4] = 1;
        j_col[4] = 0;
        i_row[5] = 1;
        j_col[5] = 1;
        i_row[6] = 1;
        j_col[6] = 2;
        i_row[7] = 1;
        j_col[7] = 3;
    } else {
        // Set Jacobian values
        let x_slice = std::slice::from_raw_parts(x, n as usize);
        let values = std::slice::from_raw_parts_mut(values, nele_jac as usize);
        values[0] = x_slice[1] * x_slice[2] * x_slice[3]; // 0,0
        values[1] = x_slice[0] * x_slice[2] * x_slice[3]; // 0,1
        values[2] = x_slice[0] * x_slice[1] * x_slice[3]; // 0,2
        values[3] = x_slice[0] * x_slice[1] * x_slice[2]; // 0,3

        values[4] = 2.0 * x_slice[0]; // 1,0
        values[5] = 2.0 * x_slice[1]; // 1,1
        values[6] = 2.0 * x_slice[2]; // 1,2
        values[7] = 2.0 * x_slice[3]; // 1,3
    }
    true
}

unsafe extern "C" fn hessian_callback(
    n: ipindex,
    x: *mut ipnumber,
    _new_x: bool,
    obj_factor: ipnumber,
    m: ipindex,
    lambda: *mut ipnumber,
    _new_lambda: bool,
    nele_hess: ipindex,
    i_row: *mut ipindex,
    j_col: *mut ipindex,
    values: *mut ipnumber,
    _user_data: UserDataPtr,
) -> bool {
    if x.is_null() {
        // Set the Hessian sparsity structure
        let i_row_slice = std::slice::from_raw_parts_mut(i_row, nele_hess as usize);
        let j_col_slice = std::slice::from_raw_parts_mut(j_col, nele_hess as usize);
        let mut idx: ipindex = 0;
        for row in 0..n {
            for col in 0..=row {
                i_row_slice[idx as usize] = row;
                j_col_slice[idx as usize] = col;
                idx += 1;
            }
        }
    } else {
        // Set the Hessian values
        let x_slice = std::slice::from_raw_parts(x, n as usize);
        let lambda_slice = std::slice::from_raw_parts(lambda, m as usize);
        let hessian = std::slice::from_raw_parts_mut(values, nele_hess as usize);

        // return the values. This is a symmetric matrix, fill the lower left
        // triangle only

        // fill the objective portion
        hessian[0] = obj_factor * (2.0 * x_slice[3]); // 0,0

        hessian[1] = obj_factor * (x_slice[3]); // 1,0
        hessian[2] = 0.; // 1,1

        hessian[3] = obj_factor * (x_slice[3]); // 2,0
        hessian[4] = 0.; // 2,1
        hessian[5] = 0.; // 2,2

        hessian[6] = obj_factor * (2.0 * x_slice[0] + x_slice[1] + x_slice[2]); // 3,0
        hessian[7] = obj_factor * (x_slice[0]); // 3,1
        hessian[8] = obj_factor * (x_slice[0]); // 3,2
        hessian[9] = 0.; // 3,3

        // add the portion for the first constraint
        hessian[1] += lambda_slice[0] * (x_slice[2] * x_slice[3]); // 1,0
        hessian[3] += lambda_slice[0] * (x_slice[1] * x_slice[3]); // 2,0
        hessian[4] += lambda_slice[0] * (x_slice[0] * x_slice[3]); // 2,1
        hessian[6] += lambda_slice[0] * (x_slice[1] * x_slice[2]); // 3,0
        hessian[7] += lambda_slice[0] * (x_slice[0] * x_slice[2]); // 3,1
        hessian[8] += lambda_slice[0] * (x_slice[0] * x_slice[1]); // 3,2

        // add the portion for the second constraint
        hessian[0] += lambda_slice[1] * 2.0; // 0,0
        hessian[2] += lambda_slice[1] * 2.0; // 1,1
        hessian[5] += lambda_slice[1] * 2.0; // 2,2
        hessian[9] += lambda_slice[1] * 2.0; // 3,3
    }
    true
}

fn main() {
    let n = 4;
    let m = 2;
    let mut x_l = vec![1.0, 1.0, 1.0, 1.0];
    let mut x_u = vec![5.0, 5.0, 5.0, 5.0];
    let mut g_l = vec![25.0, 40.0];
    let mut g_u = vec![2.0e19, 40.0];
    let nnz_jacobian = 8;
    let nnz_hessian = 10;
    const C_STYLE_INDEXING: i32 = 0;

    let objective: Eval_F_CB = Some(objective_callback);
    let constraints: Eval_G_CB = Some(constraints_callback);
    let gradients: Eval_Grad_F_CB = Some(gradient_callback);
    let jacobian: Eval_Jac_G_CB = Some(jacobian_callback);
    let hessian: Eval_H_CB = Some(hessian_callback);

    // Helper to make dealing with raw C string less miserable.
    let cstr = |s: &str| std::ffi::CString::new(s).unwrap().into_raw();

    unsafe {
        let problem = crate::CreateIpoptProblem(
            n,
            x_l.as_mut_ptr(),
            x_u.as_mut_ptr(),
            m,
            g_l.as_mut_ptr(),
            g_u.as_mut_ptr(),
            nnz_jacobian,
            nnz_hessian,
            C_STYLE_INDEXING,
            objective,
            constraints,
            gradients,
            jacobian,
            hessian,
        );

        AddIpoptNumOption(problem, cstr("tol"), 3.82e-6);
        AddIpoptStrOption(problem, cstr("mu_strategy"), cstr("adaptive"));
        AddIpoptStrOption(problem, cstr("output_file"), cstr("ipopt.out"));

        let mut variables = vec![1.0, 5.0, 5.0, 1.0];
        let mut constraint_multipliers = vec![0.0, 0.0];
        let mut lower_bound_multipliers = vec![0.0, 0.0, 0.0, 0.0];
        let mut upper_bound_multipliers = vec![0.0, 0.0, 0.0, 0.0];

        let mut objective_value: f64 = 0.0;
        let _status = IpoptSolve(
            problem,
            variables.as_mut_ptr(),
            std::ptr::null_mut(),
            std::ptr::addr_of_mut!(objective_value),
            constraint_multipliers.as_mut_ptr(),
            lower_bound_multipliers.as_mut_ptr(),
            upper_bound_multipliers.as_mut_ptr(),
            std::ptr::null_mut(),
        );
    }
}