1#![allow(non_upper_case_globals)]
16#![allow(non_camel_case_types)]
17#![allow(non_snake_case)]
18include!(concat!(env!("OUT_DIR"), "/ipopt_cnlp.rs"));
19
20#[cfg(test)]
21mod tests {
22 use super::*;
23 use approx::*;
24 use std::ffi::CString;
25 use std::slice;
26
27 #[derive(Debug)]
29 struct UserData {
30 g_offset: [CNLP_Number; 2],
31 }
32
33 #[test]
35 fn hs071_test() {
36 let approx_eq =
38 |a: f64, b: f64| assert_relative_eq!(a, b, max_relative = 1e-6, epsilon = 1e-14);
39
40 let mut nlp: CNLP_ProblemPtr = ::std::ptr::null_mut();
41 let create_status = unsafe {
42 cnlp_create_problem(
44 &mut nlp as *mut CNLP_ProblemPtr,
45 0,
46 Some(sizes),
47 Some(init),
48 Some(bounds),
49 Some(eval_f),
50 Some(eval_g),
51 Some(eval_grad_f),
52 Some(eval_jac_g),
53 Some(eval_h),
54 None,
55 )
56 };
57
58 assert_eq!(create_status, CNLP_CreateProblemStatus_CNLP_SUCCESS);
59
60 let tol_str = CString::new("tol").unwrap();
62 let mu_strategy_str = CString::new("mu_strategy").unwrap();
63 let adaptive_str = CString::new("adaptive").unwrap();
64 let print_lvl_str = CString::new("print_level").unwrap();
65 let sb_str = CString::new("sb").unwrap();
66 let yes_str = CString::new("yes").unwrap();
67
68 unsafe {
69 cnlp_add_int_option(nlp, print_lvl_str.as_ptr(), 0);
70 cnlp_add_num_option(nlp, tol_str.as_ptr(), 1e-7);
71 cnlp_add_str_option(nlp, mu_strategy_str.as_ptr(), adaptive_str.as_ptr());
72 cnlp_add_str_option(nlp, sb_str.as_ptr(), yes_str.as_ptr());
73 cnlp_set_intermediate_callback(nlp, Some(intermediate_cb));
74 }
75
76 let mut user_data = UserData {
77 g_offset: [0.0, 0.0],
78 };
79 let udata_ptr = (&mut user_data) as *mut UserData;
80
81 let sol = unsafe {
83 cnlp_solve(
84 nlp,
85 udata_ptr as CNLP_UserDataPtr, ) };
89
90 assert_eq!(
91 sol.status,
92 CNLP_ApplicationReturnStatus_CNLP_USER_REQUESTED_STOP
93 );
94
95 let m = 2;
96 let n = 4;
97
98 let mut g = vec![0.0; m];
99
100 let mut x = vec![0.0; n];
102 let mut mult_g = vec![0.0; m];
103 let mut mult_x_L = vec![0.0; n];
104 let mut mult_x_U = vec![0.0; n];
105
106 x.copy_from_slice(unsafe { slice::from_raw_parts(sol.data.x, n) });
107 g.copy_from_slice(unsafe { slice::from_raw_parts(sol.g, m) });
108 mult_g.copy_from_slice(unsafe { slice::from_raw_parts(sol.data.mult_g, m) });
109 mult_x_L.copy_from_slice(unsafe { slice::from_raw_parts(sol.data.mult_x_L, n) });
110 mult_x_U.copy_from_slice(unsafe { slice::from_raw_parts(sol.data.mult_x_U, n) });
111
112 approx_eq(x[0], 1.000000e+00);
113 approx_eq(x[1], 4.743000e+00);
114 approx_eq(x[2], 3.821150e+00);
115 approx_eq(x[3], 1.379408e+00);
116
117 approx_eq(mult_g[0], -5.522936e-01);
118 approx_eq(mult_g[1], 1.614685e-01);
119
120 approx_eq(mult_x_L[0], 1.087872e+00);
121 approx_eq(mult_x_L[1], 4.635819e-09);
122 approx_eq(mult_x_L[2], 9.087447e-09);
123 approx_eq(mult_x_L[3], 8.555955e-09);
124 approx_eq(mult_x_U[0], 4.470027e-09);
125 approx_eq(mult_x_U[1], 4.075231e-07);
126 approx_eq(mult_x_U[2], 1.189791e-08);
127 approx_eq(mult_x_U[3], 6.398749e-09);
128
129 approx_eq(sol.obj_val, 1.701402e+01);
130
131 user_data.g_offset[0] = 0.2;
136
137 let mut warm_start_str = CString::new("warm_start_init_point").unwrap();
138 let mut yes_str = CString::new("yes").unwrap();
139 let mut bound_push_str = CString::new("bound_push").unwrap();
140 let mut bound_frac_str = CString::new("bound_frac").unwrap();
141
142 unsafe {
143 cnlp_add_str_option(
144 nlp,
145 (&mut warm_start_str).as_ptr() as *mut i8,
146 (&mut yes_str).as_ptr() as *mut i8,
147 );
148 cnlp_add_num_option(nlp, (&mut bound_push_str).as_ptr() as *mut i8, 1e-5);
149 cnlp_add_num_option(nlp, (&mut bound_frac_str).as_ptr() as *mut i8, 1e-5);
150 cnlp_set_intermediate_callback(nlp, None);
151 }
152
153 let sol = unsafe { cnlp_solve(nlp, udata_ptr as CNLP_UserDataPtr) };
154
155 x.copy_from_slice(unsafe { slice::from_raw_parts(sol.data.x, n) });
157 g.copy_from_slice(unsafe { slice::from_raw_parts(sol.g, m) });
158 mult_g.copy_from_slice(unsafe { slice::from_raw_parts(sol.data.mult_g, m) });
159 mult_x_L.copy_from_slice(unsafe { slice::from_raw_parts(sol.data.mult_x_L, n) });
160 mult_x_U.copy_from_slice(unsafe { slice::from_raw_parts(sol.data.mult_x_U, n) });
161
162 assert_eq!(
163 sol.status,
164 CNLP_ApplicationReturnStatus_CNLP_SOLVE_SUCCEEDED
165 );
166
167 approx_eq(x[0], 1.000000e+00);
168 approx_eq(x[1], 4.749269e+00);
169 approx_eq(x[2], 3.817510e+00);
170 approx_eq(x[3], 1.367870e+00);
171
172 approx_eq(mult_g[0], -5.517016e-01);
173 approx_eq(mult_g[1], 1.592915e-01);
174
175 approx_eq(mult_x_L[0], 1.090362e+00);
176 approx_eq(mult_x_L[1], 2.664877e-12);
177 approx_eq(mult_x_L[2], 3.556758e-12);
178 approx_eq(mult_x_L[3], 2.718148e-11);
179 approx_eq(mult_x_U[0], 2.498100e-12);
180 approx_eq(mult_x_U[1], 4.013263e-11);
181 approx_eq(mult_x_U[2], 8.455630e-12);
182 approx_eq(mult_x_U[3], 2.755724e-12);
183
184 approx_eq(sol.obj_val, 1.690362e+01);
185
186 unsafe {
188 cnlp_free_problem(nlp);
189 }
190 }
191
192 unsafe extern "C" fn sizes(
194 n: *mut CNLP_Index,
195 m: *mut CNLP_Index,
196 nnz_jac_g: *mut CNLP_Index,
197 nnz_h_lag: *mut CNLP_Index,
198 _user_data: CNLP_UserDataPtr,
199 ) -> CNLP_Bool {
200 *n = 4; *m = 2; *nnz_jac_g = 8; *nnz_h_lag = 10; true as CNLP_Bool
205 }
206
207 unsafe extern "C" fn init(
208 n: CNLP_Index,
209 init_x: CNLP_Bool,
210 x: *mut CNLP_Number,
211 init_z: CNLP_Bool,
212 z_L: *mut CNLP_Number,
213 z_U: *mut CNLP_Number,
214 m: CNLP_Index,
215 init_lambda: CNLP_Bool,
216 lambda: *mut CNLP_Number,
217 _user_data: CNLP_UserDataPtr,
218 ) -> CNLP_Bool {
219 assert_eq!(n, 4);
220 assert_eq!(m, 2);
221 if init_x != 0 {
222 *x.offset(0) = 1.0;
224 *x.offset(1) = 5.0;
225 *x.offset(2) = 5.0;
226 *x.offset(3) = 1.0;
227 }
228 if init_z != 0 {
229 for i in 0..4 {
231 *z_L.offset(i) = 0.0;
232 *z_U.offset(i) = 0.0;
233 }
234 }
235 if init_lambda != 0 {
236 *lambda.offset(0) = 0.0;
237 *lambda.offset(1) = 0.0;
238 }
239 true as CNLP_Bool
240 }
241
242 unsafe extern "C" fn bounds(
243 n: CNLP_Index,
244 x_l: *mut CNLP_Number,
245 x_u: *mut CNLP_Number,
246 m: CNLP_Index,
247 g_l: *mut CNLP_Number,
248 g_u: *mut CNLP_Number,
249 _user_data: CNLP_UserDataPtr,
250 ) -> CNLP_Bool {
251 assert!(n == 4);
252 assert!(m == 2);
253
254 *g_l.offset(0) = 25.0;
256 *g_l.offset(1) = 40.0;
257 *g_u.offset(0) = 2.0e19;
258 *g_u.offset(1) = 40.0;
259
260 for i in 0..n as isize {
262 *x_l.offset(i) = 1.0;
263 *x_u.offset(i) = 5.0;
264 }
265
266 true as CNLP_Bool
267 }
268
269 unsafe extern "C" fn eval_f(
270 n: CNLP_Index,
271 x: *const CNLP_Number,
272 _new_x: CNLP_Bool,
273 obj_value: *mut CNLP_Number,
274 _user_data: CNLP_UserDataPtr,
275 ) -> CNLP_Bool {
276 assert!(n == 4);
277
278 *obj_value = *x.offset(0) * *x.offset(3) * (*x.offset(0) + *x.offset(1) + *x.offset(2))
279 + *x.offset(2);
280
281 true as CNLP_Bool
282 }
283
284 unsafe extern "C" fn eval_grad_f(
285 n: CNLP_Index,
286 x: *const CNLP_Number,
287 _new_x: CNLP_Bool,
288 grad_f: *mut CNLP_Number,
289 _user_data: CNLP_UserDataPtr,
290 ) -> CNLP_Bool {
291 assert!(n == 4);
292
293 *grad_f.offset(0) = *x.offset(0) * *x.offset(3)
294 + *x.offset(3) * (*x.offset(0) + *x.offset(1) + *x.offset(2));
295 *grad_f.offset(1) = *x.offset(0) * *x.offset(3);
296 *grad_f.offset(2) = *x.offset(0) * *x.offset(3) + 1.0;
297 *grad_f.offset(3) = *x.offset(0) * (*x.offset(0) + *x.offset(1) + *x.offset(2));
298
299 true as CNLP_Bool
300 }
301
302 unsafe extern "C" fn eval_g(
303 n: CNLP_Index,
304 x: *const CNLP_Number,
305 _new_x: CNLP_Bool,
306 m: CNLP_Index,
307 g: *mut CNLP_Number,
308 user_data_ptr: CNLP_UserDataPtr,
309 ) -> CNLP_Bool {
310 assert!(n == 4);
313 assert!(m == 2);
314
315 let user_data = &*(user_data_ptr as *mut UserData);
316 *g.offset(0) =
317 *x.offset(0) * *x.offset(1) * *x.offset(2) * *x.offset(3) + user_data.g_offset[0];
318 *g.offset(1) = *x.offset(0) * *x.offset(0)
319 + *x.offset(1) * *x.offset(1)
320 + *x.offset(2) * *x.offset(2)
321 + *x.offset(3) * *x.offset(3)
322 + user_data.g_offset[1];
323
324 true as CNLP_Bool
325 }
326
327 unsafe extern "C" fn eval_jac_g(
328 _n: CNLP_Index,
329 x: *const CNLP_Number,
330 _new_x: CNLP_Bool,
331 _m: CNLP_Index,
332 _nele_jac: CNLP_Index,
333 iRow: *mut CNLP_Index,
334 jCol: *mut CNLP_Index,
335 values: *mut CNLP_Number,
336 _user_data: CNLP_UserDataPtr,
337 ) -> CNLP_Bool {
338 if values.is_null() {
339 *iRow.offset(0) = 0;
343 *jCol.offset(0) = 0;
344 *iRow.offset(1) = 0;
345 *jCol.offset(1) = 1;
346 *iRow.offset(2) = 0;
347 *jCol.offset(2) = 2;
348 *iRow.offset(3) = 0;
349 *jCol.offset(3) = 3;
350 *iRow.offset(4) = 1;
351 *jCol.offset(4) = 0;
352 *iRow.offset(5) = 1;
353 *jCol.offset(5) = 1;
354 *iRow.offset(6) = 1;
355 *jCol.offset(6) = 2;
356 *iRow.offset(7) = 1;
357 *jCol.offset(7) = 3;
358 } else {
359 *values.offset(0) = *x.offset(1) * *x.offset(2) * *x.offset(3); *values.offset(1) = *x.offset(0) * *x.offset(2) * *x.offset(3); *values.offset(2) = *x.offset(0) * *x.offset(1) * *x.offset(3); *values.offset(3) = *x.offset(0) * *x.offset(1) * *x.offset(2); *values.offset(4) = 2.0 * *x.offset(0); *values.offset(5) = 2.0 * *x.offset(1); *values.offset(6) = 2.0 * *x.offset(2); *values.offset(7) = 2.0 * *x.offset(3); }
371
372 true as CNLP_Bool
373 }
374
375 unsafe extern "C" fn eval_h(
376 _n: CNLP_Index,
377 x: *const CNLP_Number,
378 _new_x: CNLP_Bool,
379 obj_factor: CNLP_Number,
380 _m: CNLP_Index,
381 lambda: *const CNLP_Number,
382 _new_lambda: CNLP_Bool,
383 nele_hess: CNLP_Index,
384 iRow: *mut CNLP_Index,
385 jCol: *mut CNLP_Index,
386 values: *mut CNLP_Number,
387 _user_data: CNLP_UserDataPtr,
388 ) -> CNLP_Bool {
389 if values.is_null() {
390 let mut idx = 0; for row in 0..4 {
396 for col in 0..row + 1 {
397 *iRow.offset(idx) = row;
398 *jCol.offset(idx) = col;
399 idx += 1;
400 }
401 }
402
403 assert!(idx == nele_hess as isize);
404 } else {
405 *values.offset(0) = obj_factor * (2.0 * *x.offset(3)); *values.offset(1) = obj_factor * (*x.offset(3)); *values.offset(2) = 0.0; *values.offset(3) = obj_factor * (*x.offset(3)); *values.offset(4) = 0.0; *values.offset(5) = 0.0; *values.offset(6) = obj_factor * (2.0 * *x.offset(0) + *x.offset(1) + *x.offset(2)); *values.offset(7) = obj_factor * (*x.offset(0)); *values.offset(8) = obj_factor * (*x.offset(0)); *values.offset(9) = 0.0; *values.offset(1) += *lambda.offset(0) * (*x.offset(2) * *x.offset(3)); *values.offset(3) += *lambda.offset(0) * (*x.offset(1) * *x.offset(3)); *values.offset(4) += *lambda.offset(0) * (*x.offset(0) * *x.offset(3)); *values.offset(6) += *lambda.offset(0) * (*x.offset(1) * *x.offset(2)); *values.offset(7) += *lambda.offset(0) * (*x.offset(0) * *x.offset(2)); *values.offset(8) += *lambda.offset(0) * (*x.offset(0) * *x.offset(1)); *values.offset(0) += *lambda.offset(1) * 2.0; *values.offset(2) += *lambda.offset(1) * 2.0; *values.offset(5) += *lambda.offset(1) * 2.0; *values.offset(9) += *lambda.offset(1) * 2.0; }
442
443 true as CNLP_Bool
444 }
445
446 extern "C" fn intermediate_cb(
447 _alg_mod: CNLP_AlgorithmMode,
448 _iter_count: CNLP_Index,
449 _obj_value: CNLP_Number,
450 inf_pr: CNLP_Number,
451 _inf_du: CNLP_Number,
452 _mu: CNLP_Number,
453 _d_norm: CNLP_Number,
454 _regularization_size: CNLP_Number,
455 _alpha_du: CNLP_Number,
456 _alpha_pr: CNLP_Number,
457 _ls_trials: CNLP_Index,
458 _user_data: CNLP_UserDataPtr,
459 ) -> CNLP_Bool {
460 (inf_pr >= 1e-4) as CNLP_Bool
461 }
462}