use ripopt::{NlpProblem, SolveStatus, SolverOptions};
struct Rosenbrock;
impl NlpProblem for Rosenbrock {
fn num_variables(&self) -> usize { 2 }
fn num_constraints(&self) -> usize { 0 }
fn bounds(&self, x_l: &mut [f64], x_u: &mut [f64]) {
for i in 0..2 {
x_l[i] = f64::NEG_INFINITY;
x_u[i] = f64::INFINITY;
}
}
fn constraint_bounds(&self, _g_l: &mut [f64], _g_u: &mut [f64]) {}
fn initial_point(&self, x0: &mut [f64]) {
x0[0] = -1.2;
x0[1] = 1.0;
}
fn objective(&self, x: &[f64], _new_x: bool, obj: &mut f64) -> bool {
*obj = (1.0 - x[0]).powi(2) + 100.0 * (x[1] - x[0] * x[0]).powi(2);
true
}
fn gradient(&self, x: &[f64], _new_x: bool, grad: &mut [f64]) -> bool {
grad[0] = -2.0 * (1.0 - x[0]) - 400.0 * x[0] * (x[1] - x[0] * x[0]);
grad[1] = 200.0 * (x[1] - x[0] * x[0]);
true
}
fn constraints(&self, _x: &[f64], _new_x: bool, _g: &mut [f64]) -> bool { true }
fn jacobian_structure(&self) -> (Vec<usize>, Vec<usize>) { (vec![], vec![]) }
fn jacobian_values(&self, _x: &[f64], _new_x: bool, _vals: &mut [f64]) -> bool { true }
fn hessian_structure(&self) -> (Vec<usize>, Vec<usize>) { (vec![], vec![]) }
fn hessian_values(&self, _x: &[f64], _new_x: bool, _: f64, _: &[f64], _: &mut [f64]) -> bool {
unreachable!("Hessian should not be called in limited-memory mode");
true
}
}
struct Hs071NoHessian;
impl NlpProblem for Hs071NoHessian {
fn num_variables(&self) -> usize { 4 }
fn num_constraints(&self) -> usize { 2 }
fn bounds(&self, x_l: &mut [f64], x_u: &mut [f64]) {
for i in 0..4 { x_l[i] = 1.0; x_u[i] = 5.0; }
}
fn constraint_bounds(&self, g_l: &mut [f64], g_u: &mut [f64]) {
g_l[0] = 25.0; g_u[0] = f64::INFINITY; g_l[1] = 40.0; g_u[1] = 40.0; }
fn initial_point(&self, x0: &mut [f64]) {
x0[0] = 1.0; x0[1] = 5.0; x0[2] = 5.0; x0[3] = 1.0;
}
fn objective(&self, x: &[f64], _new_x: bool, obj: &mut f64) -> bool {
*obj = x[0] * x[3] * (x[0] + x[1] + x[2]) + x[2];
true
}
fn gradient(&self, x: &[f64], _new_x: bool, grad: &mut [f64]) -> bool {
grad[0] = x[3] * (2.0 * x[0] + x[1] + x[2]);
grad[1] = x[0] * x[3];
grad[2] = x[0] * x[3] + 1.0;
grad[3] = x[0] * (x[0] + x[1] + x[2]);
true
}
fn constraints(&self, x: &[f64], _new_x: bool, g: &mut [f64]) -> bool {
g[0] = x[0] * x[1] * x[2] * x[3];
g[1] = x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3];
true
}
fn jacobian_structure(&self) -> (Vec<usize>, Vec<usize>) {
(vec![0, 0, 0, 0, 1, 1, 1, 1], vec![0, 1, 2, 3, 0, 1, 2, 3])
}
fn jacobian_values(&self, x: &[f64], _new_x: bool, vals: &mut [f64]) -> bool {
vals[0] = x[1] * x[2] * x[3]; vals[1] = x[0] * x[2] * x[3];
vals[2] = x[0] * x[1] * x[3]; vals[3] = x[0] * x[1] * x[2];
vals[4] = 2.0 * x[0]; vals[5] = 2.0 * x[1];
vals[6] = 2.0 * x[2]; vals[7] = 2.0 * x[3];
true
}
fn hessian_structure(&self) -> (Vec<usize>, Vec<usize>) {
let mut rows = Vec::new();
let mut cols = Vec::new();
for i in 0..4 {
for j in 0..=i {
rows.push(i);
cols.push(j);
}
}
(rows, cols)
}
fn hessian_values(&self, _x: &[f64], _new_x: bool, _: f64, _: &[f64], vals: &mut [f64]) -> bool {
for v in vals.iter_mut() { *v = 0.0; }
true
}
}
fn main() {
env_logger::init();
println!("=== Example 1: Rosenbrock (unconstrained, no Hessian) ===");
println!(" min (1-x1)^2 + 100*(x2-x1^2)^2");
println!(" Start: (-1.2, 1.0)");
println!();
let result = ripopt::solve(
&Rosenbrock,
&SolverOptions {
hessian_approximation_lbfgs: true,
..SolverOptions::default()
},
);
println!(" Status: {:?}", result.status);
println!(" Iterations: {}", result.iterations);
println!(" Objective: {:.6e}", result.objective);
println!(" Solution: ({:.6}, {:.6})", result.x[0], result.x[1]);
println!(" Expected: (1.0, 1.0) with f* = 0");
println!();
assert!(
matches!(result.status, SolveStatus::Optimal),
"Rosenbrock should converge"
);
println!("=== Example 2: HS071 (constrained, no Hessian) ===");
println!(" min x1*x4*(x1+x2+x3) + x3");
println!(" s.t. x1*x2*x3*x4 >= 25, x1^2+x2^2+x3^2+x4^2 = 40");
println!(" Start: (1, 5, 5, 1), bounds: [1, 5]^4");
println!();
let result = ripopt::solve(
&Hs071NoHessian,
&SolverOptions {
hessian_approximation_lbfgs: true,
..SolverOptions::default()
},
);
println!(" Status: {:?}", result.status);
println!(" Iterations: {}", result.iterations);
println!(" Objective: {:.6e}", result.objective);
println!(
" Solution: ({:.4}, {:.4}, {:.4}, {:.4})",
result.x[0], result.x[1], result.x[2], result.x[3]
);
println!(" Expected: ~(1.0, 4.743, 3.821, 1.379) with f* ≈ 17.014");
println!();
println!("=== Example 3: Automatic L-BFGS Hessian fallback ===");
println!(" The solver automatically retries with L-BFGS Hessian when the");
println!(" exact-Hessian IPM fails. This is enabled by default.");
println!(" (enable_lbfgs_hessian_fallback = true)");
println!();
let result = ripopt::solve(
&Rosenbrock,
&SolverOptions {
print_level: 0,
..SolverOptions::default()
},
);
println!(
" Default mode: status={:?}, obj={:.6e}",
result.status, result.objective
);
println!(" (Fallback not needed — exact Hessian path works.)");
println!(" To force L-BFGS mode: set hessian_approximation_lbfgs = true");
}