1use crate::ipopt_nlp::IpoptNlp;
27use crate::sqp::problem::SqpProblemSpec;
28use crate::sqp::qp_assembly::Triplet;
29use pounce_common::Number;
30use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
31use pounce_linalg::expansion_matrix::ExpansionMatrix;
32use pounce_linalg::triplet::{GenTMatrix, SymTMatrix};
33use std::cell::RefCell;
34use std::rc::Rc;
35
36pub struct IpoptNlpAdapter {
37 nlp: Rc<RefCell<dyn IpoptNlp>>,
38 n: usize,
39 m_c: usize,
40 m_d: usize,
41 x_l: Vec<Number>,
42 x_u: Vec<Number>,
43 d_l: Vec<Number>,
44 d_u: Vec<Number>,
45 x_init: Vec<Number>,
46 x_space: Rc<DenseVectorSpace>,
47 c_space: Rc<DenseVectorSpace>,
48 d_space: Rc<DenseVectorSpace>,
49}
50
51impl IpoptNlpAdapter {
52 pub fn new(nlp: Rc<RefCell<dyn IpoptNlp>>) -> Self {
56 let (n, m_c, m_d) = {
57 let b = nlp.borrow();
58 (b.n() as usize, b.m_eq() as usize, b.m_ineq() as usize)
59 };
60 let x_space = DenseVectorSpace::new(n as i32);
61 let c_space = DenseVectorSpace::new(m_c as i32);
62 let d_space = DenseVectorSpace::new(m_d as i32);
63
64 let (x_l, x_u, d_l, d_u, x_init) = {
71 let mut n_borrow = nlp.borrow_mut();
72 let x_l_small = vec_from_dyn(n_borrow.x_l());
73 let x_u_small = vec_from_dyn(n_borrow.x_u());
74 let d_l_small = if m_d > 0 {
75 vec_from_dyn(n_borrow.d_l())
76 } else {
77 Vec::new()
78 };
79 let d_u_small = if m_d > 0 {
80 vec_from_dyn(n_borrow.d_u())
81 } else {
82 Vec::new()
83 };
84 let px_l = n_borrow.px_l();
85 let px_u = n_borrow.px_u();
86 let pd_l = n_borrow.pd_l();
87 let pd_u = n_borrow.pd_u();
88 let x_l = scatter_bound(&*px_l, &x_l_small, n, Number::NEG_INFINITY);
89 let x_u = scatter_bound(&*px_u, &x_u_small, n, Number::INFINITY);
90 let d_l = if m_d > 0 {
91 scatter_bound(&*pd_l, &d_l_small, m_d, Number::NEG_INFINITY)
92 } else {
93 Vec::new()
94 };
95 let d_u = if m_d > 0 {
96 scatter_bound(&*pd_u, &d_u_small, m_d, Number::INFINITY)
97 } else {
98 Vec::new()
99 };
100 let mut x = x_space.make_new_dense();
101 let _ = n_borrow.get_starting_x(&mut x);
102 let x_init = x.expanded_values();
103 (x_l, x_u, d_l, d_u, x_init)
104 };
105
106 Self {
107 nlp,
108 n,
109 m_c,
110 m_d,
111 x_l,
112 x_u,
113 d_l,
114 d_u,
115 x_init,
116 x_space,
117 c_space,
118 d_space,
119 }
120 }
121
122 pub fn new_with_declared_bounds(nlp: Rc<RefCell<dyn IpoptNlp>>) -> Self {
139 let mut me = Self::new(Rc::clone(&nlp));
140 let (n, m_d) = (me.n, me.m_d);
141 let b = nlp.borrow();
142 if let Some((x_l_small, x_u_small)) = b.declared_x_bounds() {
143 me.x_l = scatter_bound(&*b.px_l(), &x_l_small, n, Number::NEG_INFINITY);
144 me.x_u = scatter_bound(&*b.px_u(), &x_u_small, n, Number::INFINITY);
145 }
146 if m_d > 0 {
147 if let Some((d_l_small, d_u_small)) = b.declared_d_bounds() {
148 me.d_l = scatter_bound(&*b.pd_l(), &d_l_small, m_d, Number::NEG_INFINITY);
149 me.d_u = scatter_bound(&*b.pd_u(), &d_u_small, m_d, Number::INFINITY);
150 }
151 }
152 drop(b);
153 me
154 }
155
156 pub fn declared_violation(&self, x: &[Number]) -> (Number, Option<Number>) {
164 let x_dv = self.dv_from_slice(&self.x_space, x);
165 let mut c = self.c_space.make_new_dense();
166 let mut d = self.d_space.make_new_dense();
167 {
168 let mut nlp = self.nlp.borrow_mut();
169 if self.m_c > 0 {
170 nlp.eval_c(&x_dv, &mut c);
171 }
172 if self.m_d > 0 {
173 nlp.eval_d(&x_dv, &mut d);
174 }
175 }
176 let nlp = self.nlp.borrow();
177 let rows = crate::ipopt_cq::unscaled_nlp_constraint_violation_max(&*nlp, &c, &d);
178 (rows, nlp.declared_box_violation(&x_dv))
179 }
180
181 pub fn obj_scale(&self) -> Number {
186 let df = self.nlp.borrow().obj_scaling_factor().abs();
187 if df == 0.0 { 1.0 } else { df }
188 }
189
190 fn dv_from_slice(&self, space: &Rc<DenseVectorSpace>, s: &[Number]) -> DenseVector {
191 let mut dv = space.make_new_dense();
192 dv.set_values(s);
193 dv
194 }
195}
196
197impl SqpProblemSpec for IpoptNlpAdapter {
198 fn n(&self) -> usize {
199 self.n
200 }
201 fn m(&self) -> usize {
202 self.m_c + self.m_d
203 }
204
205 fn x_init(&self) -> Vec<Number> {
206 self.x_init.clone()
207 }
208
209 fn variable_bounds(&self) -> (Vec<Number>, Vec<Number>) {
210 (self.x_l.clone(), self.x_u.clone())
211 }
212
213 fn constraint_bounds(&self) -> (Vec<Number>, Vec<Number>) {
214 let mut bl = vec![0.0; self.m_c];
215 bl.extend_from_slice(&self.d_l);
216 let mut bu = vec![0.0; self.m_c];
217 bu.extend_from_slice(&self.d_u);
218 (bl, bu)
219 }
220
221 fn eval_f(&mut self, x: &[Number]) -> Number {
222 let x_dv = self.dv_from_slice(&self.x_space, x);
223 let mut nlp = self.nlp.borrow_mut();
224 nlp.eval_f(&x_dv)
225 }
226
227 fn eval_grad_f(&mut self, x: &[Number]) -> Vec<Number> {
228 let x_dv = self.dv_from_slice(&self.x_space, x);
229 let mut g = self.x_space.make_new_dense();
230 {
231 let mut nlp = self.nlp.borrow_mut();
232 nlp.eval_grad_f(&x_dv, &mut g);
233 }
234 g.expanded_values()
235 }
236
237 fn eval_c(&mut self, x: &[Number]) -> Vec<Number> {
238 let x_dv = self.dv_from_slice(&self.x_space, x);
239 let mut combined = Vec::with_capacity(self.m_c + self.m_d);
240 if self.m_c > 0 {
241 let mut c_out = self.c_space.make_new_dense();
242 {
243 let mut nlp = self.nlp.borrow_mut();
244 nlp.eval_c(&x_dv, &mut c_out);
245 }
246 combined.extend(c_out.expanded_values());
247 }
248 if self.m_d > 0 {
249 let mut d_out = self.d_space.make_new_dense();
250 {
251 let mut nlp = self.nlp.borrow_mut();
252 nlp.eval_d(&x_dv, &mut d_out);
253 }
254 combined.extend(d_out.expanded_values());
255 }
256 combined
257 }
258
259 fn eval_jac_c(&mut self, x: &[Number]) -> Triplet {
260 let x_dv = self.dv_from_slice(&self.x_space, x);
261 let mut irow = Vec::new();
262 let mut jcol = Vec::new();
263 let mut vals = Vec::new();
264
265 if self.m_c > 0 {
266 let jac_c = {
267 let mut nlp = self.nlp.borrow_mut();
268 nlp.eval_jac_c(&x_dv)
269 };
270 let t = gen_t_downcast(&*jac_c);
271 irow.extend_from_slice(t.irows());
272 jcol.extend_from_slice(t.jcols());
273 vals.extend_from_slice(t.values());
274 }
275
276 if self.m_d > 0 {
277 let jac_d = {
278 let mut nlp = self.nlp.borrow_mut();
279 nlp.eval_jac_d(&x_dv)
280 };
281 let t = gen_t_downcast(&*jac_d);
282 let shift = self.m_c as pounce_common::Index;
283 irow.extend(t.irows().iter().map(|&r| r + shift));
284 jcol.extend_from_slice(t.jcols());
285 vals.extend_from_slice(t.values());
286 }
287
288 Triplet {
289 n_rows: self.m_c + self.m_d,
290 n_cols: self.n,
291 irow,
292 jcol,
293 vals,
294 }
295 }
296
297 fn eval_hess_lag(&mut self, x: &[Number], lambda_g: &[Number]) -> Triplet {
298 let x_dv = self.dv_from_slice(&self.x_space, x);
299 let y_c_dv = self.dv_from_slice(&self.c_space, &lambda_g[..self.m_c]);
300 let y_d_dv = self.dv_from_slice(&self.d_space, &lambda_g[self.m_c..]);
301
302 let h = {
303 let mut nlp = self.nlp.borrow_mut();
304 nlp.eval_h(&x_dv, 1.0, &y_c_dv, &y_d_dv)
305 };
306 let t = sym_t_downcast(&*h);
307 Triplet {
308 n_rows: self.n,
309 n_cols: self.n,
310 irow: t.irows().to_vec(),
311 jcol: t.jcols().to_vec(),
312 vals: t.values().to_vec(),
313 }
314 }
315}
316
317fn vec_from_dyn(v: &dyn pounce_linalg::Vector) -> Vec<Number> {
318 let dv = v
319 .as_any()
320 .downcast_ref::<DenseVector>()
321 .expect("IpoptNlp bound accessors must return DenseVector");
322 dv.expanded_values()
323}
324
325fn scatter_bound(
330 expansion: &dyn pounce_linalg::Matrix,
331 small: &[Number],
332 n_large: usize,
333 fill: Number,
334) -> Vec<Number> {
335 let em = expansion
336 .as_any()
337 .downcast_ref::<ExpansionMatrix>()
338 .expect("px_l / px_u / pd_l / pd_u must be ExpansionMatrix");
339 let exp_pos = em.expanded_pos_indices();
340 debug_assert_eq!(small.len(), exp_pos.len());
341 let mut out = vec![fill; n_large];
342 for (i, &pos) in exp_pos.iter().enumerate() {
343 out[pos as usize] = small[i];
344 }
345 out
346}
347
348fn gen_t_downcast(m: &dyn pounce_linalg::Matrix) -> &GenTMatrix {
349 m.as_any()
350 .downcast_ref::<GenTMatrix>()
351 .expect("IpoptNlp::eval_jac_* must return GenTMatrix")
352}
353
354fn sym_t_downcast(m: &dyn pounce_linalg::matrix::SymMatrix) -> &SymTMatrix {
355 m.as_any()
356 .downcast_ref::<SymTMatrix>()
357 .expect("IpoptNlp::eval_h must return SymTMatrix")
358}
359
360fn bound_maps(nlp: &Rc<RefCell<dyn IpoptNlp>>) -> (Vec<usize>, Vec<usize>) {
370 let b = nlp.borrow();
371 let idx = |m: &dyn pounce_linalg::Matrix| -> Vec<usize> {
372 m.as_any()
373 .downcast_ref::<ExpansionMatrix>()
374 .expect("px_l / px_u must be ExpansionMatrix")
375 .expanded_pos_indices()
376 .iter()
377 .map(|&p| p as usize)
378 .collect()
379 };
380 let lo = idx(&*b.px_l());
381 let up = idx(&*b.px_u());
382 (lo, up)
383}
384
385pub fn pack_bound_multipliers(
392 nlp: &Rc<RefCell<dyn IpoptNlp>>,
393 z_l: &[Number],
394 z_u: &[Number],
395) -> Vec<Number> {
396 let n = nlp.borrow().n() as usize;
397 let (lo_map, up_map) = bound_maps(nlp);
398 let mut out = vec![0.0; n];
399 for (i, &pos) in lo_map.iter().enumerate() {
400 if let Some(&v) = z_l.get(i) {
401 out[pos] += v;
402 }
403 }
404 for (i, &pos) in up_map.iter().enumerate() {
405 if let Some(&v) = z_u.get(i) {
406 out[pos] -= v;
407 }
408 }
409 out
410}
411
412pub fn split_bound_multipliers(
429 nlp: &Rc<RefCell<dyn IpoptNlp>>,
430 lambda_x: &[Number],
431) -> (Vec<Number>, Vec<Number>) {
432 let (lo_map, up_map) = bound_maps(nlp);
433 let z_l = lo_map
434 .iter()
435 .map(|&pos| lambda_x.get(pos).copied().unwrap_or(0.0).max(0.0))
436 .collect();
437 let z_u = up_map
438 .iter()
439 .map(|&pos| (-lambda_x.get(pos).copied().unwrap_or(0.0)).max(0.0))
440 .collect();
441 (z_l, z_u)
442}
443
444pub fn split_slack_multipliers(
454 nlp: &Rc<RefCell<dyn IpoptNlp>>,
455 y_d: &[Number],
456) -> (Vec<Number>, Vec<Number>) {
457 let b = nlp.borrow();
458 let idx = |m: &dyn pounce_linalg::Matrix| -> Vec<usize> {
459 m.as_any()
460 .downcast_ref::<ExpansionMatrix>()
461 .expect("pd_l / pd_u must be ExpansionMatrix")
462 .expanded_pos_indices()
463 .iter()
464 .map(|&p| p as usize)
465 .collect()
466 };
467 let lo_map = idx(&*b.pd_l());
468 let up_map = idx(&*b.pd_u());
469 let v_l = lo_map
470 .iter()
471 .map(|&pos| (-y_d.get(pos).copied().unwrap_or(0.0)).max(0.0))
472 .collect();
473 let v_u = up_map
474 .iter()
475 .map(|&pos| y_d.get(pos).copied().unwrap_or(0.0).max(0.0))
476 .collect();
477 (v_l, v_u)
478}