use ripopt::nl::{parse_nl_file, NlProblem, write_sol};
use ripopt::{NlpProblem, SolveResult, SolveStatus, SolverOptions};
fn solve_nl(problem: &NlProblem) -> SolveResult {
let mut opts = SolverOptions::default();
opts.print_level = 0;
ripopt::solve(problem, &opts)
}
#[test]
fn nl_parse_unconstrained_quadratic() {
let nl = "\
g3 1 1 0
2 0 1 0 0
0 1
0 0
2 2 0
0 0 0 0 0
0 0 0 0 0
0 2
0 0
0 0 0 0 0
O0 0
o0
o5
v0
n2
o5
v1
n2
b
3
3
G0 2
0 0
1 0
x2
0 0
1 0
";
let data = parse_nl_file(nl).expect("parse failed");
assert_eq!(data.header.n_vars, 2);
assert_eq!(data.header.n_constrs, 0);
assert!(data.x_l[0].is_infinite() && data.x_l[0] < 0.0);
assert!(data.x_u[0].is_infinite() && data.x_u[0] > 0.0);
assert!(data.x_l[1].is_infinite() && data.x_l[1] < 0.0);
assert!(data.x_u[1].is_infinite() && data.x_u[1] > 0.0);
}
#[test]
fn nl_parse_constrained_equality() {
let nl = "\
g3 1 1 0
2 1 1 0 1
0 1
0 0
0 0 0
0 0 0 0 0
0 0 0 0 0
2 1
0 0
0 0 0 0 0
O0 0
n0
C0
n0
b
3
3
G0 1
0 1
J0 2
0 1
1 1
k1
1
r
4 1
x2
0 0.5
1 0.5
";
let data = parse_nl_file(nl).expect("parse failed");
assert_eq!(data.header.n_vars, 2);
assert_eq!(data.header.n_constrs, 1);
assert_eq!(data.header.n_eqns, 1);
assert_eq!(data.g_l[0], 1.0);
assert_eq!(data.g_u[0], 1.0);
assert!(!data.con_linear[0].is_empty());
}
#[test]
fn nl_parse_all_bound_types() {
let nl = "\
g3 1 1 0
6 0 1 0 0
0 1
0 0
0 0 0
0 0 0 0 0
0 0 0 0 0
0 0
0 0
0 0 0 0 0
O0 0
n0
b
0 -1 1
1 5
2 -3
3
4 7
5
";
let data = parse_nl_file(nl).expect("parse failed");
assert_eq!(data.header.n_vars, 6);
assert_eq!(data.x_l[0], -1.0);
assert_eq!(data.x_u[0], 1.0);
assert!(data.x_l[1].is_infinite() && data.x_l[1] < 0.0);
assert_eq!(data.x_u[1], 5.0);
assert_eq!(data.x_l[2], -3.0);
assert!(data.x_u[2].is_infinite() && data.x_u[2] > 0.0);
assert!(data.x_l[3].is_infinite() && data.x_l[3] < 0.0);
assert!(data.x_u[3].is_infinite() && data.x_u[3] > 0.0);
assert_eq!(data.x_l[4], 7.0);
assert_eq!(data.x_u[4], 7.0);
}
#[test]
fn nl_parse_all_constraint_bound_types() {
let nl = "\
g3 1 1 0
1 6 1 0 1
0 1
0 0
0 0 0
0 0 0 0 0
0 0 0 0 0
6 1
0 0
0 0 0 0 0
O0 0
n0
C0
n0
C1
n0
C2
n0
C3
n0
C4
n0
C5
n0
b
3
J0 1
0 1
J1 1
0 1
J2 1
0 1
J3 1
0 1
J4 1
0 1
J5 1
0 1
k0
r
0 -2 2
1 10
2 -5
3
4 3
5
G0 1
0 0
x1
0 0
";
let data = parse_nl_file(nl).expect("parse failed");
assert_eq!(data.header.n_constrs, 6);
assert_eq!(data.g_l[0], -2.0);
assert_eq!(data.g_u[0], 2.0);
assert!(data.g_l[1].is_infinite() && data.g_l[1] < 0.0);
assert_eq!(data.g_u[1], 10.0);
assert_eq!(data.g_l[2], -5.0);
assert!(data.g_u[2].is_infinite() && data.g_u[2] > 0.0);
assert!(data.g_l[3].is_infinite() && data.g_l[3] < 0.0);
assert!(data.g_u[3].is_infinite() && data.g_u[3] > 0.0);
assert_eq!(data.g_l[4], 3.0);
assert_eq!(data.g_u[4], 3.0);
}
#[test]
fn nl_parse_nonlinear_expression() {
let nl = "\
g3 1 1 0
2 0 1 0 0
0 1
0 0
2 2 0
0 0 0 0 0
0 0 0 0 0
0 2
0 0
0 0 0 0 0
O0 0
o0
o39
o44
v0
o2
o5
v1
n2
v0
b
3
3
G0 2
0 0
1 0
x2
0 1
1 2
";
let data = parse_nl_file(nl).expect("parse failed");
let problem = NlProblem::from_nl_data(data);
let x = vec![1.0, 2.0];
let mut f = 0.0; problem.objective(&x, true, &mut f);
let expected = (1.0_f64).exp().sqrt() + 4.0 * 1.0;
assert!(
(f - expected).abs() < 1e-10,
"f={f}, expected={expected}"
);
}
#[test]
fn nl_parse_nary_sum() {
let nl = "\
g3 1 1 0
3 0 1 0 0
0 1
0 0
3 3 0
0 0 0 0 0
0 0 0 0 0
0 3
0 0
0 0 0 0 0
O0 0
o54
3
v0
v1
v2
b
3
3
3
G0 3
0 0
1 0
2 0
x3
0 1
1 2
2 3
";
let data = parse_nl_file(nl).expect("parse failed");
let problem = NlProblem::from_nl_data(data);
let x = vec![1.0, 2.0, 3.0];
let mut f = 0.0; problem.objective(&x, true, &mut f);
assert!(
(f - 6.0).abs() < 1e-10,
"SUMLIST at (1,2,3) = {f}, expected 6.0"
);
}
#[test]
fn nl_problem_solve_rosenbrock() {
let nl = "\
g3 1 1 0
2 0 1 0 0
0 1
0 0
2 2 0
0 0 0 0 0
0 0 0 0 0
0 2
0 0
0 0 0 0 0
O0 0
o0
o5
o1
n1
v0
n2
o2
n100
o5
o1
v1
o5
v0
n2
n2
b
3
3
G0 2
0 0
1 0
x2
0 -1.2
1 1
";
let data = parse_nl_file(nl).expect("parse failed");
let problem = NlProblem::from_nl_data(data);
let result = solve_nl(&problem);
assert_eq!(
result.status,
SolveStatus::Optimal,
"Rosenbrock should converge to Optimal, got {:?}",
result.status
);
assert!(
(result.x[0] - 1.0).abs() < 1e-4,
"x0={}, expected ~1.0",
result.x[0]
);
assert!(
(result.x[1] - 1.0).abs() < 1e-4,
"x1={}, expected ~1.0",
result.x[1]
);
assert!(
result.objective.abs() < 1e-6,
"obj={}, expected ~0",
result.objective
);
}
#[test]
fn nl_sol_writer() {
let result = SolveResult {
x: vec![1.0, 2.0],
objective: 5.0,
constraint_multipliers: vec![0.5],
bound_multipliers_lower: vec![0.0, 0.0],
bound_multipliers_upper: vec![0.0, 0.0],
constraint_values: vec![3.0],
status: SolveStatus::Optimal,
iterations: 10,
diagnostics: Default::default(),
};
let mut buf: Vec<u8> = Vec::new();
write_sol(&mut buf, &result, 2, 1).expect("write_sol failed");
let output = String::from_utf8(buf).expect("not utf8");
assert!(
output.contains("Optimal Solution Found"),
"SOL should contain status message"
);
assert!(
output.contains("objno 0 0"),
"SOL should have objno line with code 0"
);
assert!(output.contains("Options"), "SOL should contain Options section");
assert!(
output.contains("\n2\n"),
"SOL should contain variable count"
);
assert!(
output.contains("\n1\n"),
"SOL should contain constraint count"
);
}